หมีแพนด้าแปลงดาต้าเฟรมเป็นอาร์เรย์ของทูเปิล


133

ฉันได้จัดการข้อมูลบางอย่างโดยใช้แพนด้าและตอนนี้ฉันต้องการบันทึกแบทช์กลับไปที่ฐานข้อมูล สิ่งนี้ต้องการให้ฉันแปลงดาต้าเฟรมเป็นอาร์เรย์ของทูเปิลโดยแต่ละทูเพิลจะสอดคล้องกับ "แถว" ของดาต้าเฟรม

DataFrame ของฉันมีลักษณะดังนี้:

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 

ฉันต้องการแปลงเป็นอาร์เรย์ของสิ่งต่อไปนี้:

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

ข้อเสนอแนะใด ๆ เกี่ยวกับวิธีที่ฉันสามารถทำได้อย่างมีประสิทธิภาพ?


21
สำหรับผู้ที่จะมาถึงในคำตอบนี้ 2017+ มีวิธีการแก้ปัญหาสำนวนใหม่ดังต่อไปนี้ คุณสามารถใช้list(df.itertuples(index=False, name=None))
Ted Petrou

3
สองสิ่งที่ฉันกำลังมองหาเมื่อฉันมาถึงคำถามนี้: รายการสิ่งที่ดึงดูด - df.to_records(index=False)และรายการของคำสั่ง:df.to_dict('records')
Martin Thoma

@MartinThoma ทั้ง to_records และ to_dict ('records') สกรูประเภทข้อมูลของฉัน ข้อบกพร่องที่เป็นที่รู้จัก แต่ทำให้โซลูชันนี้ไร้ค่า ...
Jochen

คำตอบ:


206

เกี่ยวกับ:

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

สำหรับแพนด้า <0.24 ใช้

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

2
โปรดดูคำตอบของ @ksindi ด้านล่างสำหรับการใช้.itertuplesงานซึ่งจะมีประสิทธิภาพมากกว่าการรับค่าเป็นอาร์เรย์และเปลี่ยนเป็นทูเปิล
vy32

1
ทำความสะอาดเล็กน้อยคือ: tuples = map (tuple, subset.values)
RufusVS

สิ่งนี้สามารถโยนค่าเป็นประเภทอื่นได้ใช่ไหม?
AMC

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

ขณะที่ 17.1 ข้างต้นจะกลับมาเป็นรายชื่อของ namedtuples

หากคุณต้องการรายการสิ่งที่เพิ่มขึ้นธรรมดาให้ส่งผ่านname=Noneเป็นอาร์กิวเมนต์:

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

39
นี่ควรเป็นคำตอบที่ได้รับการยอมรับ IMHO (ขณะนี้คุณลักษณะเฉพาะมีอยู่แล้ว) BTW ถ้าคุณต้องการtuples ปกติในตัวzipวนซ้ำของคุณ(แทนที่จะnamedtupleเป็น s) ให้โทร:data_set.itertuples(index=False, name=None)
Axel


3
@coldspeed บทเรียนที่ฉันได้รับจากคำถามที่เชื่อมโยงคือ itertuples นั้นช้าเพราะการแปลงเป็น tuples มักจะช้ากว่าการดำเนินการแบบ vectorized / cython เนื่องจากคำถามกำลังขอให้แปลงเป็นสิ่งที่เพิ่มขึ้นมีเหตุผลใดที่เราคิดว่าคำตอบที่ยอมรับนั้นเร็วกว่า? การทดสอบอย่างรวดเร็วที่ฉันทำบ่งชี้ว่าเวอร์ชัน itertuples นั้นเร็วกว่า
TC Proctor

2
ฉันโพสต์ผลการทดสอบความเร็วในคำตอบนี้
TC Proctor

1
@johnDanger คล้ายกับแนวคิดของ eval () และ globals () ใน python ทุกคนรู้ว่ามีอยู่จริง ทุกคนรู้ดีว่าคุณไม่ควรใช้ฟังก์ชันเหล่านี้เนื่องจากถือว่าเป็นรูปแบบที่ไม่ดี หลักการที่นี่ก็คล้ายกันมีบางกรณีที่จะใช้ตระกูล iter * ในหมีแพนด้าซึ่งเป็นหนึ่งในนั้น ฉันยังคงใช้วิธีอื่น (เช่น list comp หรือ map) แต่นั่นคือฉัน
cs95


30

แรงจูงใจ
ชุดข้อมูลจำนวนมากมีขนาดใหญ่พอที่เราต้องคำนึงถึงความเร็ว / ประสิทธิภาพ ดังนั้นฉันจึงเสนอวิธีแก้ปัญหานี้ด้วยจิตวิญญาณนั้น มันเกิดขึ้นเพื่อรวบรัด

เพื่อประโยชน์ในการเปรียบเทียบให้วางindexคอลัมน์

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

วิธีแก้ไข
ฉันจะเสนอการใช้zipและmap

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

นอกจากนี้ยังมีความยืดหยุ่นหากเราต้องการจัดการกับคอลัมน์ย่อยที่เฉพาะเจาะจง เราจะถือว่าคอลัมน์ที่เราแสดงแล้วเป็นส่วนย่อยที่เราต้องการ

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

