Подтвердить что ты не робот

NLTK - частота подсчета Bigram

Это вопрос с новичком на Python и NLTK.

Я хочу найти частоту биграмм, которые встречаются более 10 раз вместе и имеют самый высокий PMI.

Для этого я работаю с этим кодом

def get_list_phrases(text):

    tweet_phrases = []

    for tweet in text:
        tweet_words = tweet.split()
        tweet_phrases.extend(tweet_words)


    bigram_measures = nltk.collocations.BigramAssocMeasures()
    finder = BigramCollocationFinder.from_words(tweet_phrases,window_size = 13)
    finder.apply_freq_filter(10)
    finder.nbest(bigram_measures.pmi,20)  

    for k,v in finder.ngram_fd.items():
      print(k,v)

Однако это не ограничивает результаты до 20. Я вижу результаты, которые имеют частоту < 10. Я новичок в мире Python.

Кто-нибудь может указать, как изменить это, чтобы получить только верхнюю часть 20.

Спасибо

4b9b3361

Ответ 1

Проблема заключается в том, как вы пытаетесь использовать apply_freq_filter. Мы обсуждаем словосочетания. Как вы знаете, словосочетание подразумевает зависимость между словами. Класс BigramCollocationFinder наследуется от класса с именем AbstractCollocationFinder, а функция apply_freq_filter принадлежит этому классу. apply_freq_filter не должен полностью удалять некоторые словосочетания, но предоставлять отфильтрованный список коллокаций, если некоторые другие функции пытаются получить доступ к списку.

Теперь почему? Представьте себе, что если фильтрация collocations просто их удаляла, тогда было много вероятностных мер, таких как отношение правдоподобия или самого PMI (которые вычисляют вероятность слова относительно других слов в корпусе), который не будет функционировать должным образом после удаления слов из случайных позиций в данном корпусе. Удалив некоторые совпадения из данного списка слов, многие потенциальные функции и вычисления будут отключены. Кроме того, вычисление всех этих мер до удаления приведет к массированным издержкам вычислений, которые пользователю, возможно, не понадобится в конце концов.

Теперь вопрос в том, как правильно использовать apply_freq_filter function? Есть несколько способов. Ниже я покажу проблему и ее решение.

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

tweet_phrases = "I love iphone . I am so in love with iphone . iphone is great . samsung is great . iphone sucks. I really really love iphone cases. samsung can never beat iphone . samsung is better than apple"
from nltk.collocations import *
import nltk

Для экспериментов я устанавливаю размер окна 3:

finder = BigramCollocationFinder.from_words(tweet_phrases.split(), window_size = 3)
finder1 = BigramCollocationFinder.from_words(tweet_phrases.split(), window_size = 3)

Обратите внимание, что для сравнения я использую фильтр только на finder1:

finder1.apply_freq_filter(2)
bigram_measures = nltk.collocations.BigramAssocMeasures()

Теперь, если я пишу:

for k,v in finder.ngram_fd.items():
  print(k,v)

Вывод:

