gpt4 book ai didi

python - 如何将 pandas DataFrame 中的列取消嵌套(分解)为多行

转载 作者:太空宇宙 更新时间:2023-11-03 21:21:43 25 4
gpt4 key购买 nike

我有以下 DataFrame,其中一列是一个对象(列表类型单元格):

df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [1, 2]]})

输出:

   A       B
0 1 [1, 2]
1 2 [1, 2]

我的预期输出是:

   A  B
0 1 1
1 1 2
3 2 1
4 2 2

我应该怎样做才能实现这个目标?

<小时/>

相关问题

Pandas column of lists, create a row for each list element

很好的问题和答案,但只能用列表处理一列(在我的回答中,自定义函数将适用于多列,而且接受的答案是使用最耗时的 apply ,即不推荐,查看更多信息 When should I (not) want to use pandas apply() in my code? )

最佳答案

我知道 object dtype 列使得数据很难用 pandas 函数进行转换。当我收到这样的数据时,首先想到的是“展平”或取消嵌套列。

我正在使用 pandas 和 Python 函数来解决此类问题。如果您担心上述解决方案的速度,请查看user3483203's answer ,因为它使用 numpy 并且大多数时候 numpy 更快。我推荐Cythonnumba如果速度很重要。

<小时/>

方法 0 [pandas >= 0.25]pandas 0.25开始,如果只需要分解列,可以使用pandas.DataFrame.explode功能:

df.explode('B')

A B
0 1 1
1 1 2
0 2 1
1 2 2

给定一个数据框,其中列中包含空列表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
<小时/>

方法1apply + 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
<小时/>

方法2repeatDataFrame 构造函数结合使用,重新创建数据帧(擅长性能,不擅长多列)

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 .....A.n。如果我们仍然使用上面的方法(方法2),我们很难一一重新创建列。

解决方案:在“取消嵌套”单列之后与索引加入合并

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重新创建列表

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使用reindexloc

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 cyclechain:纯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
<小时/>

按列解除嵌套

以上所有方法都在讨论垂直取消嵌套和爆炸,如果您确实需要扩展列表水平>,检查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

Update 2021-02-17 with original explode function

def unnesting(df, explode, axis):
if axis==1:
df1 = pd.concat([df[x].explode() for x in explode], axis=1)
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')

关于python - 如何将 pandas DataFrame 中的列取消嵌套(分解)为多行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54185087/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com