web-dev-qa-db-de.com

Generieren von Ngrams (Unigrams, Bigrams usw.) aus einem großen Korpus von TXT-Dateien und ihrer Häufigkeit

Ich muss ein Programm in NLTK schreiben, das einen Korpus (eine große Sammlung von TXT-Dateien) in Unigramme, Bigramme, Trigramme, Viergramme und Fünfgramme zerlegt. Ich habe bereits Code geschrieben, um meine Dateien in das Programm einzugeben.

Die Eingabe ist 300 .txt-Dateien in englischer Sprache und ich möchte die Ausgabe in Form von Ngrams und speziell die Häufigkeit zählen. 

Ich weiß, dass NLTK über Bigram- und Trigram-Module verfügt: http://www.nltk.org/_modules/nltk/model/ngram.html

aber ich bin nicht so weit fortgeschritten, sie in mein Programm aufzunehmen. 

eingabe: TXT-Dateien NICHT einzelne Sätze

ausgabebeispiel:

Bigram [('Hi', 'How'), ('How', 'are'), ('are', 'you'), ('you', '?'), ('?', 'i'), ('i', 'am'), ('am', 'fine'), ('fine', 'and'), ('and', 'you')] 

Trigram: [('Hi', 'How', 'are'), ('How', 'are', 'you'), ('are', 'you', '?'), ('you', '?', 'i'), ('?', 'i', 'am'), ('i', 'am', 'fine'), ('am', 'fine', 'and'), ('fine', 'and', 'you')]

Mein bisheriger Code lautet: 

from nltk.corpus import PlaintextCorpusReader
corpus = 'C:/Users/jack3/My folder'
files = PlaintextCorpusReader(corpus, '.*')
ngrams=2

def generate(file, ngrams):
    for gram in range(0, ngrams):
    print((file[0:-4]+"_"+str(ngrams)+"_grams.txt").replace("/","_"))


for file in files.fileids():
generate(file, ngrams)

Hilfe, was soll als nächstes getan werden? 

15
Arash

Verwenden Sie einfach ntlk.ngrams.

import nltk
from nltk import Word_tokenize
from nltk.util import ngrams
from collections import Counter

text = "I need to write a program in NLTK that breaks a corpus (a large collection of \
txt files) into unigrams, bigrams, trigrams, fourgrams and fivegrams.\ 
I need to write a program in NLTK that breaks a corpus"
token = nltk.Word_tokenize(text)
bigrams = ngrams(token,2)
trigrams = ngrams(token,3)
fourgrams = ngrams(token,4)
fivegrams = ngrams(token,5)

print Counter(bigrams)

Counter({('program', 'in'): 2, ('NLTK', 'that'): 2, ('that', 'breaks'): 2,
 ('write', 'a'): 2, ('breaks', 'a'): 2, ('to', 'write'): 2, ('I', 'need'): 2,
 ('a', 'corpus'): 2, ('need', 'to'): 2, ('a', 'program'): 2, ('in', 'NLTK'): 2,
 ('and', 'fivegrams'): 1, ('corpus', '('): 1, ('txt', 'files'): 1, ('unigrams', 
','): 1, (',', 'trigrams'): 1, ('into', 'unigrams'): 1, ('trigrams', ','): 1,
 (',', 'bigrams'): 1, ('large', 'collection'): 1, ('bigrams', ','): 1, ('of',
 'txt'): 1, (')', 'into'): 1, ('fourgrams', 'and'): 1, ('fivegrams', '.'): 1,
 ('(', 'a'): 1, (',', 'fourgrams'): 1, ('a', 'large'): 1, ('.', 'I'): 1, 
('collection', 'of'): 1, ('files', ')'): 1})

UPDATE (mit reinem Python): 

import os

corpus = []
path = '.'
for i in os.walk(path).next()[2]:
    if i.endswith('.txt'):
        f = open(os.path.join(path,i))
        corpus.append(f.read())
frequencies = Counter([])
for text in corpus:
    token = nltk.Word_tokenize(text)
    bigrams = ngrams(token, 2)
    frequencies += Counter(bigrams)
23
hellpanderr

Wenn Effizienz ein Problem ist und Sie mehrere verschiedene N-Gramme erstellen müssen, aber reinen Python verwenden möchten, würde ich Folgendes tun: 

from itertools import chain

