วิธีการแบ่งคอลัมน์ของทูเปิลในดาต้าเฟรมแพนด้า


91

ฉันมีดาต้าเฟรมแพนด้า (นี่เป็นเพียงชิ้นส่วนเล็ก ๆ น้อย ๆ )

>>> d1
   y norm test  y norm train  len(y_train)  len(y_test)  \
0    64.904368    116.151232          1645          549   
1    70.852681    112.639876          1645          549   

                                    SVR RBF  \
0   (35.652207342877873, 22.95533537448393)   
1  (39.563683797747622, 27.382483096332511)   

                                        LCV  \
0  (19.365430594452338, 13.880062435173587)   
1  (19.099614489458364, 14.018867136617146)   

                                   RIDGE CV  \
0  (4.2907610988480362, 12.416745648065584)   
1    (4.18864306788194, 12.980833914392477)   

                                         RF  \
0   (9.9484841581029428, 16.46902345373697)   
1  (10.139848213735391, 16.282141345406522)   

                                           GB  \
0  (0.012816232716538605, 15.950164822266007)   
1  (0.012814519804493328, 15.305745202851712)   

                                             ET DATA  
0  (0.00034337162272515505, 16.284800366214057)  j2m  
1  (0.00024811554516431878, 15.556506191784194)  j2m  
>>> 

ฉันต้องการแยกคอลัมน์ทั้งหมดที่มีทูเปิล ตัวอย่างเช่นฉันต้องการแทนที่คอลัมน์LCVด้วยคอลัมน์LCV-aและLCV-b.

ฉันจะทำเช่นนั้นได้อย่างไร?

คำตอบ:


167

คุณสามารถทำได้โดยทำpd.DataFrame(col.tolist())ในคอลัมน์นั้น:

In [2]: df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]})                                                                                                                      

In [3]: df                                                                                                                                                                      
Out[3]: 
   a       b
0  1  (1, 2)
1  2  (3, 4)

In [4]: df['b'].tolist()                                                                                                                                                        
Out[4]: [(1, 2), (3, 4)]

In [5]: pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                                          
Out[5]: 
   0  1
0  1  2
1  3  4

In [6]: df[['b1', 'b2']] = pd.DataFrame(df['b'].tolist(), index=df.index)                                                                                                                       

In [7]: df                                                                                                                                                                      
Out[7]: 
   a       b  b1  b2
0  1  (1, 2)   1   2
1  2  (3, 4)   3   4

หมายเหตุ: ในรุ่นก่อนหน้านี้คำตอบนี้ขอแนะนำให้ใช้แทนdf['b'].apply(pd.Series) pd.DataFrame(df['b'].tolist(), index=df.index)ที่ใช้งานได้เช่นกัน (เพราะมันทำให้แต่ละทูเปิลเป็นซีรี่ส์ซึ่งจะเห็นเป็นแถวของดาต้าเฟรม) แต่ช้ากว่า / ใช้หน่วยความจำมากกว่าtolistเวอร์ชันดังที่ระบุไว้ในคำตอบอื่น ๆ ที่นี่ (ขอบคุณ @denfromufa) .
ฉันอัปเดตคำตอบนี้เพื่อให้แน่ใจว่าคำตอบที่มองเห็นได้ชัดเจนที่สุดมีทางออกที่ดีที่สุด


2
มีวิธีทำให้อัตโนมัติเนื่องจากคอลัมน์จำนวนมากหรือไม่
Donbeo

ไม่โดยตรงฉันคิดว่า แต่คุณสามารถเขียนฟังก์ชันได้อย่างง่ายดายโดยใช้โค้ดด้านบน (+ ลบอันเดิม)
joris

หากคุณมีคอลัมน์จำนวนมากคุณอาจต้องการพิจารณาเพื่อ "จัดระเบียบ" ข้อมูลของคุณ: vita.had.co.nz/papers/tidy-data.htmlคุณสามารถทำได้โดยใช้ฟังก์ชันละลาย
Axel

.apply (pd.Series) ใช้งานได้ดี แต่สำหรับชุดข้อมูลขนาดใหญ่ใช้หน่วยความจำมากและอาจทำให้เกิดข้อผิดพลาดของหน่วยความจำได้
Yury Wallet

