Curso de introducción a la programación con Python¶

    Autor: Luis Fernando Apáez Álvarez
    -Curso PyM-
    Clase 5: Librería Seaborn-Conceptos intermedios (Parte II)
    Fecha: 12 de diciembre del 2022


Contenido¶

  • Gráficos para variables categóricas
    • Gráfico de tira
    • Gráfico de enjambre
    • Gráfico de violín
    • Boxenplot
  • Gráficos de regresión
    • Gráfico de residuales
  • Matriz de gráficos
    • Mapas de calor

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.

Gráficos para variables categóricas ¶

Gráfico de tira o stripplot¶

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

In [22]:
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).

In [2]:
# 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:

In [3]:
# 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')

Gráfico de enjambre¶

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

In [4]:
# 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.

Gráfico de violín¶

Este tipo de gráfico es una combinación entre la curva de estimación de densidad del kernel y un boxplot.

In [5]:
# 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.

Boxenplot¶

Este tipo de gráfico es una mezcla de un boxplot con y un gráfico de violín

In [6]:
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.

Gráficos de regresión ¶

Recordemos que podemos realizar un gráfico de dispersión de dos variables cuantitativas junto con su recta de regresión:

In [7]:
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

In [8]:
ax = sns.regplot(data=df, x='petal_width',
                y='petal_length',
                order=2)
In [9]:
ax = sns.regplot(data=df, x='petal_width',
                y='petal_length',
                 # Comportamiento cubico
                order=3)

Gráficos de residuales¶

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

In [10]:
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

In [23]:
# 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
Out[23]:
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

In [12]:
# 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

In [13]:
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

In [14]:
ax = sns.regplot(data=df_2, x='x',
                y='y')
In [15]:
# 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

In [21]:
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.

Matriz de gráficos ¶

Mapa de calor¶

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:

In [34]:
import numpy as np

# Simulamos los totales de las ventas
num_ventas = np.random.randint(1, 100, 60).reshape(12, 5)
num_ventas
Out[34]:
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

In [35]:
# 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
Out[35]:
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

In [38]:
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:

In [39]:
# Podemos hacer que en cada rectangulo se vea el valor
# que le corresponde con annot=True
ax = sns.heatmap(df_v, annot=True)
In [42]:
# 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:

In [44]:
ax = sns.heatmap(df_v, fmt='d',
                 cbar=False)
In [45]:
# 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

In [58]:
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.

In [59]:
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

In [61]:
df.corr(method='pearson')
Out[61]:
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

In [63]:
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.