n-Gramm in Python, vier, fünf, sechs Gramm?


136

Ich suche nach einer Möglichkeit, einen Text in n-Gramm aufzuteilen. Normalerweise würde ich so etwas machen wie:

import nltk
from nltk import bigrams
string = "I really like python, it's pretty awesome."
string_bigrams = bigrams(string)
print string_bigrams

Mir ist bewusst, dass nltk nur Bigramme und Trigramme anbietet. Gibt es jedoch eine Möglichkeit, meinen Text in vier Gramm, fünf Gramm oder sogar hundert Gramm aufzuteilen?

Vielen Dank!


Möchten Sie, dass der Text nach Wort oder Zeichen in Gruppen von n Größe aufgeteilt wird? Können Sie ein Beispiel geben, wie die Ausgabe für die oben genannten aussehen sollte?
ChrisProsser

4
Nltk noch nie gemacht, aber es sieht so aus, als gäbe es eine Funktion, ingramsderen zweiter Parameter der Grad der gewünschten ngramme ist. Ist DIES die Version von nltk, die Sie verwenden? Selbst wenn nicht, hier ist die Quelle EDIT: Es gibt ngramsund ingramses in, ingramsein Generator zu sein.
Brian

Es gibt auch eine Antwort unter diesem Thread, die nützlich sein kann: stackoverflow.com/questions/7591258/fast-n-gram-calculation
ChrisProsser

Antworten:


210

Tolle native Python-basierte Antworten von anderen Benutzern. Aber hier ist der nltkAnsatz (nur für den Fall, dass das OP dafür bestraft wird, dass es das neu erfunden hat, was bereits in der nltkBibliothek vorhanden ist).

Es gibt ein ngram-Modul , das nur selten verwendet wird nltk. Dies liegt nicht daran, dass es schwierig ist, ngramme zu lesen, sondern daran, eine Modellbasis auf ngrams zu trainieren, bei der n> 3 zu einer großen Datenarmut führt.

from nltk import ngrams

sentence = 'this is a foo bar sentences and i want to ngramize it'

n = 6
sixgrams = ngrams(sentence.split(), n)

for grams in sixgrams:
  print grams

4
Für Charakter-Ngramme schauen Sie bitte auch auf: stackoverflow.com/questions/22428020/…
alvas

Gibt es eine Möglichkeit, mit N-Gramm ein ganzes Dokument wie txt zu überprüfen? Ich bin mit Python nicht vertraut, daher weiß ich nicht, ob es eine txt-Datei öffnen und dann die N-Gramm-Analyse zum Durchchecken verwenden kann.
Maoyi

1
Kann jemand kommentieren, wie man die Genauigkeit von testet sixgrams?
LYu

64

Ich bin überrascht, dass dies noch nicht aufgetaucht ist:

In [34]: sentence = "I really like python, it's pretty awesome.".split()

In [35]: N = 4

In [36]: grams = [sentence[i:i+N] for i in xrange(len(sentence)-N+1)]

In [37]: for gram in grams: print gram
['I', 'really', 'like', 'python,']
['really', 'like', 'python,', "it's"]
['like', 'python,', "it's", 'pretty']
['python,', "it's", 'pretty', 'awesome.']

Genau das macht die erste Antwort abzüglich der Frequenzzählung und der Tupelkonvertierung.
Brian

Es ist jedoch schöner zu sehen, dass es als Verständnis umgeschrieben wurde.
Brian

@amirouche: guter Fang. Danke für die Fehlerberichte. Es wurde jetzt behoben
InspectorG4dget

16

Verwenden Sie nur nltk-Tools

from nltk.tokenize import word_tokenize
from nltk.util import ngrams

def get_ngrams(text, n ):
    n_grams = ngrams(word_tokenize(text), n)
    return [ ' '.join(grams) for grams in n_grams]

Beispielausgabe

get_ngrams('This is the simplest text i could think of', 3 )

