【发布时间】:2010-12-24 14:11:45
【问题描述】:
我有一个问题,我不知道如何解决它。请给点建议。
我有一个文本。大,大的文字。任务是找到文本中所有长度为3(包含三个单词)的重复短语。
【问题讨论】:
-
具体来说,很难遇到大到明显算法不起作用的文本(列出所有三词短语并计算它们)。
我有一个问题,我不知道如何解决它。请给点建议。
我有一个文本。大,大的文字。任务是找到文本中所有长度为3(包含三个单词)的重复短语。
【问题讨论】:
在我看来,你有两个问题。
首先是想出一种有效的方法来规范化输入。你说你想在输入中找到所有的三词短语,但是什么是短语?例如,the black dog 和 The black, dog? 是同一个短语吗?
正如 marcog 建议的那样,一种方法是使用类似re.findall 的东西。但这非常低效:它遍历您的整个输入并将单词复制到一个列表中,然后您必须处理该列表。如果你的输入文本很长,那会浪费时间和空间。
更好的方法是将输入视为流,并构建一个一次提取一个单词的生成器。这是一个示例,它使用空格作为单词之间的分隔符,然后从单词中去除非字母字符并将它们转换为小写:
>>> def words(text):
pattern = re.compile(r"[^\s]+")
non_alpha = re.compile(r"[^a-z]", re.IGNORECASE)
for match in pattern.finditer(text):
nxt = non_alpha.sub("", match.group()).lower()
if nxt: # skip blank, non-alpha words
yield nxt
>>> text
"O'er the bright blue sea, for Sir Joseph Porter K.C.B."
>>> list(words(text))
['oer', 'the', 'bright', 'blue', 'sea', 'for', 'sir', 'joseph', 'porter', 'kcb']
第二个问题是将规范化的单词分组为三个单词的短语。同样,这里是生成器可以高效执行的地方:
>>> def phrases(words):
phrase = []
for word in words:
phrase.append(word)
if len(phrase) > 3:
phrase.remove(phrase[0])
if len(phrase) == 3:
yield tuple(phrase)
>>> list(phrases(words(text)))
[('oer', 'the', 'bright'), ('the', 'bright', 'blue'), ('bright', 'blue', 'sea'), ('blue', 'sea', 'for'), ('sea', 'for', 'sir'), ('for', 'sir', 'joseph'), ('sir', 'joseph', 'porter'), ('joseph', 'porter', 'kcb')]
几乎可以肯定,该功能可能有一个更简单的版本,但这个功能很有效,而且不难理解。
值得注意的是,将生成器链接在一起只会遍历列表一次,并且不会在内存中构建任何大型临时数据结构。您可以使用结果构建一个以短语为键的defaultdict:
>>> import collections
>>> counts = collections.defaultdict(int)
>>> for phrase in phrases(words(text)):
counts[phrase] += 1
这会在计算短语时跳过text。完成后,查找字典中值大于 1 的每个条目。
【讨论】:
collections.Counter(它是most_common 方法)而不是collections.defaultdict。但 AFAIK 这仅适用于 Python > 2.7 docs.python.org/library/collections.html#collections.Counter
最粗略的方法是读取字符串中的文本。执行 string.split() 并获取列表中的单个单词。然后,您可以按三个单词对列表进行切片,并使用 collections.defaultdict(int) 来保持计数。
d = collections.defaultdict(int)
d[短语]+=1
正如我所说,它非常粗糙。但肯定会让你开始
【讨论】:
re.findall(r"[\w']+", "Hello, world!") 会是一个更好的起点。
我建议查看 NLTK 工具包。这是开源的,旨在用于自然语言教学。除了更高级别的 NLP 函数,它还有很多标记类型的函数和集合。
【讨论】:
这是一个大约 O(n) 的解决方案,它应该适用于相当大的输入文本。如果速度太慢,您可能需要考虑使用专为文本处理而设计的 Perl 或纯性能的 C++。
>>> s = 'The quick brown fox jumps over the lazy dog'
>>> words = string.lower(s).split()
>>> phrases = collections.defaultdict(int)
>>> for a, b, c in zip(words[:-3], words[1:-2], words[2:]):
... phrases[(a, b, c)] += 1
...
>>> phrases
defaultdict(<type 'int'>, {('over', 'the', 'lazy'): 1, ('quick', 'brown', 'fox'): 1, ('the', '
quick', 'brown'): 1, ('jumps', 'over', 'the'): 1, ('brown', 'fox', 'jumps'): 1, ('fox', 'jumps
', 'over'): 1})
>>> [phrase for phrase, count in phrases.iteritems() if count > 1]
>>> []
【讨论】: