【问题标题】:How to speed up pandas apply for string matching如何加快pandas申请字符串匹配
【发布时间】:2020-08-12 13:57:10
【问题描述】:

我有大量文件,我必须根据字符串列进行计算。相关列如下所示。

df = pd.DataFrame({'A': ['A', 'B', 'A', 'B'], 'B': ['B', 'C', 'D', 'A'], 'C': ['A', 'B', 'D', 'D'], 'D': ['A', 'C', 'C', 'B'],})

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

我必须创建新列,其中包含每行中某些字符串的出现次数。我这样做:

for elem in ['A', 'B', 'C', 'D']:
    df['n_{}'.format(elem)] = df[['A', 'B', 'C', 'D']].apply(lambda x: (x == elem).sum(), axis=1)

   A  B  C  D  n_A  n_B  n_C  n_D
0  A  B  A  A    3    1    0    0
1  B  C  B  C    0    2    2    0
2  A  D  D  C    1    0    1    2
3  B  A  D  B    1    2    0    1

但是,每个文件都需要几分钟,我必须为大约 900 个文件执行此操作。有什么办法可以加快速度吗?

【问题讨论】:

  • 列名和值相同的原因..?
  • 不,我只是为了简单而这样做

标签: python pandas apply


【解决方案1】:

使用stack + str.get_dummies,然后在level=0join 上使用sumdf

df1 = df.join(df.stack().str.get_dummies().sum(level=0).add_prefix('n_'))

结果:

print(df1)
   A  B  C  D  n_A  n_B  n_C  n_D
0  A  B  A  A    3    1    0    0
1  B  C  B  C    0    2    2    0
2  A  D  D  C    1    0    1    2
3  B  A  D  B    1    2    0    1

【讨论】:

    【解决方案2】:

    我没有使用apply 循环遍历每一行,而是遍历每一列来计算每个字母的总和:

    for l in ['A','B','C','D']:
        df['n_' + l] = (df == l).sum(axis=1)
    

    这似乎是本示例中的改进,但(从未显示的快速测试中)似乎它可能等于或更差,具体取决于数据的形状和大小(可能还有您要查找的字符串数量)

    一些时间比较:

    %%timeit
    for elem in ['A', 'B', 'C', 'D']:
        df['n_{}'.format(elem)] = df[['A', 'B', 'C', 'D']].apply(lambda x: (x == elem).sum(), axis=1)    
    #6.77 ms ± 145 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %%timeit
    for l in ['A','B','C','D']:
        df['n_' + l] = (df == l).sum(axis=1)
    #1.95 ms ± 17 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    这里还有其他答案:

    %%timeit
    df1 = df.join(df.stack().str.get_dummies().sum(level=0).add_prefix('n_'))
    #3.59 ms ± 62.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %%timeit
    df1=df.join(pd.get_dummies(df,prefix='n',prefix_sep='_').sum(1,level=0))
    #5.82 ms ± 52.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    
    %%timeit
    counts = df.apply(lambda s: s.value_counts(), axis=1).fillna(0)
    counts.columns = [f'n_{col}' for col in counts.columns]
    df.join(counts)
    #5.58 ms ± 71.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    【讨论】:

    • 谢谢,这个方法是最快的,也靠实际数据。我必须为 250 个字符串值(创建 250 个新列)执行此操作,检查 10 列中的字符串。我的方式是 757 秒,你的方式是 5.9 秒。大幅改进!
    【解决方案3】:

    试试 get_dummiessumlevel ,这里我们不需要 stack :-)

    df=df.join(pd.get_dummies(df,prefix='n',prefix_sep='_').sum(1,level=0))
    Out[57]: 
       A  B  C  D  n_A  n_B  n_C  n_D
    0  A  B  A  A    3    1    0    0
    1  B  C  B  C    0    2    2    0
    2  A  D  D  C    1    0    1    2
    3  B  A  D  B    1    2    0    1
    

    【讨论】:

      【解决方案4】:

      你可以这样做:

      counts = df.apply(lambda s: s.value_counts(), axis=1).fillna(0)
      counts.columns = [f'n_{col}' for col in counts.columns]
      df.join(counts)
      

      【讨论】:

        猜你喜欢
        • 2020-05-17
        • 2016-06-09
        • 2018-12-01
        • 2017-12-12
        • 2014-09-21
        • 2017-07-15
        • 1970-01-01
        • 2019-06-27
        • 2020-10-01
        相关资源
        最近更新 更多