【问题标题】:Split a Pandas column of lists into multiple columns将 Pandas 的列表列拆分为多列
【发布时间】:2016-05-31 04:45:05
【问题描述】:

我有一个带有一列的 Pandas DataFrame:

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

【问题讨论】:

    标签: python pandas list split


    【解决方案1】:

    您可以将DataFrame 构造函数与由to_list 创建的lists 一起使用:

    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)
    

    【讨论】:

    • 小警告,如果您在现有数据帧上使用它,请确保重置索引,否则将无法正确分配。
    • @user1700890 - 是的,或者在 DataFrame 构造函数中指定索引df2[['team1','team2']] = pd.DataFrame(df2.teams.values.tolist(), index= df2.index)
    • @Catbuilts - 是的,如果存在矢量化解决方案最好避免它。
    • @Catbuilts - 是的,很明显。向量化意味着通常没有循环,所以没有应用,没有 for,没有列表推导。但这取决于究竟需要什么。也许也可以帮助this
    • @Catbuilts 实际上apply() 可能会更慢,但当输入字符串和值在原始系列的各行中不相等时,这是首选方法!
    【解决方案2】:

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

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

    【讨论】:

    • 我遇到了一个错误,但我通过删除 str.split() 解决了它。如果您不知道列表中的项目数量,这会简单得多。
    • 与建议的解决方案相比,似乎有一种语法上更简单的方法,因此更容易记住。真的吗?因为这实际上与几年前发布的最佳答案相同。唯一的区别是与此特定问题无关的部分。
    • 在许多情况下,您可以将.values.tolist() 替换为简单的.to_numpy(),Pandas 也建议这样做。当你在它的时候,你可以拍一个.astype(int)或者如果你需要的话
    【解决方案3】:

    更简单的解决方案:

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

    【讨论】:

    • 如果每个列表的元素个数奇数怎么办?
    • 如果你想分割一列分隔字符串而不是列表,你可以类似地做: df["teams"].str.split('&lt;delim&gt;', expand=True) 已经返回了一个 DataFrame,所以它可能会更简单重命名列。
    【解决方案4】:

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

    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
    

    【讨论】:

    • 也是你可以在 pandas 中做的最慢的apply 之一。您应该避免使用这种方法并使用公认的答案。在最佳答案的时间安排中,这种方法大约是1400 x较慢@rajan
    • @Erfan 是的,但有时用户并不关心操作需要 1s 还​​是 1ms,而是他们最关心的是编写最简单、最易读的代码!我承认可读性/简单性是主观的,但我的观点只是速度并不是所有用户在任何时候都优先考虑的问题。
    • 此外,我发现apply 方法更可靠地用于在大型数据集上扩展大型数组(1000 多个项目)。当数据集超过 500k 行时,tolist() 方法杀死了我的进程。
    • 这是一个很好的解决方案,因为它适用于不同大小的列表。
    • @KevinMarkham 他们最关心的是编写最简单、最易读的代码 pd.DataFrame(df["teams"].to_list(), columns=["team_1", "team_2"]) 真的要复杂得多吗?
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

      之前的解决方案对我不起作用,因为我的 dataframe 中有 nan 观察结果。在我的情况下,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
      

      【讨论】:

        【解决方案7】:

        列表理解

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

        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
        

        【讨论】:

        • 这种处理不同长度的列表 - 这是对许多其他答案的改进,但导致项目不在自己的列中。
        • 这个方案不是基于pandas的
        【解决方案8】:

        这是另一个使用df.transformdf.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
        

        当然可以概括为:

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

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

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

        【讨论】:

          【解决方案9】:

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

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

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

          我的解决方案:

          %%timeit
          df['team1'], df['team2'] = zip(*list(df['teams'].values))
          >> 761 µs ± 8.35 µs per loop
          

          相比之下,最受好评的解决方案:

          %%timeit
          df[['team1','team2']] = pd.DataFrame(df.teams.tolist(), index=df.index)
          df = pd.DataFrame(df['teams'].to_list(), columns=['team1','team2'])
          >> 1.31 ms ± 11.2 µs per loop
          

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

          【讨论】:

            猜你喜欢
            • 2023-01-11
            • 2020-02-10
            • 2021-01-24
            • 1970-01-01
            • 1970-01-01
            • 2023-02-17
            • 2018-05-28
            相关资源
            最近更新 更多