27

ในชุดข้อมูลที่ใหญ่กว่ามากฉันพบว่า .apply()มีคำสั่งซื้อที่ช้ากว่าpd.DataFrame(df['b'].values.tolist(), index=df.index)

ปัญหาประสิทธิภาพนี้ถูกปิดใน GitHub แม้ว่าฉันจะไม่เห็นด้วยกับการตัดสินใจนี้:

https://github.com/pandas-dev/pandas/issues/11615

แก้ไข: ตามคำตอบนี้: https://stackoverflow.com/a/44196843/2230844


5
pd.DataFrame(df['b'].tolist())โดยที่.valuesดูเหมือนว่าจะทำงานได้ดีเช่นกัน (และขอบคุณโซลูชันของคุณเร็วกว่ามาก.apply() )
Swier

ฉันกังวลเกี่ยวกับการจับดัชนีดังนั้นการใช้. value อย่างชัดเจน
denfromufa

1
วิธีแก้ปัญหาโดย @denfromufa ทำงานได้เร็วมาก df [['b1', 'b2']] = pd.DataFrame (df ['b']. values.tolist (), index = df.index) และทำให้ไม่มี Memory Error (เป็น เทียบกับ .apply (pd.Series))
Yury Wallet

22

ตัวเข้าถึงstrที่พร้อมใช้งานสำหรับpandas.Seriesอ็อบเจ็กต์dtype == objectนั้นสามารถทำซ้ำได้

สมมติว่าpandas.DataFrame df:

df = pd.DataFrame(dict(col=[*zip('abcdefghij', range(10, 101, 10))]))

df

        col
0   (a, 10)
1   (b, 20)
2   (c, 30)
3   (d, 40)
4   (e, 50)
5   (f, 60)
6   (g, 70)
7   (h, 80)
8   (i, 90)
9  (j, 100)

เราสามารถทดสอบได้ว่าสามารถทำซ้ำได้หรือไม่

from collections import Iterable

isinstance(df.col.str, Iterable)

True

จากนั้นเราสามารถกำหนดจากมันเหมือนกับที่เราทำซ้ำอื่น ๆ :

var0, var1 = 'xy'
print(var0, var1)

x y

วิธีแก้ปัญหาที่ง่ายที่สุด

ดังนั้นในหนึ่งบรรทัดเราสามารถกำหนดทั้งสองคอลัมน์ได้

df['a'], df['b'] = df.col.str

df

        col  a    b
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

โซลูชันที่เร็วขึ้น

ซับซ้อนกว่าเล็กน้อยเท่านั้นเราสามารถใช้zipเพื่อสร้างสิ่งที่ทำซ้ำได้

df['c'], df['d'] = zip(*df.col)

df

        col  a    b  c    d
0   (a, 10)  a   10  a   10
1   (b, 20)  b   20  b   20
2   (c, 30)  c   30  c   30
3   (d, 40)  d   40  d   40
4   (e, 50)  e   50  e   50
5   (f, 60)  f   60  f   60
6   (g, 70)  g   70  g   70
7   (h, 80)  h   80  h   80
8   (i, 90)  i   90  i   90
9  (j, 100)  j  100  j  100

อินไลน์

ความหมายอย่ากลายพันธุ์ที่มีอยู่df
สิ่งนี้ใช้ได้ผลเพราะassignรับอาร์กิวเมนต์คำหลักโดยที่คำหลักเป็นชื่อคอลัมน์ใหม่ (หรือที่มีอยู่) และค่าจะเป็นค่าของคอลัมน์ใหม่ คุณสามารถใช้พจนานุกรมและคลายไฟล์**และให้มันทำหน้าที่เป็นอาร์กิวเมนต์ของคีย์เวิร์ด ดังนั้นนี่จึงเป็นวิธีที่ชาญฉลาดในการกำหนดคอลัมน์ใหม่ที่ชื่อ'g'ซึ่งเป็นรายการแรกในการทำdf.col.strซ้ำและ'h'เป็นรายการที่สองในการทำdf.col.strซ้ำ

df.assign(**dict(zip('gh', df.col.str)))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

listแนวทางของฉัน

