Pandalar, veri çerçevesini demetler dizisine dönüştürür


133

Bazı verileri pandaları kullanarak değiştirdim ve şimdi veritabanına geri toplu kaydetme yapmak istiyorum. Bu, veri çerçevesini veri çerçevesinin bir "satırına" karşılık gelen her bir demet ile bir demet dizisine dönüştürmemi gerektiriyor.

DataFrame'im şuna benzer:

In [182]: data_set
Out[182]: 
  index data_date   data_1  data_2
0  14303 2012-02-17  24.75   25.03 
1  12009 2012-02-16  25.00   25.07 
2  11830 2012-02-15  24.99   25.15 
3  6274  2012-02-14  24.68   25.05 
4  2302  2012-02-13  24.62   24.77 
5  14085 2012-02-10  24.38   24.61 

Bunu aşağıdaki gibi bir diziye dönüştürmek istiyorum:

[(datetime.date(2012,2,17),24.75,25.03),
(datetime.date(2012,2,16),25.00,25.07),
...etc. ]

Bunu nasıl verimli bir şekilde yapabileceğime dair herhangi bir öneriniz var mı?


21
2017 + 'da bu cevaba gelenler için aşağıda yeni bir deyimsel çözüm var . Sadece kullanabilirsinizlist(df.itertuples(index=False, name=None))
Ted Petrou

3
Bu soruya geldiğimde aradığım iki şey: Bir df.to_records(index=False)demet listesi - ve bir söz listesi:df.to_dict('records')
Martin Thoma

@MartinThoma hem to_records hem de to_dict ('kayıtlar') veri türlerimi bozuyor. Bilinen bir hata ama bu çözümleri değersiz kılıyor ...
Jochen

Yanıtlar:


206

Peki ya:

subset = data_set[['data_date', 'data_1', 'data_2']]
tuples = [tuple(x) for x in subset.to_numpy()]

pandalar için <0,24 kullanım

tuples = [tuple(x) for x in subset.values]

2
Lütfen aşağıdaki kullanım için @ ksindi'nin cevabına bakın .itertuples, bu, değerleri bir dizi olarak alıp onları bir demete dönüştürmekten daha verimli olacaktır.
vy32

1
biraz daha temiz: tuples = map (tuple, subset.values)
RufusVS

Bu, değerleri farklı bir türe dönüştürebilir, değil mi?
AMC

163
list(data_set.itertuples(index=False))

17.1'den itibaren , yukarıdaki isim isimlendirilmiş çiftlerin bir listesini döndürecektir .

Sıradan demetlerden oluşan bir liste istiyorsanız, name=Noneargüman olarak iletin :

list(data_set.itertuples(index=False, name=None))

39
Bu, kabul edilen cevap IMHO olmalıdır (artık özel bir özellik var). BTW, yineleyicinizde normal tuplee- zippostalar istiyorsanız ( namedtuples yerine ) şunu arayın:data_set.itertuples(index=False, name=None)
Axel


3
@coldspeed Bağlantılı sorudan aldığım ders, yinelemelerin yavaş olduğudur çünkü tuplelere dönüştürme genellikle vektörize / cython işlemlerinden daha yavaştır. Sorunun demetlere dönüştürülmesini istediği göz önüne alındığında, kabul edilen cevabın daha hızlı olduğunu düşünmemiz için herhangi bir sebep var mı? Yaptığım hızlı test, itertuples sürümünün daha hızlı olduğunu gösteriyor.
TC Proctor

2
Hız testi sonuçlarımı bu cevapta
TC Proctor

1
@johnDanger python'daki eval () ve globals () kavramlarına benzer. Var olduklarını herkes bilir. Ayrıca herkes, bu işlevleri genellikle kullanmamanız gerektiğini bilir çünkü bu, kötü biçim olarak kabul edilir. Buradaki ilke benzerdir, pandalarda iter * ailesini kullanmak için çok az durum vardır, bu muhtemelen onlardan biridir. Yine de farklı bir yöntem kullanırım (bir liste kompozisyonu veya harita gibi) ama bu benim.
cs95


30

