【问题标题】:Pandas - Explode multiple columns in pandas and assign value based on the exploded columnPandas - 展开 pandas 中的多个列并根据展开的列分配值
【发布时间】:2021-08-13 15:03:11
【问题描述】:

可重现的例子

ex = [{"explode1": ["a", "e", "i"], "word": "US_12", "explode2": []}, 
      {"explode1": [], "word": "US_34", "explode2": ["a", "e", "i"]}, 
      {"explode1": ["a", "e", "i"], "word": "US_56", "explode2": ["o", "u"]}]

df = pd.DataFrame(ex)

给你

        explode1   word   explode2
    0  [a, e, i]  US_12         []
    1         []  US_34  [a, e, i]
    2  [a, e, i]  US_56     [o, u]

您可以假设还有一个 explode3 和一个 explode4 列(为简洁起见排除)

预期结果数据框

   exploded_alphabet   word    exploded_type
0                  a  US_12    explode1
1                  e  US_12    explode1
2                  i  US_12    explode1
3                  a  US_34    explode2
4                  e  US_34    explode2
5                  i  US_34    explode2
6                  a  US_54    explode1
7                  e  US_54    explode1
8                  i  US_54    explode1
9                  o  US_34    explode2
10                 u  US_34    explode2

解决方案必须是可重现的 4 列,而不仅仅是上面提到的 2 列(为了简洁起见,我没有在我的示例中包含 explode3explode4

因此总行数将等于 explode1explode2explode3explode4 扁平化的所有列表中的元素数。

我的努力

老实说,我认为必须有一种更短的 Pythonic 方式,而不是单独分解每一个,然后分解具有多种类型的那些。

df = df.explode("explode1")
df = df.explode("explode2")

以上内容不正确。因为这不会同时爆炸行。如果列表在多个爆炸列中不为空,则会创建重复项。


另一种是非pythonic方式,您可以逐行迭代并创建和分配一个新列——这很长而且很容易做到。但这个问题可能已经以不同的方式解决了。


我的问题与其他“分解多列”问题有何不同?

  1. 分别爆炸。这些列中的每个元素都会创建一个新行(这可能已经存在于 SO)

  2. exploded_type 中分配值 - 不确定这是否已在 SO 上与 1 一起解决。

【问题讨论】:

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


    【解决方案1】:

    explode 之前使用DataFrame.melt 进行反透视,然后删除具有缺失值的行(从空列表中):

    df = (df.melt('word', value_name='exploded_alphabet', var_name='exploded_type')
            .explode("exploded_alphabet")
            .dropna(subset=['exploded_alphabet'])
            .reset_index(drop=True))
    print (df)
         word exploded_type exploded_alphabet
    0   US_12      explode1                 a
    1   US_12      explode1                 e
    2   US_12      explode1                 i
    3   US_56      explode1                 a
    4   US_56      explode1                 e
    5   US_56      explode1                 i
    6   US_34      explode2                 a
    7   US_34      explode2                 e
    8   US_34      explode2                 i
    9   US_56      explode2                 o
    10  US_56      explode2                 u
    

    【讨论】:

      【解决方案2】:

      你可以stack,然后explode

      result = df.set_index('word').stack().explode().dropna().reset_index(
          name='exploded_alphabet').rename(columns={'level_1': 'exploded_type'})
      

      输出:

           word exploded_type exploded_alphabet
      0   US_12      explode1                 a
      1   US_12      explode1                 e
      2   US_12      explode1                 i
      3   US_34      explode2                 a
      4   US_34      explode2                 e
      5   US_34      explode2                 i
      6   US_56      explode1                 a
      7   US_56      explode1                 e
      8   US_56      explode1                 i
      9   US_56      explode2                 o
      10  US_56      explode2                 u
      

      性能:

      
      for _ in range(20):
          df = df.append(df)
          
      len(df) # 3145728
      
      %%timeit 
      (
          df.set_index('word')
          .stack().
          explode().
          dropna().
          reset_index(name='exploded_alphabet').
          rename(columns={'level_1': 'exploded_type'})
      )
      
      4.77 s ± 62.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
      
      %%timeit
      (
           df.melt('word', value_name='exploded_alphabet', var_name='exploded_type')
              .explode("exploded_alphabet")
              .dropna(subset=['exploded_alphabet'])
      )
      6.68 s ± 224 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
      
      %%timeit
      explode_columns = ['explode1', 'explode2']
      pd.melt(
          frame=df,
          id_vars='word',
          value_vars=explode_columns,
          var_name='exploded_type',
          value_name='exploded_alphabet'
      ).explode('exploded_alphabet').dropna()
      
      7.17 s ± 109 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
      

      【讨论】:

      • NOTE: Not tested for large dataframes. - 嗯,那么小数据的计时原因是什么?
      • 嗯...有道理!让我测试大型数据框,并添加结果。 :)
      【解决方案3】:

      您可以使用pd.melt 将列堆叠然后分解。

      explode_columns = ['explode1', 'explode2']
      pd.melt(
          frame=df,
          id_vars='word',
          value_vars=explode_columns,
          var_name='exploded_type',
          value_name='exploded_alphabet'
      ).explode('exploded_alphabet').dropna()
      

      它不保留与上面相同的顺序,但行相同。

      【讨论】:

        猜你喜欢
        • 2022-11-12
        • 2019-03-22
        • 1970-01-01
        • 2020-12-16
        • 1970-01-01
        • 2018-07-07
        • 2016-11-07
        • 2021-10-05
        • 1970-01-01
        相关资源
        最近更新 更多