Вычисление N граммов с использованием Python
Мне нужно было вычислить Unigrams, BiGrams и Trigrams для текстового файла, содержащего текст, такой как:
"Муковисцидоз затрагивает 30000 детей и молодых людей только в США. Вдыхание туманов с соленой водой может уменьшить гной и инфекцию, которая заполняет дыхательные пути страдающих муковисцидозом, хотя побочные эффекты включают неприятный приступ кашля и резкий вкус. Это заключение. из двух исследований, опубликованных на этой неделе в выпуске "Медицинского журнала Новой Англии".
Я начал в 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-граммов в слове, когда я хочу это между словами, как в ЦИСТИЧЕСКОМ и ФИБРОЗЕ или ЦИСТИЧЕСКОМ ФИБРОЗЕ. Может ли кто-нибудь помочь мне с тем, как я могу это сделать?
8 ответов
Предполагая, что input является строкой, содержит слова, разделенные пробелом, например x = "a b c d"
Вы можете использовать следующую функцию (edit: см. последнюю функцию для более полного решения):
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'
, вам нужно посчитать их в диктовку:
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}
Краткое 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')]
Используйте NLTK (Natural Language Toolkit) и используйте функции для токенизации (разделения) вашего текста в список, а затем для поиска биграмм и триграмм.
import nltk
words = nltk.word_tokenize(my_text)
my_bigrams = nltk.bigrams(words)
my_trigrams = nltk.trigrams(words)
В 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. Вот ссылка для этого.
С помощью 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)]
Хотя пост старый, я подумал упомянуть мой ответ здесь, чтобы большая часть логики создания ngrams была в одном посте.
В 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/
Если эффективность является проблемой, и вам нужно построить несколько разных n-грамм, я бы подумал об использовании следующего кода (основываясь на превосходном ответе Франка):
from itertools import chain
def n_grams(seq, n=1):
"""Returns an itirator over the n-grams given a listTokens"""
shiftToken = lambda i: (el for j,el in enumerate(seq) if j>=i)
shiftedTokens = (shiftToken(i) for i in range(n))
tupleNGrams = zip(*shiftedTokens)
return tupleNGrams # if join in generator : (" ".join(i) for i in tupleNGrams)
def range_ngrams(listTokens, ngramRange=(1,2)):
"""Returns an itirator over all n-grams for n in range(ngramRange) given a listTokens."""
return chain(*(n_grams(listTokens, i) for i in range(*ngramRange)))
Использование:
>>> input_list = input_list = 'test the ngrams generator'.split()
>>> list(range_ngrams(input_list, ngramRange=(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, ngramRange=(1,6))
# 7.13 ms ± 165 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
nltk имеет встроенную поддержку ngrams
'n' - размер нграммы, например: n=3 для триграммы
from nltk import ngrams
def ngramize(texts, n):
output=[]
for text in texts:
output += ngrams(text,n)
return output