
Matplotlib#
¿Qué es Matplotlib?#
Puede que Matplotlib sea la primera librería de Python, por años de vida y comunidad de usuarios, desarrollada para la representación gráfica de datos. Su flexibilidad y la posibilidad de editar cada elemento de una gráfica hace que el uso de Matplotlib requiera de un poco de aprendizaje. Es cierto que, como veremos en otros notebooks, despues de Matplotlib han aparecido varias librerías de representación gráfica de datos en 2D y/o 3D que pueden dar resultados muy vistosos con muy pocas lineas de código. Pero entender como se usa Matplotlib es un buen primer para iniciarse en el trabajo de edición de gráficas científicas.

¿Cómo se instala?#
Para ver instrucciones generales de instalación puedes visitar la página oficial. Allí puedes encontrar indicaciones específicas para tu sistema operativo y/o tu gestor de entornos y paquetes. Aquí supondremos que estás trabajando en tu entorno de conda, así que el comando que debes teclear en la terminal es:
conda install -c conda-forge matplotlib
¿Cómo se usa?#
Antes de empezar#
Si estás ejecutando este Jupyter Notebook en tu computadora haciendo uso de tu Jupyter Lab, te recomendamos que lo reinicialices completamente borrando la salida de las celdas. Para ello acude al menú Kernel (top menú de la ventana) y elige la opción Restart Kernel and Clear All Outputs…. Puede que hayamos dejado en la copia maestra de GitHub el resultado de la ejecución de las celdas, pero es sólo para que cualquiera pueda leer el notebook allí sin necesidad de ejecutarlo. Pero tu no lo vas a leer. Vas a jugar con él. Así que resetéalo.
Recuerda también que para ejecutar una celda debes presionar la combinación de teclas “mayús”+”entrar” cuanto esta está seleccionada.
Importando Matplotlib#
Matplotlib contiene en su estructura de módulos, un objeto que jerárquicamente domina sobre los demás, Pyplot. No suele ser muy habitual, vimos por ejemplo que Numpy podía invocarse directamente para su uso, pero esta librería para ser operada requiere que carguemos Pyplot como conjunto de métodos y clases como interface entre el usuario y iPython o Jupyter. El convenio sugerido por los desarrolladores es llamar a Pyplot como plt
, y por comodidad así haremos.
Verás también que cargar Numpy de inicio se va a convertir en un hábito. Es una librería a la que recurriremos casi en todos nuestros notebooks o scripts de análisis:
# Comprenderás el por qué de esta celda al final de notebook
#%matplotlib inline
#%matplotlib notebook
#%matplotlib widget
import numpy as np
import matplotlib.pyplot as plt
Ya podemos comenzar a ver cómo podemos en el mismo Jupyter Notebook representar gráficamente el resultado de nuestros análisis. Esto le da al notebook una nueva dimensión. Ya podemos reportar sobre el mismo formato la evolución de nuetros proyectos con comentarios, código y el gráficas.
Anteriormente al uso de iPython o Jupyter, y ahora como modo de uso alternativo, la secuencia de ordenes de Matplotlib se implementaba en el script de análisis exportando el resultado gráfico a cualquier fichero eps, png, etc. Veremos estos también al final del notebook.
Primera representación gráfica sencilla#
Supongamos que queremos representar la linea definida por unos puntos con coordenadas X e Y:
puntos_x = [1,2,3,4]
puntos_y = [0,4,0,2]
plt.plot(puntos_x, puntos_y)
plt.show()

Vamos ahora a ponerle un título y etiquetas a los ejes X e Y
plt.plot(puntos_x, puntos_y)
plt.title("Figura 1")
plt.xlabel("Eje X (unidades x)")
plt.ylabel("Eje Y (unidades y)")
plt.show()