(('.', 'is'), 3)
(('iphone', '.'), 3)
(('love', 'iphone'), 3)
(('.', 'iphone'), 2)
(('.', 'samsung'), 2)
(('great', '.'), 2)
(('iphone', 'I'), 2)
(('iphone', 'samsung'), 2)
(('is', '.'), 2)
(('is', 'great'), 2)
(('samsung', 'is'), 2)
(('.', 'I'), 1)
(('.', 'am'), 1)
(('.', 'sucks.'), 1)
(('I', 'am'), 1)
(('I', 'iphone'), 1)
(('I', 'love'), 1)
(('I', 'really'), 1)
(('I', 'so'), 1)
(('am', 'in'), 1)
(('am', 'so'), 1)
(('beat', '.'), 1)
(('beat', 'iphone'), 1)
(('better', 'apple'), 1)
(('better', 'than'), 1)
(('can', 'beat'), 1)
(('can', 'never'), 1)
(('cases.', 'can'), 1)
(('cases.', 'samsung'), 1)
(('great', 'iphone'), 1)
(('great', 'samsung'), 1)
(('in', 'love'), 1)
(('in', 'with'), 1)
(('iphone', 'cases.'), 1)
(('iphone', 'great'), 1)
(('iphone', 'is'), 1)
(('iphone', 'sucks.'), 1)
(('is', 'better'), 1)
(('is', 'than'), 1)
(('love', '.'), 1)
(('love', 'cases.'), 1)
(('love', 'with'), 1)
(('never', 'beat'), 1)
(('never', 'iphone'), 1)
(('really', 'iphone'), 1)
(('really', 'love'), 1)
(('samsung', 'better'), 1)
(('samsung', 'can'), 1)
(('samsung', 'great'), 1)
(('samsung', 'never'), 1)
(('so', 'in'), 1)
(('so', 'love'), 1)
(('sucks.', 'I'), 1)
(('sucks.', 'really'), 1)
(('than', 'apple'), 1)
(('with', '.'), 1)
(('with', 'iphone'), 1)

Я получу тот же результат, если я напишу то же самое для finder1. Итак, на первый взгляд фильтр не работает. Однако посмотрите, как это работает: трюк заключается в использовании score_ngrams.

Если я использую score_ngrams на finder, это будет:

finder.score_ngrams (bigram_measures.pmi)

а выход:

[(('am', 'in'), 5.285402218862249), (('am', 'so'), 5.285402218862249), (('better', 'apple'), 5.285402218862249), (('better', 'than'), 5.285402218862249), (('can', 'beat'), 5.285402218862249), (('can', 'never'), 5.285402218862249), (('cases.', 'can'), 5.285402218862249), (('in', 'with'), 5.285402218862249), (('never', 'beat'), 5.285402218862249), (('so', 'in'), 5.285402218862249), (('than', 'apple'), 5.285402218862249), (('sucks.', 'really'), 4.285402218862249), (('is', 'great'), 3.7004397181410926), (('I', 'am'), 3.7004397181410926), (('I', 'so'), 3.7004397181410926), (('cases.', 'samsung'), 3.7004397181410926), (('in', 'love'), 3.7004397181410926), (('is', 'better'), 3.7004397181410926), (('is', 'than'), 3.7004397181410926), (('love', 'cases.'), 3.7004397181410926), (('love', 'with'), 3.7004397181410926), (('samsung', 'better'), 3.7004397181410926), (('samsung', 'can'), 3.7004397181410926), (('samsung', 'never'), 3.7004397181410926), (('so', 'love'), 3.7004397181410926), (('sucks.', 'I'), 3.7004397181410926), (('samsung', 'is'), 3.115477217419936), (('.', 'am'), 2.9634741239748865), (('.', 'sucks.'), 2.9634741239748865), (('beat', '.'), 2.9634741239748865), (('with', '.'), 2.9634741239748865), (('.', 'is'), 2.963474123974886), (('great', '.'), 2.963474123974886), (('love', 'iphone'), 2.7004397181410926), (('I', 'really'), 2.7004397181410926), (('beat', 'iphone'), 2.7004397181410926), (('great', 'samsung'), 2.7004397181410926), (('iphone', 'cases.'), 2.7004397181410926), (('iphone', 'sucks.'), 2.7004397181410926), (('never', 'iphone'), 2.7004397181410926), (('really', 'love'), 2.7004397181410926), (('samsung', 'great'), 2.7004397181410926), (('with', 'iphone'), 2.7004397181410926), (('.', 'samsung'), 2.37851162325373), (('is', '.'), 2.37851162325373), (('iphone', 'I'), 2.1154772174199366), (('iphone', 'samsung'), 2.1154772174199366), (('I', 'love'), 2.115477217419936), (('iphone', '.'), 1.963474123974886), (('great', 'iphone'), 1.7004397181410922), (('iphone', 'great'), 1.7004397181410922), (('really', 'iphone'), 1.7004397181410922), (('.', 'iphone'), 1.37851162325373), (('.', 'I'), 1.37851162325373), (('love', '.'), 1.37851162325373), (('I', 'iphone'), 1.1154772174199366), (('iphone', 'is'), 1.1154772174199366)]

Теперь обратите внимание, что происходит, когда я вычисляю то же самое для finder1, который был отфильтрован на частоту 2:

finder1.score_ngrams(bigram_measures.pmi)

и вывод:

[(('is', 'great'), 3.7004397181410926), (('samsung', 'is'), 3.115477217419936), (('.', 'is'), 2.963474123974886), (('great', '.'), 2.963474123974886), (('love', 'iphone'), 2.7004397181410926), (('.', 'samsung'), 2.37851162325373), (('is', '.'), 2.37851162325373), (('iphone', 'I'), 2.1154772174199366), (('iphone', 'samsung'), 2.1154772174199366), (('iphone', '.'), 1.963474123974886), (('.', 'iphone'), 1.37851162325373)]

Обратите внимание, что все совпадения с частотой менее 2 не существуют в этом списке; и это именно то, что вы искали. Так фильтр работал. Кроме того, в документации дается минимальный намек на эту проблему.

Надеюсь, это ответит на ваш вопрос. В противном случае, пожалуйста, дайте мне знать.

Отказ от ответственности: если вы в основном занимаетесь твитами, размер окна 13 слишком велик. Если вы заметили, что в моем образце тела размер моих тестовых твитов был слишком мал, что применение размера окна 13 может вызвать поиск совпадений, которые не имеют отношения.

Ответ 2

Пройдите учебник по http://nltk.googlecode.com/svn/trunk/doc/howto/collocations.html для большего использования функций collocation в NLTK, а также математику в https://en.wikipedia.org/wiki/Pointwise_mutual_information. Надеемся, что следующий script поможет вам, так как ваш вопрос с кодом не указал, какой вход.

# This is just a fancy way to create document. 
# I assume you have your texts in a continuous string format
# where each sentence ends with a fullstop.
>>> from itertools import chain
>>> docs = ["this is a sentence", "this is a foo bar", "you are a foo bar", "yes , i am"]
>>> texts = list(chain(*[(j+" .").split() for j in [i for i in docs]]))

# This is the NLTK part
>>> from nltk.collocations import BigramAssocMeasures, BigramCollocationFinder>>> bigram_measures= BigramAssocMeasures()
>>> finder  BigramCollocationFinder.from_words(texts)
# This gets the top 20 bigrams according to PMI
>>> finder.nbest(bigram_measures.pmi,20)
[(',', 'i'), ('i', 'am'), ('yes', ','), ('you', 'are'), ('foo', 'bar'), ('this', 'is'), ('a', 'foo'), ('is', 'a'), ('a', 'sentence'), ('are', 'a'), ('bar', '.'), ('.', 'yes'), ('.', 'you'), ('am', '.'), ('sentence', '.'), ('.', 'this')]

PMI измеряет ассоциацию двух слов, вычисляя log ( p(x|y) / p(x) ), так что это не только частота появления слова или совокупность слов, совпадающих друг с другом. Для достижения высокого PMI вам нужны оба:

  • Высокий p (x | y)
  • низкий p (x)

Вот некоторые экстремальные примеры PMI.

скажем, что у вас есть 100 слов в корпусе, и если частота имеет определенное слово X равна 1, и она встречается только с другим словом Y только один раз, тогда:

p(x|y) = 1
p(x) = 1/100
PMI = log(1 / 1/100) = log 0.01 = -2

скажем, что у вас есть 100 слов в корпусе, и если частота определенного слова равна 90, но никогда не происходит с другим словом Y, тогда PMI

p(x|y) = 0
p(x) = 90/100
PMI = log(0 / 90/100) = log 0 = -infinity

поэтому в этом смысле первый сценарий: → > PMI между X, Y, чем второй сценарий, даже если частота второго слова очень высока.