En el mundo de los datos se emplean diversos lenguajes de programación y uno de los más famosos es Python. Dentro de la multitud de tareas que puede realizar, se utiliza para leer, tratar o transformar datos, cuando necesitamos manipular aquellos de los que disponemos, incorpora librerías como Scikit-learn para ejecutar algoritmos de Machine Learning y también cuenta con otras librerías interesantes enfocadas a la visualización de estos datos.
Seguro que a todos nos ha pasado: queremos realizar un gráfico con unos detalles muy concretos que se adapten a nuestro problema y nos pasamos tiempo buscando por internet. En este post vamos a explicar 2 tips útiles: generar múltiples ejes verticales para diferentes curvas en un mismo gráfico y crear franjas y líneas verticales para resaltar valores del eje x.
Las dos librerías más famosas que hay, y tal vez las más usadas, son Matplotlib y Seaborn. Esta última está basada en Matplotlib y se utiliza para crear gráficos estadísticos más atractivos e informativos. Cada una de ellas permite realizar los gráficos más comunes como diagramas de línea, diagramas de barras, diagramas de áreas, histogramas, etc. La elección de cuál usar es personal, aquí utilizaremos ambas para poder reflejar un poco la diferencia de sintaxis que hay y que el lector pueda ver las diferentes opciones.
Lectura y exploración de los datos
Vamos a necesitar las dos librerías de generación de gráficos mencionadas, la librería pandas para leer y tratar los datos y las funciones HostAxes y ParasiteAxes para la creación de los diversos ejes verticales. A continuación, importamos todas las herramientas mencionadas:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from mpl_toolkits.axisartist.parasite_axes import HostAxes, ParasiteAxes
Hemos extraído un open dataset para nuestro propósito que podemos descargar aquí. Se trata de un dataset básico para ilustrar ejemplos como nuestro caso. Leemos los datos en una variable y vemos cómo son:
path = "Directorio donde están los datos"
df = pd.read_csv(path)
Tenemos 5 campos en nuestra tabla pero nos quedaremos con estos 4: Period, Revenue, Sales_quantity y Average_cost. Hay valores NaN que eliminaremos y además seleccionaremos únicamente el período que incluye 2017 para simplificar la visualización.
df.drop("The_average_annual_payroll_of_the_region", axis=1, inplace=True)
df.dropna(inplace=True)
df.Period = pd.to_datetime(df.Period, format="%d.%m.%Y")
df = df.loc[df.Period.dt.year == 2017, :]
Múltiples ejes verticales
Hay ocasiones en las que queremos representar en un mismo gráfico diversas curvas pero se nos aplana una o varias de ellas porque las magnitudes difieren. En estos casos, es muy útil incorporar un eje vertical para cada curva, de forma que podemos observar el comportamiento de cada una en su magnitud correspondiente.
Si nos fijamos en la tabla de datos, vemos que Revenue, Sales_quantity y Average_cost están en magnitudes diferentes y, a la hora de realizar los gráficos, la curva con la magnitud más pequeña puede aplanarse. Construiremos un eje vertical para cada columna de forma que evitamos el problema comentado anteriormente.
host = fig.add_axes([0.15, 0.1, 0.65, 0.8], axes_class=HostAxes)
par1 = ParasiteAxes(host, sharex=host)
par2 = ParasiteAxes(host, sharex=host)
host.parasites.append(par1)
host.parasites.append(par2)
host.axis["right"].set_visible(False)
par1.axis["right"].set_visible(True)
par1.axis["right"].major_ticklabels.set_visible(True)
par1.axis["right"].label.set_visible(True)
par2.axis["right2"] = par2.new_fixed_axis(loc="right", offset=(60, 0))
p1, = host.plot(df.Period, df.Revenue, linestyle="--", label="Revenue")
p2, = par1.plot(df.Period, df.Sales_quantity, linestyle=":",label="Sales quantity")
p3, = par2.plot(df.Period, df.Average_cost, label="Average cost")
host.set_xlabel("Date")
host.set_ylabel("Revenue")
par1.set_ylabel("Sales Quantity")
par2.set_ylabel("Average Cost")
host.legend()
host.axis["left"].label.set_color(p1.get_color())
par1.axis["right"].label.set_color(p2.get_color())
par2.axis["right2"].label.set_color(p3.get_color())
La idea es sencilla, necesitamos un eje para empezar que hará de molde (lo llamaremos host) y creamos diferentes ejes que comparten la escala del eje x pero cada uno tiene su propia escala del eje y. Estas son las funciones utilizadas para crear dichos ejes:
- plt.figure() crea una nueva figura, especificamos que tenga 20 pulgadas de anchura y 10 de altura con el parámetro fig_size. La guardamos en la variable fig.
- fig.add_axes() añade un eje a la figura, siendo [0.15, 0.1, 0.65, 0.8] las dimensiones del nuevo eje y el parámetro axes_class para definir de qué tipo es.
- ParasiteAxes() agrega un eje diferente respecto del eje principal con su propia escala del eje y pero compartiendo la escala del eje x.
El resto del código es muy intuitivo y los nombres de las funciones son autoexplicativos.
Franjas y líneas verticales
Otra situación interesante se produce cuando necesitamos resaltar un periodo entero o una fecha en concreto (en este ejemplo son fechas, pero depende de la magnitud del eje x). En el primer caso, tendremos que añadir una franja vertical en el gráfico y, en el segundo, nos basta con añadir una línea vertical que represente la fecha. Para ello, emplearemos la librería seaborn y, de esta forma, vemos otra manera de graficar.
Imaginemos que queremos resaltar todo el periodo de verano y, además, queremos señalar el día de Acción de Gracias y el día de San Valentín para ver el efecto de estas fechas en la variable Revenue, ya que intuimos que puede haber un comportamiento diferente del ingreso en estas fechas.
plt.figure(figsize=(20, 10))
sns.set_theme()
ax = sns.lineplot(x="Period", y="Revenue", data=df)
ax.axvline(pd.to_datetime("2017-02-14"), color="darkred", linestyle="--", label="Valentine's Day")
ax.axvline(pd.to_datetime("2017-11-23"), color="darkgoldenrod", linestyle="--", label="Thanksgiving Day")
ax.axvspan("2017-06-21", "2017-09-21",facecolor="darkgreen", edgecolor='black', hatch="x", alpha=.3, label="Summer")
ax.axvspan("2017-04-08", "2017-04-16",facecolor="darkmagenta", edgecolor='black', hatch="o", alpha=.3, label="Easter week")
ax.set_xlabel("Date")
ax.set_ylabel("Revenue")
ax.legend(loc="upper right")
- sns.set_theme() ajusta el aspecto visual de los gráficos que hagamos. Dejamos los valores por defecto.
- sns.lineplot() es la función de seaborn para graficar líneas.
- axvline() añade líneas verticales al gráfico dependiendo del valor del eje x que especifiquemos.
- axvspan() es similar que axvline() pero en vez de líneas verticales, especificamos un intervalo del eje x y graficamos una franja.
El código restante trata los ejes x e y y el título de la figura. En caso de que queramos añadir más de una franja, se pueden distinguir entre ellas por el relleno (se indica por el parámetro hatch, en este caso hemos puesto “x” y “o”) y no solamente por el color. También se puede modificar el estilo de la línea vertical para identificar diferentes valores del eje x con el parámetro linestyle.
Conclusión
Realizar gráficos y lidiar con todas las especificaciones puede ser realmente frustrante, sobre todo si queremos conseguir unas visualizaciones estéticas y que contengan todos los aspectos que queremos. Con estos 2 tips sencillos pero útiles esperamos que os pueda servir en vuestros proyectos para que consigáis unos gráficos top.