Guión palabra recuento de frecuencia es demasiado lento
-
30-09-2019 - |
Pregunta
Fondo
Creación de un script para contar la frecuencia de las palabras en un archivo de texto plano. Las secuencias de comandos lleva a cabo los siguientes pasos:
- conde la frecuencia de las palabras de un corpus.
- Conservar cada palabra en el corpus encuentra en un diccionario.
- Crea un archivo separado por comas de las frecuencias.
El script se encuentra en: http://pastebin.com/VAZdeKXs
#!/bin/bash
# Create a tally of all the words in the corpus.
#
echo Creating tally of word frequencies...
sed -e 's/ /\n/g' -e 's/[^a-zA-Z\n]//g' corpus.txt | \
tr [:upper:] [:lower:] | \
sort | \
uniq -c | \
sort -rn > frequency.txt
echo Creating corpus lexicon...
rm -f corpus-lexicon.txt
for i in $(awk '{if( $2 ) print $2}' frequency.txt); do
grep -m 1 ^$i\$ dictionary.txt >> corpus-lexicon.txt;
done
echo Creating lexicon...
rm -f lexicon.txt
for i in $(cat corpus-lexicon.txt); do
egrep -m 1 "^[0-9 ]* $i\$" frequency.txt | \
awk '{print $2, $1}' | \
tr ' ' ',' >> lexicon.txt;
done
Problema
Las siguientes líneas continuamente ciclo a través del diccionario para que coincida con las palabras:
for i in $(awk '{if( $2 ) print $2}' frequency.txt); do
grep -m 1 ^$i\$ dictionary.txt >> corpus-lexicon.txt;
done
Funciona, pero es lento porque está escaneando las palabras que había constatado que elimine las que no están en el diccionario. El código realiza esta tarea mediante el escaneo del diccionario para cada palabra. (El parámetro -m 1
detiene la exploración cuando se encuentra el partido.)
Pregunta ??h2>
¿Cómo optimizar la secuencia de comandos para que el diccionario no es escaneada de principio a fin para cada una de las palabras? La mayoría de las palabras no estará en el diccionario.
Gracias!
Solución
Puede utilizar grep -f
a buscar todas las palabras en un solo pase frequency.txt:
awk '{print $2}' frequency.txt | grep -Fxf dictionary.txt > corpus-lexicon.txt
-
-F
para buscar cadenas fijas. -
-x
para que coincida con solamente líneas enteras. -
-f
para leer los patrones de búsqueda de dictionary.txt
De hecho, incluso se podría combinar esto con el segundo bucle y eliminar el archivo corpus-lexicon.txt intermedia. Los dos bucles pueden ser reemplazados por un solo grep:
grep -Fwf dictionary.txt frequency.txt | awk '{print $2 "," $1}'
Tenga en cuenta que he cambiado -x
a -w
.
Otros consejos
Esto es típicamente una de esas secuencias de comandos que escribiría en Perl para la velocidad. Pero si, como yo, que odio idiomas de sólo escritura programación, puede hacerlo todo en AWK:
awk '
BEGIN {
while ((getline < "dictionary.txt") > 0)
dict[$1] = 1
}
($2 && $2 in dict) { print $2 }
' < frequency.txt > corpus-lexicon.txt
No hay necesidad de que el rm -f corpus-lexicon.txt
en esta versión.
El uso de un lenguaje de programación real. Todos los de la aplicación para start ups y análisis de archivos que están matando. Por ejemplo, he aquí un ejemplo que acabo prepararon rápidamente en Python (minimización de líneas de código):
import sys, re
words = re.findall(r'(\w+)',open(sys.argv[1]).read())
counts = {}
for word in words:
counts[word] = counts.setdefault(word,0) + 1
open(sys.argv[2],'w').write("\n".join([w+','+str(c) for (w,c) in counts.iteritems()]))
Pruebas de un aeropuerto internacional alrededor contra un archivo de texto grande que había sentado (1,4 MB, 80.000 palabras de acuerdo con WC), esto se completa en menos de un segundo (18k palabras únicas) en un viejo PowerMac 5 años.