如何在验证文本列表时使正则表达式无序?
我的输入是这个数据框(其实也可以是一个简单的列表):
import pandas as pd
df = pd.DataFrame({'description': ['ij edf m-nop ij abc', 'abc ij mnop yz', 'yz yz mnop aa abc', 'i j y y abc xxx mnop y z', 'yz mnop ij kl abc uvwxyz', 'aaabc ijij uuu yz mnop']})
我还有一个关键词列表(大约有3到7个关键词),我需要对这些关键词进行验证。我们只需要验证这些关键词的确切组合,忽略它们之间的字符。问题是,这些关键词的顺序和我在列表中放的顺序不一致(这里是keywords
)。
我在谷歌和这里搜索过,但找不到类似主题的帖子。所以我写了下面的代码,它会对关键词进行排列组合,并把它们放入一个正则表达式字符串中。
import re
import itertools
keywords = ['abc', 'ij', 'mnop', 'yz']
regex = ''
for perm in list(itertools.permutations(keywords)):
perm = [fr'\b{key}\b' for key in perm]
regex += f'(?:{".*".join(perm)})|'
regex = regex.rstrip('|')
这是我的正则表达式的一部分:
# (?:\babc\b.*\bij\b.*\bmnop\b.*\byz\b)|(?:\babc\b.*\bij\b.*\byz\b.*\bmnop\b)|(?:\
# babc\b.*\bmnop\b.*\bij\b.*\byz\b)|(?:\babc\b.*\bmnop\b.*\byz\b.*\bij\b)|(?:\babc
# \b.*\byz\b.*\bij\b.*\bmnop\b)|(?:\babc\b.*\byz\b.*\bmnop\b.*\bij\b)|(?:\bij\b.*\
# babc\b.*\bmnop\b.*\byz\b)|(?:\bij\b.*\babc\b.*\byz\b.*\bmnop\b)|(?:\bij\b.*\bmno
# p\b.*\babc\b.*\byz\b)|(?:\bij\b.*\bmnop\b.*\byz\b.*\babc\b)|(?:\bij\b.*\byz\b.*\
# babc\b.*\bmnop\b)|(?:\bij\b.*\byz\b.*\bmnop\b.*\babc\b)|(?:\bmnop\b.*\babc\b.*\b
# ij\b.*\byz\b)|(?:\bmnop\b.*\babc\b.*\byz\b.*\bij\b)|(?:\bmnop\b.*\bij\b.*\babc\b
# .*\byz\b)|(?:\bmnop\b.*\bij\b.*\byz\b.*\babc\b)|(?:\bmnop\b.*\byz\b.*\babc\b.*\b
# ij\b)|(?:\bmnop\b.*\byz\b.*\bij\b.*\babc\b)|(?:\byz\b.*\babc\b.*\bij\b.*\bmnop\b
# )|(?:\byz\b.*\babc\b.*\bmnop\b.*\bij\b)|(?:\byz\b.*\bij\b.*\babc\b.*\bmnop\b)|(?
# :\byz\b.*\bij\b.*\bmnop\b.*\babc\b)|(?:\byz\b.*\bmnop\b.*\babc\b.*\bij\b)|(?:\by
# z\b.*\bmnop\b.*\bij\b.*\babc\b)
虽然在我给的例子中它能正常工作,但在我的真实数据集上(有5万行和很长的描述,里面还有换行符)运行需要5到15分钟,我不确定我的方法是否能正确处理所有行。而且还有一个问题,有时候我需要验证一个包含6个关键词的列表,这样会产生720种排列组合!
你们能帮我解决这个问题吗?使用正则表达式是否是解决我问题的正确方法?
我期望的输出是这样的:
description valid
0 ij edf m-nop ij abc
1 abc ij mnop yz True
2 yz yz mnop aa abc
3 i j y y abc xxx mnop y z
4 yz mnop ij kl abc uvwxyz True
5 aaabc ijij uuu yz mnop
1 个回答
3
正则表达式(regex)可以很有用,但生成所有可能的排列组合并不合适。
我会用正则表达式来提取单词,然后检查这些关键词是否是提取出来的单词的一个子集,可以使用set.issubset
:
import re
keywords = {'abc', 'ij', 'mnop', 'yz'} # this is a SET
reg = re.compile(r'\b[a-z]+\b', flags=re.I)
df['valid'] = [keywords.issubset(reg.findall(x)) for x in df['description']]
注意:你可能想加一个casefold
的步骤来忽略大小写。
输出结果:
description valid
0 ij edf m-nop ij abc False
1 abc ij mnop yz True
2 yz yz mnop aa abc False
3 i j y y abc xxx mnop y z False
4 yz mnop ij kl abc uvwxyz True
5 aaabc ijij uuu yz mnop False
为了好玩,你可以通过调整代码,甚至得到缺失单词的集合,而不是False
:
df['valid'] = [keywords.issubset(S:=set(reg.findall(x))) or keywords-S
for x in df['description']]
description valid
0 ij edf m-nop ij abc {mnop, yz}
1 abc ij mnop yz True
2 yz yz mnop aa abc {ij}
3 i j y y abc xxx mnop y z {yz, ij}
4 yz mnop ij kl abc uvwxyz True
5 aaabc ijij uuu yz mnop {abc, ij}
# or
df['missing'] = [keywords-set(reg.findall(x)) for x in df['description']]
df['valid'] = df['missing'].eq(set())
description missing valid
0 ij edf m-nop ij abc {mnop, yz} False
1 abc ij mnop yz {} True
2 yz yz mnop aa abc {ij} False
3 i j y y abc xxx mnop y z {yz, ij} False
4 yz mnop ij kl abc uvwxyz {} True
5 aaabc ijij uuu yz mnop {abc, ij} False