【问题标题】:Merge generator objects to calculate frequency in NLTK合并生成器对象以计算 NLTK 中的频率
【发布时间】:2018-03-09 01:14:32
【问题描述】:

我正在尝试使用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。 有没有更蟒蛇的方式来正确地做到这一点?

【问题讨论】:

    标签: python-2.7 nltk generator word-frequency


    【解决方案1】:

    使用everygrams,它返回给定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')]
    

    结合计算不同顺序的ngram:

    >>> 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, ...})
    

    或者,FreqDist is essentially a collections.Counter sub-class,因此您可以像这样组合计数器:

    >>> 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})
    

    【讨论】:

      【解决方案2】:

      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 函数接受它们而不是列表,从而为您节省大量空间和时间。

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

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

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

      【讨论】:

      • 虽然我接受了上述答案,因为它是这项工作的正确工具,但感谢您提供极其相关的信息和解释。我一直在努力使用生成器,现在我对使用和加入它们的不同方法有了更清晰的认识。
      猜你喜欢
      • 2012-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-21
      • 2020-11-06
      • 1970-01-01
      相关资源
      最近更新 更多