在我看来,你有两个问题。
第一个是提出一种标准化输入的有效方法。你说你想找到输入中的所有三词短语,但是短语是由什么组成的呢?例如,有the black dog
and 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 的每个条目。