lunes, 5 de enero de 2015

Contar frecuencia de palabras en un texto con los filtros (comandos de consola) de Linux


Esta es una de esas necesidades recurrentes que aparecen cada poco tiempo. Y al final, siempre termino buscándolo en Internet, así que me he decidido a ponerlo aquí simplemente para tenerlo a mano.


El caso es que tengo un documento del que me gustaría sacar la estadística de palabras más utilizadas. O ya puestos, no limitarme a las más utilizadas, sino contar el número de veces que aparece cada palabra, para todas ellas.

Hay muchas soluciones para esto, podéis, por ejemplo, mirar aquí, en StackOverflow [1] varias formas de hacerlo.

La que a mí me ha parecido más sencilla, dentro de lo que yo necesitaba, y con alguna adaptación por mi parte (la opción -r en el sort) es esta:


Figura 1. Las 20 palabras más utilizadas en un texto



ALGUNOS DETALLES


¿Y si quiero saber las menos utilizadas? Simplemente, miremos el final del fichero:


Figura 2. Las palabras menos utilizadas (una vez) están al final


Al mirar el fichero de resultados, me he dado cuenta de que me incluye los dígitos. Puede ser que alguien quiera contar el número de veces que se menciona un año, por poner un ejemplo en el que interese contar los números, pero no es mi caso, así que me gustaría descartar los dígitos.

Por otro lado, me gustaría también, una vez puesto, ignorar los signos de puntuación como el punto, la coma, el punto y coma, paréntesis, comillas dobles, etc... Solucionar esto es fácil.

Habréis visto que el espacio en blanco es el único separador de palabras que estoy utilizando, así que lo único que tengo que hacer es decirle al comando tr (translate, traducir unos caracteres en otros) que no sólo me "traduzca" los espacios en blanco a nueva línea, sino también todos los caracteres que quiero excluir. Algo así:

Figura 3. ¿Y si quitamos los dígitos y signos de puntuación?

Hmmm... vaya. En la primera línea vemos que aparece una "palabra" que es el espacio en blanco. Bueno, no era mi intención contarlo, pero esto tampoco es un problema, se puede ignorar. Por otro lado, vemos que la palabra "de" ahora se cuenta algunas veces más (ha pasado de 1709 a 1712 apariciones). Seguramente antes había algunas apariciones en la que estaba pegada a una coma o algún otro signo de puntuación. Lo mismo ocurre con "la", "que" y otras palabras. Parece que eso pinta bien.

Pero si vemos las últimas líneas, aún siguen apareciendo dígitos. ¿Qué ha pasado? Y, por cierto, el símbolo del % y el € y "las otras" comillas dobles también los quitaré en la próxima versión de mi script.

Lo que ocurre con los dígitos es que le he querido decir que traduzca cualquier cosa que esté en el intervalo 0..9 en un carácter de nueva línea. Y yo, ignorante, he pensado que el intervalo para definir un dígito se podía poner como para otros filtros de unix, como un rango. Pues resulta que no, para el filtro tr podemos usar ciertas categorías de caracteres especiales como :digit: :alpha: y otras (más info tecleando "man tr").

Así que la forma correcta de decírselo sería utilizar esta categoría o simplemente teclear directamente los diez dígitos en la orden:
 
Figura 4.Ahora sí

Como veréis, he introducido algunos caracteres especiales (como tres versiones para las comillas dobles, pues el texto provenía de un documento de Writer de LibreOffice y tenía comillas normales y tipográficas, que a su vez diferencian entre las de apertura y las de cierre), o los tres puntos suspensivos como un único carácter. Como estos caracteres no he sabido cómo ponerlos directamente con el teclado, he usado el copiar / pegar de la consola para conseguir introducirlos en la línea del script.

Por último, os pego aquí la línea final del script en modo texto para que podáis copiarla si os interesa:

cat texto.txt  | tr ' .,;:0123456789()[]"“”%€…' '\n' | sort | uniq -c | sort -nr > resul.txt


Quedan un par de cosas pendientes, como las dichosas tildes, pero eso ya os lo dejo a vosotros, que no es cuestión de darlo todo masticado.

Creo que esto es un buen ejemplo de la potencia de los filtros en el mundo *NIX, y de cómo combinar herramientas pequeñas que hacen pequeñas tareas de forma muy eficiente, para conseguir resultados combinados verdaderamente potentes (¿alguien ha dicho sinergia?).


REFERENCIAS

[1] Contar palabras de diferentes maneras. http://stackoverflow.com/questions/10552803/how-to-create-a-frequency-list-of-every-word-in-a-file

1 comentario: