查找短语共生矩阵的有效算法

时间:2013-04-13 00:14:05

标签: python regex algorithm

我有一个大约40,000个短语的列表L和一个大约1000万字的文档。我要检查的是在4个单词的窗口中出现哪一对这些短语。例如,考虑L = [“棕色狐狸”,“懒狗”]。该文件中包含“快速的棕色狐狸跳过懒狗”的字样。我想看看,有多少次棕色狐狸和懒狗出现在四个单词的窗口中并将其存储在一个文件中。我有以下代码:

content=open("d.txt","r").read().replace("\n"," ");
for i in range(len(L)):
 for j in range(i+1,len(L)):
  wr=L[i]+"\W+(?:\w+\W+){1,4}"+L[j]
  wrev=L[j]+"\W+(?:\w+\W+){1,4}"+L[i]
  phrasecoccur=len(re.findall(wr, content))+len(re.findall(wrev,content))
  if (phrasecoccur>0):
    f.write(L[i]+", "+L[j]+", "+str(phrasecoccur)+"\n")

基本上,对于列表L中的每对短语,我在文档内容中检查这些短语在4个单词的窗口中出现的次数。然而,当列表L非常大时,例如40K元素,该方法在计算上是低效的。有没有更好的方法呢?

3 个答案:

答案 0 :(得分:3)

您可以使用与Aho-Corasick string matching algorithm类似的内容。从短语列表中构建状态机。然后开始向状态机输入单词。每当匹配发生时,状态机将告诉您哪个短语匹配以及哪个单词编号。所以你的输出将是这样的:

"brown fox", 3
"lazy dog", 8
etc.

您可以捕获所有输出并对其进行后处理,也可以在找到匹配项时对其进行处理。

构建状态机需要一些时间(40,000个短语需要几秒钟),但之后输入令牌数量,短语数量和匹配数量呈线性关系。

我使用类似的东西来匹配5000万个YouTube视频,以及MusicBrainz数据库中的数百万首歌曲和艺术家名称。工作得很好。并且非常快。

答案 1 :(得分:1)

应该可以将40000短语组合成一个大的正则表达式模式,并使用它来匹配您的文档。它可能没有特定于工作的那么快,但确实有效。我就是这样做的:

import re

class Matcher(object):
    def __init__(self, phrases):
        phrase_pattern = "|".join("(?:{})".format(phrase) for phrase in phrases)
        gap_pattern = r"\W+(?:\w+\W+){0,4}?"
        full_pattern = "({0}){1}({0})".format(phrase_pattern, gap_pattern)

        self.regex = re.compile(full_pattern)

    def match(self, doc):
        return self.regex.findall(doc) # or use finditer to generate match objs

以下是如何使用它:

>>> L = ["brown fox", "lazy dog"]
>>> matcher = Matcher(L)
>>> doc = "The quick brown fox jumps over the lazy dog."
>>> matcher.match(doc)
[('brown fox', 'lazy dog')]

此解决方案确实有一些限制。一个是它不会检测重叠的短语对。因此,在示例中,如果您将短语"jumps over"添加到词组列表中,您仍然只能获得一个匹配的对("brown fox", "jumps over")。它会遗漏("brown fox", "lazy dog")("jumps over", "lazy dog"),因为它们包含一些相同的单词。

答案 2 :(得分:0)

扩展Joel的答案,你的迭代器可能是这样的:

def doc_iter(doc):
  words=doc[0:4]
  yield words
  for i in range(3,len(doc)):
    words=words[1:]
    words.append(doc[i])
    yield words

将你的短语放在dict中并在文档上使用迭代器,在每次迭代时检查短语。这应该可以提供O(n)和O(n * log(n))之间的性能。