【问题标题】:Unnest (explode) a Pandas Series取消嵌套(爆炸)熊猫系列
【发布时间】:2018-06-20 04:51:41
【问题描述】:

我有:

df = pd.DataFrame({'col1': ['asdf', 'xy', 'q'], 'col2': [1, 2, 3]})

   col1  col2
0  asdf     1
1    xy     2
2     q     3

我想从col1 中的字符串中获取每个字母的“组合乘积”,每个元素都在col2 中。即:

  col1  col2
0    a    1
1    s    1
2    d    1
3    f    1
4    x    2
5    y    2
6    q    3

当前方法:

from itertools import product

pieces = []
for _, s in df.iterrows():
    letters = list(s.col1)
    prods = list(product(letters, [s.col2]))
    pieces.append(pd.DataFrame(prods))

pd.concat(pieces)

还有更有效的解决方法吗?

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

使用 Explode (pandas>=0.25)

df = pd.DataFrame({'col1': ['asdf', 'xy', 'q'], 'col2': [1, 2, 3]})

df.col1=df.col1.apply(list)
df = df.explode('col1')

结果:

  col1  col2
0   a   1
0   s   1
0   d   1
0   f   1
1   x   2
1   y   2
2   q   3

【讨论】:

    【解决方案2】:

    你也可以尝试itertools.chainitertools.repeat函数来达到类似的效果。

    一个例子是

    import pandas as pd
    from itertools import chain, repeat
    
    d = {'col1': ['asdf', 'xy', 'q'], 'col2': [1, 2, 3]}
    
    expanded_d = {
        "col1": list(chain(*[list(item) for item in d["col1"]])),
        "col2": list(chain(*[list(repeat(d["col2"][idx], len(list(d["col1"][idx])))) for idx in range(len(d["col1"])) ]))
        }
    
    result = pd.DataFrame(data=expanded_d)
    
      col1  col2
    0    a     1
    1    s     1
    2    d     1
    3    f     1
    4    x     2
    5    y     2
    6    q     3
    

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      使用list + str.joinnp.repeat -

      pd.DataFrame(
      {
           'col1' : list(''.join(df.col1)), 
           'col2' : df.col2.values.repeat(df.col1.str.len(), axis=0)
      })
      
        col1  col2
      0    a     1
      1    s     1
      2    d     1
      3    f     1
      4    x     2
      5    y     2
      6    q     3
      

      任意数量的列的通用解决方案很容易实现,无需对解决方案进行太多更改 -

      i = list(''.join(df.col1))
      j = df.drop('col1', 1).values.repeat(df.col1.str.len(), axis=0)
      
      df = pd.DataFrame(j, columns=df.columns.difference(['col1']))
      df.insert(0, 'col1', i)
      
      df
      
        col1 col2
      0    a    1
      1    s    1
      2    d    1
      3    f    1
      4    x    2
      5    y    2
      6    q    3
      

      性能

      df = pd.concat([df] * 100000, ignore_index=True)
      

      # MaxU's solution
      
      %%timeit
      df.col1.str.extractall(r'(.)') \
                 .reset_index(level=1, drop=True) \
                 .join(df['col2']) \
                 .reset_index(drop=True)
      
      1 loop, best of 3: 1.98 s per loop
      

      # piRSquared's solution
      
      %%timeit
      pd.DataFrame(
           [[x] + b for a, *b in df.values for x in a],
           columns=df.columns
      )
      
      1 loop, best of 3: 1.68 s per loop
      

      # Wen's solution
      
      %%timeit
      v = df.col1.apply(list)
      pd.DataFrame({'col1':np.concatenate(v.values),'col2':df.col2.repeat(v.apply(len))})
      
      1 loop, best of 3: 835 ms per loop
      

      # Alexander's solution
      
      %%timeit
      pd.DataFrame([(letter, i) 
                    for letters, i in zip(df['col1'], df['col2']) 
                    for letter in letters],
                   columns=df.columns)
      
      1 loop, best of 3: 316 ms per loop
      

      %%timeit
      pd.DataFrame(
      {
           'col1' : list(''.join(df.col1)), 
           'col2' : df.col2.values.repeat(df.col1.str.len(), axis=0)
      })
      
      10 loops, best of 3: 124 ms per loop
      

      我尝试为 Vaishali 计时,但在此数据集上花费的时间太长。

      【讨论】:

      • 这里的所有解决方案都很好,我会将其标记为 dup :-),正如我们之前讨论过的,pandas 应该添加一个用于取消嵌套的小 api :-)
      【解决方案4】:

      具有列表理解和巧妙拆包的通用解决方案:

      pd.DataFrame(
          [[x] + b for a, *b in df.values for x in a],
          columns=df.columns
      )
      
        col1  col2
      0    a     1
      1    s     1
      2    d     1
      3    f     1
      4    x     2
      5    y     2
      6    q     3
      

      【讨论】:

      • 这很棒。目前,它需要1 loop, best of 3: 1.68 s per loop。完成后,我会为您的所有选项添加时间。
      • 我会坚持这个
      • 啊,在那种情况下,在我的答案中添加了计时结果!
      【解决方案5】:

      还有一个:)

      df.set_index('col2').col1.apply(lambda x: pd.Series(list(x))).stack()\
      .reset_index(1,drop = True).reset_index(name = 'col1')
      
          col2    col1
      0   1       a
      1   1       s
      2   1       d
      3   1       f
      4   2       x
      5   2       y
      6   3       q
      

      【讨论】:

        【解决方案6】:

        来自list 的技巧 :-)

        df.col1=df.col1.apply(list)
        df
        Out[489]: 
                   col1  col2
        0  [a, s, d, f]     1
        1        [x, y]     2
        2           [q]     3
        pd.DataFrame({'col1':np.concatenate(df.col1.values),'col2':df.col2.repeat(df.col1.apply(len))})
        Out[490]: 
          col1  col2
        0    a     1
        0    s     1
        0    d     1
        0    f     1
        1    x     2
        1    y     2
        2    q     3
        

        【讨论】:

        【解决方案7】:
        pd.DataFrame([(letter, i) 
                      for letters, i in zip(df['col1'], df['col2']) 
                      for letter in letters],
                     columns=df.columns)
        

        【讨论】:

          【解决方案8】:
          In [86]: df.col1.str.extractall(r'(.)') \
                     .reset_index(level=1, drop=True) \
                     .join(df['col2']) \
                     .reset_index(drop=True)
          Out[86]:
             0  col2
          0  a     1
          1  s     1
          2  d     1
          3  f     1
          4  x     2
          5  y     2
          6  q     3
          

          【讨论】:

            猜你喜欢
            • 2019-10-18
            • 2019-01-15
            • 2018-08-03
            • 1970-01-01
            • 2021-08-03
            • 1970-01-01
            • 2022-10-05
            • 2019-12-20
            相关资源
            最近更新 更多