Como utilizar Python para encontrar la distribución Zipf de un archivo de texto

Seguramente te estés preguntando que qué es eso de la distribución Zipf. Para entender lo que queremos decir con este término, es necesario definir la ley de Zipf antes que nada. A continuación te explicaremos qué es eso de la ley de Zipf.

La ley de Zipf

La ley de Zipf es una ley empírica según la cual en una determinada lengua, la frecuencia de aparición de distintas palabras sigue una distribución que puede aproximarse por Pn != 1/nª, donde Pn representa la frecuencia de la n-ésima palabra más frecuente y el exponente a es un número real positivo, en general ligeramente superior a 1.1 Esto significa que el segundo elemento se repetirá aproximadamente con una frecuencia de 1/2 de la del primero, el tercer elemento con una frecuencia de 1/3 y así sucesivamente.

Veamos un ejemplo de ello. En el idioma español, las palabras más frecuentes suelen ser artículos y preposiciones. En el caso del inglés, seguramente la palabra que más se repita es “the”. Teniendo en cuenta que la palabra “La” tiene rango 1, palabras menos frecuentes como “serendipia” o “melifluo” contarán con un rango muy alto. No importa que texto cojas de ejemplo, dichas palabras siempre tendrán un rango alto en un texto normal de más de 5000 palabras.

Por lo tanto, la ley de Zipf está tratando de decirnos que un pequeño número de palabras, por lo general, representan la mayor parte de las actividades que observamos. Por ejemplo, un pequeño número de enfermedades (cáncer, enfermedades cardiovasculares) representan la mayor parte de las muertes. Esto también se aplica a las palabras que dan cuenta de la mayor parte de todas las apariciones de la palabra en la literatura, y muchos otros ejemplos en nuestras vidas.

Preparación de los datos

Antes de avanzar, te indicaré los datos que vamos a utilizar para experimentar con nuestro tutorial. Los datos de ejemplo en esta ocasión vendrán de la National Library of Medicine. Descargaremos un fichero ASCII llamado MeSH (Medical Subject Heading) desde aquí.

No entraré en detalles sobre de qué trata este archivo, ya que va más allá del alcance de este tutorial, y sólo lo necesitaremos para experimentar con nuestro código.

Implementando el programa

Después de haberte descargado los datos según te he indicado en la sección anterior, vamos a empezar a implementar nuestro script de Python que encontrará la distribución zipf de los datos descargados.

Lo primero a realizar será abrir el archivo:

open_file = open('d2016.bin', 'r')

Con el fin de llevar a cabo las operaciones necesarias en el archivo bin, necesitamos cargar el archivo en una variable string. Esto se puede conseguir simplemente utilizando la función read(), tal que así:

file_to_string = open_file.read()

Puesto que vamos a buscar cierto patrón (por ejemplo, palabras), tendremos que utilizar las tan odiadas expresiones regulares. Para ello utilizaremos el módulo re de Python.

En este punto ya hemos leído el archivo bin y cargado su contenido en una variable string. Encontrar la distribución de Zipf significa encontrar la frecuencia con la que aparecen las distintas palabras en el archivo bin. La expresión regular, por lo tanto, la utilizaremos para localizar las palabras del archivo.

El método que vamos a utilizar para llevar a cabo todo esto será el método findall (). Como se puede leer en la documentación del módulo re sobre findall(), el método hará lo siguiente:

Return all non-overlapping matches of pattern in string, as a list of strings. The string is scanned left-to-right, and matches are returned in the order found. If one or more groups are present in the pattern, return a list of groups; this will be a list of tuples if the pattern has more than one group. Empty matches are included in the result unless they touch the beginning of another match.

Lo que haremos es escribir una expresión regular que localice todas las palabras individuales dentro de la variable string. La expresión regular que puedes utilizar para realizar esta tarea es:

b[A-Za-z][a-z]{2,10}b

En Python, esto puede representarse tal que así:

words = re.findall(r'(b[A-Za-z][a-z]{2,9}b)', file_to_string)

Esta expresión regular básicamente nos encontrará todas las palabras que comienzan con una letra (mayúscula o minúscula) y que, seguido, cuenten con una secuencia de letras de al menos 2 caracteres y de no más de 9 caracteres. En otras palabras, el tamaño de las palabras que se incluirán en la salida estará en el intervalo de 3 a 10 caracteres.

Ahora podemos ejecutar un bucle para calcular la frecuencia de cada una de las palabras:

for word in words:
    count = frequency.get(word,0)
    frequency[word] = count + 1

En este caso, si la palabra no se encuentra todavía en la lista de palabras, en lugar de mostrar un KeyError, se devolverá el valor por defecto 0. De lo contrario, el recuento se incrementa en 1, que representa el número de veces que la palabra ha aparecido en la lista hasta el momento.

Por último, vamos a imprimir el par clave-valor del diccionario, mostrando la palabra (clave) y el número de veces que ha aparecido en la lista (valor):

for key, value in reversed(sorted(frequency.items(), key = itemgetter(1))):
    print key, value

Esta parte sorted(frequency.items(), key = itemgetter(1)) ordena la salida por valor en orden ascendente, es decir, que muestra las palabras desde la menos frecuente hasta la más frecuente.Si quieres enumerar las palabras más frecuentes al principio, podemos utilizar el método reverse().

Poniéndolo todo junto

Vamos a ver cómo se ve nuestro programa al completo:

import re
from operator import itemgetter    
 
frequency = {}
open_file = open('d2016.bin', 'r')
file_to_string = open_file.read()
words = re.findall(r'(b[A-Za-z][a-z]{2,9}b)', file_to_string)
 
for word in words:
    count = frequency.get(word,0)
    frequency[word] = count + 1
     
for key, value in reversed(sorted(frequency.items(), key = itemgetter(1))):
    print key, value

Mostraré aquí las primeras diez palabras y sus frecuencias devueltas por el programa:

the 42602
abcdef 31913
and 30699
abbcdef 27016
was 17430
see 16189
with 14380
under 13127
for 9767
abcdefv 8694

Fuente: Abder-Rahman Ali

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR