Quiero encontrar, digamos, las 10 palabras más comunes en un archivo de texto. En primer lugar, la solución debe optimizarse para las pulsaciones de teclas (en otras palabras, mi tiempo). En segundo lugar, por el rendimiento. Esto es lo que tengo hasta ahora para llegar al top 10:
cat test.txt | tr -c '[:alnum:]' '[\n*]' | uniq -c | sort -nr | head -10
6 k
2 g
2 e
2 a
1 r
1 k22
1 k
1 f
1 eeeeeeeeeeeeeeeeeeeee
1 d
Podría hacer un programa java, python, etc. donde almaceno (word, numberOfOccurences) en un diccionario y ordenar el valor o podría usar MapReduce, pero optimizo para las pulsaciones de teclas.
¿Hay algún falso positivo? ¿Hay una mejor manera?
command-line
shell-script
Lukasz Madon
fuente
fuente
Respuestas:
Esa es más o menos la forma más común de encontrar "N cosas más comunes", excepto que te falta una
sort
, y tienes un servicio gratuitocat
:Si no pone un
sort
antesuniq -c
, probablemente obtendrá muchas palabras únicas falsas.uniq
solo ejecuta líneas únicas, no uniquness general.EDITAR: Olvidé un truco, "deja de palabras". Si está buscando texto en inglés (lo siento, aquí en América del Norte monolingüe), palabras como "de", "y", "el" casi siempre ocupan los primeros dos o tres lugares. Probablemente quieras eliminarlos. La distribución GNU Groff tiene un archivo llamado
eign
que contiene una lista bastante decente de palabras de detención. Mi distribución de Arch sí/usr/share/groff/current/eign
, pero creo que también he visto/usr/share/dict/eign
o/usr/dict/eign
en viejos Unixes.Puede usar palabras de detención como esta:
Supongo que la mayoría de los idiomas humanos necesitan "palabras de detención" similares eliminadas de los recuentos significativos de frecuencia de palabras, pero no sé dónde sugerir que otros idiomas detengan las listas de palabras.
EDITAR:
fgrep
debe usar el-w
comando, que permite la coincidencia de palabras completas. Esto evita falsos positivos en palabras que simplemente contienen trabajos de parada corta, como "a" o "i".fuente
cat
Agrega alguna sobrecarga de rendimiento significativo? Me gusta la sintaxis de la tubería. ¿Qué hace el * en '[\ n *]'?find
salida? Es decir, dividir palabras en/
lugar de caracteres de espacio en blanco y similares.find somewhere optoins | tr '/' '\n' | sort | uniq -c | sort -k1.1nr | head -10
Esto funciona mejor con utf-8:
fuente
¡Usemos AWK!
Esta función enumera la frecuencia de cada palabra que aparece en el archivo proporcionado en orden descendente:
Puede llamarlo en su archivo así:
y para las 10 palabras principales:
Fuente: AWK-ward Ruby
fuente
¡Usemos Haskell!
Esto se está convirtiendo en una guerra de idiomas, ¿no?
Uso:
Alternativamente:
fuente
sort | uniq -c | sort -nr
.Text
, oByteString
en lugar, lo cual es tan simple como su importación calificado y anteponiendo las funciones con el calificador.Algo como esto debería funcionar usando Python, que está comúnmente disponible:
Esto supone palabra por línea. Si hay más, la división también debería ser fácil.
fuente
cat README.md | python -c 'import collections, sys, pprint; pprint.pprint(collections.Counter(sys.stdin));'
Este es un problema clásico que tuvo cierta resonancia en 1986, cuando Donald Knuth implementó una solución rápida con pruebas hash en un programa de 8 páginas para ilustrar su técnica de programación alfabetizada, mientras que Doug McIlroy, el padrino de las pipas de Unix, respondió con un one-liner, eso no fue tan rápido, pero hizo el trabajo:
Por supuesto, la solución de McIlroy tiene una complejidad de tiempo O (N log N), donde N es un número total de palabras. Hay soluciones mucho más rápidas. Por ejemplo:
Aquí hay una implementación de C ++ con la complejidad de tiempo límite superior O ((N + k) log k), típicamente, casi lineal.
A continuación se muestra una implementación rápida de Python usando diccionarios hash y montón con complejidad de tiempo O (N + k log Q), donde Q es una cantidad de palabras únicas:
Comparación de tiempo de CPU (en segundos):
Notas:
fuente