2013-04-09 16 views
11

Próbuję stworzyć terminową macierz dokumentów z NLTK i pandami. napisałem następującą funkcję:Efektywna macierz dokumentów terminowych z NLTK

def fnDTM_Corpus(xCorpus): 
    import pandas as pd 
    '''to create a Term Document Matrix from a NLTK Corpus''' 
    fd_list = [] 
    for x in range(0, len(xCorpus.fileids())): 
     fd_list.append(nltk.FreqDist(xCorpus.words(xCorpus.fileids()[x]))) 
    DTM = pd.DataFrame(fd_list, index = xCorpus.fileids()) 
    DTM.fillna(0,inplace = True) 
    return DTM.T 

uruchomić go

import nltk 
from nltk.corpus import PlaintextCorpusReader 
corpus_root = 'C:/Data/' 

newcorpus = PlaintextCorpusReader(corpus_root, '.*') 

x = fnDTM_Corpus(newcorpus) 

To działa dobrze dla kilku małych plików w korpusie, ale daje mi MemoryError gdy próbuję uruchomić go z korpusu 4000 plików (po około 2 KB).

Czy czegoś brakuje?

Używam 32-bitowego python. (jestem w systemie Windows 7, 64-bitowym systemie operacyjnym, Core Quad CPU, 8 GB RAM). Czy naprawdę potrzebuję 64-bitowego korpusu tego rozmiaru?

+1

wypróbowałeś 'gensim' lub podobne biblioteki, które zoptymalizowały swój kod dla tf-idf? http://radimrehurek.com/gensim/ – alvas

+0

Pliki 4000 to maleńki korpus. Potrzebujesz reprezentacji [sparse] (https://en.wikipedia.org/wiki/Sparse_matrix). Pandy mają takie, jak Gensim i nauka naukowa. –

+0

Myślałem, że 'pd.get_dummies (df_column)' może wykonać zadanie. Być może brakuje mi czegoś o dokumencie termin macierzyna –

Odpowiedz

19

Dzięki Radim i Larsmanom. Moim celem było posiadanie DTM jak ten, który dostaniesz w R tm. Zdecydowałem się użyć scikit-learn i częściowo zainspirowany przez this blog entry. To kod, który wymyśliłem.

Zamieszczam to tutaj w nadziei, że ktoś inny uzna to za przydatne.

import pandas as pd 
from sklearn.feature_extraction.text import CountVectorizer 

def fn_tdm_df(docs, xColNames = None, **kwargs): 
    ''' create a term document matrix as pandas DataFrame 
    with **kwargs you can pass arguments of CountVectorizer 
    if xColNames is given the dataframe gets columns Names''' 

    #initialize the vectorizer 
    vectorizer = CountVectorizer(**kwargs) 
    x1 = vectorizer.fit_transform(docs) 
    #create dataFrame 
    df = pd.DataFrame(x1.toarray().transpose(), index = vectorizer.get_feature_names()) 
    if xColNames is not None: 
     df.columns = xColNames 

    return df 

używać go na liście tekstu w katalogu

DIR = 'C:/Data/' 

def fn_CorpusFromDIR(xDIR): 
    ''' functions to create corpus from a Directories 
    Input: Directory 
    Output: A dictionary with 
      Names of files ['ColNames'] 
      the text in corpus ['docs']''' 
    import os 
    Res = dict(docs = [open(os.path.join(xDIR,f)).read() for f in os.listdir(xDIR)], 
       ColNames = map(lambda x: 'P_' + x[0:6], os.listdir(xDIR))) 
    return Res 

aby utworzyć dataframe

d1 = fn_tdm_df(docs = fn_CorpusFromDIR(DIR)['docs'], 
      xColNames = fn_CorpusFromDIR(DIR)['ColNames'], 
      stop_words=None, charset_error = 'replace') 
22

Znam OP chciał stworzyć TDM w NLTK, ale pakiet textmining (pip install textmining) sprawia, że ​​jest on prosty:

import textmining 

def termdocumentmatrix_example(): 
    # Create some very short sample documents 
    doc1 = 'John and Bob are brothers.' 
    doc2 = 'John went to the store. The store was closed.' 
    doc3 = 'Bob went to the store too.' 
    # Initialize class to create term-document matrix 
    tdm = textmining.TermDocumentMatrix() 
    # Add the documents 
    tdm.add_doc(doc1) 
    tdm.add_doc(doc2) 
    tdm.add_doc(doc3) 
    # Write out the matrix to a csv file. Note that setting cutoff=1 means 
    # that words which appear in 1 or more documents will be included in 
    # the output (i.e. every word will appear in the output). The default 
    # for cutoff is 2, since we usually aren't interested in words which 
    # appear in a single document. For this example we want to see all 
    # words however, hence cutoff=1. 
    tdm.write_csv('matrix.csv', cutoff=1) 
    # Instead of writing out the matrix you can also access its rows directly. 
    # Let's print them to the screen. 
    for row in tdm.rows(cutoff=1): 
      print row 

termdocumentmatrix_example() 

wyjściowa:

['and', 'the', 'brothers', 'to', 'are', 'closed', 'bob', 'john', 'was', 'went', 'store', 'too'] 
[1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0] 
[0, 2, 0, 1, 0, 1, 0, 1, 1, 1, 2, 0] 
[0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1] 

Alternatywnie, można użyć pandy i sklearn [source]:

import pandas as pd 
from sklearn.feature_extraction.text import CountVectorizer 

docs = ['why hello there', 'omg hello pony', 'she went there? omg'] 
vec = CountVectorizer() 
X = vec.fit_transform(docs) 
df = pd.DataFrame(X.toarray(), columns=vec.get_feature_names()) 
print(df) 

wyjściowa:

hello omg pony she there went why 
0  1 0  0 0  1  0 1 
1  1 1  1 0  0  0 0 
2  0 1  0 1  1  1 0 
+1

Wystąpił błąd podczas uruchamiania twojego kodu: importer łodygi ImportError: Brak modułu o nazwie 'stemmer' Jak mogę to naprawić? Próbowałem już trzpienia do instalacji pip. –

+0

Jaką wersję Python używasz? Możliwe, że istnieje moduł modułu macierzystego, który można zaimportować do pakietu tekstowego, który wymachuje.Właśnie uruchomiłem 'pip textmining', a następnie uruchomiłem powyższy kod w 2.7.9 i otrzymałem oczekiwany wynik. – duhaime

+0

Używam python 3.5, anaconda, Windows 10. Uruchomiłem 'pip textmining'. Skopiowałem i uruchomiłem kod taki, jaki jest. –

0

Alternatywne podejście pomocą znaków i ramki danych

import nltk 
comment #nltk.download() to get toenize 
from urllib import request 
url = "http://www.gutenberg.org/files/2554/2554-0.txt" 
response = request.urlopen(url) 
raw = response.read().decode('utf8') 
type(raw) 

tokens = nltk.word_tokenize(raw) 
type(tokens) 

tokens[1:10] 
['Project', 
'Gutenberg', 
'EBook', 
'of', 
'Crime', 
'and', 
'Punishment', 
',', 
'by'] 

tokens2=pd.DataFrame(tokens) 
tokens2.columns=['Words'] 
tokens2.head() 


Words 
0 The 
1 Project 
2 Gutenberg 
3 EBook 
4 of 

    tokens2.Words.value_counts().head() 
,     16178 
.     9589 
the    7436 
and    6284 
to     5278