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.
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):
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.
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]:
In [6]:
plt.plot([0.0, 0.1, 0.2, 0.7, 0.9], [1, -2, 3, 4, 1])
Out[6]:
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:
In [7]:
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 [8]:
x = np.linspace(-1, 3, 100)
Y representamos la función:
In [9]:
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[9]:
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.
In [10]:
plt.plot(x, f(x), 'ro')
plt.plot(x, 1 - f(x), 'g--')
Out[10]:
Esto en realidad son códigos abreviados, que se corresponden con argumentos de la función plot
:
In [11]:
plt.plot(x, f(x), color='red', linestyle='', marker='o')
plt.plot(x, 1 - f(x), c='g', ls='--')
Out[11]:
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
.
In [12]:
plt.style.available
Out[12]:
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 [13]:
#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")
:
In [14]:
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 [15]:
with plt.xkcd():
plt.plot(x, f(x))
plt.plot(x, 1 - f(x))
plt.xlabel("Eje x")
¡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.
In [16]:
N = 100
x = np.random.randn(N)
y = np.random.randn(N)
plt.scatter(x, y)
Out[16]:
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]:
In [18]:
plt.scatter(x, y, s=s, c=c, cmap=plt.cm.Oranges)
plt.colorbar()
Out[18]:
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:
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]:
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]:
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.
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]:
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]:
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.
In [70]:
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 [68]:
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[68]:
No tenemos mucho tiempo pero vamos a ver algo interesante que se ha introducido hace poco en el notebook: componentes interactivos.
In [69]:
from IPython.html.widgets import interactive
interactive(frecuencias, f1=(10.0,200.0), f2=(10.0,200.0))
In [1]:
# Esta celda da el estilo al notebook
from IPython.core.display import HTML
css_file = './css/aeropython.css'
HTML(open(css_file, "r").read())
Out[1]:
In [ ]: