Là một người dùng với cả hai R
và 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 object
các cột type
luôn làm cho dữ liệu khó chuyển đổi với một pandas
hà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 pandas
và các python
chứ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 numpy
và hầu hết thời gian numpy
đều nhanh hơn. Tôi đề nghị Cpython
và numba
nế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 explode
chứ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 repeat
với hàm DataFrame
tạ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: join
hoặc merge
với index
sau '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 reindex
và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 numpy
cho 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
cycle
và 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.DataFrame
xâ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