3

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

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 个元素)时,这种方法的计算效率很低。有没有更好的方法来做到这一点?

4

3 回答 3

3

您可以使用类似于Aho-Corasick 字符串匹配算法的东西。从您的短语列表中构建状态机。然后开始将单词输入状态机。每当发生匹配时,状态机都会告诉您匹配的短语和单词编号。所以你的输出会是这样的:

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

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

构建状态机需要一点时间(40,000 个短语需要几秒钟),但在那之后,输入标记的数量、短语的数量和匹配的数量是线性的。

我使用类似的方法将 5000 万个 YouTube 视频标题与 MusicBrainz 数据库中的数百万个歌曲标题和艺术家姓名进行匹配。工作得很好。而且非常快。

于 2013-04-13T04:28:32.637 回答
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")and ("jumps over", "lazy dog"),因为它们包含一些相同的词。

于 2013-04-13T08:24:35.633 回答
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

将您的短语放在字典中并在文档上使用迭代器,在每次迭代时检查短语。这应该会给你 O(n) 和 O(n*log(n)) 之间的性能。

于 2013-04-13T00:55:57.010 回答