使用单个正则表达式匹配多个重叠的n-gram

时间:2012-10-23 07:51:10

标签: python regex search nlp n-gram

我正在尝试执行正则表达式以匹配文档中的多个n-gram。 我首先得到一个n-gram列表,我将它们编译成正则表达式,如下所示:

sNgrams = '|'.join(('\s+'.join(re.escape(gram) for gram in nGram.split())) for nGram in aNgrams)

(将n-gram拆分为任何空格字符上的标记,重新设置这些标记并将它们与'\ s +' - es连接起来(所以我可以在新行,双空格,制表符和诸如此类的地方匹配ngrams),以及然后用正则表达式的'|'加入n-gram

我的正则表达式如下:

reNgram = re.compile('(\A|\W+)(' + sNgrams + ')(?=\W+|\Z)',flags=re.UNICODE|re.IGNORECASE)

现在,这适用于大多数情况,但是,当n-gram与另一个重叠时,只找到一个匹配:

doc = 'aap noot mies'

aNgrams = ['aap','noot','aap noot']
sNgrams = 'aap|noot|aap\\s+noot'
re.findall(reNgram,doc)
[('', 'aap'), (' ', 'noot')]

aNgrams = ['mies','aap noot']
re.findall(reNgram,doc)
[('', 'aap noot'), (' ', 'mies')]
  • 有什么方法可以解决这个问题吗?返回匹配的所有(子)字符串 在文件中?

  • 此外,速度/效率非常重要 (我正在解雇成千上万的这些正则表达式),有什么我的 可以做优化吗?我已经读过预编译正则表达式没有 做得多,因为无论如何都会缓存“即时”编译的正则表达式,是否有任何(其他)明显的步骤可以加速这些表达式?

1 个答案:

答案 0 :(得分:3)

我认为你不能用一个正则表达式做到这一点。

你可以靠近一点

  • 使用先行断言来找到至少那些不从同一位置开始的重叠匹配
  • 按长度对n-gram进行降序排序,以确保首先找到较大的匹配

现在,可以找到noot之后的实际重叠匹配(app noot):

>>> sNgrams = '|'.join(('\s+'.join(re.escape(gram) 
...                    for gram in nGram.split())) 
...                    for nGram in reversed(sorted(aNgrams, key=len)))
>>> sNgrams
'aap\\s+noot|noot|aap'
>>> reNgrams = re.compile(r"(?<!\w)(?=(" + sNgrams + r")(?!\w))",
...                         flags=re.UNICODE|re.IGNORECASE)
>>> reNgrams.findall(doc)
['aap noot', 'noot']

但它仍然找不到aapaap noot。正则表达式只能在字符串中的每个位置报告一个匹配项,因此它必须与两个中的一个匹配。

要解决此问题,您必须将n-gram列表拆分为列表,其中没有字符串以相同的单词开头,并按顺序应用这些正则表达式。我怀疑这不会很高效,但我没有看到任何其他方式(除了检查自己的正则表达式中的每个单词,并且这也不会非常快)。