Autor: Luis Fernando Apáez Álvarez
-Curso PyM-
Clase 5: Librería Seaborn-Conceptos intermedios (Parte II)
Fecha: 12 de diciembre del 2022
Continuaremos indagando en algunos conceptos sobre la librería Seaborn. Recordemos que en clases anteriores hemos indagado en gráficos para representar variables categóricas, luego, iniciando con esta clase indagaremos en algunos otros de ellos.
Este tipo de gráfico es útil para representar variables categóricas junto con la distribución subyacente de una variable cuantitativa, esto es, mediante este tipo de gráfico podemos representar un diagrama de dispersión por clase o categoría.
Por ejemplo, podemos ver el diagrama de dispersión de la variable sepal_length
por cada especie
import seaborn as sns
# Cargamos el conjunto de datos
df = sns.load_dataset('iris')
# graficamos el diagrama de tira.
# x: variable categorica
# y: variable continua
ax = sns.stripplot(data=df, x='species',
y='sepal_length')
Este tipo de gráfico complementa otro tipo de gráficos como boxplots o diagramas de violín (los cuales veremos más adelante).
# Podemos agregar un poco de ruido a los puntos
# con el parametro jitter
ax = sns.stripplot(data=df, x='species',
y='sepal_length',
jitter=True)
Podemos combinar un boxplot con el gráfico de tira:
# Graficamos el diagrama de tira
ax1 = sns.stripplot(data=df, x='species',
y='sepal_length',
# tamanio de los puntos
size=7,
# contorno de los puntos
linewidth=1,
# transparencia
alpha=0.7,
jitter=True)
# graficamos el boxplot
ax2 = sns.boxplot(data=df, x='species',
y='sepal_length')
Es un tipo de gráfico muy similar al gráfico de tira, pero el gráfico de enjambre no superpone un punto sobre el otro, esto es, cada punto del diagrama estará separado. Así, podemos escribir
# Diagrama de enjambre
ax = sns.swarmplot(data=df, x='species',
y='sepal_length')
Una desventaja es que este tipo de gráfico no se adapta bien para gran cantidad de datos.
Ahora bien, las funciones utilizadas anteriormente para los gráficos son de bajo nivel. Podríamos utilizar catplot()
como hemos visto en clases anteriores para obtener los mismos gráficos.
Este tipo de gráfico es una combinación entre la curva de estimación de densidad del kernel y un boxplot.
# grafico de violin
ax = sns.violinplot(data=df, x='species',
y='sepal_length')
con el que podemos ver la distribución de los datos por categoría y su densidad de probabilidad:
De modo que la línea vertical negra y la caja negra, de la imagen anterior, representan la misma información que en un boxplot, luego, la curva de color amarillo es la curva de la densidad de la distribución de los datos.
Este tipo de gráfico es una mezcla de un boxplot con y un gráfico de violín
ax = sns.boxenplot(data=df, x='species',
y='sepal_length')
en el cual podemos ver la misma información que en un boxplot y adicionalmente vemos el comportamiento de la densidad de la distribución de los datos, lo cual se representa con las cajas.
Recordemos que podemos realizar un gráfico de dispersión de dos variables cuantitativas junto con su recta de regresión:
ax = sns.regplot(data=df, x='petal_width',
y='petal_length',
# establecemos el tipo de punto
marker='*')
Notemos que en el gráfico anterior se dibuja una recta referente a la regresión lineal, lo cual nos indica que la relación entre las variables es lineal, pero también puede ser el caso en que la relación entre las variables sea no lineal. Así, si colocamos en la función anterior order=2
estaremos especificando que la relación no es lineal, más bien sería una relación cuadrática, por lo cual no se dibujará un recta, sino una curva cuadrática. En efecto
ax = sns.regplot(data=df, x='petal_width',
y='petal_length',
order=2)
ax = sns.regplot(data=df, x='petal_width',
y='petal_length',
# Comportamiento cubico
order=3)
Este gráfico nos ayudará a ver qué tan buena es la relación lineal entre las variables en cuestión. De este gráfico buscamos que los puntos estén acomodados de manera aleatoria a lo largo de la recta horizontal que pasa por cero, o en términos más simples, buscamos que alrededor de la recta horizontal que pasa por cero podamos ver una nube de puntos y que no se detecten patrones
ax = sns.residplot(data=df, x='petal_width',
y='petal_length')
Asimismo, de manera similar podemos agregar el parámetro de order=
a la función anterior para tratar con relaciones no lineales.
Veamos un ejemplo de regresión no lineal. Para ello consideremos los siguientes datos
# Importaciones necesarias
import pandas as pd
import random
# creamos los puntos para que sigan
# cierto comportamiento cuadratico
x = [i for i in range(-10,11) for j in range(40)]
y = [(i + random.random()) ** 2 for i in range(-10,11) for j in range(40)]
# Creamos un dataframe
df_2 = pd.DataFrame({'x': x, 'y': y})
df_2
x | y | |
---|---|---|
0 | -10 | 93.456814 |
1 | -10 | 82.899819 |
2 | -10 | 85.589977 |
3 | -10 | 84.282628 |
4 | -10 | 98.856297 |
... | ... | ... |
835 | 10 | 101.432297 |
836 | 10 | 113.053408 |
837 | 10 | 103.559355 |
838 | 10 | 113.799052 |
839 | 10 | 108.740252 |
840 rows × 2 columns
# Graficamos el diagrama de dispersion
ax = sns.relplot(data=df_2, x='x', y='y', kind='scatter')
Luego, veamos el gráfico de los residuales
ax = sns.residplot(data=df_2, x='x',
y='y')
el cual nos muestra claramente un patrón cuadrático, lo cual nos indicaría que la relación entre las variables sigue un comportamiento cuadrático.
Realizamos el gráfico de dispersión con la recta de regresión lineal
ax = sns.regplot(data=df_2, x='x',
y='y')
# Configuramos un orden de 2
ax = sns.regplot(data=df_2, x='x',
y='y',
order=2)
Vemos que el ajuste es notablemente mejor. Asimismo para el gráfico de los residuales
ax = sns.residplot(data=df_2, x='x',
y='y',
order=2)
donde ahora vemos de cierto modo una nube de puntos alrededor de la recta horizontal, donde el comportamiento de dichos residuales viene de cómo definimos el conjunto de datos df
.
Este tipo de gráficos son útiles para observar de manera rápida tendencias, patrones o cambios utilizando para ello rectángulos de colores. Para poder utilizar un mapa de color requerimos que las variables sean únicamente numéricas, además, usualmente en el eje $x$ veremos una variable categórica. Los mapas de calor se llevan bien con datos que varían a lo largo del tiempo.
Por ejemplo, consideremos un conjunto de datos en el cual podamos ver las ventas de cierta empresa a lo largo de los meses del año y a lo largo de los últimos 5 años. Para crearemos el dataframe como sigue:
import numpy as np
# Simulamos los totales de las ventas
num_ventas = np.random.randint(1, 100, 60).reshape(12, 5)
num_ventas
array([[93, 30, 74, 64, 46], [82, 7, 99, 45, 16], [ 6, 82, 85, 51, 82], [93, 30, 50, 30, 84], [24, 88, 31, 25, 46], [ 3, 76, 5, 42, 52], [92, 56, 44, 8, 67], [88, 11, 30, 64, 40], [30, 24, 38, 17, 74], [74, 8, 28, 99, 9], [31, 91, 39, 91, 77], [82, 3, 99, 39, 45]])
donde vemos que la forma es de 12 por 5, puesto que las filas representan cada mes del año y las columnas representan los 5 años que consideraremos. Luego
# Creamos una lista con los meses del agnio
meses = ["ene", "feb", "mar", "abr", "may",
"jun", "jul", "ago", "sep", "oct",
"nov", "dic"]
# Creamos el dataframe:
# las columnas seran del 2017
# al 2022
df_v = pd.DataFrame(num_ventas, columns = np.arange(2017, 2022),
# el indice del dataframe seran los meses
index = meses)
df_v
2017 | 2018 | 2019 | 2020 | 2021 | |
---|---|---|---|---|---|
ene | 93 | 30 | 74 | 64 | 46 |
feb | 82 | 7 | 99 | 45 | 16 |
mar | 6 | 82 | 85 | 51 | 82 |
abr | 93 | 30 | 50 | 30 | 84 |
may | 24 | 88 | 31 | 25 | 46 |
jun | 3 | 76 | 5 | 42 | 52 |
jul | 92 | 56 | 44 | 8 | 67 |
ago | 88 | 11 | 30 | 64 | 40 |
sep | 30 | 24 | 38 | 17 | 74 |
oct | 74 | 8 | 28 | 99 | 9 |
nov | 31 | 91 | 39 | 91 | 77 |
dic | 82 | 3 | 99 | 39 | 45 |
Con base en el dataframe anterior podemos graficar un gráfico de calor como sigue
ax = sns.heatmap(df_v)
lo cual nos dice, por ejemplo, que en el 2017 tuvimos 7 meses de buenas ventas y dos meses (marzo y junio) muy malos. No podemos realizar un análisis propiamente del gráfico anterior debido a que los datos del dataframe que construimos fueron aleatorios, no obstante, lo que es claro es que mientras el color sea más cálido mayores ventas tuvimos.
Podemos personalizar los mapas de calor:
# Podemos hacer que en cada rectangulo se vea el valor
# que le corresponde con annot=True
ax = sns.heatmap(df_v, annot=True)
# Podemos cambiar la paleta de color con cmap=
ax = sns.heatmap(df_v, cmap='Blues',
annot=True)
Si colocamos el parámetro fmt='d'
aseguramos que los valores que se muestran en los rectángulos de color sean números enteros. Por otro lado, podemos quitar la barra de calor de la derecha con cbar=False
:
ax = sns.heatmap(df_v, fmt='d',
cbar=False)
# Podemos aumentar el tamagnio de las lineas que
# separan a los rectangulos
ax = sns.heatmap(df_v, linewidths=.5)
Asimismo, podemos centrar los colores del mapa de calor para algún valor en específico. Por ejemplo, consideremos
fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, figsize=(12,6))
g1 = sns.heatmap(df_v, cmap='Blues', ax=ax0)
g2 = sns.heatmap(df_v, cmap='Blues',
center=df_v.loc['oct', 2020],
ax=ax1)
# Titulos
g1.set(title='Normal')
g2.set(title='Cambio de centro')
plt.show()
donde cambiamos el centro el cual nos define la manera de colorear el mapa. Notemos que la información no se ha modificado, lo único que modificamos fue la manera que en se coloreo el mapa de calor. Dado que colocamos como centro octubre del 2020, el cual tiene en el gráfico de la izquierda un color fuerte, entonces la manera de colorear del gráfico de la derecha ha pasado a colores más claros. Así, si el centro es un color claro, entonces la manera de colorear el mapa de calor será con colores más oscuros.
ax = sns.heatmap(df_v, cmap='Blues',
center=df_v.loc['dic', 2018])
Finalmente, recordemos que podemos obtener un dataframe con las correlaciones, obtenidas a partir del coeficiente de correlación de pearson, de las variables de un dataframes. Así, consideremos dicho dataframe para el conjunto de datos iris
df.corr(method='pearson')
sepal_length | sepal_width | petal_length | petal_width | |
---|---|---|---|---|
sepal_length | 1.000000 | -0.117570 | 0.871754 | 0.817941 |
sepal_width | -0.117570 | 1.000000 | -0.428440 | -0.366126 |
petal_length | 0.871754 | -0.428440 | 1.000000 | 0.962865 |
petal_width | 0.817941 | -0.366126 | 0.962865 | 1.000000 |
de donde podremos graficar un gráfico de calor del mismo
ax = sns.heatmap(df.corr(method='pearson'),
linewidth=0.2)
en el cual podemos decir que que los colores más claros nos indican la presencia de una alta relación lineal entre las variables involucradas, asimismo, los colores oscuros y morados (los cuales están asociados a valores entre -0.4 y 0.4) nos dicen que no hay una relación lineal entre las variables involucradas.