Introducción a la visualización con matplotlib

Después de estudiar la sintaxis de Python y empezar a manejar datos numéricos de manera un poco más profesional, ha llegado el momento de visualizarlos. Con la biblioteca matplotlib podemos crear gráficos de muy alta calidad y altamente personalizables.

matplotlib es una biblioteca muy potente que requiere tiempo de práctica para dominarla. Vamos a empezar por lo más sencillo.

¿Qué es matplotlib?

  • Estándar de facto para visualización en Python
  • Pretende ser similar a las funciones de visualización de MATLAB
  • Diferentes formas de usarla: interfaz pyplot y orientada a objetos

Lo primero que vamos a hacer es activar el modo inline - de esta manera las figuras aparecerán automáticamente incrustadas en el notebook.


In [1]:
%matplotlib inline

Importamos los paquetes necesarios:


In [2]:
import numpy as np
import matplotlib.pyplot as plt

La biblioteca matplotlib es gigantesca y es difícil hacerse una idea global de todas sus posibilidades en una primera toma de contacto. Es recomendable tener a mano la documentación y la galería (http://matplotlib.org/gallery.html#pylab_examples):


In [3]:
from IPython.display import HTML
HTML('<iframe src="http://matplotlib.org/gallery.html#pylab_examples" width="800" height="600"></iframe>')


Out[3]:

Si hacemos clic en cualquiera de las imágenes, accedemos al código fuente que la ha generado (ejemplo: http://matplotlib.org/examples/pylab_examples/annotation_demo.html):


In [4]:
HTML('<iframe src="http://matplotlib.org/examples/pylab_examples/annotation_demo.html" width="800" height="600"></iframe>')


Out[4]:

Interfaz pyplot

La interfaz pyplot proporciona una serie de funciones que operan sobre un estado global - es decir, nosotros no especificamos sobre qué gráfica o ejes estamos actuando. Es una forma rápida y cómoda de crear gráficas pero perdemos parte del control.

Función plot

El paquete pyplot se suele importar bajo el alias plt, de modo que todas las funciones se acceden a través de plt.<funcion>. La función más básica es la función plot:


In [5]:
plt


Out[5]:
<module 'matplotlib.pyplot' from 'C:\\Users\\cacheme\\Miniconda3\\lib\\site-packages\\matplotlib\\pyplot.py'>

In [6]:
plt.plot([0.0, 0.1, 0.2, 0.7, 0.9], [1, -2, 3, 4, 1])


Out[6]:
[<matplotlib.lines.Line2D at 0x9f03588>]

La función plot recibe una sola lista (si queremos especificar los valores y) o dos listas (si especificamos x e y). Naturalmente si especificamos dos listas ambas tienen que tener la misma longitud.

La tarea más habitual a la hora de trabajar con matplotlib es representar una función. Lo que tendremos que hacer es definir un dominio y evaluarla en dicho dominio. Por ejemplo:

$$ f(x) = e^{-x^2} $$

In [27]:
def f(x):
    return np.exp(-x ** 2)

Definimos el dominio con la función np.linspace, que crea un vector de puntos equiespaciados:


In [28]:
x = np.linspace(-1, 3, 100)

Y representamos la función:


In [29]:
plt.plot(x, f(x), label="Función f(x)")
plt.xlabel("Eje $x$")
plt.ylabel("$f(x)$")
plt.legend()
plt.title("Función $f(x)$")


Out[29]:
<matplotlib.text.Text at 0xb34b198>

Notamos varias cosas:

  • Con diversas llamadas a funciones dentro de plt. se actualiza el gráfico actual. Esa es la forma de trabajar con la interfaz pyplot.
  • Podemos añadir etiquetas, y escribir $\LaTeX$ en ellas. Tan solo hay que encerrarlo entre signos de dólar $$.
  • Añadiendo como argumento label podemos definir una leyenda.

Personalización

La función plot acepta una serie de argumentos para personalizar el aspecto de la función. Con una letra podemos especificar el color, y con un símbolo el tipo de línea.


In [30]:
plt.plot(x, f(x), 'ro')
plt.plot(x, 1 - f(x), 'g--')


Out[30]:
[<matplotlib.lines.Line2D at 0xd1a7320>]

Esto en realidad son códigos abreviados, que se corresponden con argumentos de la función plot:


In [31]:
plt.plot(x, f(x), color='red', linestyle='', marker='o')
plt.plot(x, 1 - f(x), c='g', ls='--')


Out[31]:
[<matplotlib.lines.Line2D at 0xb97ea58>]

La lista de posibles argumentos y abreviaturas está disponible en la documentación de la función plot http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.plot.

Más personalización, pero a lo loco

Desde matplotlib 1.4 se puede manipular fácilmente la apariencia de la gráfica usando estilos. Para ver qué estilos hay disponibles, escribiríamos plt.style.available.


In [32]:
plt.style.available


Out[32]:
['seaborn-muted',
 'seaborn-deep',
 'seaborn-dark',
 'seaborn-poster',
 'seaborn-paper',
 'seaborn-white',
 'bmh',
 'grayscale',
 'seaborn-colorblind',
 'fivethirtyeight',
 'seaborn',
 'dark_background',
 'seaborn-talk',
 'seaborn-ticks',
 'seaborn-darkgrid',
 'seaborn-pastel',
 'seaborn-whitegrid',
 'seaborn-notebook',
 'ggplot',
 'classic',
 'seaborn-dark-palette',
 'seaborn-bright']

No hay muchos pero podemos crear los nuestros. Para activar uno de ellos, usamos plt.style.use. ¡Aquí va el que uso yo! https://gist.github.com/Juanlu001/edb2bf7b583e7d56468a


In [33]:
#plt.style.use("ggplot")  # Afecta a todos los plots
No he sido capaz de encontrar una manera fácil de volver a la apariencia por defecto en el notebook. A ver qué dicen los desarrolladores (https://github.com/ipython/ipython/issues/6707) ¡pero de momento si quieres volver a como estaba antes toca reiniciar el notebook!

Para emplear un estilo solo a una porción del código, creamos un bloque with plt.style.context("STYLE"):


In [34]:
with plt.style.context('ggplot'):
    plt.plot(x, f(x))
    plt.plot(x, 1 - f(x))


Y hay otro tipo de personalización más loca todavía:


In [35]:
with plt.xkcd():
    plt.plot(x, f(x))
    plt.plot(x, 1 - f(x))
    plt.xlabel("Eje x")
    plt.ylabel("Eje y")


¡Nunca imitar a XKCD fue tan fácil! http://xkcd.com/353/

Otros tipo de gráficas

La función scatter muestra una nube de puntos, con posibilidad de variar también el tamaño y el color.


In [16]:
N = 100
x = np.random.randn(N)
y = np.random.randn(N)

plt.scatter(x, y)


Out[16]:
<matplotlib.collections.PathCollection at 0xba229e8>

Con s y c podemos modificar el tamaño y el color respectivamente. Para el color, a cada valor numérico se le asigna un color a través de un mapa de colores; ese mapa se puede cambiar con el argumento cmap. Esa correspondencia se puede visualizar llamando a la función colorbar.


In [17]:
s = np.abs(50 + 50 * np.random.randn(N))
c = np.random.randn(N)

plt.scatter(x, y, s=s, c=c, cmap=plt.cm.Blues)
plt.colorbar()


Out[17]:
<matplotlib.colorbar.Colorbar at 0xbb11470>

In [18]:
plt.scatter(x, y, s=s, c=c, cmap=plt.cm.Oranges)
plt.colorbar()


Out[18]:
<matplotlib.colorbar.Colorbar at 0xbbd25c0>

matplotlib trae por defecto muchos mapas de colores. En las SciPy Lecture Notes dan una lista de todos ellos (http://scipy-lectures.github.io/intro/matplotlib/matplotlib.html#colormaps)

La función contour se utiliza para visualizar las curvas de nivel de funciones de dos variables y está muy ligada a la función np.meshgrid. Veamos un ejemplo:

$$f(x) = x^2 - y^2$$

In [19]:
def f(x, y):
    return x ** 2 - y ** 2

In [20]:
x = np.linspace(-2, 2)
y = np.linspace(-2, 2)
xx, yy = np.meshgrid(x, y)
zz = f(xx, yy)

plt.contour(xx, yy, zz)
plt.colorbar()


Out[20]:
<matplotlib.colorbar.Colorbar at 0xbc93710>

La función contourf es casi idéntica pero rellena el espacio entre niveles. Podemos especificar manualmente estos niveles usando el cuarto argumento:


In [21]:
plt.contourf(xx, yy, zz, np.linspace(-4, 4, 100))
plt.colorbar()


Out[21]:
<matplotlib.colorbar.Colorbar at 0xbe6a1d0>

Para guardar las gráficas en archivos aparte podemos usar la función plt.savefig. matplotlib usará el tipo de archivo adecuado según la extensión que especifiquemos. Veremos esto con más detalle cuando hablemos de la interfaz orientada a objetos.

Varias figuras

Podemos crear figuras con varios sistemas de ejes, pasando a subplot el número de filas y de columnas.


In [22]:
x = np.linspace(-1, 7, 1000)

fig = plt.figure()
plt.subplot(211)
plt.plot(x, np.sin(x))
plt.grid(False)
plt.title("Función seno")

plt.subplot(212)
plt.plot(x, np.cos(x))
plt.grid(False)
plt.title("Función coseno")


Out[22]:
<matplotlib.text.Text at 0xba53e10>
¿Cómo se ajusta el espacio entre gráficas para que no se solapen los textos? Buscamos en Google "plt.subplot adjust" en el primer resultado tenemos la respuesta http://stackoverflow.com/a/9827848

Como hemos guardado la figura en una variable, puedo recuperarla más adelate y seguir editándola.


In [23]:
fig.tight_layout()
fig


Out[23]:
Si queremos manipular la figura una vez hemos abandonado la celda donde la hemos definido, tendríamos que utilizar la interfaz orientada a objetos de matplotlib. Es un poco lioso porque algunas funciones cambian de nombre, así que en este curso no la vamos a ver. Si te interesa puedes ver los notebooks de la primera edición, donde sí la introdujimos. https://github.com/AeroPython/Curso_AeroPython/releases/tag/v1.0

Ejercicio

Crear una función que represente gráficamente esta expresión:

$$\sin(2 \pi f_1 t) + \sin(2 \pi f_2 t)$$

Siendo $f_1$ y $f_2$ argumentos de entrada (por defecto $10$ y $100$) y $t \in [0, 0.5]$. Además, debe mostrar:

  • leyenda,
  • título "Dos frecuencias",
  • eje x "Tiempo ($t$)"

y usar algún estilo de los disponibles.


In [24]:
def frecuencias(f1=10.0, f2=100.0):
    max_time = 0.5
    times = np.linspace(0, max_time, 1000)
    signal = np.sin(2 * np.pi * f1 * times) + np.sin(2 * np.pi * f2 * times)
    with plt.style.context("ggplot"):
        plt.plot(signal, label="Señal")
        plt.xlabel("Tiempo ($t$)")
        plt.title("Dos frecuencias")
        plt.legend()

frecuencias()


Ejercicio

Representar las curvas de nivel de esta función:

$$g(x, y) = \cos{x} + \sin^2{y}$$

Para obtener este resultado:


In [25]:
def g(x, y):
    return np.cos(x) + np.sin(y) ** 2

# Necesitamos muchos puntos en la malla, para que cuando se
# crucen las líneas no se vean irregularidades
x = np.linspace(-2, 3, 1000)
y = np.linspace(-2, 3, 1000)

xx, yy = np.meshgrid(x, y)

zz = g(xx, yy)

# Podemos ajustar el tamaño de la figura con figsize
fig = plt.figure(figsize=(6, 6))

# Ajustamos para que tenga 13 niveles y que use el colormap Spectral
# Tenemos que asignar la salida a la variable cs para luego crear el colorbar
cs = plt.contourf(xx, yy, zz, np.linspace(-1, 2, 13), cmap=plt.cm.Spectral)

# Creamos la barra de colores
plt.colorbar()

# Con `colors='k'` dibujamos todas las líneas negras
# Asignamos la salida a la variable cs2 para crear las etiquetas
cs = plt.contour(xx, yy, zz, np.linspace(-1, 2, 13), colors='k')

# Creamos las etiquetas sobre las líneas
plt.clabel(cs)

# Ponemos las etiquetas de los ejes
plt.xlabel("Eje x")
plt.ylabel("Eje y")
plt.title(r"Función $g(x, y) = \cos{x} + \sin^2{y}$")


Out[25]:
<matplotlib.text.Text at 0xbfc7a20>

Referencias



¡Síguenos en Twitter!



Este notebook ha sido realizado por: Juan Luis Cano, y Álex Sáez



<span xmlns:dct="http://purl.org/dc/terms/" property="dct:title">Curso AeroPython</span> por <span xmlns:cc="http://creativecommons.org/ns#" property="cc:attributionName">Juan Luis Cano Rodriguez y Alejandro Sáez Mollejo</span> se distribuye bajo una Licencia Creative Commons Atribución 4.0 Internacional.