Pandas将列表列分成多列

use*_*093 86 python split list dataframe pandas

我有一个pandas dataFrame,其中一列如下所示:

In [207]:df2.teams
Out[207]: 
0         [SF, NYG]
1         [SF, NYG]
2         [SF, NYG]
3         [SF, NYG]
4         [SF, NYG]
5         [SF, NYG]
6         [SF, NYG]
7         [SF, NYG]
Run Code Online (Sandbox Code Playgroud)

我需要使用pandas将这列列表拆分为2列,名为team1和team2

jez*_*ael 162

您可以使用DataFrame与构造函数lists通过转换为创建numpy array通过values使用tolist:

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]
Run Code Online (Sandbox Code Playgroud)
df2[['team1','team2']] = pd.DataFrame(df2.teams.values.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
Run Code Online (Sandbox Code Playgroud)

对于新的DataFrame:

df3 = pd.DataFrame(df2['teams'].values.tolist(), 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
Run Code Online (Sandbox Code Playgroud)

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

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

In [89]: %timeit df2['teams'].apply(pd.Series)
1 loop, best of 3: 1.15 s per loop

In [90]: %timeit pd.DataFrame(df2['teams'].values.tolist(), columns=['team1','team2'])
1000 loops, best of 3: 820 µs per loop
Run Code Online (Sandbox Code Playgroud)

  • 小问题,如果您在现有数据帧上使用它,请确保重置索引,否则将无法正确分配. (4认同)
  • @user1700890 - 是的,或者在 DataFrame 构造函数中指定索引 `df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)` (2认同)
  • @Catbuilts实际上,`apply()`可能会比较慢,但是当输入字符串和原始Series系列中的行的值不相等时,这是首选方法! (2认同)

Jos*_*son 29

更简单的解决方案:

pd.DataFrame(df2.teams.tolist(), columns=['team1', 'team2'])
Run Code Online (Sandbox Code Playgroud)

产量,

  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
Run Code Online (Sandbox Code Playgroud)

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

pd.DataFrame(df.teams.str.split('<delim>', expand=True).values,
             columns=['team1', 'team2'])
Run Code Online (Sandbox Code Playgroud)

  • 如果每个列表的元素数量不均匀怎么办? (17认同)
  • _如果您想拆分一列分隔字符串而不是列表,您可以类似地执行以下操作:_ `df["teams"].str.split('&lt;delim&gt;', Expand=True)` 已经返回一个 DataFrame,因此它重命名列可能会更简单。 (4认同)

小智 16

我想推荐一种更高效、Pythonic 的方法。

\n

首先将 DataFrame 定义为原始帖子:

\n
df = pd.DataFrame({"teams": [["SF", "NYG"] for _ in range(7)]})\n
Run Code Online (Sandbox Code Playgroud)\n

我的解决方案:

\n
%%timeit\ndf[\'team1\'], df[\'team2\'] = zip(*list(df[\'teams\'].values))\n>> 761 \xc2\xb5s \xc2\xb1 8.35 \xc2\xb5s per loop\n
Run Code Online (Sandbox Code Playgroud)\n

相比之下,获得最多支持的解决方案:

\n
%%timeit\ndf[[\'team1\',\'team2\']] = pd.DataFrame(df.teams.tolist(), index=df.index)\ndf = pd.DataFrame(df[\'teams\'].to_list(), columns=[\'team1\',\'team2\'])\n>> 1.31 ms \xc2\xb1 11.2 \xc2\xb5s per loop\n
Run Code Online (Sandbox Code Playgroud)\n

我的解决方案节省了 40% 的时间并且时间短得多。您唯一需要记住的是如何使用 来解压和重塑二维列表zip(*list)

\n


Kev*_*ham 11

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

df3 = df2.teams.apply(pd.Series)
df3.columns = ['team1', 'team2']
Run Code Online (Sandbox Code Playgroud)

结果如下:

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Run Code Online (Sandbox Code Playgroud)

  • 这也是你在 pandas 中可以做的最慢的“apply”之一。您应该避免使用这种方法并使用已接受的答案。在最佳答案的计时中,此方法大约慢“1400 x”@rajan (7认同)
  • 这是一个很好的解决方案,因为它适用于不同大小的列表。 (6认同)
  • @Erfan是的,但有时用户并不关心一个操作需要1s还是1ms,相反他们最关心的是编写最简单、最易读的代码!我承认可读性/简单性是主观的,但我的观点很简单,速度并不是所有用户在任何时候都优先考虑的。 (5认同)
  • 此外,我发现“apply”方法对于在大型数据集上扩展大型数组(1000+ 项)更可靠。当数据集超过 500k 行时,“tolist()”方法终止了我的进程。 (2认同)
  • `.apply(pd.Series)` 很容易记住和输入。不幸的是,正如其他答案中所述,对于大量观察来说,它也非常慢。_如果_要保留的索引很容易访问,那么使用 DataFrame 构造函数方法进行保存就像将“index”参数传递给构造函数一样简单,如其他答案中所示。在方法链的中间,一种解决方法是使用赋值表达式 (Python 3.8+) 存储中间 Series 或 DataFrame,然后从那里访问索引。 (2认同)

mik*_*ila 10

与提出的解决方案相反,似乎存在语法上更简单的方式,因此更容易记住.我假设该列在数据帧df中称为"meta":

df2 = pd.DataFrame(df['meta'].str.split().values.tolist())
Run Code Online (Sandbox Code Playgroud)


Tal*_*lis 8

列表理解

一个简单的列表理解实现(我最喜欢的)

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

输出时序:

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

Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)


Luc*_*cas 7

以前的解决方案对我不起作用,因为我nan在我的dataframe. 在我的情况下df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)产生:

object of type 'float' has no len()
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

输出:

        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
Run Code Online (Sandbox Code Playgroud)

用列表理解来解决,

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
Run Code Online (Sandbox Code Playgroud)

产量:

    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
Run Code Online (Sandbox Code Playgroud)


Cyt*_*rak 7

这是使用df.transformand的另一个解决方案df.set_index

>>> from operator import itemgetter
>>> df['teams'].transform({'item1': itemgetter(0), 'item2': itemgetter(1)})

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Run Code Online (Sandbox Code Playgroud)

当然可以概括为:

>>> indices = range(len(df['teams'][0]))

>>> df['teams'].transform({f'team{i+1}': itemgetter(i) for i in indices})

  team1 team2
0    SF   NYG
1    SF   NYG
2    SF   NYG
3    SF   NYG
4    SF   NYG
5    SF   NYG
6    SF   NYG
Run Code Online (Sandbox Code Playgroud)

这种方法具有提取所需索引的额外好处:

>>> df
                 teams
0  [SF, NYG, XYZ, ABC]
1  [SF, NYG, XYZ, ABC]
2  [SF, NYG, XYZ, ABC]
3  [SF, NYG, XYZ, ABC]
4  [SF, NYG, XYZ, ABC]
5  [SF, NYG, XYZ, ABC]
6  [SF, NYG, XYZ, ABC]

>>> indices = [0, 2]
>>> df['teams'].transform({f'team{i+1}': itemgetter(i) for i in indices})

  team1 team3
0    SF   XYZ
1    SF   XYZ
2    SF   XYZ
3    SF   XYZ
4    SF   XYZ
5    SF   XYZ
6    SF   XYZ
Run Code Online (Sandbox Code Playgroud)


小智 5

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

\n\n
pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)\n
Run Code Online (Sandbox Code Playgroud)\n\n

时间:

\n\n
In [1]:\nimport pandas as pd\nd1 = {'teams': [['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],\n                ['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG'],['SF', 'NYG']]}\ndf2 = pd.DataFrame(d1)\ndf2 = pd.concat([df2]*1000).reset_index(drop=True)\n\nIn [2]: %timeit df2['teams'].apply(pd.Series)\n\n8.27 s \xc2\xb1 2.73 s per loop (mean \xc2\xb1 std. dev. of 7 runs, 1 loop each)\n\nIn [3]: %timeit pd.DataFrame([{x: y for x, y in enumerate(item)} for item in df2['teams'].values.tolist()], index=df2.index)\n\n35.4 ms \xc2\xb1 5.22 ms per loop (mean \xc2\xb1 std. dev. of 7 runs, 10 loops each)\n
Run Code Online (Sandbox Code Playgroud)\n