ด้วยความเข้าใจในรายการที่ทันสมัยและการเปิดตัวตัวแปร
หมายเหตุ:อินไลน์โดยใช้ไฟล์join

df.join(pd.DataFrame([*df.col], df.index, [*'ef']))

        col  g    h
0   (a, 10)  a   10
1   (b, 20)  b   20
2   (c, 30)  c   30
3   (d, 40)  d   40
4   (e, 50)  e   50
5   (f, 60)  f   60
6   (g, 70)  g   70
7   (h, 80)  h   80
8   (i, 90)  i   90
9  (j, 100)  j  100

เวอร์ชันที่กลายพันธุ์จะเป็น

df[['e', 'f']] = pd.DataFrame([*df.col], df.index)

การทดสอบเวลาไร้เดียงสา

DataFrame แบบสั้น

ใช้หนึ่งที่กำหนดไว้ข้างต้น

%timeit df.assign(**dict(zip('gh', df.col.str)))
%timeit df.assign(**dict(zip('gh', zip(*df.col))))
%timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh']))

1.16 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
635 µs ± 18.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
795 µs ± 42.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
DataFrame แบบยาว

ใหญ่กว่า 10 ^ 3 เท่า

df = pd.concat([df] * 1000, ignore_index=True)

%timeit df.assign(**dict(zip('gh', df.col.str)))
%timeit df.assign(**dict(zip('gh', zip(*df.col))))
%timeit df.join(pd.DataFrame([*df.col], df.index, [*'gh']))

11.4 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.1 ms ± 41.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
2.33 ms ± 35.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

2
พิจารณาเพิ่ม TL; DR: df['a'], df['b'] = df.col.str:)
mirekphd

11

ฉันคิดว่าวิธีที่ง่ายกว่าคือ:

>>> import pandas as pd
>>> df = pd.DataFrame({'a':[1,2], 'b':[(1,2), (3,4)]}) 
>>> df
   a       b
0  1  (1, 2)
1  2  (3, 4)
>>> df['b_a']=df['b'].str[0]
>>> df['b_b']=df['b'].str[1]
>>> df
   a       b  b_a  b_b
0  1  (1, 2)    1    2
1  2  (3, 4)    3    4

1
โซลูชันนี้ง่ายกว่ามากแน่นอน
ApplePie

@jinhuawang ดูเหมือนว่านี่เป็นการแฮ็กด้านบนของการstrเป็นตัวแทนของpd.Seriesวัตถุ คุณช่วยอธิบายได้ไหมว่ามันทำงานอย่างไร!
denfromufa

ฉันคิดว่ามันเป็นเพียงวิธีการทำงานของวัตถุ str? คุณสามารถเข้าถึงวัตถุอาร์เรย์ด้วย str
Jinhua Wang

จะเกิดอะไรขึ้นถ้าบางแถวมีทูเปิลที่มีจำนวนค่าต่างกัน?
mammykins

ฉันคิดว่านี่ควรจะเป็นที่ยอมรับ มันเป็น 'แพนด้า - ออนิก' มากกว่า ... ถ้าเป็นอย่างนั้น
Natacha

8

ฉันรู้ว่านี่มาจากที่แล้ว แต่ข้อแม้ของวิธีแก้ปัญหาที่สอง:

pd.DataFrame(df['b'].values.tolist())

คือจะทิ้งดัชนีอย่างชัดเจนและเพิ่มในดัชนีลำดับเริ่มต้นในขณะที่คำตอบที่ยอมรับ

apply(pd.Series)

จะไม่เนื่องจากผลของการใช้จะคงดัชนีแถวไว้ แม้ว่าลำดับแรกจะถูกเก็บไว้จากอาร์เรย์ดั้งเดิม แต่แพนด้าจะพยายามจับคู่ตัวบ่งชี้จากดาต้าเฟรมทั้งสอง

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

โซลูชันไฮบริดที่ดีกว่าคือการตั้งค่าดัชนีของดาต้าเฟรมเดิมไปยังใหม่เช่น

pd.DataFrame(df['b'].values.tolist(), index=df.index)

ซึ่งจะรักษาความเร็วของการใช้วิธีที่สองในขณะที่มั่นใจว่าคำสั่งซื้อและการจัดทำดัชนีจะยังคงอยู่ในผลลัพธ์


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