Puedes cambiar el color de la linea de manera sencilla. Existe definida por defecto una gran paleta de color. Pero puedes comenzar con algo sencillo: rojo (“r”), verde (“g”), azul (“b”), cian (“c”), magenta (“m”), amarillo (“y”) y negro (“k”). O puedes hacer uso de los código de color RGB o HEX.
plt.plot(puntos_x, puntos_y, color = 'r') # Puedes probar con color="#800080" o color=[1.0,0.5,0.0]
plt.title("Figura 1")
plt.xlabel("Eje X (unidades x)")
plt.ylabel("Eje Y (unidades y)")
plt.show()

Ahora cambiemos los símbolos de representación o el tipo de linea y su tamaño o anchura:
plt.plot(puntos_x, puntos_y,'o', color = "blue", markersize= 10.0)
plt.title("Figura 1")
plt.xlabel("Eje X (unidades x)")
plt.ylabel("Eje Y (unidades y)")
plt.show()

plt.plot(puntos_x, puntos_y,'+-', color = "indigo", markersize= 20.0)
plt.title("Figura 1")
plt.xlabel("Eje X (unidades x)")
plt.ylabel("Eje Y (unidades y)")
plt.show()

Todas estas opciones y posibilidades no tienes por qué recordarlas. Como verás al final del notebook, Matplotlib tiene una buena documentación a la que acudirás en repetidas ocasiones para consultar la sintaxis de sus funciones o para descubrir nuevas posibilidades. Pero esto suele suceder siempre a medida que vamos trabajando y conforme a las necesidades que van surgiendo. Vamos a poner un ejemplo en el siguiente párrafo.
Supongamos que queremos aumentar el tamaño de letra del título y además el eje X en escala logarítmica. Podemos proceder de dos maneras:
Acudir a la documentación y bucear allí -muy instructivo pero lento-.
Buscar en internet -rápido y eficaz aunque en ocasiones no tan instructivo-
Vamos a tomar ahora la segunda opción para descubrir cómo cambiar el tamaño de letra: búsqueda de «matplotlib font size title» y búsqueda de «matplotlib log scale». Puede que hayas visto que se puede resolver así:
plt.plot(puntos_x, puntos_y, '--', color = "k", linewidth = 3.0, label='Datos A')
plt.title("Figura 1",fontsize=20)
plt.xlabel("Eje X (unidades x)")
plt.ylabel("Eje Y (unidades y)")
plt.xscale("log")
plt.legend()
plt.show()

Otros tipos de representaciones gráficas de datos#
El método plt.plot
sirve como hemos visto para representar una gráfica en términos generales. Puedes encontrar otros ejemplos de plt.plot
aquí. Pero para propósitos más específicos podemos encontrar otras funciones más útiles. Veamos algunos ejemplos:
Figura de dispersión#
Para representar una distribución de puntos inconexos, Matplotlib cuenta con la función plt.scatter
-ejemplos aquí-. Generemos una distribución bidimensional gaussiana aleatoria de 5000 puntos centrados en (4.0,10.0) con desviación estandard igual a 1 en X y 3 en Y con ayuda del método de Numpy np.random.normal
].
dispersión_aleatoria = np.random.normal((4.0,10.0),(1.0,3.0),(5000,2)) # ver
plt.scatter(dispersión_aleatoria[:,0],dispersión_aleatoria[:,1],s=0.5)
plt.show()

Histograma#
Matplotlib cuenta con funciones para representar histogramas. Histogramas en 1D con la función plt.hist
(ver ejemplos aquí) o histogramas a modo de mapa de calor en 2D con la función plt.hist2d
(ver ejemplos aquí). Vamos a usar la distrubución aleatoria de la subsección anterior para ilustrar el uso de ambos métodos.
n, bins, patches = plt.hist(dispersión_aleatoria[:,0],bins=50)
plt.title('Histograma en X')
plt.show()
n, bins, patches = plt.hist(dispersión_aleatoria[:,1],bins=50)
plt.title('Histograma en Y')
plt.show()


Además, puedes matplotlib te permite crear gráficos que te brinden información adicional para la toma de decisiones. Por ejemplo, un gráfico de distribución de probabilidad.
from scipy import stats ## 'conda install scipy' en la terminal si scipy no está instalado
h=list(dispersión_aleatoria[:,0]) ## Disperción en X
h.sort()
hmean = np.mean(h)
hstd = np.std(h)
pdf = stats.norm.pdf(h, hmean, hstd)
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
Cell In[13], line 1
----> 1 from scipy import stats ## 'conda install scipy' en la terminal si scipy no está instalado
2 h=list(dispersión_aleatoria[:,0]) ## Disperción en X
3 h.sort()
ModuleNotFoundError: No module named 'scipy'
plt.plot(h,pdf,'k--',linewidth=2.0)
plt.hist(h,density=True,histtype='step',color='gray',linewidth=1.5)
plt.title ('Distribución de probabilidad',fontsize=14,fontweight='bold')
plt.xlabel ('Dispersión aleatoria en X',fontsize=12,fontweight='bold')
plt.ylabel ('Probabilidad',fontsize=12,fontweight='bold')
plt.tick_params ('both',width=2,labelsize=12)
## Añade una linea en el promedio de los datos
plt.axvline(x=hmean,color='r',linewidth=1,linestyle='--',)
plt.tight_layout()
plt.show()

h = plt.hist2d(dispersión_aleatoria[:,0],dispersión_aleatoria[:,1],bins=[50,50])
plt.title("Histograma 2D")
plt.colorbar()
plt.show()

Vamos a aprovechar aquí para ver cómo podemos hacer una figura múltiple (multiplot con la función plt.subplot
, ejemplos aquí):
plt.subplot(2, 1, 1)
n, bins, patches = plt.hist(dispersión_aleatoria[:,0],bins=50)
plt.title('Histograma en X')
plt.xticks([])
plt.subplot(2, 1, 2)
h = plt.hist2d(dispersión_aleatoria[:,0],dispersión_aleatoria[:,1],bins=[50,50])
plt.title("Histograma 2D")
plt.show()

Otras representaciones 2D#
Hemos visto cómo con una secuencia de datos Matplotlib puede representar el histograma 2D fácilmente. Veamos ahora qué hacer si ya tenemos la información en forma de magnitudes escalares asignadas a una grid o matrix (con la función plt.matshow
, ejemplos aquí):
valores_3X3=[[10,0,20],[0,5,30],[5,10,0]] # Probemos con una matrix 3x3
valores_3X3
[[10, 0, 20], [0, 5, 30], [5, 10, 0]]
plt.matshow(valores_3X3)
plt.colorbar()
plt.show()

La generalización de este tipo de gráfico viene dada por el método plt.imshow
, que nos puede servir para hacer mapas de calor en 2D interpolados (ejemplos aquí). Probemos para esto a hacer nuestro propio mapa de densidad 2D del histograma que calculamos acontinuación para la secuencia aleatoria usada al comienzo de este notebook.
frecuencia, x_edges, y_edges = np.histogram2d(dispersión_aleatoria[:,0],dispersión_aleatoria[:,1],bins=[20,20])
plt.imshow(frecuencia.T, origin='lower', extent=[x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]])
plt.colorbar()
plt.show()

Vamos a hora a cambiar la escala de color. Elegiremos un degradado de blanco a negro como podemos ver en esta lista de opciones.
plt.imshow(frecuencia.T, origin='lower', extent=[x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]],
cmap="Greys")
plt.colorbar()
plt.show()

Interpolemos ahora para que el mapa de densidad quede más suave.
plt.imshow(frecuencia.T, origin='lower', extent=[x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]],
interpolation='gaussian', cmap="Greys")
plt.colorbar()
plt.show()

Y añadamos ahora isolineas:
extent = [x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]]
plt.imshow(frecuencia.T, origin='lower', extent=extent,
interpolation='gaussian', cmap="Greys")
plt.colorbar()
plt.contour(frecuencia.T, [10,40,70], colors='r', origin='lower', extent=extent,linewidths=0.8)
plt.show()