def n_grams(tokens, n=1):
    """Returns an iterator over the n-grams given a list of tokens"""
    shiftToken = lambda i: (el for j,el in enumerate(tokens) 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(tokens, ngramRange=(1,2)):
    """Returns an itirator over all n-grams for n in range(ngramRange) given a list of tokens."""
    return chain(*(n_grams(tokens, i) for i in range(*ngramRange)))

Verwendungszweck : 

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

Wiederholen Sie meine vorherige Antwort .

4
Yann Dubois

Hier ist ein einfaches Beispiel, das reines Python verwendet, um ngram zu generieren:

>>> def ngrams(s, n=2, i=0):
...     while len(s[i:i+n]) == n:
...         yield s[i:i+n]
...         i += 1
...
>>> txt = 'Python is one of the awesomest languages'

>>> unigram = ngrams(txt.split(), n=1)
>>> list(unigram)
[['Python'], ['is'], ['one'], ['of'], ['the'], ['awesomest'], ['languages']]

>>> bigram = ngrams(txt.split(), n=2)
>>> list(bigram)
[['Python', 'is'], ['is', 'one'], ['one', 'of'], ['of', 'the'], ['the', 'awesomest'], ['awesomest', 'languages']]

>>> trigram = ngrams(txt.split(), n=3)
>>> list(trigram)
[['Python', 'is', 'one'], ['is', 'one', 'of'], ['one', 'of', 'the'], ['of', 'the', 'awesomest'], ['the', 'awesomest',
'languages']]
2
Aziz Alto

Ok, da Sie also nach einer NLTK-Lösung gefragt haben, ist dies möglicherweise nicht genau das, wonach Sie gesucht haben, aber haben Sie TextBlob in Betracht gezogen? Es hat ein NLTK-Backend, aber es hat eine einfachere Syntax. Es würde ungefähr so ​​aussehen:

from textblob import TextBlob

text = "Paste your text or text-containing variable here" 
blob = TextBlob(text)
ngram_var = blob.ngrams(n=3)
print(ngram_var)

Output:
[WordList(['Paste', 'your', 'text']), WordList(['your', 'text', 'or']), WordList(['text', 'or', 'text-containing']), WordList(['or', 'text-containing', 'variable']), WordList(['text-containing', 'variable', 'here'])]

Natürlich müssen Sie immer noch Counter oder eine andere Methode verwenden, um eine Anzahl pro Gramm hinzuzufügen. 

Die schnellste Methode (bei weitem), die ich gefunden habe, um sowohl beliebige Ngrams zu erstellen, als auch sie in einer einzigen Funktion zählen zu lassen, stammt sie aus this von 2012 und verwendet Itertools. Es ist toll.

2
Montmons

vielleicht hilft es. siehe link

import spacy  
nlp_en = spacy.load("en_core_web_sm")
[x.text for x in doc]
0
madjardi

Die Antwort von @hellpander war korrekt, aber für einen sehr großen Körper nicht effizient (ich hatte Probleme mit ~ 650K-Dokumenten). Der Code würde sich bei jeder Aktualisierung der Frequenzen erheblich verlangsamen, da das Wörterbuch mit zunehmendem Inhalt aufwändig nachgeschlagen wird. Sie benötigen also eine zusätzliche Puffervariable, um die Frequenzen zu speichern. Counter of @hellpander answer. Daher ist es nicht ratsam, bei jeder Wiederholung eines neuen Dokuments nach Schlüsseln für sehr große Frequenzen (Wörterbuch) zu suchen. Sie würden es dem temporären, kleineren Counter-Dikt hinzufügen. Nach einigen Iterationen addiert es sich dann zu den globalen Frequenzen. Auf diese Weise wird es viel schneller sein, da die umfangreiche Suche nach Wörterbüchern viel seltener durchgeführt wird.

import os

corpus = []
path = '.'
for i in os.walk(path).next()[2]:
    if i.endswith('.txt'):
        f = open(os.path.join(path,i))
        corpus.append(f.read())
frequencies = Counter([])

for i in range(0, len(corpus)):
    token = nltk.Word_tokenize(corpus[i])
    bigrams = ngrams(token, 2)
    f += Counter(bigrams)
    if (i%10000 == 0):
        # store to global frequencies counter and clear up f every 10000 docs.
        frequencies += Counter(bigrams)
        f = Counter([])
0
A. Dew