Curso de Python para Ingenieros Mecánicos

Por: Eduardo Vieira



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):

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 '/home/juanlu/.local/lib/python3.4/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 0x7f9ff97b70b8>]

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 [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]:
<matplotlib.text.Text at 0x7f9ff9732fd0>

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 [10]:
plt.plot(x, f(x), 'ro')
plt.plot(x, 1 - f(x), 'g--')


Out[10]:
[<matplotlib.lines.Line2D at 0x7f9ff9708828>]

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]:
[<matplotlib.lines.Line2D at 0x7f9ff9600400>]

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 [12]:
plt.style.available


Out[12]:
['pybonacci',
 'grayscale',
 'ggplot',
 'dark_background',
 'fivethirtyeight',
 'bmh']

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
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 [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")


/home/juanlu/.local/lib/python3.4/site-packages/matplotlib/font_manager.py:1279: UserWarning: findfont: Font family ['Humor Sans', 'Comic Sans MS'] not found. Falling back to Bitstream Vera Sans
  (prop.get_family(), self.defaultFamily[fontext]))

¡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 0x7f9ff9466b70>

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 0x7f9ff93a94e0>

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


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

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 0x7f9ff9292048>

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 0x7f9ff969be48>

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 0x7f9ff90be080>
¿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 [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]:
<matplotlib.text.Text at 0x7f9ff2f7e710>

El truco final: componentes interactivos

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))


Referencias


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]:
/* This template is inspired in the one used by Lorena Barba in the numerical-mooc repository: https://github.com/numerical-mooc/numerical-mooc We thank her work and hope you also enjoy the look of the notobooks with this style */ El estilo se ha aplicado =)

In [ ]: