我需要过滤 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/59279772/