如何在Pandas DataFrame中将包含值列表的列转换为行

62

嗨,我有一个像这样的数据框:

    A             B 
0:  some value    [[L1, L2]]

我希望你能将其改变成:

    A             B 
0:  some value    L1
1:  some value    L2

我该怎么做?

6个回答

82

Pandas >= 0.25

->

数据处理库 Pandas 版本需不低于 0.25

df1 = pd.DataFrame({'A':['a','b'],
               'B':[[['1', '2']],[['3', '4', '5']]]})
print(df1)

    A   B
0   a   [[1, 2]]
1   b   [[3, 4, 5]]

df1 = df1.explode('B')
df1.explode('B')

    A   B
0   a   1
0   a   2
1   b   3
1   b   4
1   b   5

我不知道这个方法有多好,但当你有一系列项目时,它能够起到作用。


3
太好了!我模糊地记得有一个函数可以在一步内完成这个操作,但是无法完全记住它的名称,并且在文档中也找不到它。我几乎要放弃,选择用函数串联的方法来解决,直到我找到了这个 :) - kerwei
2
比其他提供的解决方案都要好 - Shiv Krishna Jaiswal
1
在使用之前,您可能需要检查此问题(可能要等待0.26版本发布):https://github.com/pandas-dev/pandas/issues/30748 - NoCompliance
1
经验法则:如果我可以用几个词解释清楚,那么它应该只需要几个步骤。这个答案似乎比被接受的更好。 - Victor Marconi
1
df1.explode('B')可以完成任务。谢谢! - Tushar
1
你可以在行末添加.reset_index(drop=True)来删除相同的索引值。因此,df1.explode('B').reset_index(drop=True) 将是答案。 - msklc

38

您可以以下方式完成:

In [84]: df
Out[84]:
               A               B
0     some value      [[L1, L2]]
1  another value  [[L3, L4, L5]]

In [85]: (df['B'].apply(lambda x: pd.Series(x[0]))
   ....:         .stack()
   ....:         .reset_index(level=1, drop=True)
   ....:         .to_frame('B')
   ....:         .join(df[['A']], how='left')
   ....: )
Out[85]:
    B              A
0  L1     some value
0  L2     some value
1  L3  another value
1  L4  another value
1  L5  another value

更新:更通用的解决方案


1
@ soupault,没错,谢谢!这段代码适用于特定的问题(被问到的那个)。部分原因是我发布了一个更通用的解决方案链接... - MaxU - stand with Ukraine
@nurma_a,请查看此解决方案 - MaxU - stand with Ukraine
@Roy,针对我答案中的输出数据框:df.groupby("A")["B"].apply(list) - MaxU - stand with Ukraine
@MaxU。太好了。谢谢。如果我们只想要列值(没有索引),我们该怎么做呢? - Roy
1
哦,是的。谢谢你告诉我,@MaxU :) - Roy
显示剩余4条评论

10

使用chain.from_iterablenumpy.repeat可以更快地解决问题:

from itertools import chain
import numpy as np
import pandas as pd

df = pd.DataFrame({'A':['a','b'],
                   'B':[[['A1', 'A2']],[['A1', 'A2', 'A3']]]})

print (df)
   A               B
0  a      [[A1, A2]]
1  b  [[A1, A2, A3]]


df1 = pd.DataFrame({ "A": np.repeat(df.A.values, 
                                    [len(x) for x in (chain.from_iterable(df.B))]),
                     "B": list(chain.from_iterable(chain.from_iterable(df.B)))})

print (df1)
   A   B
0  a  A1
1  a  A2
2  b  A1
3  b  A2
4  b  A3

时间:

A = np.unique(np.random.randint(0, 1000, 1000))
B = [[list(string.ascii_letters[:random.randint(3, 10)])] for _ in range(len(A))]
df = pd.DataFrame({"A":A, "B":B})
print (df)
       A                                 B
0      0        [[a, b, c, d, e, f, g, h]]
1      1                       [[a, b, c]]
2      3     [[a, b, c, d, e, f, g, h, i]]
3      5                 [[a, b, c, d, e]]
4      6     [[a, b, c, d, e, f, g, h, i]]
5      7           [[a, b, c, d, e, f, g]]
6      8              [[a, b, c, d, e, f]]
7     10              [[a, b, c, d, e, f]]
8     11           [[a, b, c, d, e, f, g]]
9     12     [[a, b, c, d, e, f, g, h, i]]
10    13        [[a, b, c, d, e, f, g, h]]
...
...

In [67]: %timeit pd.DataFrame({ "A": np.repeat(df.A.values, [len(x) for x in (chain.from_iterable(df.B))]),"B": list(chain.from_iterable(chain.from_iterable(df.B)))})
1000 loops, best of 3: 818 µs per loop

In [68]: %timeit ((df['B'].apply(lambda x: pd.Series(x[0])).stack().reset_index(level=1, drop=True).to_frame('B').join(df[['A']], how='left')))
10 loops, best of 3: 103 ms per loop

这个解决方案比“apply”解决方案快了125倍。 - jezrael
从itertools导入chain函数 - pomber

3
我找不到一个优雅的方法来处理这个问题,但以下代码可以正常工作...
import pandas as pd
import numpy as np
df = pd.DataFrame([{"a":1,"b":[[1,2]]},{"a":4, "b":[[3,4,5]]}])
z = []
for k,row in df.iterrows():
    for j in list(np.array(row.b).flat):
        z.append({'a':row.a, 'b':j})
result = pd.DataFrame(z)

这对我来说是最容易理解工作原理的...谢谢。 - ihightower

1

我认为这是最快和最简单的方法:

df = pd.DataFrame({'A':['a','b'],
               'B':[[['A1', 'A2']],[['A1', 'A2', 'A3']]]})


df.set_index('A')['B'].apply(lambda x: pd.Series(x[0]))

虽然这段代码可能回答了问题,但提供有关它如何以及/或为什么解决问题的附加上下文将改善答案的长期价值。阅读此处 - Shanteshwar Inde

0

这里还有另一个选择

unpacked = (pd.melt(df.B.apply(pd.Series).reset_index(),id_vars='index')
 .merge(df, left_on = 'index', right_index = True))
unpacked = (unpacked.loc[unpacked.value.notnull(),:]
.drop(columns=['index','variable','B'])
.rename(columns={'value':'B'})
  1. 使用pd.series将B列拆分为不同的行
  2. 对此进行融合,以便每个条目都是单独的行(保留索引)
  3. 将其与原始数据框合并
  4. 整理 - 删除不必要的列并重命名值列

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接