熊猫将列表的一列分为多列


136

我有一列的pandas DataFrame:

import pandas as pd

df = pd.DataFrame(
    data={
        "teams": [
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
            ["SF", "NYG"],
        ]
    }
)

print(df)

输出:

       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

如何将列表的这一列分为两列?

Answers:


243

您可以将DataFrame构造函数与lists创建者to_list

import pandas as pd

d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
print (df2)
       teams
0  [SF, NYG]
1  [SF, NYG]
2  [SF, NYG]
3  [SF, NYG]
4  [SF, NYG]
5  [SF, NYG]
6  [SF, NYG]

df2[['team1','team2']] = pd.DataFrame(df2.teams.tolist(), index= df2.index)
print (df2)
       teams team1 team2
0  [SF, NYG]    SF   NYG
1  [SF, NYG]    SF   NYG
2  [SF, NYG]    SF   NYG
3  [SF, NYG]    SF   NYG
4  [SF, NYG]    SF   NYG
5  [SF, NYG]    SF   NYG
6  [SF, NYG]    SF   NYG

对于新的DataFrame

df3 = pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
print (df3)
  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

解决方案apply(pd.Series)非常慢:

#7k rows
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [121]: %timeit df2['teams'].apply(pd.Series)
1.79 s ± 52.5 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [122]: %timeit pd.DataFrame(df2['teams'].to_list(), columns=['team1','team2'])
1.63 ms ± 54.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

4
较小的警告,如果在现有数据帧上使用它,请确保重置索引,否则将无法正确分配索引。
user1700890

1
@ user1700890-是,或者在DataFrame构造函数中指定索引df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
jezrael

1
@Catbuilts-是的,如果存在矢量化解决方案,则最好避免使用它。
jezrael

1
@Catbuilts-是的,显然。向量化通常意味着没有循环,因此没有适用,没有理由,没有列表理解。但这取决于确切需要什么。也许还可以帮助这个
jezrael

2
@Catbuilts确实apply()可能会慢一些,但是当原始系列的各行中输入字符串和值不相等时,这是首选方法!
CheTesta

52

更简单的解决方案:

pd.DataFrame(df2["teams"].to_list(), columns=['team1', 'team2'])

产量

  team1 team2
-------------
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
7    SF   NYG

如果要拆分一列分隔字符串而不是列表,则可以类似地执行以下操作:

pd.DataFrame(df["teams"].str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])

5
如果每个列表的元素数量不均匀怎么办?
ikel

如果要拆分一列分隔字符串而不是列表,则可以类似地执行以下操作: df["teams"].str.split('<delim>', expand=True)已经返回一个DataFrame,因此重命名列可能更简单。
AMC

26

df2与使用tolist()以下解决方案的解决方案不同,此解决方案保留了DataFrame 的索引:

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']

结果如下:

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG

2
也是apply您在大熊猫中最慢的动作之一。您应该避免这种方法,并使用可接受的答案。在顶部的答案的定时,这个方法大约是1400 x较慢@rajan
尔法恩

2
@Erfan是的,但是有时用户并不关心操作耗时1s还是1ms,相反,他们最关心的是编写最简单,最易读的代码!我承认可读性/简单性是主观的,但我的意思只是说,速度并非始终是所有用户的首要任务。
凯文·马克汉姆

1
此外,我发现该apply方法对于在大型数据集上扩展大型数组(1000多个项目)更可靠。tolist()当数据集超过50万行时,该方法终止了我的进程。
莫里茨

2
这是一个很好的解决方案,因为它可以很好地处理不同大小的列表。
dasilvadaniel

@KevinMarkham 他们最关心写最简单,最可读的代码是否pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"])真的这么多复杂?
AMC

15

与提议的解决方案相比,似乎在语法上更简单,因此更容易记住。我假设该列在数据帧df中称为“元”:

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())

1
我收到一个错误,但我通过删除来解决了str.split()。如果您不知道列表中的项目数,这会简单得多,并且具有优势。
otteheng

与提议的解决方案相比,似乎在语法上更简单,因此更容易记住。真?因为这实际上与几年前发布的最高答案相同。唯一的区别是与该特定问题无关的部分。
AMC

它对我有用!
EduardoUstarez

3

根据先前的答案,这是另一个解决方案,它以更快的运行时间返回与df2.teams.apply(pd.Series)相同的结果:

pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

时间:

In [1]:
import pandas as pd
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2 = pd.concat([df2]*1000).reset_index(drop=True)

In [2]: %timeit df2['teams'].apply(pd.Series)

8.27 s ± 2.73 s per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)

35.4 ms ± 5.22 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

3

由于我的nan观察中有上述发现,上述解决方案对我不起作用dataframe。就我而言,df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)收益:

object of type 'float' has no len()

我使用列表理解来解决这个问题。这里是可复制的示例:

import pandas as pd
import numpy as np
d1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],
            ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}
df2 = pd.DataFrame(d1)
df2.loc[2,'teams'] = np.nan
df2.loc[4,'teams'] = np.nan
df2

输出:

        teams
0   [SF, NYG]
1   [SF, NYG]
2   NaN
3   [SF, NYG]
4   NaN
5   [SF, NYG]
6   [SF, NYG]

df2['team1']=np.nan
df2['team2']=np.nan

用列表理解来解决:

for i in [0,1]:
    df2['team{}'.format(str(i+1))]=[k[i] if isinstance(k,list) else k for k in df2['teams']]

df2

产量:

    teams   team1   team2
0   [SF, NYG]   SF  NYG
1   [SF, NYG]   SF  NYG
2   NaN        NaN  NaN
3   [SF, NYG]   SF  NYG
4   NaN        NaN  NaN
5   [SF, NYG]   SF  NYG
6   [SF, NYG]   SF  NYG

1

清单理解

列表理解的简单实现(我的最爱)

df = pd.DataFrame([pd.Series(x) for x in df.teams])
df.columns = ['team_{}'.format(x+1) for x in df.columns]

输出定时:

CPU times: user 0 ns, sys: 0 ns, total: 0 ns
Wall time: 2.71 ms

输出:

team_1  team_2
0   SF  NYG
1   SF  NYG
2   SF  NYG
3   SF  NYG
4   SF  NYG
5   SF  NYG
6   SF  NYG

这种类型的句柄可以处理不同长度的列表-这是对许多其他答案的改进,但会导致项目不在自己的列中。
以撒

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.