繁体   English   中英

将列表的 Pandas 列拆分为多列

[英]Split a Pandas column of lists into multiple columns

我有一个 Pandas DataFrame 与一列:

import pandas as pd

df = pd.DataFrame({"teams": [["SF", "NYG"] for _ in range(7)]})

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

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

期望的结果:

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

您可以将DataFrame<\/code>构造函数与lists<\/code>创建的列表to_list<\/code><\/a>使用:

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]

更简单的解决方案:

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'])

与使用tolist()<\/code>的任何解决方案不同,此解决方案保留了df2<\/code> DataFrame 的索引:

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

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

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

以前的解决方案对我不起作用,因为我的dataframe<\/code>有nan<\/code>观察结果。 在我的情况下df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)<\/code>产生:

object of type 'float' has no len()

列表理解

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

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

这是使用df.transform<\/code><\/a>和df.set_index<\/code><\/a>的另一个解决方案:

>>> 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

根据之前的答案,这是另一个解决方案,它返回与 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)

我想推荐一种更有效和 Pythonic 的方式。

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

df = pd.DataFrame({"teams": [["SF", "NYG"] for _ in range(7)]})

如果有人来这里找一个现成的function,我写了一个。

  • 如果未指定columns ,它会查找所有带有列表的列并展开它们;
  • 添加了名为column_name_0column_name_1等的列;
  • 列顺序保留在最终 df 中;
  • 如果strict=True ,它检查给定列中的列表是否大小相等。

改进和评论表示赞赏。

def unfold_columns(df, columns=[], strict=False):
    assert isinstance(columns, list), "Columns should be a list of column names"
    if len(columns) == 0:
        columns = [
            column for column in df.columns 
            if df.applymap(lambda x: isinstance(x, list)).all()[column]
        ]
    else:
        assert(all([(column in df.columns) for column in columns])), \
            "Not all given columns are found in df"
    columns_order = df.columns
    for column_name in columns:
        if df[column_name].apply(lambda x: isinstance(x, list)).all():
            if strict:
                assert len(set(df[column_name].apply(lambda x: len(x)))) == 1, \
                    f"Lists in df['{column_name}'] are not of equal length"
            unfolded = pd.DataFrame(df[column_name].tolist())
            unfolded.columns = [f'{column_name}_{x}' for x in unfolded.columns]
            columns_order = [
                *columns_order[:list(columns_order).index(column_name)], 
                *unfolded.columns, 
                *columns_order[list(columns_order).index(column_name)+1:]
            ]
            df = df.join(unfolded).drop([column_name], axis=1)
    return df[columns_order]

您可以尝试使用两次 apply 在您的 df 中创建新列 'team1' 和 'team2'

df = pd.DataFrame({"teams": [["SF", "NYG"] for _ in range(7)]})
df["team1"]=df['teams'].apply(lambda x: x[0]  )
df["team2"]=df['teams'].apply(lambda x: x[1]  )
df

在此处输入图像描述

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM