Подсчет биграмм (пара двух слов) в файле с использованием python

Я хочу подсчитать количество вхождений всех биграмм (пару смежных слов) в файл с помощью python. Здесь я имею дело с очень большими файлами, поэтому я ищу эффективный способ. Я попытался использовать метод count с регулярным выражением "\ w +\s\w +" в содержимом файла, но он не оказался эффективным.

например. Скажем, я хочу подсчитать количество биграмм из файла a.txt, который имеет следующий контент:

"the quick person did not realize his speed and the quick person bumped "

Для вышеуказанного файла набор bigram и их счет будут следующими:

(the,quick) = 2
(quick,person) = 2
(person,did) = 1
(did, not) = 1
(not, realize) = 1
(realize,his) = 1
(his,speed) = 1
(speed,and) = 1
(and,the) = 1
(person, bumped) = 1

Я столкнулся с примером объектов Counter в Python, который используется для подсчета униграмм (одиночных слов). Он также использует подход с регулярным выражением.

Пример выглядит следующим образом:

>>> # Find the ten most common words in Hamlet
>>> import re
>>> from collections import Counter
>>> words = re.findall('\w+', open('a.txt').read())
>>> print Counter(words)

Выходной код выше:

[('the', 2), ('quick', 2), ('person', 2), ('did', 1), ('not', 1),
 ('realize', 1),  ('his', 1), ('speed', 1), ('bumped', 1)]

Мне было интересно, можно ли использовать объект Counter для получения числа биграмм. Также будет оценен любой подход, отличный от объекта Counter или регулярного выражения.

Ответ 1

Немного itertools magic:

>>> import re
>>> from itertools import islice, izip
>>> words = re.findall("\w+", 
   "the quick person did not realize his speed and the quick person bumped")
>>> print Counter(izip(words, islice(words, 1, None)))

Вывод:

Counter({('the', 'quick'): 2, ('quick', 'person'): 2, ('person', 'did'): 1, 
  ('did', 'not'): 1, ('not', 'realize'): 1, ('and', 'the'): 1, 
  ('speed', 'and'): 1, ('person', 'bumped'): 1, ('his', 'speed'): 1, 
  ('realize', 'his'): 1})

Bonus

Получить частоту любого n-грамма:

from itertools import tee, islice

def ngrams(lst, n):
  tlst = lst
  while True:
    a, b = tee(tlst)
    l = tuple(islice(a, n))
    if len(l) == n:
      yield l
      next(b)
      tlst = b
    else:
      break

>>> Counter(ngrams(words, 3))

Вывод:

Counter({('the', 'quick', 'person'): 2, ('and', 'the', 'quick'): 1, 
  ('realize', 'his', 'speed'): 1, ('his', 'speed', 'and'): 1, 
  ('person', 'did', 'not'): 1, ('quick', 'person', 'did'): 1, 
  ('quick', 'person', 'bumped'): 1, ('did', 'not', 'realize'): 1, 
  ('speed', 'and', 'the'): 1, ('not', 'realize', 'his'): 1})

Это работает с ленивыми итерами и генераторами. Таким образом, вы можете написать генератор, который читает файл по строкам, генерирует слова и передает его на ngarms, чтобы лениво потреблять, не читая весь файл в памяти.

Ответ 2

Как насчет zip()?

import re
from collections import Counter
words = re.findall('\w+', open('a.txt').read())
print(Counter(zip(words,words[1:])))

Ответ 3

Как получить счетчик биграма и триграммы в двумерном и трехмерном словаре? пример для униграммы ниже

copyText = unigramModel.prepData(текст)

для списка в copyText && & слово в списке:

    unigramCount = sum(x.count(word) for x in copyText)
    self.nGramCounts[word] = unigramCount

return self.nGramCounts

Ответ 4

Прошло много времени с тех пор, как этот вопрос был задан и успешно ответил. Я получаю ответы от ответов на создание собственного решения. Я хотел бы поделиться им:

    import regex
    bigrams_tst = regex.findall(r"\b\w+\s\w+", open(myfile).read(), overlapped=True)

Это предоставит все биграммы, которые не прерываются пунктуацией.

Ответ 5

Вы можете просто использовать Counter для любой n_gram следующим образом:

from collections import Counter
text = "the quick person did not realize his speed and the quick person bumped "
n_gram = 2
Counter(ngrams(text.split(), n_gram))
>>>
Counter({('and', 'the'): 1,
         ('did', 'not'): 1,
         ('his', 'speed'): 1,
         ('not', 'realize'): 1,
         ('person', 'bumped'): 1,
         ('person', 'did'): 1,
         ('quick', 'person'): 2,
         ('realize', 'his'): 1,
         ('speed', 'and'): 1,
         ('the', 'quick'): 2})

Для 3 граммов просто измените n_gram на 3:

n_gram = 3
Counter(ngrams(text.split(), n_gram))
>>>
Counter({('and', 'the', 'quick'): 1,
         ('did', 'not', 'realize'): 1,
         ('his', 'speed', 'and'): 1,
         ('not', 'realize', 'his'): 1,
         ('person', 'did', 'not'): 1,
         ('quick', 'person', 'bumped'): 1,
         ('quick', 'person', 'did'): 1,
         ('realize', 'his', 'speed'): 1,
         ('speed', 'and', 'the'): 1,
         ('the', 'quick', 'person'): 2})