Вычисление N граммов с использованием Python

Мне нужно было вычислить униграммы, биграммы и триграммы для текстового файла, содержащего текст вроде:

«Муковисцидоз поражает 30 000 детей и молодых людей только в США. Вдыхание тумана с соленой водой может уменьшить гной и инфекцию, заполняющую дыхательные пути больных муковисцидозом, хотя побочные эффекты включают неприятный приступ кашля и резкий вкус. из двух исследований, опубликованных в выпуске Медицинского журнала Новой Англии за эту неделю ".

Я начал с Python и использовал следующий код:

#!/usr/bin/env python
# File: n-gram.py
def N_Gram(N,text):
NList = []                      # start with an empty list
if N> 1:
    space = " " * (N-1)         # add N - 1 spaces
    text = space + text + space # add both in front and back
# append the slices [i:i+N] to NList
for i in range( len(text) - (N - 1) ):
    NList.append(text[i:i+N])
return NList                    # return the list
# test code
for i in range(5):
print N_Gram(i+1,"text")
# more test code
nList = N_Gram(7,"Here is a lot of text to print")
for ngram in iter(nList):
print '"' + ngram + '"'

http://www.daniweb.com/software-development/python/threads/39109/generating-n-grams-from-a-word.

Но это работает для всех n-граммов в слове, когда я хочу, чтобы он находился между словами, как в КИСТИКАХ и ФИБРОЗЕ или КИСТИЧЕСКИМ ФИБРОЗЕ. Может ли кто-нибудь помочь мне, как я могу это сделать?


person gran_profaci    schedule 16.11.2012    source источник
comment
вы исходите из фона MATLAB? вам больше не нужны точки с запятой в конце каждой строки !!   -  person Cameron Sparr    schedule 17.11.2012
comment
Ой! Извините .. Я здесь новичок и не знал, что должен принимать ответы !!! .... Код не мой ... Это с веб-сайта, который я дал ...   -  person gran_profaci    schedule 17.11.2012


Ответы (8)


Предполагая, что ввод - это строка, содержащая слова, разделенные пробелами, например x = "a b c d", вы можете использовать следующую функцию (отредактируйте: см. Последнюю функцию для возможно более полного решения):

def ngrams(input, n):
    input = input.split(' ')
    output = []
    for i in range(len(input)-n+1):
        output.append(input[i:i+n])
    return output

ngrams('a b c d', 2) # [['a', 'b'], ['b', 'c'], ['c', 'd']]

Если вы хотите, чтобы они снова были объединены в строки, вы можете вызвать что-то вроде:

[' '.join(x) for x in ngrams('a b c d', 2)] # ['a b', 'b c', 'c d']

Наконец, это не суммирует вещи в итоги, поэтому, если ваш ввод был 'a a a a', вам нужно подсчитать их в dict:

for g in (' '.join(x) for x in ngrams(input, 2)):
    grams.setdefault(g, 0)
    grams[g] += 1

Объединение всего этого в одну последнюю функцию дает:

def ngrams(input, n):
   input = input.split(' ')
   output = {}
   for i in range(len(input)-n+1):
       g = ' '.join(input[i:i+n])
       output.setdefault(g, 0)
       output[g] += 1
    return output

ngrams('a a a a', 2) # {'a a': 3}
person dave mankoff    schedule 16.11.2012
comment
Итак, я собираюсь получить общее количество РАЗЛИЧНЫХ СЛОВ в качестве вывода для n = 1 ?? - person gran_profaci; 17.11.2012
comment
Кроме того, есть ли подсказка, как я могу сгенерировать предложение, используя это? Это действительно поможет! - person gran_profaci; 17.11.2012
comment
Да, вы получите отдельные слова (хотя пунктуация в определенной степени повлияет на все это). Чтобы генерировать предложения, я предполагаю, что вам нужно что-то вроде цепи Маркова? Несколько лет назад я написал статью о генерации слов с помощью цепей Маркова. Основные идеи те же: ohthehugemanatee.net/2009/10/. Вам нужно будет найти способ пометить начальные слова в структуре данных, чего это не делает, а также конечные или конечные слова. - person dave mankoff; 17.11.2012
comment
Хм .. Мне действительно пришлось сделать один, используя частоту слов ... которую, как я вижу, код может вычислить .. - person gran_profaci; 18.11.2012
comment
Есть ли способ использовать N-gram для проверки всего документа, такого как txt? Я не знаком с Python, поэтому я не знаю, может ли он открыть текстовый файл, а затем использовать анализ N-грамм, чтобы проверить и дать такой результат? - person maoyi; 28.04.2016

