如何加速 Pandas 中用于字符串匹配的 apply 函数

5

我有大量的文件需要根据字符串列进行计算。相关的列看起来像这样。

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个这样的文件。有没有办法可以加快处理速度?


列名和值相同是有原因的吗? - Adam.Er8
不,我只是为了简单起见这样做的。 - Wouter
4个回答

6
使用 stack + str.get_dummies,然后在 level=0 上使用 sum,再与 df 进行 join
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

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

3

我没有使用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)

1
谢谢,这个方法是最快的,而且在实际数据上也是如此。我必须对250个字符串值(创建250个新列)进行操作,在10个列中检查字符串。我的方法需要757秒,而你的只需要5.9秒。巨大的改进! - Wouter

1

you could do:

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接