web-dev-qa-db-fra.com

Calcul de N grammes en utilisant Python

J'avais besoin de calculer les Unigrams, BiGrams et Trigrams pour un fichier texte contenant du texte comme:

"La fibrose kystique affecte 30 000 enfants et jeunes adultes aux États-Unis seulement. L'inhalation des brouillards d'eau salée peut réduire le pus et l'infection qui remplissent les voies respiratoires des personnes atteintes de fibrose kystique, bien que les effets secondaires incluent une quinte de toux désagréable et un goût dur. C'est la conclusion de deux études publiées dans le numéro de cette semaine du New England Journal of Medicine. "

J'ai commencé en Python et utilisé le code suivant:

#!/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

Mais cela fonctionne pour tous les n-grammes dans un mot, quand je le veux entre les mots comme dans CYSTIC et FIBROSIS ou CYSTIC FIBROSIS. Quelqu'un peut-il m'aider sur la façon dont je peux y arriver?

21
gran_profaci

En supposant que l'entrée est une chaîne contenant des mots séparés par des espaces, comme x = "a b c d" vous pouvez utiliser la fonction suivante (modifier: voir la dernière fonction pour une solution éventuellement plus complète):

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']]

Si vous voulez que ceux-ci soient rejoints en chaînes, vous pouvez appeler quelque chose comme:

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

Enfin, cela ne résume pas les choses en totaux, donc si votre entrée était 'a a a a', vous devez les compter dans un dict:

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

Mettre tout cela ensemble dans une fonction finale donne:

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}
29
dave mankoff

Une courte solution Pythonesque de cette blog :

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

Usage:

>>> 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')]
35

Utilisez NLTK (la boîte à outils du langage naturel) et utilisez les fonctions pour symboliser (diviser) votre texte en une liste, puis rechercher des bigrammes et des trigrammes.

import nltk
words = nltk.Word_tokenize(my_text)
my_bigrams = nltk.bigrams(words)
my_trigrams = nltk.trigrams(words)
25
Spaceghost

Il y a un autre module intéressant dans python appelé Scikit. Voici le code. Cela vous aidera à obtenir tous les grammes donnés dans une plage particulière. Voici le code

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)

La sortie est

[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']

Ici, il donne tous les grammes donnés dans une plage de 1 à 6. Son utilisation de la méthode appelée countVectorizer. Voici le lien pour cela.

9
Gunjan

En utilisant 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)

... ou peut-être pourriez-vous le faire en une seule ligne pour comprendre la liste:

n = 2
message = "Hello, how are you?".split()
myNgrams = [message[i:i+n] for i in range(len(message) - n)]
3
Joel Cornett

Si l'efficacité est un problème et que vous devez créer plusieurs n-grammes différents, j'envisagerais d'utiliser le code suivant (en s'appuyant sur l'excellente réponse de Franck):

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)))

Utilisation:

>>> 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')]

~ Même vitesse que 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)
1
Yann Dubois

Bien que le message soit ancien, j'ai pensé à mentionner ma réponse ici afin que la plupart de la logique de création de ngrams puisse être dans un seul message.

Il y a quelque chose par nom TextBlob en Python. Il crée des ngrammes très facilement similaires à NLTK.

Voici l'extrait de code avec sa sortie pour une compréhension facile.

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)

Et la sortie est:

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'])]

Aussi simple que cela.

Il y a plus à faire par TextBlob. Veuillez consulter ce document pour plus de détails - https://textblob.readthedocs.io/en/dev/

1
JKC

nltk a un support natif pour ngrams

'n' est la taille ngram ex: n = 3 est pour un trigramme

from nltk import ngrams

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