如何在验证文本列表时使正则表达式无序?

2 投票
1 回答
39 浏览
提问于 2025-04-13 18:10

我的输入是这个数据框(其实也可以是一个简单的列表):

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

撰写回答