Краткое решение Pythonesque из этого блога:

def find_ngrams(input_list, n):
  return zip(*[input_list[i:] for i in range(n)])

Использование:

>>> input_list = ['all', 'this', 'happened', 'more', 'or', 'less']
>>> find_ngrams(input_list, 1)
[('all',), ('this',), ('happened',), ('more',), ('or',), ('less',)]
>>> find_ngrams(input_list, 2)
[('all', 'this'), ('this', 'happened'), ('happened', 'more'), ('more', 'or'), ('or', 'less')]
>>> find_ngrams(input_list, 3))
[('all', 'this', 'happened'), ('this', 'happened', 'more'), ('happened', 'more', 'or'), ('more', 'or', 'less')]
person Franck Dernoncourt    schedule 03.06.2015

Используйте NLTK (набор инструментов для естественного языка) и используйте функции для токенизации (разделения) текста в список, а затем найдите биграммы и триграммы.

import nltk
words = nltk.word_tokenize(my_text)
my_bigrams = nltk.bigrams(words)
my_trigrams = nltk.trigrams(words)
person Spaceghost    schedule 17.11.2012
comment
Вот это гениально ... Но хотелось отличных ... !! - person gran_profaci; 18.11.2012
comment
@gran_profaci в python вычислить отдельные элементы в списке очень просто: distinct_list= list(set(original_list)) Вы можете пропустить это последнее преобразование, чтобы снова перечислить, если набор имеет для вас достаточную функциональность. - person loopbackbee; 18.11.2012
comment
Если вам это нравится, то вам понравится книга NLTK, особенно глава 1: nltk.org/book /ch01.html - person Spaceghost; 18.11.2012

В Python есть еще один интересный модуль под названием Scikit. Вот код. Это поможет вам получить все граммы, указанные в определенном диапазоне. Вот код

from sklearn.feature_extraction.text import CountVectorizer 
text = "this is a foo bar sentences and i want to ngramize it"
vectorizer = CountVectorizer(ngram_range=(1,6))
analyzer = vectorizer.build_analyzer()
print analyzer(text)

Выход

[u'this', u'is', u'foo', u'bar', u'sentences', u'and', u'want', u'to', u'ngramize', u'it', u'this is', u'is foo', u'foo bar', u'bar sentences', u'sentences and', u'and want', u'want to', u'to ngramize', u'ngramize it', u'this is foo', u'is foo bar', u'foo bar sentences', u'bar sentences and', u'sentences and want', u'and want to', u'want to ngramize', u'to ngramize it', u'this is foo bar', u'is foo bar sentences', u'foo bar sentences and', u'bar sentences and want', u'sentences and want to', u'and want to ngramize', u'want to ngramize it', u'this is foo bar sentences', u'is foo bar sentences and', u'foo bar sentences and want', u'bar sentences and want to', u'sentences and want to ngramize', u'and want to ngramize it', u'this is foo bar sentences and', u'is foo bar sentences and want', u'foo bar sentences and want to', u'bar sentences and want to ngramize', u'sentences and want to ngramize it']

Здесь он дает все граммы, указанные в диапазоне от 1 до 6. Он использует метод countVectorizer. Вот для этого ссылка.

person Gunjan    schedule 30.10.2014

Использование collections.deque:

from collections import deque
from itertools import islice

def ngrams(message, n=1):
    it = iter(message.split())
    window = deque(islice(it, n), maxlen=n)
    yield tuple(window)
    for item in it:
        window.append(item)
        yield tuple(window)

... или, может быть, вы могли бы сделать это в одной строке как понимание списка:

n = 2
message = "Hello, how are you?".split()
myNgrams = [message[i:i+n] for i in range(len(message) - n)]
person Joel Cornett    schedule 16.11.2012
comment
Вы можете избежать лишнего первого урожая, если вы islice (it, n-1) - person fivetentaylor; 27.08.2015
comment
myNgrams = [сообщение [i: i + n] для i в диапазоне (len (сообщение) - n + 1)] - person anubhavashok; 09.10.2015

nltk имеет встроенную поддержку ngrams

'n' - размер nграммы, например: n = 3 для триграммы