['This is the', 'is the simplest', 'the simplest text', 'simplest text i', 'text i could', 'i could think', 'could think of']

Um die ngramme im Array-Format zu halten, entfernen Sie sie einfach ' '.join


15

Hier ist ein weiterer einfacher Weg, um n-Gramm zu machen

>>> from nltk.util import ngrams
>>> text = "I am aware that nltk only offers bigrams and trigrams, but is there a way to split my text in four-grams, five-grams or even hundred-grams"
>>> tokenize = nltk.word_tokenize(text)
>>> tokenize
['I', 'am', 'aware', 'that', 'nltk', 'only', 'offers', 'bigrams', 'and', 'trigrams', ',', 'but', 'is', 'there', 'a', 'way', 'to', 'split', 'my', 'text', 'in', 'four-grams', ',', 'five-grams', 'or', 'even', 'hundred-grams']
>>> bigrams = ngrams(tokenize,2)
>>> bigrams
[('I', 'am'), ('am', 'aware'), ('aware', 'that'), ('that', 'nltk'), ('nltk', 'only'), ('only', 'offers'), ('offers', 'bigrams'), ('bigrams', 'and'), ('and', 'trigrams'), ('trigrams', ','), (',', 'but'), ('but', 'is'), ('is', 'there'), ('there', 'a'), ('a', 'way'), ('way', 'to'), ('to', 'split'), ('split', 'my'), ('my', 'text'), ('text', 'in'), ('in', 'four-grams'), ('four-grams', ','), (',', 'five-grams'), ('five-grams', 'or'), ('or', 'even'), ('even', 'hundred-grams')]
>>> trigrams=ngrams(tokenize,3)
>>> trigrams
[('I', 'am', 'aware'), ('am', 'aware', 'that'), ('aware', 'that', 'nltk'), ('that', 'nltk', 'only'), ('nltk', 'only', 'offers'), ('only', 'offers', 'bigrams'), ('offers', 'bigrams', 'and'), ('bigrams', 'and', 'trigrams'), ('and', 'trigrams', ','), ('trigrams', ',', 'but'), (',', 'but', 'is'), ('but', 'is', 'there'), ('is', 'there', 'a'), ('there', 'a', 'way'), ('a', 'way', 'to'), ('way', 'to', 'split'), ('to', 'split', 'my'), ('split', 'my', 'text'), ('my', 'text', 'in'), ('text', 'in', 'four-grams'), ('in', 'four-grams', ','), ('four-grams', ',', 'five-grams'), (',', 'five-grams', 'or'), ('five-grams', 'or', 'even'), ('or', 'even', 'hundred-grams')]
>>> fourgrams=ngrams(tokenize,4)
>>> fourgrams
[('I', 'am', 'aware', 'that'), ('am', 'aware', 'that', 'nltk'), ('aware', 'that', 'nltk', 'only'), ('that', 'nltk', 'only', 'offers'), ('nltk', 'only', 'offers', 'bigrams'), ('only', 'offers', 'bigrams', 'and'), ('offers', 'bigrams', 'and', 'trigrams'), ('bigrams', 'and', 'trigrams', ','), ('and', 'trigrams', ',', 'but'), ('trigrams', ',', 'but', 'is'), (',', 'but', 'is', 'there'), ('but', 'is', 'there', 'a'), ('is', 'there', 'a', 'way'), ('there', 'a', 'way', 'to'), ('a', 'way', 'to', 'split'), ('way', 'to', 'split', 'my'), ('to', 'split', 'my', 'text'), ('split', 'my', 'text', 'in'), ('my', 'text', 'in', 'four-grams'), ('text', 'in', 'four-grams', ','), ('in', 'four-grams', ',', 'five-grams'), ('four-grams', ',', 'five-grams', 'or'), (',', 'five-grams', 'or', 'even'), ('five-grams', 'or', 'even', 'hundred-grams')]