Quicker คืออะไร?

การเปิดออกrecordsจะเร็วที่สุดตามด้วยการบรรจบกันอย่างไม่มีอาการzipmapและiter_tuples

ฉันจะใช้ห้องสมุดsimple_benchmarksที่ได้มาจากโพสต์นี้

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

ตรวจสอบผลลัพธ์

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

ป้อนคำอธิบายภาพที่นี่


12

นี่คือวิธีการแบบเวกเตอร์ (สมมติว่า dataframe data_setจะถูกกำหนดdfแทน) ที่ส่งคืน a listของtuplesดังที่แสดง:

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

ผลิต:

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

แนวคิดในการตั้งค่าคอลัมน์วันที่และเวลาเป็นแกนดัชนีเพื่อช่วยในการแปลงTimestampค่าให้datetime.datetimeเทียบเท่ากับรูปแบบที่สอดคล้องกันโดยใช้convert_datetime64อาร์กิวเมนต์DF.to_recordsที่ทำกับDateTimeIndexดาต้าเฟรม

สิ่งนี้จะส่งคืนrecarrayซึ่งสามารถทำได้เพื่อส่งคืนlistโดยใช้.tolist


โซลูชันทั่วไปเพิ่มเติมขึ้นอยู่กับกรณีการใช้งานคือ:

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

10

วิธีที่ง่ายและมีประสิทธิภาพที่สุด:

list(data_set.to_records())

คุณสามารถกรองคอลัมน์ที่ต้องการก่อนการโทรนี้


2
ฉันคิดว่าควรให้ 'index = False' เป็นอาร์กิวเมนต์ to_records () ดังนั้นรายการ (data_set.to_records (index = False))
user3415167

8

คำตอบนี้ไม่ได้เพิ่มคำตอบใด ๆ ที่ยังไม่ได้กล่าวถึง แต่นี่คือผลลัพธ์ความเร็วบางส่วน ฉันคิดว่าสิ่งนี้น่าจะช่วยแก้คำถามที่เกิดขึ้นในความคิดเห็นได้ ทั้งหมดนี้มีลักษณะเหมือนO (n)ตามค่าทั้งสามนี้

TL; DR : tuples = list(df.itertuples(index=False, name=None))และเสมอtuples = list(zip(*[df[c].values.tolist() for c in df]))กันให้เร็วที่สุด

ฉันทำการทดสอบความเร็วอย่างรวดเร็วสำหรับผลลัพธ์สำหรับคำแนะนำสามข้อที่นี่:

  1. คำตอบ zip จาก @pirsquared: tuples = list(zip(*[df[c].values.tolist() for c in df]))
  2. คำตอบที่ยอมรับจาก @ wes-mckinney: tuples = [tuple(x) for x in df.values]
  3. คำตอบ itertuples จาก @ksindi พร้อมname=Noneคำแนะนำจาก @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)})

ขนาดเล็ก:

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

ให้:

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)

ขนาดใหญ่ที่สุด:

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

ให้:

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)

ความอดทนเท่าที่ฉันมี:

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

ให้:

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 และเวอร์ชัน itertuples อยู่ในช่วงความเชื่อมั่นซึ่งกันและกัน ฉันสงสัยว่าพวกเขากำลังทำสิ่งเดียวกันภายใต้ประทุน

การทดสอบความเร็วเหล่านี้อาจไม่เกี่ยวข้อง ผลักดันขีด จำกัด ของหน่วยความจำของคอมพิวเตอร์ของฉันไม่ได้ใช้เป็นจำนวนมากของเวลาและคุณจริงๆไม่ควรทำเช่นนี้ในชุดข้อมูลขนาดใหญ่ การทำงานกับสิ่งเหล่านั้นหลังจากทำสิ่งนี้จะไม่มีประสิทธิภาพจริงๆ ไม่น่าจะเป็นปัญหาคอขวดที่สำคัญในโค้ดของคุณดังนั้นให้ยึดติดกับเวอร์ชันที่คุณคิดว่าอ่านง่ายที่สุด


ฉันอัปเดตโพสต์เก่าของฉัน ฉันใช้มา[*zip(*map(df.get, df))]ระยะหนึ่งแล้ว อย่างไรก็ตามคิดว่าคุณน่าสนใจ
piRSquared

@piRSquared Oooh. ฉันชอบพล็อตสวย ผมคิดว่ามีลักษณะเหมือนมันว่าO (n)
TC Proctor


2

การเปลี่ยนรายการ data frames เป็นรายการ tuples

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
โปรดอย่าโพสต์โค้ดเพื่อเป็นคำตอบเท่านั้น แต่ให้อธิบายด้วยว่าโค้ดของคุณทำหน้าที่อะไรและจะแก้ปัญหาของคำถามได้อย่างไร คำตอบที่มีคำอธิบายมักจะมีคุณภาพสูงกว่าและมีแนวโน้มที่จะดึงดูดการโหวตได้มากกว่า
Mark Rotteveel

1

วิธี pythonic เพิ่มเติม:

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

วิธี pythonic เพิ่มเติม:ตรงข้ามกันจริง map()เป็นเรื่องที่ไม่น่าเชื่ออย่างฉาวโฉ่
AMC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.