Para que se vea mejor, y aunque no respetemos la igualdad de escalas entre X e Y, hagamos el plot con una relación 1:4 entre los ejes:
extent = [x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]]
plt.imshow(frecuencia.T, origin='lower', extent=extent,
interpolation='gaussian', cmap="Greys", aspect=1/4)
plt.colorbar()
plt.contour(frecuencia.T, [10,40,70], colors='r', origin='lower', extent=extent, linewidths=0.8)
plt.show()

Por último probemos a poner etiquetas sobre las isolineas.
extent = [x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]]
plt.imshow(frecuencia.T, origin='lower', extent=extent,
interpolation='gaussian', cmap="Greys", aspect=1/4)
plt.colorbar()
contour_lines = plt.contour(frecuencia.T, [10,40,70], colors='red', origin='lower',
extent=extent, linewidths=0.8)
plt.clabel(contour_lines, contour_lines.levels, fmt="%2.1f", inline=True, fontsize=10)
plt.show()

Ya por último vamos a añadir las etiquetas a los ejes y a la figura:
extent = [x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]]
plt.imshow(frecuencia.T, origin='lower', extent=extent,
interpolation='gaussian', cmap="Greys", aspect=1/4)
plt.colorbar()
contour_lines = plt.contour(frecuencia.T, [10,40,70], colors='red', origin='lower',
extent=extent, linewidths=0.8)
plt.clabel(contour_lines, contour_lines.levels, fmt="%2.1f", inline=True, fontsize=10)
plt.title('Mapa de densidad', fontsize=18)
plt.xlabel('Coordenada X', fontsize=14)
plt.ylabel('Coordenada Y', fontsize=14)
plt.show()

Exportando la figura a un fichero#
Matplotlib permite guardar la figura en una multitud de formatos gracias a la función plt.savefig
(ejemplos aquí). Aquí, para ilustrar su uso vamos a guardar la imagen en eps.
extent = [x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]]
plt.imshow(frecuencia.T, origin='lower', extent=extent,
interpolation='gaussian', cmap="Greys", aspect=1/4)
plt.colorbar()
contour_lines = plt.contour(frecuencia.T, [10,40,70], colors='red', origin='lower',
extent=extent, linewidths=0.8)
plt.clabel(contour_lines, contour_lines.levels, fmt="%2.1f", inline=True, fontsize=10)
plt.title('Mapa de densidad', fontsize=18)
plt.xlabel('Coordenada X', fontsize=14)
plt.ylabel('Coordenada Y', fontsize=14)
plt.savefig('figura.png', dpi=300)

Si estás ejecutando este notebook en JupyterLab, verás que en el navegador de ficheros ha aparecido en la carpeta en la que te encuentras el fichero “figura.png”. Vamos a visualizarlo desde esta misma celda gracias a la sintaxis de Markdown <img src="figura.png">
. Ejecuta de nuevo esta celda de texto con la combinación de teclas “mayús”+”entrar”.

Antes de seguir con la próxima sección, borremos el fichero que acabamos de crear.
import os
os.remove('figura.png')
Otra perspectiva más completa del trabajo con figuras en Matplotlib#
Invocar directamente al comando de matplotlib.pyplot
es una manera muy rápida de visualizar nuestros datos. Pero si lo que queremos es construir a conciencia una buena figura que luego va a ser exportada por ejemplo para una publicación, es mejor trabajar de manera más fina y crear, antes que nada, el objeto figure
para trabajar sobre el. Esto ensancha aún más la versatilidad del uso que haces de Matplotlib y nos da más control.
Aunque pueda parecer más complicado, y requiera un poquito más de paciencia las primeras veces, es mejor preparar así una figura. Veamos un ejemplo no tan sencillo y comprenderás el cambio de enfoque. Vamos a crear una figura triple añadiendo al mapa de densidad anterior los histogramas unidimensionales.
mi_figura = plt.figure(figsize=(6,6))
grid = mi_figura.add_gridspec(3, 3)
ax1 = mi_figura.add_subplot(grid[0,0:2])
ax2 = mi_figura.add_subplot(grid[1:3,2])
ax3 = mi_figura.add_subplot(grid[1:3,0:2])
for ax in [ax1, ax2, ax3]:
ax.tick_params(direction='in')
n, bins, patches = ax1.hist(dispersión_aleatoria[:,0],bins=50, color='grey')
ax1.set_xticks([])
ax1.set_ylim([0,320])
ax1.set_yticks([0,150,300])
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.yaxis.set_ticks_position('left')
ax1.xaxis.set_ticks_position('bottom')
n, bins, patches = ax2.hist(dispersión_aleatoria[:,1],bins=50,orientation='horizontal', color='grey')
ax2.set_yticks([])
ax2.set_xlim([0,320])
ax2.set_xticks([0,150,300])
ax2.spines['right'].set_visible(False)
ax2.spines['top'].set_visible(False)
ax2.yaxis.set_ticks_position('left')
ax2.xaxis.set_ticks_position('bottom')
ax3.imshow(frecuencia.T, origin='lower', # extent=extent,
interpolation='gaussian', cmap="Greys")
contour_lines = ax3.contour(frecuencia.T, [10,40,70], colors='red', origin='lower', linewidths=0.8)
ax3.clabel(contour_lines, contour_lines.levels, fmt="%2.1f", inline=True, fontsize=10)
ax3.set_xlabel("Coordenada X")
ax3.set_ylabel("Coordenada Y")
plt.show()

