【问题标题】:String split using a delimiter on pandas column to create new columns在 pandas 列上使用分隔符拆分字符串以创建新列
【发布时间】:2020-12-24 08:41:58
【问题描述】:

我有一个这样的列的数据框

Col1
1 A, 2 B, 3 C
2 B, 4 C
1 B, 2 C, 4 D

我用过.str.split(',', expand=True),结果是这样的

0   | 1   | 2
1 A | 2 B | 3 C
2 B | 4 C | None
1 B | 2 C | 4 D

我想要实现的是得到这个:

Col A| Col B| Col C| Col D
1 A  | 2 B  | 3 C  | None
None | 2 B  | 4 C  | None
None | 1 B  | 2 C  | 4 D

我被卡住了,如何将新列格式化为这样?

【问题讨论】:

    标签: python python-3.x pandas list dataframe


    【解决方案1】:

    我们试试吧:

    # split and explode
    s = df['Col1'].str.split(', ').explode()
    
    # create new multi-level index
    s.index = pd.MultiIndex.from_arrays([s.index, s.str.split().str[-1].tolist()])
    
    # unstack to reshape
    out = s.unstack().add_prefix('Col ')
    

    详情:

    # split and explode
    0    1 A
    0    2 B
    0    3 C
    1    2 B
    1    4 C
    2    1 B
    2    2 C
    2    4 D
    Name: Col1, dtype: object
    
    # create new multi-level index
    0  A    1 A
       B    2 B
       C    3 C
    1  B    2 B
       C    4 C
    2  B    1 B
       C    2 C
       D    4 D
    Name: Col1, dtype: object
    
    # unstack to reshape
      Col A Col B Col C Col D
    0   1 A   2 B   3 C   NaN
    1   NaN   2 B   4 C   NaN
    2   NaN   1 B   2 C   4 D
    

    【讨论】:

      【解决方案2】:

      很可能您可以使用更通用的方法,但这对我有用。请注意,这是基于您的特定示例的许多假设和约束。

      test_dict = {'col_1': ['1 A, 2 B, 3 C', '2 B, 4 C', '1 B, 2 C, 4 D']}
      df = pd.DataFrame(test_dict)
      

      首先,我们将 df 拆分为初始列:

      df2 = df.col_1.str.split(pat=',', expand=True)
      

      结果:

          0   1   2
      0   1 A 2 B 3 C
      1   2 B 4 C None
      2   1 B 2 C 4 D
      

      接下来,(第一个假设)我们需要确保以后可以使用' ' 作为分隔符来提取列。为了做到这一点,我们需要从每个字符串中删除所有开始和结尾的空格

      func = lambda x: pd.Series([i.strip() for i in x])
      df2 = df2.astype(str).apply(func, axis=1)
      

      接下来,我们需要获取唯一列的列表。为此,我们首先从每个单元格中提取列名:

      func = lambda x: pd.Series([i.split(' ')[1] for i in x if i != 'None'])
      df3 = df2.astype(str).apply(func, axis=1)
      

      结果:

          0   1   2
      0   A   B   C
      1   B   C   NaN
      2   B   C   D
      

      然后创建一个包含在您的 DataFrame 中的唯一列 ['A', 'B', 'C', 'D'] 的列表:

      columns_list = pd.unique(df3[df3.columns].values.ravel('K'))
      columns_list = [x for x in columns_list if not pd.isna(x)]
      

      并使用这些列创建一个空的基本数据框,这些列将用于分配相应的值: result_df = pd.DataFrame(columns=columns_list) 准备工作完成后,我们可以为每一行分配列值,并使用pd.concat 将它们合并回一个 DataFrame:

      result_list = []
      result_list.append(result_df)  # Adding the empty base table to ensure the columns are present
      for row in df2.iterrows():
          result_object = {}  # dict that will be used to represent each row in source DataFrame
          for column in columns_list:
              for value in row[1]:  # row is returned in the format of tuple where first value is row_index that we don't need
                  if value != 'None':
                      if value.split(' ')[1] == column:  # Checking for a correct column to assign
                          result_object[column] = [value]
          result_list.append(pd.DataFrame(result_object))  # Adding dicts per row
      

      一旦生成了 DataFrames 列表,我们就可以使用pd.concat 将其放在一起:

      final_df = pd.concat(result_list, ignore_index=True)  # ignore_index will rebuild the index for the final_df
      

      结果将是:

          A   B   C   D
      0   1 A 2 B 3 C NaN
      1   NaN 2 B 4 C NaN
      2   NaN 1 B 2 C 4 D
      

      我不认为这是最优雅和最有效的方法,但它会产生你需要的结果

      【讨论】:

      • 不错的解决方案,虽然不建议选择循环解决方案而不是矢量化解决方案,但性能差异巨大
      • 完全同意 - 使用原生 pandas 方法总是比围绕循环构建更好。为此答案投票赞成 Shubham Sharma 的解决方案。
      猜你喜欢
      • 1970-01-01
      • 2015-02-22
      • 1970-01-01
      • 2018-07-15
      • 2020-06-18
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 2019-04-27
      相关资源
      最近更新 更多