Panda mengkonversi dataframe ke array tuple

131

Saya telah memanipulasi beberapa data menggunakan panda dan sekarang saya ingin melakukan batch save kembali ke database. Ini mengharuskan saya untuk mengubah kerangka data menjadi array tupel, dengan masing-masing tupel sesuai dengan "baris" dari kerangka data.

DataFrame saya terlihat seperti:

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 

Saya ingin mengubahnya menjadi array tupel seperti:

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

Adakah saran tentang bagaimana saya dapat melakukan ini secara efisien?

enrishi
sumber
21
Bagi mereka yang datang ke jawaban ini di 2017+, ada solusi idiomatik baru di bawah ini . Anda bisa menggunakanlist(df.itertuples(index=False, name=None))
Ted Petrou
3
Dua hal yang saya cari ketika saya sampai pada pertanyaan ini: Daftar tupel - df.to_records(index=False)dan daftar dikte:df.to_dict('records')
Martin Thoma
@ MartinThoma baik to_records dan to_dict ('records') mengacaukan tipe data saya. Bug yang dikenal tetapi membuat solusi ini tidak berharga ...
Jochen

Jawaban:

206

Bagaimana tentang:

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

untuk panda <0,24 digunakan

tuples = [tuple(x) for x in subset.values]
Wes McKinney
sumber
2
Silakan lihat jawaban @ ksindi di bawah ini untuk menggunakan .itertuples, yang akan lebih efisien daripada mendapatkan nilai-nilai sebagai array dan mengubahnya menjadi tuple.
vy32
1
sedikit lebih bersih adalah: tuples = map (tuple, subset.values)
RufusVS
Ini dapat memberikan nilai ke tipe yang berbeda, kan?
AMC
160
list(data_set.itertuples(index=False))

Pada 17.1, yang di atas akan mengembalikan daftar namedTuple .

Jika Anda ingin daftar tupel biasa, sampaikan name=Nonesebagai argumen:

list(data_set.itertuples(index=False, name=None))
Kamil Sindi
sumber
39
Ini seharusnya jawaban IMHO yang diterima (sekarang ada fitur khusus). BTW, jika Anda ingin yang normal tupledi zipiterator Anda (bukan namedtuples), maka hubungi:data_set.itertuples(index=False, name=None)
Axel
3
@oldspeed Pelajaran yang saya dapatkan dari pertanyaan terkait adalah bahwa itertuple lambat karena mengkonversi ke tuple biasanya lebih lambat daripada operasi vektor / cython. Mengingat bahwa pertanyaannya adalah meminta konversi ke tupel, apakah ada alasan bahwa kita akan berpikir bahwa jawaban yang diterima lebih cepat? Tes cepat yang saya lakukan menunjukkan bahwa versi itertuples lebih cepat.
TC Proctor
2
Saya memposting hasil tes kecepatan saya dalam jawaban ini
TC Proctor
1
@ JohnDanger mirip dengan konsep eval () dan global () dalam python. Semua orang tahu mereka ada. Semua orang juga tahu Anda biasanya tidak boleh menggunakan fungsi-fungsi ini karena itu dianggap bentuk yang buruk. Prinsipnya di sini mirip, ada sangat sedikit kasus untuk menggunakan keluarga iter * di panda, ini bisa dibilang salah satunya. Saya masih menggunakan metode yang berbeda (seperti daftar comp atau peta) tapi itu saya.
cs95
45

Cara umum:

[tuple(x) for x in data_set.to_records(index=False)]
Ramón J Romero y Vigil
sumber
1
Bukankah data_set.to_records(index=False).tolist()lebih baik?
Amir A. Shabani
30

Motivasi
Banyak set data yang cukup besar yang perlu kita perhatikan dengan kecepatan / efisiensi. Jadi saya menawarkan solusi ini dalam semangat itu. Itu kebetulan juga ringkas.

Demi perbandingan, mari letakkan indexkolom

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

Solusi
Saya akan mengusulkan penggunaan zipdanmap

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)]

Kebetulan juga fleksibel jika kita ingin berurusan dengan subset kolom tertentu. Kami akan menganggap kolom yang sudah kami tampilkan adalah subset yang kami inginkan.

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)]

Apa yang lebih cepat?