Podríamos profundizar mucho más en la modificación de todos los parámetros de la gráfica. De hecho necesitaría varios ajustes, pero el propósito de este notebook es introductorio. Encontraremos más ejemplos y más opciones en sucesivas unidades de manera aplicada.
Comandos mágicos para Matplotlib en Jupyter o iPython#
El modo en que la visualización de Matplotlib es integrado en Jupyter o iPython puede ser modificado según se invoquen o no unos comandos mágicos al comienzo del notebook. Un comando mágico de Jupyter es una orden dada en una celda de código que modifica el comportamiento del notebook. Estas órdenes van precedidas del caracter “%”. Puedes listar con %lsmagic
los distintos comandos mágicos que puedes usar:
%lsmagic
Available line magics:
%alias %alias_magic %autoawait %autocall %automagic %autosave %bookmark %cat %cd %clear %colors %conda %config %connect_info %cp %debug %dhist %dirs %doctest_mode %ed %edit %env %gui %hist %history %killbgscripts %ldir %less %lf %lk %ll %load %load_ext %loadpy %logoff %logon %logstart %logstate %logstop %ls %lsmagic %lx %macro %magic %man %matplotlib %mkdir %more %mv %notebook %page %pastebin %pdb %pdef %pdoc %pfile %pinfo %pinfo2 %pip %popd %pprint %precision %prun %psearch %psource %pushd %pwd %pycat %pylab %qtconsole %quickref %recall %rehashx %reload_ext %rep %rerun %reset %reset_selective %rm %rmdir %run %save %sc %set_env %store %sx %system %tb %time %timeit %unalias %unload_ext %who %who_ls %whos %xdel %xmode
Available cell magics:
%%! %%HTML %%SVG %%bash %%capture %%debug %%file %%html %%javascript %%js %%latex %%markdown %%perl %%prun %%pypy %%python %%python2 %%python3 %%ruby %%script %%sh %%svg %%sx %%system %%time %%timeit %%writefile
Automagic is ON, % prefix IS NOT needed for line magics.
Ahora comprenderás la presencia de la primera celda de este notebook con tres lineas de código comentadas. %matplotlib notebook
ofrece un representación en el notebook con funciones interactivas, como hacer zoom, mientras que %matplotlib inline
devuelve la visualización en linea pasiva por defecto. Así sucedía antes de Jupyter Lab con Jupyter Notebook. Te propongo que lo compruebes, desde Jupyter Notebook, reseteando el kernel de este notebook y ejecutando primeramente esa celda con una de las líneas «descomentada» antes de generar cualquier representación. Ahora seguramente pensaras… desde Jupyter Notebook? Bueno, puedes estar en una de las siguientes situaciones.
Estás ejecutando esto desde Jupyter Notebook#
¿Estás lanzando el servidor de Jupyter con la versión “Notebook” (anterior a Lab)? ¿Hiciste el siguiente comando en tu terminal para navegar por estos notebooks?
jupyter notebook
En ese caso puedes alternar %matplotlib notebook
y %matplotlib inline
para ver el cambio de comportamiento del notebook. Resetea el kernel y el output del notebook en cada caso para que el comando mágico tenga efecto.
Estás ejecutando esto desde Jupyter Lab y quieres probar Jupyter Notebook#
¿Estás lanzando el servidor de Jupyter con la versión “Lab”? ¿Hiciste el siguiente comando en tu terminal para navegar por estos notebooks?
jupyter lab
Puedes cambiar entre Jupyter Lab y Jupyter Notebook sin más que hacer una pequeña modificación a la url que ves en el navegador. Ahora mismo debe ser algo parecido a localhost:8889/lab
. Cámbiala a localhost:8889/notebooks/Python/Matplotlib.ipynb
y presiona la tecla entra
para que vuelva a cargar la página web. Ahora estás viendo el notebook con la versión Jupyter Notebook. Deja que te de otro tip antes de que alternes entre los comandos mágicos %matplotlib notebook
y %matplotlib inline
para ver el cambio de comportamiento del notebook (siempre reseteando el kernel y el output para que los comandos mágicos tengan efecto). La dirección en el navegador localhost:8889/tree
en lugar de localhost:8889/lab
te lleva al explorador de ficheros de Jupyter Notebook. Si lo pruebas, recuerda que este notebook está en la carpeta Python
.
Estás ejecutando esto desde Jupyter Lab y no quieres ir a Jupyter Notebook#
¿Estás lanzando el servidor de Jupyter con la versión “Lab”? ¿Hiciste el siguiente comando en tu terminal para navegar por estos notebooks?
jupyter lab
Sucede que en el momento de elaboración de este notebook, Jupyter Lab tiene menos de un año de desarrollo. El comando mágico %matplotlib notebook
no funciona, pero se está desarrollando un widget para la visualización interactiva de Matplotlib. Puedes probarlo de la siguiente manera. Sal de Jupyter Lab y ejecuta estas cuatro lineas en tu terminal (si estás trabajando en un entorno de conda, asegurate que se ejecutan en el mismo entorno):
conda install -c conda-forge ipympl
conda install nodejs
jupyter labextension install @jupyter-widgets/jupyterlab-manager
jupyter labextension install jupyter-matplotlib
Ahora puedes lanzar otra vez Jupyter Lab y regresar a este notebook. Prueba el comando mágico %matplotlib widget
con el kernel del notebook reinicializado y la salida borrada. El comando mágico %matplotlib inline
, o no poner nada, da una visualización no interactiva.
Dudas, problemas técnicos y soluciones. #
Para centralizar esas dudas técnicas sobre el tema de este notebook o proponer soluciones o sugerencias más técnicas que queremos encontrar en el futuro comentadas y visibles para todos, haz uso del siguiente canal:
Más recursos útiles #
El propósito de este notebook es ser un documento únicamente introductorio. Puedes encontrar -o contribuir añadiendo- más información útil en el siguiente listado:
Documentación #
https://matplotlib.org/
https://matplotlib.org/contents.html
Tutoriales, Webinars y cursos gratuitos #
https://realpython.com/python-matplotlib-guide/
https://jakevdp.github.io/PythonDataScienceHandbook/04.00-introduction-to-matplotlib.html
https://github.com/damianavila/Python-Cientifico-HCC/blob/master/4_Matplotlib.ipynb
https://scipy-cookbook.readthedocs.io/items/idx_matplotlib.html
Un poquito más de XKCD #
https://deparkes.co.uk/2018/04/16/matplotlib-xkcd-style-plots/
https://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/
https://matplotlib.org/xkcd/examples/showcase/xkcd.html
https://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/
https://www.chrisstucchio.com/blog/2014/why_xkcd_style_graphs_are_important.html
https://www.gnuband.org/2017/12/29/gallery-of-xkcd-and-other-python-matplotlib-styles/