1
Musste nltk.download ('punkt') ausführen, um die Funktion nltk.word_tokenize () zu verwenden. Um die Ergebnisse zu drucken, musste das Generatorobjekt wie Bigrams, Trigramme und Fourgrams mithilfe von list (<genrator_object>) in eine Liste konvertiert werden.
Bhatman

11

Die Leute haben bereits ziemlich gut auf das Szenario geantwortet, in dem Sie Bigrams oder Trigramme benötigen, aber wenn Sie in diesem Fall jedes Gramm für den Satz benötigen , können Sie es verwendennltk.util.everygrams

>>> from nltk.util import everygrams

>>> message = "who let the dogs out"

>>> msg_split = message.split()

>>> list(everygrams(msg_split))
[('who',), ('let',), ('the',), ('dogs',), ('out',), ('who', 'let'), ('let', 'the'), ('the', 'dogs'), ('dogs', 'out'), ('who', 'let', 'the'), ('let', 'the', 'dogs'), ('the', 'dogs', 'out'), ('who', 'let', 'the', 'dogs'), ('let', 'the', 'dogs', 'out'), ('who', 'let', 'the', 'dogs', 'out')]

Wenn Sie ein Limit haben, wie im Fall von Trigrammen, bei denen die maximale Länge 3 sein sollte, können Sie den Parameter max_len verwenden, um ihn anzugeben.

>>> list(everygrams(msg_split, max_len=2))
[('who',), ('let',), ('the',), ('dogs',), ('out',), ('who', 'let'), ('let', 'the'), ('the', 'dogs'), ('dogs', 'out')]

Sie können einfach den Parameter max_len ändern, um jedes Gramm zu erreichen, dh vier Gramm, fünf Gramm, sechs oder sogar hundert Gramm.

Die zuvor erwähnten Lösungen können modifiziert werden, um die oben erwähnte Lösung zu implementieren, aber diese Lösung ist viel einfacher als diese.

Für weitere Informationen klicken Sie hier

Und wenn Sie nur ein bestimmtes Gramm wie Bigram oder Trigramm usw. benötigen, können Sie die nltk.util.ngrams verwenden, wie in MAHassans Antwort erwähnt.


6

Sie können einfach Ihre eigene Funktion aufrüsten, um dies zu tun, indem Sie itertools:

from itertools import izip, islice, tee
s = 'spam and eggs'
N = 3
trigrams = izip(*(islice(seq, index, None) for index, seq in enumerate(tee(s, N))))
list(trigrams)
# [('s', 'p', 'a'), ('p', 'a', 'm'), ('a', 'm', ' '),
# ('m', ' ', 'a'), (' ', 'a', 'n'), ('a', 'n', 'd'),
# ('n', 'd', ' '), ('d', ' ', 'e'), (' ', 'e', 'g'),
# ('e', 'g', 'g'), ('g', 'g', 's')]

1
Kannst du bitte erklären, dass izip(*(islice(seq, index, None) for index, seq in enumerate(tee(s, N))))ich es nicht ganz verstehe?
TomazStoiljkovic

4

Ein eleganterer Ansatz, um Bigrams mit Python zu bauen zip(). Konvertieren Sie einfach die ursprüngliche Zeichenfolge in eine Liste von split()und übergeben Sie die Liste einmal normal und einmal um ein Element versetzt.

string = "I really like python, it's pretty awesome."

def find_bigrams(s):
    input_list = s.split(" ")
    return zip(input_list, input_list[1:])

def find_ngrams(s, n):
  input_list = s.split(" ")
  return zip(*[input_list[i:] for i in range(n)])

find_bigrams(string)

[('I', 'really'), ('really', 'like'), ('like', 'python,'), ('python,', "it's"), ("it's", 'pretty'), ('pretty', 'awesome.')]

2

Ich habe mich nie mit nltk befasst, sondern N-Gramm als Teil eines kleinen Klassenprojekts gemacht. Wenn Sie die Häufigkeit aller in der Zeichenfolge vorkommenden N-Gramme ermitteln möchten, finden Sie hier eine Möglichkeit, dies zu tun. Dwürde Ihnen das Histogramm Ihrer N-Wörter geben.

D = dict()
string = 'whatever string...'
strparts = string.split()
for i in range(len(strparts)-N): # N-grams
    try:
        D[tuple(strparts[i:i+N])] += 1
    except:
        D[tuple(strparts[i:i+N])] = 1

collections.Counter(tuple(strparts[i:i+N]) for i in xrange(len(strparts)-N))wird schneller als die try-außer arbeiten
inspectorG4dget

2

Für four_grams ist es bereits in NLTK enthalten . Hier ist ein Code, der Ihnen dabei helfen kann:

 from nltk.collocations import *
 import nltk
 #You should tokenize your text
 text = "I do not like green eggs and ham, I do not like them Sam I am!"
 tokens = nltk.wordpunct_tokenize(text)
 fourgrams=nltk.collocations.QuadgramCollocationFinder.from_words(tokens)
 for fourgram, freq in fourgrams.ngram_fd.items():  
       print fourgram, freq

Ich hoffe, es hilft.


2

Sie können sklearn.feature_extraction.text.CountVectorizer verwenden :

import sklearn.feature_extraction.text # FYI http://scikit-learn.org/stable/install.html
ngram_size = 4
string = ["I really like python, it's pretty awesome."]
vect = sklearn.feature_extraction.text.CountVectorizer(ngram_range=(ngram_size,ngram_size))
vect.fit(string)
print('{1}-grams: {0}'.format(vect.get_feature_names(), ngram_size))

Ausgänge:

4-grams: [u'like python it pretty', u'python it pretty awesome', u'really like python it']

Sie können eine ngram_sizebeliebige positive Ganzzahl festlegen . Das heißt, Sie können einen Text in vier Gramm, fünf Gramm oder sogar hundert Gramm aufteilen.


2

Wenn Effizienz ein Problem ist und Sie mehrere verschiedene n-Gramm erstellen müssen (bis zu hundert, wie Sie sagen), aber Sie reines Python verwenden möchten, würde ich Folgendes tun:

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

Verwendung :

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

~ Gleiche Geschwindigkeit wie 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)

Repost von meiner vorherigen Antwort .


0

Nltk ist großartig, aber manchmal ist es ein Overhead für einige Projekte:

import re
def tokenize(text, ngrams=1):
    text = re.sub(r'[\b\(\)\\\"\'\/\[\]\s+\,\.:\?;]', ' ', text)
    text = re.sub(r'\s+', ' ', text)
    tokens = text.split()
    return [tuple(tokens[i:i+ngrams]) for i in xrange(len(tokens)-ngrams+1)]

Anwendungsbeispiel:

>> text = "This is an example text"
>> tokenize(text, 2)
[('This', 'is'), ('is', 'an'), ('an', 'example'), ('example', 'text')]
>> tokenize(text, 3)
[('This', 'is', 'an'), ('is', 'an', 'example'), ('an', 'example', 'text')]

0

Sie können alle 4-6 Gramm mit dem folgenden Code ohne anderes Paket erhalten:

from itertools import chain

def get_m_2_ngrams(input_list, min, max):
    for s in chain(*[get_ngrams(input_list, k) for k in range(min, max+1)]):
        yield ' '.join(s)

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

if __name__ == '__main__':
    input_list = ['I', 'am', 'aware', 'that', 'nltk', 'only', 'offers', 'bigrams', 'and', 'trigrams', ',', 'but', 'is', 'there', 'a', 'way', 'to', 'split', 'my', 'text', 'in', 'four-grams', ',', 'five-grams', 'or', 'even', 'hundred-grams']
    for s in get_m_2_ngrams(input_list, 4, 6):
        print(s)

