Dibujando una rosa de frecuencias

Este post ha sido actualizado para usar nuevas funcionalidades de matplotlib. Pincha sobre este enlace para ver la nueva versión.

Para la siguiente entrada se ha usado python 2.7.2, math (de la librería estándar), numpy 1.6.1 y matplotlib 1.1.0

Imaginaos que estáis de vacaciones en Agosto en la playa y la única preocupación que tenéis es observar las nubes. Como sois un poco frikis y no podéis desconectar de vuestra curiosidad científica decidís apuntar las ocurrencias de la procedencia de las nubes y al final de las vacaciones decidís representar esos datos. La forma más normal de hacerlo sería usando una rosa de frecuencias.

Primero de todo vamos a importar los módulos que nos harán falta:

## De la librería estándar
import math
## De librerías de terceros
import numpy as np
import matplotlib.pyplot as plt

A continuación creamos nuestra muestra de datos totalmente inventada. Para ello vamos a usar el módulo random incluido en numpy:

## Creamos un conjunto de 1000 datos entre 0 y 1 de forma aleatoria
## a partir de una distribución estándar normal
datos = np.random.randn(1000)
## Discretizamos el conjunto de valores en n intervalos,
## en este caso 8 intervalos
datosbin = np.histogram(datos,
                       bins = np.linspace(np.min(datos), np.max(datos), 9))[0]
## Los datos los queremos en tanto por ciento
datosbin = datosbin * 100. / len(datos)

En el bloque anterior de código lo único que hemos hecho es obtener una muestra aleatoria de una distribución normal y la hemos separado en 8 intervalos que pretenden ser las 8 direcciones de donde provienen las nubes empezando por el Norte y en el sentido de las agujas del reloj. Finalmente los datos los expresamos como frecuencia en tanto por ciento en cada una de las 8 direcciones.

Matplotlib nos permite hacer gráficos polares pero estos gráficos están pensados para gráficos en sentido contrario a las agujas del reloj y empezando a las tres en punto (o al este). Por ello debemos modificar como se verán los datos en el gráfico polar. Para ello hacemos lo siguiente:

## Los datos los queremos en n direcciones/secciones/sectores,
## en este caso usamos 8 sectores de una circunferencia
sect = np.array([90, 45, 0, 315, 270, 225, 180, 135]) * 2. * math.pi / 360.
nombresect = ['E','NE','N','NW','W','SW','S','SE']

[Actualización: después de escribir este script he vsto que desde la versión 1.1 de matplotlib se puede definir como quieres que sean los ejes en un gráfico polar. Podéis investigar por vuestra cuenta con la información de este enlace.]

Por último solo nos queda dibujar la rosa de frecuencias:

## Dibujamos la rosa de frecuencias
plt.axes([0.1,0.1,0.8,0.8], polar = True)
plt.bar(sect, datosbin, align='center', width=45 * 2 * math.pi / 360.,
        facecolor='b', edgecolor='k', linewidth=2, alpha=0.5)
plt.thetagrids(np.arange(0, 360, 45),nombresect,frac = 1.1, fontsize = 10)
plt.title(u'Procedencia de las nubes en marzo')
plt.show()

Definimos el tipo de gráfico y el área que ocupará. definimos colores de las barras, anchos de las líneas, transparencia de las barras,..., colocamos el nombre de la dirección en cada sector definido (en este caso hemos usado 8 sectores), ponemos un título a nuestro gráfico y hemos acabado.

Rosa de frecuencias de las nubes durante mis últimas vacaciones

Vuestra gráfica no tiene porque ser igual a esta, recordad que los datos los obtenemos de una muestra aleatoria.

Aquí os dejo el script completo:

## Importamos las librerías que necesitamos
## De la librería estándar
import math
## De librerías de terceros
import numpy as np
import matplotlib.pyplot as plt
## Creamos un conjunto de 1000 datos entre 0 y 1 de forma aleatoria
## a partir de una distribución estándar normal
datos = np.random.randn(1000)
## Discretizamos el conjunto de valores en n intervalos,
## en este caso 8 intervalos
datosbin = np.histogram(datos,
                       bins = np.linspace(np.min(datos), np.max(datos), 9))[0]
## Los datos los queremos en tanto por ciento
datosbin = datosbin * 100. / len(datos)
## Los datos los queremos en n direcciones/secciones/sectores,
## en este caso usamos 8 sectores de una circunferencia
sect = np.array([90, 45, 0, 315, 270, 225, 180, 135]) * 2. * math.pi / 360.
nombresect = ['E','NE','N','NW','W','SW','S','SE']
## Dibujamos la rosa de frecuencias
plt.axes([0.1,0.1,0.8,0.8], polar = True)
plt.bar(sect, datosbin, align='center', width=45 * 2 * math.pi / 360.,
        facecolor='b', edgecolor='k', linewidth=2, alpha=0.5)
plt.thetagrids(np.arange(0, 360, 45),nombresect,frac = 1.1, fontsize = 10)
plt.title(u'Procedencia de las nubes en marzo')
plt.show()

Saludos.

Kiko Correoso

Licenciado y PhD en Ciencias Físicas, especializado en temas de física, meteorología, climatología, energías renovables, estadística, aprendizaje automático, análisis y visualización de datos. Apasionado de Python y su comunidad. Fundador de pybonacci y editor del sitio en el que se divulga Python, Ciencia y el conocimiento libre en español.

More Posts

Follow Me:
TwitterLinkedIn

8 thoughts on “Dibujando una rosa de frecuencias

  1. ¡Qué buen aporte! Muy útil para la gente que se dedica a Aeropuertos para hacer el estudio de los vientos dominantes 😀

    Tan sólo una sugerencia: como sólo usas el módulo math para coger la constante pi, yo usaría la que viene en NumPy! (np.pi) Ya que lo tienes importado 😛

    Un saludo!

  2. Tienes razón en lo de numpy.pi pero la idea era meter una librería más, que si no la gente se va a aburrir del trinomio numpy/scipy/matplotlib 🙂

    Además, creo que algunas funciones de math son más eficientes que las mismas en numpy. A ver si algún día saco tiempo para investigarlo y hago una entrada.

    Saludos.

    1. Las funciones de numpy son un orden de magnitud más lentas que las de math para argumentos escalares, pero acceder a las constantes de uno u otro es indiferente.

      El resultado muy bueno!
      Un saludo

      1. Sí. lo de las constantes es lógico. No pretendía acelerar nada 🙂
        Respecto a los argumentos escalares, gracias por la aclaración, Lo había experimentado y leido alguna vez pero nunca llegué a investigarlo en profundidad. ¿Sabes por qué sucede? Supongo que la implementación de esas funciones en numpy está pensada para vectores y cuando solo se pasa un escalar está penalizada, ¿no?

        Saludos.

      2. No conozco los detalles, pero math llama casi directamente a las funciones de C de la plataforma, y numpy tendrá que hacer muchas más comprobaciones antes de empezar a calcular realmente.

  3. Parece que el código ya no funciona en matplotlib 1.3.1 🙁 específicamente la línea de plt.bar al final.

    1. Tengo que rehacer esta entrada ya que han incluido un par de cosas en mpl que hacen que dibujar algo así sea más sencillo.

      En breve una entrada actualizada

Leave a Reply

Your email address will not be published. Required fields are marked *