veces una tiene separar saber repite repetidos que palabras palabra lista letras frecuencia frase elementos elemento cuantas contar como archivo python nlp scikit-learn word-count frequency-distribution

una - Cuenta eficientemente las frecuencias de palabras en python



frecuencia de letras en una palabra python (7)

Aquí hay algunos puntos de referencia. Se verá extraño pero el código más crudo gana.

[código]:

from collections import Counter, defaultdict import io, time import numpy as np from sklearn.feature_extraction.text import CountVectorizer infile = ''/path/to/file'' def extract_dictionary_sklearn(file_path): with io.open(file_path, ''r'', encoding=''utf8'') as fin: ngram_vectorizer = CountVectorizer(analyzer=''word'') X = ngram_vectorizer.fit_transform(fin) vocab = ngram_vectorizer.get_feature_names() counts = X.sum(axis=0).A1 return Counter(dict(zip(vocab, counts))) def extract_dictionary_native(file_path): dictionary = Counter() with io.open(file_path, ''r'', encoding=''utf8'') as fin: for line in fin: dictionary.update(line.split()) return dictionary def extract_dictionary_paddle(file_path): dictionary = defaultdict(int) with io.open(file_path, ''r'', encoding=''utf8'') as fin: for line in fin: for words in line.split(): dictionary[word] +=1 return dictionary start = time.time() extract_dictionary_sklearn(infile) print time.time() - start start = time.time() extract_dictionary_native(infile) print time.time() - start start = time.time() extract_dictionary_paddle(infile) print time.time() - start

[fuera]:

38.306814909 24.8241138458 12.1182529926

Tamaño de datos (154 MB) utilizado en el punto de referencia anterior:

$ wc -c /path/to/file 161680851 $ wc -l /path/to/file 2176141

Algunas cosas a tener en cuenta:

  • Con la versión sklearn , hay una sobrecarga de creación de vectorizadores + manipulación y conversión numpy en un objeto Counter
  • Entonces, la versión de actualización nativa de Counter , parece que Counter.update() es una operación costosa

Me gustaría contar las frecuencias de todas las palabras en un archivo de texto.

>>> countInFile(''test.txt'')

debería devolver {''aaa'':1, ''bbb'': 2, ''ccc'':1} si el archivo de texto de destino es como:

# test.txt aaa bbb ccc bbb

Lo he implementado con Python puro después de algunas publicaciones . Sin embargo, descubrí que las formas de Python puro son insuficientes debido al gran tamaño de archivo (> 1 GB).

Creo que tomar prestado el poder de Sklearn es un candidato.

Si deja que CountVectorizer cuente las frecuencias para cada línea, supongo que obtendrá frecuencias de palabras al resumir cada columna. Pero, suena un poco indirecto.

¿Cuál es la forma más eficiente y directa de contar palabras en un archivo con python?

Actualizar

Mi código (muy lento) está aquí:

from collections import Counter def get_term_frequency_in_file(source_file_path): wordcount = {} with open(source_file_path) as f: for line in f: line = line.lower().translate(None, string.punctuation) this_wordcount = Counter(line.split()) wordcount = add_merge_two_dict(wordcount, this_wordcount) return wordcount def add_merge_two_dict(x, y): return { k: x.get(k, 0) + y.get(k, 0) for k in set(x) | set(y) }


El enfoque más sucinto es utilizar las herramientas que Python le brinda.

from future_builtins import map # Only on Python 2 from collections import Counter from itertools import chain def countInFile(filename): with open(filename) as f: return Counter(chain.from_iterable(map(str.split, f)))

Eso es. map(str.split, f) está haciendo un generador que devuelve una list de palabras de cada línea. Al envolver en chain.from_iterable eso se convierte en un solo generador que produce una palabra a la vez. Counter toma una entrada iterable y cuenta todos los valores únicos en ella. Al final, return un objeto tipo dict (un Counter ) que almacena todas las palabras únicas y sus recuentos, y durante la creación, solo almacena una línea de datos a la vez y los recuentos totales, no todo el archivo a la vez.

En teoría, en Python 2.7 y 3.1, puede hacer un mejor bucle sobre los resultados encadenados usted mismo y usar un dict o collections.defaultdict(int) para contar (porque Counter está implementado en Python, lo que puede hacerlo más lento en algunos casos) , pero dejar que Counter haga el trabajo es más simple y más autodocumentado (quiero decir, todo el objetivo es contar, así que use un Counter ). Más allá de eso, en CPython (el intérprete de referencia) 3.2 y superior Counter tiene un acelerador de nivel C para contar entradas iterables que se ejecutarán más rápido que cualquier cosa que pueda escribir en Python puro.

Actualización: Parece que quiere eliminar la puntuación y la insensibilidad a mayúsculas y minúsculas, así que aquí hay una variante de mi código anterior que hace eso:

from string import punctuation def countInFile(filename): with open(filename) as f: linewords = (line.translate(None, punctuation).lower().split() for line in f) return Counter(chain.from_iterable(linewords))

Su código se ejecuta mucho más lentamente porque está creando y destruyendo muchos Counter pequeños y set objetos, en lugar de .update , .update un solo Counter una vez por línea (que, aunque un poco más lento de lo que di en el bloque de código actualizado, sería al menos algorítmicamente similar en factor de escala).


En lugar de decodificar los bytes completos leídos de la URL, proceso los datos binarios. Debido a que bytes.translate espera que su segundo argumento sea una cadena de bytes, utf-8 codifica la punctuation . Después de eliminar los signos de puntuación, utf-8 decodifica la cadena de bytes.

La función freq_dist espera un iterable. Es por eso que pasé data.splitlines() .

from urllib2 import urlopen from collections import Counter from string import punctuation from time import time import sys from pprint import pprint url = ''https://raw.githubusercontent.com/Simdiva/DSL-Task/master/data/DSLCC-v2.0/test/test.txt'' data = urlopen(url).read() def freq_dist(data): """ :param data: file-like object opened in binary mode or sequence of byte strings separated by ''/n'' :type data: an iterable sequence """ #For readability #return Counter(word for line in data # for word in line.translate( # None,bytes(punctuation.encode(''utf-8''))).decode(''utf-8'').split()) punc = punctuation.encode(''utf-8'') words = (word for line in data for word in line.translate(None, punc).decode(''utf-8'').split()) return Counter(words) start = time() word_dist = freq_dist(data.splitlines()) print(''elapsed: {}''.format(time() - start)) pprint(word_dist.most_common(10))

Salida;

elapsed: 0.806480884552 [(u''de'', 11106), (u''a'', 6742), (u''que'', 5701), (u''la'', 4319), (u''je'', 4260), (u''se'', 3938), (u''/u043d/u0430'', 3929), (u''na'', 3623), (u''da'', 3534), (u''i'', 3487)]

Parece que dict es más eficiente que Counter object.

def freq_dist(data): """ :param data: A string with sentences separated by ''/n'' :type data: str """ d = {} punc = punctuation.encode(''utf-8'') words = (word for line in data for word in line.translate(None, punc).decode(''utf-8'').split()) for word in words: d[word] = d.get(word, 0) + 1 return d start = time() word_dist = freq_dist(data.splitlines()) print(''elapsed: {}''.format(time() - start)) pprint(sorted(word_dist.items(), key=lambda x: (x[1], x[0]), reverse=True)[:10])

Salida;

elapsed: 0.642680168152 [(u''de'', 11106), (u''a'', 6742), (u''que'', 5701), (u''la'', 4319), (u''je'', 4260), (u''se'', 3938), (u''/u043d/u0430'', 3929), (u''na'', 3623), (u''da'', 3534), (u''i'', 3487)]

Para ser más eficiente en la memoria al abrir archivos grandes, debe pasar solo la url abierta. Pero el momento también incluirá el tiempo de descarga de archivos.

data = urlopen(url) word_dist = freq_dist(data)


Esto debería ser suficiente.

def countinfile(filename): d = {} with open(filename, "r") as fin: for line in fin: words = line.strip().split() for word in words: try: d[word] += 1 except KeyError: d[word] = 1 return d


Omita CountVectorizer y scikit-learn.

El archivo puede ser demasiado grande para cargarlo en la memoria, pero dudo que el diccionario de Python sea demasiado grande. La opción más fácil para usted puede ser dividir el archivo grande en 10-20 archivos más pequeños y extender su código para recorrer los archivos más pequeños.


Una forma eficiente y precisa de memoria es hacer uso de

  • CountVectorizer en scikit (para extracción de ngram)
  • word_tokenize para word_tokenize
  • suma de matriz numpy para recoger los recuentos
  • collections.Counter para recopilar los recuentos y el vocabulario

Un ejemplo:

import urllib.request from collections import Counter import numpy as np from nltk import word_tokenize from sklearn.feature_extraction.text import CountVectorizer # Our sample textfile. url = ''https://raw.githubusercontent.com/Simdiva/DSL-Task/master/data/DSLCC-v2.0/test/test.txt'' response = urllib.request.urlopen(url) data = response.read().decode(''utf8'') # Note that `ngram_range=(1, 1)` means we want to extract Unigrams, i.e. tokens. ngram_vectorizer = CountVectorizer(analyzer=''word'', tokenizer=word_tokenize, ngram_range=(1, 1), min_df=1) # X matrix where the row represents sentences and column is our one-hot vector for each token in our vocabulary X = ngram_vectorizer.fit_transform(data.split(''/n'')) # Vocabulary vocab = list(ngram_vectorizer.get_feature_names()) # Column-wise sum of the X matrix. # It''s some crazy numpy syntax that looks horribly unpythonic # For details, see http://.com/questions/3337301/numpy-matrix-to-array # and http://.com/questions/13567345/how-to-calculate-the-sum-of-all-columns-of-a-2d-numpy-array-efficiently counts = X.sum(axis=0).A1 freq_distribution = Counter(dict(zip(vocab, counts))) print (freq_distribution.most_common(10))

[fuera]:

[('','', 32000), (''.'', 17783), (''de'', 11225), (''a'', 7197), (''que'', 5710), (''la'', 4732), (''je'', 4304), (''se'', 4013), (''на'', 3978), (''na'', 3834)]

Esencialmente, también puedes hacer esto:

from collections import Counter import numpy as np from nltk import word_tokenize from sklearn.feature_extraction.text import CountVectorizer def freq_dist(data): """ :param data: A string with sentences separated by ''/n'' :type data: str """ ngram_vectorizer = CountVectorizer(analyzer=''word'', tokenizer=word_tokenize, ngram_range=(1, 1), min_df=1) X = ngram_vectorizer.fit_transform(data.split(''/n'')) vocab = list(ngram_vectorizer.get_feature_names()) counts = X.sum(axis=0).A1 return Counter(dict(zip(vocab, counts)))

Vamos a timeit :

import time start = time.time() word_distribution = freq_dist(data) print (time.time() - start)

[fuera]:

5.257147789001465

Tenga en cuenta que CountVectorizer también puede tomar un archivo en lugar de una cadena y no es necesario leer todo el archivo en la memoria . En codigo:

import io from collections import Counter import numpy as np from sklearn.feature_extraction.text import CountVectorizer infile = ''/path/to/input.txt'' ngram_vectorizer = CountVectorizer(analyzer=''word'', ngram_range=(1, 1), min_df=1) with io.open(infile, ''r'', encoding=''utf8'') as fin: X = ngram_vectorizer.fit_transform(fin) vocab = ngram_vectorizer.get_feature_names() counts = X.sum(axis=0).A1 freq_distribution = Counter(dict(zip(vocab, counts))) print (freq_distribution.most_common(10))


puedes probar con sklearn

from sklearn.feature_extraction.text import CountVectorizer vectorizer = CountVectorizer() data=[''i am student'',''the student suffers a lot''] transformed_data =vectorizer.fit_transform(data) vocab= {a: b for a, b in zip(vectorizer.get_feature_names(), np.ravel(transformed_data.sum(axis=0)))} print (vocab)