Là một người dùng với cả hai Rvà python, tôi đã gặp loại câu hỏi này một vài lần.
Trong R, chúng có hàm tích hợp từ gói tidyrđược gọi unnest. Nhưng trong Python( pandas) không có chức năng cài sẵn cho dạng câu hỏi này.
Tôi biết objectcác cột typeluôn làm cho dữ liệu khó chuyển đổi với một pandashàm '. Khi tôi nhận được dữ liệu như thế này, điều đầu tiên tôi nghĩ đến là 'làm phẳng' hoặc tách các cột.
Tôi đang sử dụng pandasvà các pythonchức năng cho loại câu hỏi này. Nếu bạn lo lắng về tốc độ của các giải pháp trên, hãy kiểm tra câu trả lời của user3483203, vì anh ấy đang sử dụng numpyvà hầu hết thời gian numpyđều nhanh hơn. Tôi đề nghị Cpythonvà numbanếu tốc độ quan trọng trong trường hợp của bạn.
Phương pháp 0 [pandas> = 0,25]
Bắt đầu từ gấu trúc 0,25 , nếu bạn chỉ cần phát nổ một cột, bạn có thể sử dụng explodechức năng:
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
Phương pháp 1
apply + pd.Series (dễ hiểu nhưng về mặt hiệu suất thì không nên.)
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
Phương pháp 2
Sử dụng repeatvới hàm DataFrametạo, tạo lại khung dữ liệu của bạn (tốt ở hiệu suất, không tốt ở nhiều cột)
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
Ví dụ như phương pháp 2.1 ngoài A chúng ta có A.1 ..... An Nếu chúng ta vẫn sử dụng phương pháp ( Phương pháp 2 ) ở trên, chúng ta khó có thể tạo lại từng cột một.
Giải pháp: joinhoặc mergevới indexsau 'unnest' các cột đơn
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
Nếu bạn cần thứ tự cột giống hệt như trước đây, hãy thêm reindexvào cuối.
s.join(df.drop('B',1),how='left').reindex(columns=df.columns)
Phương pháp 3
tạo lạilist
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
Nếu nhiều hơn hai cột, hãy sử dụng
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]
Phương pháp 4
sử dụng reindex hoặcloc
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))
Phương pháp 5
khi danh sách chỉ chứa các giá trị duy nhất:
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
Phương pháp 6
sử dụng numpycho hiệu suất cao:
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
Phương pháp 7
sử dụng hàm cơ sở itertools cyclevà chain: Giải pháp python tinh khiết chỉ để giải trí
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
Tổng quát hóa thành nhiều cột
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]
Chức năng tự định nghĩa:
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
Bỏ ghi chú theo cột
Tất cả phương pháp trên đang nói về việc mở rộng danh sách theo chiều dọc và bùng nổ, Nếu bạn cần mở rộng danh sách theo chiều ngang , Hãy kiểm tra với nhà pd.DataFramexây dựng
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
Đã cập nhật chức năng
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')
Kiểm tra đầu ra
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