Manual de introducción a matplotlib.pyplot (VIII): Texto y anotaciones

Por Kiko Correoso

Esto pretende ser un tutorial del módulo pyplot de la librería matplotlib. El tutorial lo dividiremos de la siguiente forma (que podrá ir cambiando a medida que vayamos avanzando).

  1. Primeros pasos
  2. Creando ventanas, manejando ventanas y configurando la sesión
  3. Configuración del gráfico
  4. Tipos de gráfico I
  5. Tipos de gráfico II
  6. Tipos de gráfico III
  7. Tipos de gráfico IV
  8. Texto y anotaciones (arrow, annotate, table, text...)
  9. Herramientas estadísticas (acorr, cohere, csd, psd, specgram, spy, xcorr, ...)
  10. Eventos e interactividad (connect, disconnect, ginput, waitforbuttonpress...)
  11. Miscelánea

[Para este tutorial se ha usado python 2.7.1, ipython 0.11, numpy 1.6.1 y matplotlib 1.1.0 ]

[DISCLAIMER: Muchos de los gráficos que vamos a representar no tienen ningún sentido físico y los resultados solo pretenden mostrar el uso de la librería].

En todo momento supondremos que se ha iniciado la sesión y se ha hecho

import matplotlib.pyplot as plt
import numpy as np
plt.ion()

Hasta ahora hemos visto como configurar las ventanas, manejo de las mismas, definir áreas de gráfico, algunos tipos de gráficos... En esta ocasión nos interesa ver como podemos meter anotaciones, tablas,..., en nuestros gráficos.

A lo largo de las anteriores entregas del tutorial hemos podido ver algunas formas de tener anotaciones típicas para el título, los ejes, leyenda,... (title, suptitle, xlabel, ylabel, figtext, legend,...). En este caso vamos a revisar las posibilidades de escribir texto personalizado mediante el uso de plt.text, plt.arrow, plt.annotate y plt.table.

Como caso sencillo para anotar texto en nuestro gráfico podemos usar plt.text. En el siguiente ejemplo vamos a resaltar donde está el valor máximo y el valor mínimo de una serie de datos:

a = np.random.rand(10)  # Creamos una serie de 10 valores pseudo-aleatorios entre 0 y 1
plt.plot(a)  # Los dibujamos
plt.ylim(-0.2, 1.2)  # Definimos el rango de valores para el eje y
plt.text(np.argmin(a), np.min(a) - 0.1, u'Mínimo', fontsize = 10, horizontalalignment='center', verticalalignment='center')  # Colocamos texto cerca del valor donde se encuentra el mínimo
plt.text(np.argmax(a), np.max(a) + 0.1, u'Máximo', fontsize = 10, horizontalalignment='center', verticalalignment='center')  # Colocamos texto cerca del valor donde se encuentra el máximo

El resultado es el siguiente:

texto

Lo que hemos hecho en plt. text es definir la posición del texto con un valor para la x y un valor para la y (en el sistema de referencia de los datos), la cadena de texto a mostrar, como queremos que sea la fuente, donde queremos que vaya colocado, si la queremos rotar, si la queremos en negrita,...

Al anterior ejemplo le podemos incluir una flecha que una el texto con la representación del valor máximo y del valor mínimo. Para ello podemos usar plt.arrow modificando ligeramente el anterior código:

plt.plot(a)
plt.ylim(-0.5, 1.5)  # Extendemos un poco el rango del eje y
plt.text(np.argmax(a), np.max(a) + 0.4, u'Máximo', fontsize = 10, horizontalalignment='center', verticalalignment='center')  # Recolocamos el texto del máximo
plt.text(np.argmin(a), np.min(a) - 0.4, u'Mínimo', fontsize = 10, horizontalalignment='center', verticalalignment='center')  # Recolocamos el texto del mínimo
plt.arrow(np.argmax(a), np.max(a) + 0.3, 0, -0.3, length_includes_head = "True", shape = "full", width=0.07, head_width=0.1)  # Unimos el texto al valor representado
plt.arrow(np.argmin(a), np.min(a) - 0.3, 0, 0.3, length_includes_head = "True", shape = "full", width=0.07, head_width=0.1)  # Unimos el texto al valor representado

El resultado obtenido es el siguiente:

texto y flecha

En plt.arrow hemos de definir el origen de la flecha, la distancia desde ese origen hasta el otro extremo de la flecha, si queremos que tenga cabecera, si queremos que la cabecera esté en el origen, el color de la flecha,...

Lo que hemos hecho con plt.text y con plt.arrow lo podemos hacer de forma más compacta y elegante con plt.annotate. Como anteriormente, hacemos uso de un ejemplo y vamos viendo las partes a modificar de plt.annotate:

plt.plot(a)
plt.ylim(-0.5, 1.5)  # Extendemos un poco el rango del eje y
plt.annotate(u'Máximo', xy = (np.argmax(a), np.max(a)), xycoords = 'data', xytext = (np.argmax(a) - 1.5, np.max(a) + 0.4), textcoords = 'data', arrowprops = dict(arrowstyle = "->"))
plt.annotate(u'Mínimo', xy = (np.argmin(a), np.min(a)), xycoords = 'data', xytext = (np.argmin(a) + 1, np.min(a) + 1.2), textcoords = 'data', arrowprops = dict(arrowstyle = "->"))

Siendo el resultado el siguiente:

annotate

En plt.annotate introducimos la cadena de caracteres a mostrar, indicamos hacia donde apuntará esa cadena de caracteres (xy, en este caso estamos usando el sistema de referencia de los datos, 'data', pero podemos usar píxeles, puntos,...), la posición del texto (xytext), y como se representará la flecha. Con plt.annotate podemos tener anotaciones elegantes de forma sencilla como puedes ver en estos enlaces [1], [2].

Por último, vamos a ver como podemos dibujar una tabla de forma sencilla. Con plt.table podemos meter rápidamente una tabla pero por defecto la mete debajo del eje x. Vamos a ver un ejemplo que he encontrado en SO donde metemos la tabla dentro de los ejes.

valores = [[np.argmax(a), np.argmin(a)], [np.max(a), np.min(a)]]
etiquetas_fil = ('x', 'y')
etiquetas_col = (u'Máximo', u'Mínimo')
plt.plot(a)
plt.table(cellText=valores, rowLabels=etiquetas_fil, colLabels = etiquetas_col, colWidths = [0.3]*len(a), loc='upper center')

Cuyo resultado es el siguiente:

table

Donde hemos definido los valores de las celdas internas (cellText), Las etiquetas de filas y columnas (rowLabels y colLabels), el ancho de las celdas y la localización de la tabla.

Y, después de este breve entrada, hemos acabado por hoy haciendo un montón de anotaciones. Si quieres ver las anteriores entregas del tutorial pulsa aquí. Y si quieres ver la nueva entrega tendrás que esperar un poquito (pero muy poquito esta vez, espero).


photo

Kiko es doctor en ciencias físicas y esta especializado en física de la atmósfera, meteorología y climatología. Además de estar en las nubes es especialista en temas de energías renovables en Iberdrola. Ávido de seguir mejorando siempre está abierto a participar en nuevos proyectos y retos por lo que no dudes en contactarle si necesitas servicios especializados de Python o meteorología.

Comentarios