合并生成器对象以计算 NLTK 中的频率

Merge generator objects to calculate frequency in NLTK

我正在尝试使用 nltk 中的 ngramfreqDist 函数计算各种 ngrams 的频率。 由于 ngram 函数输出是一个 generator 对象,我想在计算频率之前合并每个 ngram 的输出。 但是,我 运行 遇到了合并各种生成器对象的问题。

我已经尝试 itertools.chain,它创建了一个 itertools 对象,而不是合并生成器。 我终于选择了 permutations,但之后解析对象似乎是多余的。

到目前为止的工作代码是:

import nltk
from nltk import word_tokenize, pos_tag
from nltk.collocations import *
from itertools import *
from nltk.util import ngrams
import re
corpus = 'testing sentences to see if if if this works'
token = word_tokenize(corpus)
unigrams = ngrams(token,1)
bigrams = ngrams(token,2)
trigrams = ngrams(token,3)


perms = list(permutations([unigrams,bigrams,trigrams]))
fdist = nltk.FreqDist(perms)
for x,y in fdist.items():
    for k in x:
        for v in k:
            words = '_'.join(v)
            print words, y

正如您在结果中看到的那样,freq dist 没有正确计算来自各个生成器对象的单词,因为每个生成器对象的频率都是 1。 有没有更 pythonic 的方法来正确地做到这一点?

使用everygrams,它returns给定范围n的所有n-gram。

>>> from nltk import everygrams
>>> from nltk import FreqDist
>>> corpus = 'testing sentences to see if if if this works'
>>> everygrams(corpus.split(), 1, 3)
<generator object everygrams at 0x7f4e272e9730>
>>> list(everygrams(corpus.split(), 1, 3))
[('testing',), ('sentences',), ('to',), ('see',), ('if',), ('if',), ('if',), ('this',), ('works',), ('testing', 'sentences'), ('sentences', 'to'), ('to', 'see'), ('see', 'if'), ('if', 'if'), ('if', 'if'), ('if', 'this'), ('this', 'works'), ('testing', 'sentences', 'to'), ('sentences', 'to', 'see'), ('to', 'see', 'if'), ('see', 'if', 'if'), ('if', 'if', 'if'), ('if', 'if', 'this'), ('if', 'this', 'works')]

合并ngrams不同顺序的计数:

>>> from nltk import everygrams
>>> from nltk import FreqDist
>>> corpus = 'testing sentences to see if if if this works'.split()
>>> fd = FreqDist(everygrams(corpus, 1, 3))
>>> fd
FreqDist({('if',): 3, ('if', 'if'): 2, ('to', 'see'): 1, ('sentences', 'to', 'see'): 1, ('if', 'this'): 1, ('to', 'see', 'if'): 1, ('works',): 1, ('testing', 'sentences', 'to'): 1, ('sentences', 'to'): 1, ('sentences',): 1, ...})

或者,,因此您可以这样组合计数器:

>>> from collections import Counter
>>> x = Counter([1,2,3,4,4,5,5,5])
>>> y = Counter([1,1,1,2,2])
>>> x + y
Counter({1: 4, 2: 3, 5: 3, 4: 2, 3: 1})
>>> x

>>> from nltk import FreqDist
>>> FreqDist(['a', 'a', 'b'])
FreqDist({'a': 2, 'b': 1})
>>> a = FreqDist(['a', 'a', 'b'])
>>> b = FreqDist(['b', 'b', 'c', 'd', 'e'])
>>> a + b
FreqDist({'b': 3, 'a': 2, 'c': 1, 'e': 1, 'd': 1})

Alvas 是对的,nltk.everygrams 是完成这项工作的完美工具。但是合并多个迭代器确实没有那么难,也没有那么罕见,所以您应该知道如何去做。关键是任何迭代器都可以转换为列表,但最好只转换一次:

用几个迭代器制作一个列表

  1. 就用列表(简单但效率低)

    allgrams = list(unigrams) + list(bigrams) + list(trigrams)
    
  2. 或者构建一个列表,正确

    allgrams = list(unigrams)
    allgrams.extend(bigrams)
    allgrams.extend(trigrams)
    
  3. 或使用itertools.chain(),然后做一个列表

    allgrams = list(itertools.chain(unigrams, bigrams, trigrams))
    

以上产生相同的结果(只要您不尝试重用迭代器 unigrams 等——您需要在示例之间重新定义它们)。

自己使用迭代器

不要与迭代器作对,学会与他们一起工作。许多 Python 函数接受它们而不是列表,为您节省很多 space 和时间。

  1. 您可以形成一个迭代器并将其传递给 nltk.FreqDist():

    fdist = nltk.FreqDist(itertools.chain(unigrams, bigrams, trigrams))
    
  2. 您可以使用多个迭代器。 FreqDistCounter 一样,有一个 update() 方法可以用来递增计数:

    fdist = nltk.FreqDist(unigrams)
    fdist.update(bigrams)
    fdist.update(trigrams)