ฉันรู้ว่าobject
คอลัมน์type
ทำให้ข้อมูลแปลงด้วยpandas
ฟังก์ชันได้ยาก เมื่อฉันได้รับข้อมูลเช่นนี้สิ่งแรกที่ควรคำนึงถึงก็คือการ "แบน" หรือทำให้คอลัมน์ไม่สมบูรณ์
ฉันใช้pandas
และpython
ฟังก์ชั่นสำหรับคำถามประเภทนี้ หากคุณกังวลเกี่ยวกับความเร็วของโซลูชันข้างต้นโปรดตรวจสอบคำตอบของ user3483203 เนื่องจากใช้งานอยู่numpy
และเวลาส่วนใหญ่numpy
จะเร็วกว่า ฉันแนะนำCpython
และnumba
ถ้าความเร็วมีความสำคัญ
วิธีที่ 0 [แพนด้า> = 0.25]
เริ่มจากแพนด้า 0.25หากคุณต้องการระเบิดเพียงคอลัมน์เดียวคุณสามารถใช้pandas.DataFrame.explode
ฟังก์ชัน:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
กำหนดดาต้าเฟรมด้วยช่องว่างlist
หรือNaN
คอลัมน์ รายการที่ว่างเปล่าจะไม่ทำให้เกิดปัญหา แต่NaN
จะต้องกรอกข้อมูลด้วยไฟล์list
df = pd.DataFrame({'A': [1, 2, 3, 4],'B': [[1, 2], [1, 2], [], np.nan]})
df.B = df.B.fillna({i: [] for i in df.index}) # replace NaN with []
df.explode('B')
A B
0 1 1
0 1 2
1 2 1
1 2 2
2 3 NaN
3 4 NaN
วิธีที่ 1
apply + pd.Series
(เข้าใจง่าย แต่ไม่แนะนำในแง่ของประสิทธิภาพ)
df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0:'B'})
Out[463]:
A B
0 1 1
1 1 2
0 2 1
1 2 2
วิธีที่ 2
ใช้repeat
กับDataFrame
ตัวสร้างสร้างดาต้าเฟรมของคุณใหม่ (มีประสิทธิภาพดี แต่ไม่ดีในหลายคอลัมน์)
df=pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
df
Out[465]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
วิธีที่ 2.1
เช่นนอกเหนือจาก A เรามี A.1 ..... An ถ้าเรายังคงใช้วิธีการ ( วิธีที่ 2 ) ข้างต้นจะเป็นการยากที่เราจะสร้างคอลัมน์ใหม่ทีละคอลัมน์
วิธีแก้ไข: join
หรือmerge
ด้วยindex
คอลัมน์เดียวที่ 'ไม่สำคัญที่สุด'
s=pd.DataFrame({'B':np.concatenate(df.B.values)},index=df.index.repeat(df.B.str.len()))
s.join(df.drop('B',1),how='left')
Out[477]:
B A
0 1 1
0 2 1
1 1 2
1 2 2
หากคุณต้องการลำดับคอลัมน์เหมือนเดิมreindex
ทุกประการให้เพิ่มในตอนท้าย
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
วิธีที่ 3
สร้างไฟล์list
pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
Out[488]:
A B
0 1 1
1 1 2
2 2 1
3 2 2
หากมีมากกว่าสองคอลัมน์ให้ใช้
s=pd.DataFrame([[x] + [z] for x, y in zip(df.index,df.B) for z in y])
s.merge(df,left_on=0,right_index=True)
Out[491]:
0 1 A B
0 0 1 1 [1, 2]
1 0 2 1 [1, 2]
2 1 1 2 [1, 2]
3 1 2 2 [1, 2]
วิธีที่ 4
โดยใช้reindex
หรือloc
df.reindex(df.index.repeat(df.B.str.len())).assign(B=np.concatenate(df.B.values))
Out[554]:
A B
0 1 1
0 1 2
1 2 1
1 2 2
#df.loc[df.index.repeat(df.B.str.len())].assign(B=np.concatenate(df.B.values))
วิธีที่ 5
เมื่อรายการมีเฉพาะค่าที่ไม่ซ้ำกัน:
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]]})
from collections import ChainMap
d = dict(ChainMap(*map(dict.fromkeys, df['B'], df['A'])))
pd.DataFrame(list(d.items()),columns=df.columns[::-1])
Out[574]:
B A
0 1 1
1 2 1
2 3 2
3 4 2
วิธีที่ 6
ใช้numpy
เพื่อประสิทธิภาพสูง:
newvalues=np.dstack((np.repeat(df.A.values,list(map(len,df.B.values))),np.concatenate(df.B.values)))
pd.DataFrame(data=newvalues[0],columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
วิธีที่ 7
โดยใช้ฟังก์ชันพื้นฐานitertools
cycle
และchain
: โซลูชัน Python บริสุทธิ์เพื่อความสนุกสนาน
from itertools import cycle,chain
l=df.values.tolist()
l1=[list(zip([x[0]], cycle(x[1])) if len([x[0]]) > len(x[1]) else list(zip(cycle([x[0]]), x[1]))) for x in l]
pd.DataFrame(list(chain.from_iterable(l1)),columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
การสรุปเป็นหลายคอลัมน์
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[3,4]],'C':[[1,2],[3,4]]})
df
Out[592]:
A B C
0 1 [1, 2] [1, 2]
1 2 [3, 4] [3, 4]
ฟังก์ชั่นป้องกันตัวเอง:
def unnesting(df, explode):
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
unnesting(df,['B','C'])
Out[609]:
B C A
0 1 1 1
0 2 2 1
1 3 3 2
1 4 4 2
คอลัมน์ฉลาด Unnesting
วิธีการทั้งหมดข้างต้นกำลังพูดถึงการระเบิดและระเบิดในแนวตั้งหากคุณต้องการใช้รายการแนวนอนตรวจสอบกับตัวpd.DataFrame
สร้าง
df.join(pd.DataFrame(df.B.tolist(),index=df.index).add_prefix('B_'))
Out[33]:
A B C B_0 B_1
0 1 [1, 2] [1, 2] 1 2
1 2 [3, 4] [3, 4] 3 4
อัปเดตฟังก์ชัน
def unnesting(df, explode, axis):
if axis==1:
idx = df.index.repeat(df[explode[0]].str.len())
df1 = pd.concat([
pd.DataFrame({x: np.concatenate(df[x].values)}) for x in explode], axis=1)
df1.index = idx
return df1.join(df.drop(explode, 1), how='left')
else :
df1 = pd.concat([
pd.DataFrame(df[x].tolist(), index=df.index).add_prefix(x) for x in explode], axis=1)
return df1.join(df.drop(explode, 1), how='left')
ผลการทดสอบ
unnesting(df, ['B','C'], axis=0)
Out[36]:
B0 B1 C0 C1 A
0 1 2 1 2 1
1 3 4 3 4 2