Die Ausgabe ist unten:

I am aware that
am aware that nltk
aware that nltk only
that nltk only offers
nltk only offers bigrams
only offers bigrams and
offers bigrams and trigrams
bigrams and trigrams ,
and trigrams , but
trigrams , but is
, but is there
but is there a
is there a way
there a way to
a way to split
way to split my
to split my text
split my text in
my text in four-grams
text in four-grams ,
in four-grams , five-grams
four-grams , five-grams or
, five-grams or even
five-grams or even hundred-grams
I am aware that nltk
am aware that nltk only
aware that nltk only offers
that nltk only offers bigrams
nltk only offers bigrams and
only offers bigrams and trigrams
offers bigrams and trigrams ,
bigrams and trigrams , but
and trigrams , but is
trigrams , but is there
, but is there a
but is there a way
is there a way to
there a way to split
a way to split my
way to split my text
to split my text in
split my text in four-grams
my text in four-grams ,
text in four-grams , five-grams
in four-grams , five-grams or
four-grams , five-grams or even
, five-grams or even hundred-grams
I am aware that nltk only
am aware that nltk only offers
aware that nltk only offers bigrams
that nltk only offers bigrams and
nltk only offers bigrams and trigrams
only offers bigrams and trigrams ,
offers bigrams and trigrams , but
bigrams and trigrams , but is
and trigrams , but is there
trigrams , but is there a
, but is there a way
but is there a way to
is there a way to split
there a way to split my
a way to split my text
way to split my text in
to split my text in four-grams
split my text in four-grams ,
my text in four-grams , five-grams
text in four-grams , five-grams or
in four-grams , five-grams or even
four-grams , five-grams or even hundred-grams

Weitere Details finden Sie in diesem Blog


0

Nach ungefähr sieben Jahren ist hier eine elegantere Antwort mit collections.deque:

def ngrams(words, n):
    d = collections.deque(maxlen=n)
    d.extend(words[:n])
    words = words[n:]
    for window, word in zip(itertools.cycle((d,)), words):
        print(' '.join(window))
        d.append(word)

words = ['I', 'am', 'become', 'death,', 'the', 'destroyer', 'of', 'worlds']

Ausgabe:

In [15]: ngrams(words, 3)                                                                                                                                                                                                                     
I am become
am become death,
become death, the
death, the destroyer
the destroyer of

In [16]: ngrams(words, 4)                                                                                                                                                                                                                     
I am become death,
am become death, the
become death, the destroyer
death, the destroyer of

In [17]: ngrams(words, 1)                                                                                                                                                                                                                     
I
am
become
death,
the
destroyer
of

In [18]: ngrams(words, 2)                                                                                                                                                                                                                     
I am
am become
become death,
death, the
the destroyer
destroyer of

0

Wenn Sie eine reine Iteratorlösung für große Zeichenfolgen mit konstanter Speichernutzung wünschen:

from typing import Iterable  
import itertools

def ngrams_iter(input: str, ngram_size: int, token_regex=r"[^\s]+") -> Iterable[str]:
    input_iters = [ 
        map(lambda m: m.group(0), re.finditer(token_regex, input)) 
        for n in range(ngram_size) 
    ]
    # Skip first words
    for n in range(1, ngram_size): list(map(next, input_iters[n:]))  

    output_iter = itertools.starmap( 
        lambda *args: " ".join(args),  
        zip(*input_iters) 
    ) 
    return output_iter

Prüfung:

input = "If you want a pure iterator solution for large strings with constant memory usage"
list(ngrams_iter(input, 5))

Ausgabe:

['If you want a pure',
 'you want a pure iterator',
 'want a pure iterator solution',
 'a pure iterator solution for',
 'pure iterator solution for large',
 'iterator solution for large strings',
 'solution for large strings with',
 'for large strings with constant',
 'large strings with constant memory',
 'strings with constant memory usage']
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.