from nltk import ngrams

def ngramize(texts, n):
    output=[]
    for text in texts:
        output += ngrams(text,n)
    return output
person r11    schedule 28.09.2016

Если эффективность является проблемой и вам нужно создать несколько разных n-граммов, я бы подумал об использовании следующего кода (основанного на отличном ответе Франка):

from itertools import chain

def n_grams(seq, n=1):
    """Returns an iterator over the n-grams given a list_tokens"""
    shift_token = lambda i: (el for j,el in enumerate(seq) if j>=i)
    shifted_tokens = (shift_token(i) for i in range(n))
    tuple_ngrams = zip(*shifted_tokens)
    return tuple_ngrams # if join in generator : (" ".join(i) for i in tuple_ngrams)

def range_ngrams(list_tokens, ngram_range=(1,2)):
    """Returns an itirator over all n-grams for n in range(ngram_range) given a list_tokens."""
    return chain(*(n_grams(list_tokens, i) for i in range(*ngram_range)))

Использование :

>>> input_list = input_list = 'test the ngrams generator'.split()
>>> list(range_ngrams(input_list, ngram_range=(1,3)))
[('test',), ('the',), ('ngrams',), ('generator',), ('test', 'the'), ('the', 'ngrams'), ('ngrams', 'generator'), ('test', 'the', 'ngrams'), ('the', 'ngrams', 'generator')]

~ Та же скорость, что и у NLTK:

import nltk
%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
nltk.ngrams(input_list,n=5)
# 7.02 ms ± 79 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
n_grams(input_list,n=5)
# 7.01 ms ± 103 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
nltk.ngrams(input_list,n=1)
nltk.ngrams(input_list,n=2)
nltk.ngrams(input_list,n=3)
nltk.ngrams(input_list,n=4)
nltk.ngrams(input_list,n=5)
# 7.32 ms ± 241 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%%timeit
input_list = 'test the ngrams interator vs nltk '*10**6
range_ngrams(input_list, ngram_range=(1,6))
# 7.13 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
person Yann Dubois    schedule 18.01.2018
comment
Отличный ответ! Но можно было бы улучшить, если бы ваши имена переменных были в соответствии с PEP :) Значение в нижнем регистре с подчеркиванием. - person Adam; 14.01.2019
comment
Обычно мне нравится различать имена вызываемых объектов и переменных, но поскольку это ответ для других, я редактировал его в стиле PEP8 :) - person Yann Dubois; 15.01.2019

Хотя пост старый, я подумал упомянуть здесь свой ответ, чтобы большая часть логики создания ngram могла быть в одном посте.

В Python есть что-то по имени TextBlob. Он очень легко создает нграммы, похожие на NLTK.

Ниже приведен фрагмент кода с его выводом для облегчения понимания.

sent = """This is to show the usage of Text Blob in Python"""
blob = TextBlob(sent)
unigrams = blob.ngrams(n=1)
bigrams = blob.ngrams(n=2)
trigrams = blob.ngrams(n=3)

И результат:

unigrams
[WordList(['This']),
 WordList(['is']),
 WordList(['to']),
 WordList(['show']),
 WordList(['the']),
 WordList(['usage']),
 WordList(['of']),
 WordList(['Text']),
 WordList(['Blob']),
 WordList(['in']),
 WordList(['Python'])]

bigrams
[WordList(['This', 'is']),
 WordList(['is', 'to']),
 WordList(['to', 'show']),
 WordList(['show', 'the']),
 WordList(['the', 'usage']),
 WordList(['usage', 'of']),
 WordList(['of', 'Text']),
 WordList(['Text', 'Blob']),
 WordList(['Blob', 'in']),
 WordList(['in', 'Python'])]

trigrams
[WordList(['This', 'is', 'to']),
 WordList(['is', 'to', 'show']),
 WordList(['to', 'show', 'the']),
 WordList(['show', 'the', 'usage']),
 WordList(['the', 'usage', 'of']),
 WordList(['usage', 'of', 'Text']),
 WordList(['of', 'Text', 'Blob']),
 WordList(['Text', 'Blob', 'in']),
 WordList(['Blob', 'in', 'Python'])]

Так просто.

TextBlob делает еще кое-что. Для получения дополнительных сведений ознакомьтесь с этим документом - https://textblob.readthedocs.io/en/dev/

person JKC    schedule 20.09.2017