Motivasyon
Birçok veri seti, hız / verimlilikle ilgilenmemiz için yeterince büyüktür. Bu yüzden bu çözümü o ruhla sunuyorum. Aynı zamanda özlü de olur.

Karşılaştırma adına indexsütunu bırakalım

df = data_set.drop('index', 1)

Çözüm
ı kullanımını önermek edeceğiz zipvemap

list(zip(*map(df.get, df)))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Belirli bir sütun alt kümesiyle uğraşmak istersek de esnek olur. Önceden görüntülediğimiz sütunların istediğimiz alt küme olduğunu varsayacağız.

list(zip(*map(df.get, ['data_date', 'data_1', 'data_2'])))

[('2012-02-17', 24.75, 25.03),
 ('2012-02-16', 25.0, 25.07),
 ('2012-02-15', 24.99, 25.15),
 ('2012-02-14', 24.68, 25.05),
 ('2012-02-13', 24.62, 24.77),
 ('2012-02-10', 24.38, 24.61)]

Daha Hızlı nedir?

En recordshızlı dönüşün ardından asimptotik olarak yakınsama zipmapveiter_tuples

Bir kitaplık kullanacağız simple_benchmarksben aldığım o bu yazı

from simple_benchmark import BenchmarkBuilder
b = BenchmarkBuilder()

import pandas as pd
import numpy as np

def tuple_comp(df): return [tuple(x) for x in df.to_numpy()]
def iter_namedtuples(df): return list(df.itertuples(index=False))
def iter_tuples(df): return list(df.itertuples(index=False, name=None))
def records(df): return df.to_records(index=False).tolist()
def zipmap(df): return list(zip(*map(df.get, df)))

funcs = [tuple_comp, iter_namedtuples, iter_tuples, records, zipmap]
for func in funcs:
    b.add_function()(func)

def creator(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

@b.add_arguments('Rows in DataFrame')
def argument_provider():
    for n in (10 ** (np.arange(4, 11) / 2)).astype(int):
        yield n, creator(n)

r = b.run()

Sonuçları kontrol edin

r.to_pandas_dataframe().pipe(lambda d: d.div(d.min(1), 0))

        tuple_comp  iter_namedtuples  iter_tuples   records    zipmap
100       2.905662          6.626308     3.450741  1.469471  1.000000
316       4.612692          4.814433     2.375874  1.096352  1.000000
1000      6.513121          4.106426     1.958293  1.000000  1.316303
3162      8.446138          4.082161     1.808339  1.000000  1.533605
10000     8.424483          3.621461     1.651831  1.000000  1.558592
31622     7.813803          3.386592     1.586483  1.000000  1.515478
100000    7.050572          3.162426     1.499977  1.000000  1.480131

r.plot()

görüntü açıklamasını buraya girin


12

Aşağıda gösterildiği gibi a değerini döndüren vektörleştirilmiş bir yaklaşım ( bunun yerine data_settanımlanacak veri çerçevesi varsayılarak df) :listtuples

>>> df.set_index(['data_date'])[['data_1', 'data_2']].to_records().tolist()

üretir:

[(datetime.datetime(2012, 2, 17, 0, 0), 24.75, 25.03),
 (datetime.datetime(2012, 2, 16, 0, 0), 25.0, 25.07),
 (datetime.datetime(2012, 2, 15, 0, 0), 24.99, 25.15),
 (datetime.datetime(2012, 2, 14, 0, 0), 24.68, 25.05),
 (datetime.datetime(2012, 2, 13, 0, 0), 24.62, 24.77),
 (datetime.datetime(2012, 2, 10, 0, 0), 24.38, 24.61)]

İndeks ekseni olarak tarih saat ayarı sütunu fikri dönüştürülmesinde yardım etmektir Timestamp, bu şekilde ilgili oluyor değerin datetime.datetimeyararlanarak biçim eşdeğer convert_datetime64içinde argüman DF.to_recordsbir şekilde yapar DateTimeIndexdataframe.

Bu recarray, daha sonra bir listkullanarak döndürmek için yapılabilecek bir.tolist


Kullanım durumuna bağlı olarak daha genel bir çözüm şöyle olacaktır:

df.to_records().tolist()                              # Supply index=False to exclude index

10

En verimli ve kolay yol:

list(data_set.to_records())

Bu görüşmeden önce ihtiyacınız olan sütunları filtreleyebilirsiniz.


2
'İndex = False' to_records () 'a argüman olarak verilmelidir. Bu nedenle, liste (data_set.to_records (dizin = Yanlış))
user3415167

8

Bu cevap, daha önce tartışılmayan herhangi bir cevabı eklemez, ancak işte bazı hız sonuçları. Bunun yorumlarda ortaya çıkan soruları çözmesi gerektiğini düşünüyorum. Bunların tümü , bu üç değere göre O (n) gibi görünür .

TL; DR : tuples = list(df.itertuples(index=False, name=None))ve tuples = list(zip(*[df[c].values.tolist() for c in df]))en hızlı şekilde berabere.

Burada üç öneri için sonuçlar üzerinde hızlı bir hız testi yaptım:

  1. @Pirsquared'den zip cevabı: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. @ Wes-mckinney'den kabul edilen cevap: tuples = [tuple(x) for x in df.values]
  3. @ Axel'in name=Noneönerisiyle @ksindi'den gelen yinelemeli yanıt:tuples = list(df.itertuples(index=False, name=None))
from numpy import random
import pandas as pd


def create_random_df(n):
    return pd.DataFrame({"A": random.randint(n, size=n), "B": random.randint(n, size=n)})

Küçük boyutlu:

df = create_random_df(10000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

verir:

1.66 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
15.5 ms ± 1.52 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
1.74 ms ± 75.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Büyük:

df = create_random_df(1000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

verir:

202 ms ± 5.91 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
1.52 s ± 98.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
209 ms ± 11.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Sahip olduğum kadar sabır:

df = create_random_df(10000000)
%timeit tuples = list(zip(*[df[c].values.tolist() for c in df]))
%timeit tuples = [tuple(x) for x in df.values]
%timeit tuples = list(df.itertuples(index=False, name=None))

verir:

1.78 s ± 118 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
15.4 s ± 222 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
1.68 s ± 96.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Zip versiyonu ve itertuples versiyonu birbirinin güven aralığı içindedir. Kaputun altında aynı şeyi yaptıklarından şüpheleniyorum.

Bu hız testleri muhtemelen alakasızdır. Bilgisayarımın belleğinin sınırlarını zorlamak çok fazla zaman almaz ve bunu gerçekten büyük bir veri kümesinde yapmamalısınız. Bunu yaptıktan sonra bu tuple'larla çalışmak gerçekten verimsiz hale gelecektir. Kodunuzda büyük bir darboğaz olması olası değildir, bu nedenle en okunabilir olduğunu düşündüğünüz sürüme bağlı kalmanız yeterlidir.


Eski gönderimi güncelledim. Bir süredir kullanıyordum [*zip(*map(df.get, df))]. Her neyse, ilginç bulacağını düşündüm.
piRSquared

@piRSquared Oooh. Güzel komployu beğendim. Sanırım bu O (n) gibi görünüyor .
TC Proctor

2
#try this one:

tuples = list(zip(data_set["data_date"], data_set["data_1"],data_set["data_2"]))
print (tuples)

2

Veri çerçeveleri listesini bir demet listesi olarak değiştirme.

df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6]})
print(df)
OUTPUT
   col1  col2
0     1     4
1     2     5
2     3     6

records = df.to_records(index=False)
result = list(records)
print(result)
OUTPUT
[(1, 4), (2, 5), (3, 6)]

1
Lütfen yanıt olarak yalnızca kod göndermeyin, aynı zamanda kodunuzun ne yaptığını ve sorunun sorununu nasıl çözdüğünü de açıklayın. Açıklamalı yanıtlar genellikle daha kalitelidir ve olumlu oylar alma olasılığı daha yüksektir.
Mark Rotteveel

1

Daha pitonik yol:

df = data_set[['data_date', 'data_1', 'data_2']]
map(tuple,df.values)

Daha pitonik yol: Aslında tam tersi. map()herkesin bildiği gibi pirtonik değildir.
AMC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.