Вычислить нграммы по списку списков предложений, используя nltk

У меня есть список списков, где каждый внутренний список представляет собой предложение, которое разбито на слова:

sentences = [['farmer', 'plants', 'grain'], 
             ['fisher', 'catches', tuna'], 
             ['police', 'officer', 'fights', 'crime']]

В настоящее время я пытаюсь вычислить nGrams следующим образом:

numSentences = len(sentences)
nGrams = []
for i in range(0, numSentences):
       nGrams.append(list(ngrams(sentences, 2)))

Это приводит к поиску биграмм всего списка, а не отдельных слов для каждого внутреннего списка (и это повторяется для количества предложений, что несколько предсказуемо):

[[(['farmer', 'plants', 'grain'], ['fisher', 'catches', tuna']),
  (['fisher', 'catches', tuna'], ['police', 'officer', 'fights', 'crime'])], 
[(['farmer', 'plants', 'grain'], ['fisher', 'catches', tuna']), 
 (['fisher', 'catches', tuna'], ['police', 'officer', 'fights', 'crime'])], 
[(['farmer', 'plants', 'grain'], ['fisher', 'catches', tuna']), 
 (['fisher', 'catches', tuna'], ['police', 'officer', 'fights', 'crime'])]]

Как вычислить nGrams каждого предложения (по слову)? Другими словами, как я могу гарантировать, что nGrams не охватывают несколько элементов списка? Вот мой желаемый результат:

farmer plants
plants grain
fisher catches
catches tuna
police officer
officer fights
fights crime

person OverflowingTheGlass    schedule 12.05.2017    source источник


Ответы (3)


Возьмите энграммы каждого предложения и просуммируйте результаты вместе. Вы, вероятно, хотите их пересчитать, а не хранить в огромной коллекции. Начиная с sentences в виде списка списков слов:

counts = collections.Counter()   # or nltk.FreqDist()
for sent in sentences:
    counts.update(nltk.ngrams(sent, 2))

Или, если вы предпочитаете одну строку, а не кортеж, ваш ключ:

for sent in sentences:
    count.update(" ".join(n) for n in nltk.ngrams(sent, 2))

Вот и все. Затем вы можете увидеть самые распространенные из них и т. д.

print(counts.most_common(10))

PS. Если бы вы действительно хотели накапливать биграммы, вы бы сделали это так. (Ваш код формирует «биграммы» предложений, а не слов, потому что вы забыли написать sentences[i].) Но пропустите этот шаг и просто посчитайте их напрямую.

all_ngrams = []
for sent in sentences:
    all_ngrams.extend(nltk.ngrams(sent, 2))
person alexis    schedule 12.05.2017

Вы также можете рассмотреть возможность использования scikit-learn CountVectorizer в качестве альтернативы.

from sklearn.feature_extraction.text import CountVectorizer

sents = list(map(lambda x: ' '.join(x), sentences)) # input is a list of sentences so I map join first
count_vect = CountVectorizer(ngram_range=(2,2)) # bigram
count_vect.fit(sents)
count_vect.vocabulary_

Это даст вам:

{'catches tuna': 0,
 'farmer plants': 1,
 'fights crime': 2,
 'fisher catches': 3,
 'officer fights': 4,
 'plants grain': 5,
 'police officer': 6}
person titipata    schedule 12.05.2017

Используйте понимание списков и chain, чтобы сгладить список:

>>> from itertools import chain
>>> from collections import Counter
>>> from nltk import ngrams

>>> x = [['farmer', 'plants', 'grain'], ['fisher', 'catches', 'tuna'], ['police', 'officer', 'fights', 'crime']]

>>> Counter(chain(*[ngrams(sent,2) for sent in x]))
Counter({('plants', 'grain'): 1, ('police', 'officer'): 1, ('farmer', 'plants'): 1, ('officer', 'fights'): 1, ('fisher', 'catches'): 1, ('fights', 'crime'): 1, ('catches', 'tuna'): 1})

>>> c = Counter(chain(*[ngrams(sent,2) for sent in x]))

Получите ключи словаря Counter:

>>> c.keys()
[('plants', 'grain'), ('police', 'officer'), ('farmer', 'plants'), ('officer', 'fights'), ('fisher', 'catches'), ('fights', 'crime'), ('catches', 'tuna')]

Объединять строки пробелами

>>> [' '.join(b) for b in c.keys()]
['plants grain', 'police officer', 'farmer plants', 'officer fights', 'fisher catches', 'fights crime', 'catches tuna']
person alvas    schedule 12.05.2017