我有以下数据框,其中一列是对象(列表类型单元格):
df=pd.DataFrame({'A':[1,2],'B':[[1,2],[1,2]]})
df
Out[458]:
A B
0 1 [1, 2]
1 2 [1, 2]
我的预期产出是:
A B
0 1 1
1 1 2
3 2 1
4 2 2
我应该怎么做才能做到这一点?
相关问题
熊猫:当单元格内容是列表时,为列表中的每个元素创建一行
很好的问题和答案,但只处理一列列表(在我的回答中,self-def函数将适用于多个列,而且接受的答案是使用最耗时的应用
,这是不推荐的,请查看更多信息我应该在代码中何时使用pandas apply()
在pandas 0.25中,通过添加explode()
方法,可以显著简化列表式列的分解:
df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [1, 2]]})
df.explode('B')
出:
A B
0 1 1
0 1 2
1 2 1
1 2 2
选项1
如果另一列中的所有子列表长度相同,numpy
在这里可能是一个有效的选项:
vals = np.array(df.B.values.tolist())
a = np.repeat(df.A, vals.shape[1])
pd.DataFrame(np.column_stack((a, vals.ravel())), columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
选项2
如果子列表的长度不同,则需要额外的步骤:
vals = df.B.values.tolist()
rs = [len(r) for r in vals]
a = np.repeat(df.A, rs)
pd.DataFrame(np.column_stack((a, np.concatenate(vals))), columns=df.columns)
A B
0 1 1
1 1 2
2 2 1
3 2 2
选项3
我尝试将其推广到扁平化N
列和平铺M
列,我稍后将努力使其更有效:
df = pd.DataFrame({'A': [1,2,3], 'B': [[1,2], [1,2,3], [1]],
'C': [[1,2,3], [1,2], [1,2]], 'D': ['A', 'B', 'C']})
A B C D
0 1 [1, 2] [1, 2, 3] A
1 2 [1, 2, 3] [1, 2] B
2 3 [1] [1, 2] C
def unnest(df, tile, explode):
vals = df[explode].sum(1)
rs = [len(r) for r in vals]
a = np.repeat(df[tile].values, rs, axis=0)
b = np.concatenate(vals.values)
d = np.column_stack((a, b))
return pd.DataFrame(d, columns = tile + ['_'.join(explode)])
unnest(df, ['A', 'D'], ['B', 'C'])
A D B_C
0 1 A 1
1 1 A 2
2 1 A 1
3 1 A 2
4 1 A 3
5 2 B 1
6 2 B 2
7 2 B 3
8 2 B 1
9 2 B 2
10 3 C 1
11 3 C 1
12 3 C 2
功能
def wen1(df):
return df.set_index('A').B.apply(pd.Series).stack().reset_index(level=0).rename(columns={0: 'B'})
def wen2(df):
return pd.DataFrame({'A':df.A.repeat(df.B.str.len()),'B':np.concatenate(df.B.values)})
def wen3(df):
s = pd.DataFrame({'B': np.concatenate(df.B.values)}, index=df.index.repeat(df.B.str.len()))
return s.join(df.drop('B', 1), how='left')
def wen4(df):
return pd.DataFrame([[x] + [z] for x, y in df.values for z in y],columns=df.columns)
def chris1(df):
vals = np.array(df.B.values.tolist())
a = np.repeat(df.A, vals.shape[1])
return pd.DataFrame(np.column_stack((a, vals.ravel())), columns=df.columns)
def chris2(df):
vals = df.B.values.tolist()
rs = [len(r) for r in vals]
a = np.repeat(df.A.values, rs)
return pd.DataFrame(np.column_stack((a, np.concatenate(vals))), columns=df.columns)
时间安排
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from timeit import timeit
res = pd.DataFrame(
index=['wen1', 'wen2', 'wen3', 'wen4', 'chris1', 'chris2'],
columns=[10, 50, 100, 500, 1000, 5000, 10000],
dtype=float
)
for f in res.index:
for c in res.columns:
df = pd.DataFrame({'A': [1, 2], 'B': [[1, 2], [1, 2]]})
df = pd.concat([df]*c)
stmt = '{}(df)'.format(f)
setp = 'from __main__ import df, {}'.format(f)
res.at[f, c] = timeit(stmt, setp, number=50)
ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N")
ax.set_ylabel("time (relative)")
表演
我知道object
dtype列使数据很难用函数转换。当我收到这样的数据时,首先想到的是“展平”或取消列的测试。
我使用pandas和Python函数来回答这类问题。如果您担心上述解决方案的速度,请查看user3483203的答案,因为它使用的是numpy,而且大多数情况下numpy更快。如果速度很重要,我推荐Cython或numba。
方法0[1]
df.explode('B')
A B
0 1 1
1 1 2
0 2 1
1 2 2
给定一个列中具有空list
或NaN
的数据帧。空列表不会导致问题,但NaN
需要填充列表
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<代码>应用pd。系列(简单易懂,但不推荐在性能方面。)
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。。。。。答:如果我们仍然使用上述方法(方法2),我们很难逐个重新创建列。
解决方案:join
或merge
与索引
在“unest”单个列之后
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使用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
使用numpy
实现高性能的方法6:
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
循环
和链
:纯粹的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
以上所有方法都是在讨论垂直方向上的untesting和explode,如果您确实需要将列表扩展到水平方向,请使用pd进行检查。数据帧
构造函数
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
使用原始爆炸功能更新2021-02-17
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')
问题内容: 我有以下DataFrame,其中列之一是对象(列表类型单元格): 我的预期输出是: 我应该怎么做才能做到这一点? 问题答案: 作为同时使用和python,我已经多次看到这种类型的问题。 在中,它们具有名为的包中的内置函数。但是)中没有针对此类问题的内置函数。 我知道列总是使数据难以通过函数进行转换。当我收到这样的数据时,想到的第一件事就是“弄平”或取消嵌套列。 我正在使用和函数来解决此
当我试图打印我的数据集的单个列时,它显示错误 KeyError回溯(最近一次调用上次)~\anaconda3\lib\site packages\pandas\core\index\base。py in get_loc(自身、键、方法、公差)2645 try:- 熊猫库\索引。大熊猫中的pyx_图书馆。指数IndexEngine。获取_loc() 熊猫库\索引。大熊猫中的pyx_图书馆。指数Ind
我有一个火花数据帧看起来像: 我想将此数据帧转换为: 我应该使用什么功能?
我正在Spark 3.0.0上执行Spark结构流的示例,为此,我使用了twitter数据。我在Kafka中推送了twitter数据,单个记录如下所示 2020-07-21 10:48:19|1265200268284588034|RT@narendramodi:与@IBM首席执行官@ArvindKrishna先生进行了广泛的互动。我们讨论了几个与技术相关的主题,…|印度海得拉巴 在这里,每个字段
我有以下结构的数据帧: 我想写一个UDF,那个 < li >压缩DF中每列第I个位置的元素 < li >分解每个压缩元组的DF 生成的列应如下所示: 目前,我正在对UDF使用多调用(每个列名一个,在运行时之前收集列名列表),如下所示: 由于dataframe可能有数百列,因此这种对< code>withColumn的迭代调用似乎需要很长时间。 问题:这是否可能与一个UDF和一个<code>DF.w
我有一个数据帧: 现在我想把它转换成一个新的数据帧,比如 我怎样才能用熊猫做到这一点?