python - Pandas 过滤串联的多个子字符串

标签 python string pandas dataframe series

我需要过滤 pandas 数据框中的行,以便特定字符串列至少包含所提供的子字符串列表之一。子字符串可能包含不寻常/正则表达式字符。比较不应涉及正则表达式且不区分大小写。

例如:

lst = ['kdSj;af-!?', 'aBC+dsfa?\-', 'sdKaJg|dksaf-*']

我目前这样敷面膜:

mask = np.logical_or.reduce([df[col].str.contains(i, regex=False, case=False) for i in lst])
df = df[mask]

我的数据框很大(~1mio 行),并且 lst 的长度为 100。是否有更有效的方法?例如,如果找到 lst 中的第一项,我们不必测试该行的任何后续字符串。

最佳答案

如果您坚持使用纯 Pandas ,出于性能和实用性的考虑,我认为您应该使用正则表达式来完成此任务。但是,您需要首先正确转义子字符串中的任何特殊字符,以确保它们按字面匹配(并且不用作正则表达式元字符)。

使用 re.escape 可以轻松做到这一点:

>>> import re
>>> esc_lst = [re.escape(s) for s in lst]

然后可以使用正则表达式管道 | 连接这些转义的子字符串。可以根据字符串检查每个子字符串,直到有一个匹配(或者它们都已被测试)。

>>> pattern = '|'.join(esc_lst)

然后,屏蔽阶段变成通过行的单个低级循环:

df[col].str.contains(pattern, case=False)
<小时/>

这里有一个简单的设置来了解性能:

from random import randint, seed

seed(321)

# 100 substrings of 5 characters
lst = [''.join([chr(randint(0, 256)) for _ in range(5)]) for _ in range(100)]

# 50000 strings of 20 characters
strings = [''.join([chr(randint(0, 256)) for _ in range(20)]) for _ in range(50000)]

col = pd.Series(strings)
esc_lst = [re.escape(s) for s in lst]
pattern = '|'.join(esc_lst)

建议的方法大约需要 1 秒(因此对于 100 万行可能最多需要 20 秒):

%timeit col.str.contains(pattern, case=False)
1 loop, best of 3: 981 ms per loop

问题中的方法使用相同的输入数据大约需要 5 秒。

值得注意的是,这些时间是“最坏情况”,因为没有匹配项(因此所有子字符串都被检查)。如果有匹配的话,时间将会改善。

关于python - Pandas 过滤串联的多个子字符串,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52299082/

相关文章:

java - 字符串不变性

C:从文件读取有问题

php - 在文本 block 中查找并替换多个不同的关键字

python - Pandas "columns of child subrecords (as lists of dicts)"到更传统的 "SQL Join"输出样式

python - 在我的 Eclipse 项目中重命名源文件logging.py之一后,无法导入logging.py

python - 导入错误: cannot import name 'SimpleImputer'

python - 如何计算 Pandas 系列中重复出现的相同值

python - 计算两个数据帧之间的半正矢距离

Python 脚本仅使用 "python"命令运行

python - 关键字不能是表达式 - 在 sqlalchemy 中过滤