Turn keluar recordspaling cepat diikuti oleh konvergen asimtotik zipmapdaniter_tuples

Saya akan menggunakan perpustakaan simple_benchmarksyang saya dapatkan dari posting ini

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()

Periksa hasilnya

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()

masukkan deskripsi gambar di sini

piRquared
sumber
12

Berikut ini pendekatan Vectorized (dengan asumsi dataframe, data_setuntuk didefinisikan sebagai dfgantinya) bahwa return suatu listdari tuplesseperti yang ditunjukkan:

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

menghasilkan:

[(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)]

Gagasan menetapkan kolom datetime sebagai sumbu indeks adalah untuk membantu dalam konversi Timestampnilai ke datetime.datetimeformat yang sesuai dengan memanfaatkan convert_datetime64argumen DF.to_recordsyang digunakan untuk DateTimeIndexkerangka data.

Ini mengembalikan recarrayyang bisa kemudian dibuat untuk kembali listmenggunakan.tolist


Solusi yang lebih umum tergantung pada use case adalah:

df.to_records().tolist()                              # Supply index=False to exclude index
Nickil Maveli
sumber
10

Cara paling efisien dan mudah:

list(data_set.to_records())

Anda dapat memfilter kolom yang Anda butuhkan sebelum panggilan ini.

Gustavo Gonçalves
sumber
1
Saya pikir 'index = False' harus diberikan sebagai argumen untuk to_records (). Dengan demikian, daftar (data_set.to_records (index = False))
user3415167
8

Jawaban ini tidak menambahkan jawaban apa pun yang belum dibahas, tetapi berikut adalah beberapa hasil kecepatan. Saya pikir ini harus menyelesaikan pertanyaan yang muncul di komentar. Semua ini terlihat seperti O (n) , berdasarkan pada tiga nilai ini.

TL; DR : tuples = list(df.itertuples(index=False, name=None))dan tuples = list(zip(*[df[c].values.tolist() for c in df]))terikat untuk yang tercepat.

Saya melakukan tes kecepatan cepat pada hasil untuk tiga saran di sini:

  1. Jawaban zip dari @pirsquared: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. Jawaban yang diterima dari @ wes-mckinney: tuples = [tuple(x) for x in df.values]
  3. Jawaban itertuples dari @ksindi dengan name=Nonesaran dari @Axel: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)})

Ukuran kecil:

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))

Memberi:

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)

Lebih besar:

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))

Memberi:

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)

Sebanyak kesabaran yang saya miliki:

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))

Memberi:

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)

Versi zip dan versi itertuples berada dalam interval kepercayaan satu sama lain. Saya curiga mereka melakukan hal yang sama di bawah tenda.

Tes kecepatan ini mungkin tidak relevan. Mendorong batas memori komputer saya tidak membutuhkan banyak waktu, dan Anda benar - benar tidak boleh melakukan ini pada kumpulan data besar. Bekerja dengan tupel setelah melakukan ini akan menjadi sangat tidak efisien. Ini tidak mungkin menjadi hambatan utama dalam kode Anda, jadi tetaplah dengan versi yang Anda pikir paling mudah dibaca.

TC Proctor
sumber
Saya memperbarui posting basi saya. Saya telah menggunakan [*zip(*map(df.get, df))]untuk beberapa saat sekarang. Ngomong-ngomong, kupikir kau akan menganggapnya menarik.
piRSquared
@piRquared Oooh. Saya suka plot yang cantik. Saya kira sepertinya itu O (n) .
TC Proctor
2
#try this one:

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

Mengubah daftar bingkai data menjadi daftar tupel.

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)]
Gowtham Balusamy
sumber
1
Tolong jangan memposting kode hanya sebagai jawaban, tetapi juga memberikan penjelasan apa yang kode Anda lakukan dan bagaimana memecahkan masalah pertanyaan. Jawaban dengan penjelasan biasanya berkualitas lebih tinggi, dan lebih cenderung menarik upvotes.
Mark Rotteveel
1

Lebih banyak cara pythonic:

df = data_set[['data_date', 'data_1', 'data_2']]
map(tuple,df.values)
Ankur Panwar
sumber
Cara yang lebih pythonic: Sebaliknya, sebenarnya. map()terkenal unpythonic.
AMC