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.
pyplot
y orientada a objetosLo primero que vamos a hacer es activar el modo inline - de esta manera las figuras aparecerán automáticamente incrustadas en el notebook.
%matplotlib inline
Importamos los paquetes necesarios:
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):
from IPython.display import HTML
HTML('<iframe src="http://matplotlib.org/gallery.html#pylab_examples" width="800" height="600"></iframe>')
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):
HTML('<iframe src="http://matplotlib.org/examples/pylab_examples/annotation_demo.html" width="800" height="600"></iframe>')
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.
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
:
plt
<module 'matplotlib.pyplot' from '/home/juanlu/.miniconda36/envs/aeropython36/lib/python3.6/site-packages/matplotlib/pyplot.py'>
plt.plot([0.0, 0.1, 0.2, 0.7, 0.9], [1, -2, 3, 4, 1])
[<matplotlib.lines.Line2D at 0x7fa63fa0e048>]
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:
def f(x):
return np.exp(-x ** 2)
Definimos el dominio con la función np.linspace
, que crea un vector de puntos equiespaciados:
x = np.linspace(-1, 3, 100)
Y representamos la función:
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)$")
<matplotlib.text.Text at 0x7fa63f99eb70>
Notamos varias cosas:
plt.
se actualiza el gráfico actual. Esa es la forma de trabajar con la interfaz pyplot.label
podemos definir una leyenda.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.
plt.plot(x, f(x), 'ro')
plt.plot(x, 1 - f(x), 'g--')
[<matplotlib.lines.Line2D at 0x7fa63f58a860>]
Esto en realidad son códigos abreviados, que se corresponden con argumentos de la función plot
:
plt.plot(x, f(x), color='red', linestyle='', marker='o')
plt.plot(x, 1 - f(x), c='g', ls='--')
[<matplotlib.lines.Line2D at 0x7fa63f52ae10>]
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.
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
.
plt.style.available
['seaborn-ticks', 'seaborn-paper', 'seaborn-white', 'seaborn-darkgrid', 'fivethirtyeight', 'dark_background', 'seaborn-colorblind', 'grayscale', 'seaborn-muted', 'classic', 'seaborn-poster', 'seaborn-whitegrid', 'seaborn-bright', 'seaborn-dark-palette', 'seaborn-dark', 'seaborn-talk', 'seaborn', 'seaborn-pastel', 'ggplot', 'seaborn-deep', 'bmh', 'seaborn-notebook', 'pybonacci']
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
#plt.style.use("ggplot") # Afecta a todos los plots
Para emplear un estilo solo a una porción del código, creamos un bloque with plt.style.context("STYLE")
:
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:
with plt.xkcd():
plt.plot(x, f(x))
plt.plot(x, 1 - f(x))
plt.xlabel("Eje x")
/home/juanlu/.miniconda36/envs/aeropython36/lib/python3.6/site-packages/matplotlib/font_manager.py:1297: UserWarning: findfont: Font family ['xkcd', 'Humor Sans', 'Comic Sans MS'] not found. Falling back to DejaVu Sans (prop.get_family(), self.defaultFamily[fontext]))
¡Nunca imitar a XKCD fue tan fácil! http://xkcd.com/353/
La función scatter
muestra una nube de puntos, con posibilidad de variar también el tamaño y el color.
N = 100
x = np.random.randn(N)
y = np.random.randn(N)
plt.scatter(x, y)
<matplotlib.collections.PathCollection at 0x7fa63f3aef60>
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
.
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()
<matplotlib.colorbar.Colorbar at 0x7fa63f293438>
plt.scatter(x, y, s=s, c=c, cmap=plt.cm.Oranges)
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7fa63f2bac50>
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:
def f(x, y):
return x ** 2 - y ** 2
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()
<matplotlib.colorbar.Colorbar at 0x7fa63f0509b0>
La función contourf
es casi idéntica pero rellena el espacio entre niveles. Podemos especificar manualmente estos niveles usando el cuarto argumento:
plt.contourf(xx, yy, zz, np.linspace(-4, 4, 100))
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0x7fa63ede5c50>
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.
Podemos crear figuras con varios sistemas de ejes, pasando a subplot
el número de filas y de columnas.
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")
<matplotlib.text.Text at 0x7fa63edb84a8>
Como hemos guardado la figura en una variable, puedo recuperarla más adelate y seguir editándola.
fig.tight_layout()
fig
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:
y usar algún estilo de los disponibles.
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:
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}$")
<matplotlib.text.Text at 0x7fa63ec42710>
Las siguientes celdas contienen configuración del Notebook
Para visualizar y utlizar los enlaces a Twitter el notebook debe ejecutarse como seguro
File > Trusted Notebook
# Esta celda da el estilo al notebook
from IPython.core.display import HTML
css_file = '../styles/aeropython.css'
HTML(open(css_file, "r").read())