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

    Autor: Luis Fernando Apáez Álvarez
    -Curso PyM-
    Clase 4: Librería Seaborn-Conceptos intermedios
    Fecha: 10 de diciembre del 2022


Contenido¶

  • Gráficos de distribución
    • Histogramas
    • Gráficos de regresión lineal
    • Más sobre paletas de colores
  • Personalización con matplotlib

En clases anteriores indagamos en los conceptos más básicos sobre seaborn, asimismo, logramos escalar a temas un poco más complejos. Ahora, con esta clase indagaremos en temas más avanzados sobre la librería Seaborn.

Gráficos de distribución ¶

Histogramas¶

Un histograma es una representación gráfica de una variable en forma de barras y nos sirve para ver la distribución de dicha variable. De tal manera, los histogramas nos permiten dar un vistazo al comportamiento, variabilidad o dispersión de una variable cuantitativa.

Por ejemplo, graficaremos el histograma de la variable sepal_length del conjunto de datos iris

In [6]:
# Importaciones necesarias
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# Cargamos el conjunto de datos
df = sns.load_dataset('iris')
df
Out[6]:
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
... ... ... ... ... ...
145 6.7 3.0 5.2 2.3 virginica
146 6.3 2.5 5.0 1.9 virginica
147 6.5 3.0 5.2 2.0 virginica
148 6.2 3.4 5.4 2.3 virginica
149 5.9 3.0 5.1 1.8 virginica

150 rows × 5 columns

In [42]:
# Graficamos el histograma
g = sns.displot(data=df, x='sepal_length', kde=True)

donde kde hace alusión a la curva de estimación de densidad del kernel$\ ^{(1)}$, el cual sirve para visualizar la distribución de observaciones en un conjunto de datos, y tiene un uso similar a un histograma, por lo cual en ocasiones es conveniente visualizarlos simultáneamente. Si no queremos ver dicha curva configuramos kde=False, o simplemente no colocamos dicho parámetro dentro de la función displot().


$(1)$ KDE representa los datos utilizando una curva de densidad de probabilidad continua.


Por otro lado, podemos realizar el gráfico de la curva kde de manera individual

In [9]:
g = sns.kdeplot(data=df, x="sepal_length")

O de manera alternativa:

In [12]:
g = sns.displot(data=df, x='sepal_length', 
                kind='kde')
In [16]:
# Podemos rellenar el area debajo de la curva kde
g = sns.displot(data=df, x='sepal_length', 
                # rellenamos el area debajo de 
                # la curva 
                fill=True,
                kind='kde')

Gracias a lo anterior nos podemos dar cuenta que la función displot() es muy similar a las funciones relplot(), catplot(), de donde displot() se utiliza para gráficos de distribución y este tipo de función son consideradas de alto nivel (más versátiles) a diferencia de la función kdeplot() la cual es considerada de bajo nivel.

De tal manera, otro camino para crear un histograma es:

In [13]:
g = sns.displot(data=df, x='sepal_length', 
                kind='hist')
In [14]:
# Ahora el histograma con la curva kde
g = sns.displot(data=df, x='sepal_length',
                kde=True,
                kind='hist')

Curva ECDF¶

La curva ECDF (Empirical cumulative Distribution Function), o también denominada curva de la función de distribución acumulada, representa la proporción o el recuento de observaciones que caen por debajo de cada valor único en un conjunto de datos. En comparación con un histograma o un gráfico de densidad, tiene la ventaja de que cada observación se visualiza directamente.

Para crear una curva ECDF escribimos

In [36]:
# graficamos la curva ecdf
sns.displot(data=df, x='sepal_length',
                kind='ecdf')

# lineas auxiliares
plt.plot([6,6], [0, 0.549], color='orange')
plt.plot([6,0], [0.549, 0.549], color='orange')
plt.show()

donde, por ejemplo, para el valor 6.0 del eje x podemos decir que alrededor del 55% de los valores almecenado en la columna sepal_length son menores a 6.0. De tal modo, para el último valor del eje x podemos decir que el 100% de los valores en la misma columna son menores a dicho valor, esto es, todos los valores de la columna sepal_length son menores a 8. En efecto, notemos que el valor mayor en esa columna es:

In [34]:
df['sepal_length'].max()
Out[34]:
7.9

Gráficos de regresión lineal¶

Básicamente en un análisis de regresión buscamos hallar y describir relaciones, lineales, entre dos variables cuantitativas. Por ejemplo, un primer paso para detectar posibles relaciones lineales entre dos variables es realizando un diagrama de dispersión, por ejemplo

In [39]:
g = sns.relplot(data=df, x='petal_width',
           y='petal_length',
           kind='scatter')

podemos notar que dichas variables parecen estar relacionadas de manera lineal, donde al aumentar el valor de petal_width el valor de petal_length también aumenta. Una vez que el diagrama de dispersión nos ayuda a identificar la posible relación entre dos variables, podemos utilizar el coeficiente de correlación de pearson para comprobar si la relación es lineal o no. Dicho coeficiente va del -1 al 1, donde valores cercanos al 1 nos indica que la relación es lineal, valores cercanos a -1 nos indican que la relación es lineal inversa, es decir que si una variable aumenta la otra disminuye y viceversa. Si el coeficiente está cercano a 0 nos indicará que no hay indicios de relación lineal entre las variables.

Podemos obtener , como vimos en clases anteriores, el coeficiente de correlación de pearson para todas las variables de un dataframe:

In [40]:
df.corr(method='pearson')
Out[40]:
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 petal_length y petal_width están relacionadas de manera lineal, y así podríamos implementar un análisis de regresión para dichas variables. No indagaremos de manera profunda en el tema de regresión lineal, el cual es muy rico y muy utilizado en la estadística y el aprendizaje automático.

Continuando, podemos realizar un gráfico de regresión para las variables anteriores, para ello utilizamos la función regplot() como sigue

In [41]:
g = sns.regplot(data=df, x='petal_width',
           y='petal_length')

donde vemos el diagrama de dispersión de las variables y además vemos una líneas recta. Dicha línea describe la relación lineal entre las variables. Además podemos ver un intervalo de confianza (el área sombreada alrededor de la recta de regresión) correspondiente a la recta de regresión.

Tenemos que la función regplot() es de bajo nivel. Para realizar gráficos de regresión de una manera de alto nivel utilizaremos la función lmplot():

In [43]:
# Obtenemos el mismo grafico que el anterior
g = sns.lmplot(data=df, x='petal_width',
           y='petal_length')

Así, podemos implementar más cosas a la función lmplot(), como por ejemplo

In [45]:
g = sns.lmplot(data=df, x='petal_width',
           y='petal_length', 
           hue='species')

Lo cual nos da una recta de regresión por especie. O también podemos obtener tres gráficos, uno por especie, con su respectiva recta de regresión.

In [46]:
g = sns.lmplot(data=df, x='petal_width',
           y='petal_length', 
           col='species')

Para finalizar esta parte, realicemos el gráfico de regresión colocando mayor información con etiquetas y títulos, así como personalizando el gráfico:

In [59]:
# creamos una paleta de colores con 3 colores
paleta_custom = ['red', 'green', 'blue']

# especificamos dicha paleta en la funcion set_palette()
sns.set_palette(paleta_custom)

# colocamos un estilo de graficion
sns.set_style('whitegrid')

# grafico de regresion
g = sns.lmplot(data=df, x='petal_width',
           y='petal_length', 
           hue='species')

# Titulo a una altura un poco mayor de la predeterminada
# y aumentamos el tamanio
g.fig.suptitle('Gráfico de regresión', y=1.05, size=16)

# titulos para las etiquetas de los ejes
g.set(xlabel='Ancho del pétalo',
      ylabel='Largo del pétalo')
plt.show()

Más sobre paletas de colores¶

Podemos visualizar la paleta de color que definimos antes:

In [63]:
# a la funcion palplot() le pasamos la pelata de color
sns.palplot(sns.color_palette())

Hay algunas paletas de colores predeterminadas

In [64]:
# algunas paletas predeterminadas
paletas = ['deep', 'muted', 'pastel', 'bright', 'dark', 'colorblind']

# visualizamos las paletas en consola

for _ in paletas:
    # elegimos una paleta de la lista anterior
    sns.set_palette(_)
    # mostramos dicha paleta
    sns.palplot(sns.color_palette())
    plt.show()

Además, tendremos tres tipos básicos de paletas de colores:

  • Circulares: se utilizan para datos categóricos que no están ordenados.
In [67]:
# Por ejemplo
# configuramos que queremos 12 colores
sns.palplot(sns.color_palette('Paired', 12))
  • Secuenciales: se utilizan para datos que tienen un rango de valores ordenados, donde tenemos valores altos y bajos y donde los valores altos son considerados más interesantes que los bajos.
In [69]:
# Por ejemplo
sns.palplot(sns.color_palette('Reds', 12))
In [70]:
# Por ejemplo
sns.palplot(sns.color_palette('Blues', 12))
  • Divergente: Se utiliza en caso como en los secuenciales, pero la diferencia de las paletas divergentes es que los valores altos y bajos son interesantes.
In [71]:
# Por ejemplo
sns.palplot(sns.color_palette('BrBG', 12))

Personalización con matplotlib ¶

Dado que seaborn está basado en matplotlib, podemos modificar gráficos hechos con seaborn utilizando elementos de matplotlib.

Si pasamos en las funciones de graficación de seaborn un objeto del tipo Axes de matplotlib, podremos implementar modificaciones a dichos gráficos de seaborn utilizando directamente matplotlib. Para ello deberemos de colorar al inicio dle gráfico el código

In [73]:
fig, ax = plt.subplots()
print(type(ax))
print(type(fig))
<class 'matplotlib.axes._subplots.AxesSubplot'>
<class 'matplotlib.figure.Figure'>
In [78]:
fig, ax = plt.subplots()

# Creamos un histograma
sns.histplot(data=df, x='sepal_length',
                # al parametro ax pasamos el
                # objeto ax
                ax=ax)

# podemos colocar una etiqueta al grafico utilizando
# directamente el objeto ax
ax.set(xlabel='Largo del sépalo')

plt.show()

Más aún, podemos colocar las etiquetas de los ejes y un título

In [79]:
fig, ax = plt.subplots()

# Creamos un histograma
sns.histplot(data=df, x='sepal_length',
                # al parametro ax pasamos el
                # objeto ax
                ax=ax)

# podemos colocar una etiqueta al grafico utilizando
# directamente el objeto ax
ax.set(xlabel='Largo del sépalo', ylabel='Distribución',
       # agregamos un titulo
       title='Histograma',
       # podemos personalizar el limite de graficacion
       xlim=(3,8.5))

plt.show()

Podemos configurar y combinar múltiples gráficos, para lo cual necesitaremos dos objetos del tipo Axes, uno por cada gráfico. Y podremos agregar configuraciones nuevas como veremos en la siguiente celda de código

In [88]:
#                 configuramos el numero filas para
#                 el grafica y el numero de columnas
fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2,
                               # tamanio del grafico principal,
                               # 7 de largo y 4 de ancho
                               figsize=(7,4))

# Creamos un primer histograma
sns.histplot(data=df, x='sepal_length',
                # al parametro ax pasamos el
                # objeto ax0
                ax=ax0)

# Creamos un segundo histograma, donde dicho histograma
# tendra visible la curva de la densidad
sns.histplot(data=df, x='sepal_length',
                stat='density',
                # al parametro ax pasamos el
                # objeto ax1
                ax=ax1)

# Etiquetas para el primer grafico
ax0.set(xlabel='Largo del sépalo', ylabel='Distribución',
       # agregamos un titulo
       title='Histograma')
# Etiquetas para el segundo grafico
ax1.set(xlabel='Largo del sépalo', ylabel='Distribución',
       # agregamos un titulo
       title='Histograma con la curva de densidad')

# Podemos agregar una recta vertical para el segundo grafico
ax1.axvline(x=6, linestyle='--', color='blue', label='Línea vertical')

# o una linea horizontal al primer grafico
ax0.axhline(y=15, alpha=0.2, color='blue', label='Línea horizontal')

# mostraremos las legendas
ax0.legend()
ax1.legend()

plt.show()

Fig y Ax¶

Una figura (Fig) es una ventana (en el GUI) en la cual se muestran los gráficos. Por ello en la celda de código donde solo colocamos fig, ax = plt.subplots() se mostró un cuadrículado de gráfico vacío.

Así, por ejemplo, podemos colocar facecolor= dentro de la función plt.subplots() para cambiar el color de fondo del dibujo:

In [91]:
fig, ax = plt.subplots(facecolor='black')

Cabe mencionar que la función plt.subplots() nos permite organizar gráficos en una cuadrícula.

Por otro lado, los ejes (Axes) son muy similares a los subplots, permiten colocar gráficos en cualquier ubicación en la figura, y además nos permite modificar, personalizar y configurar nuestros gráficos, así como vimos en la sección anterior.


Finalmente, una curiosidad que podemos hacer con numpy y matplotlib:

In [97]:
import numpy as np

eqs = []
eqs.append((r"$W^{3\beta}_{\delta_1 \rho_1 \sigma_2} = U^{3\beta}_{\delta_1 \rho_1}" +
            r" + \frac{1}{8 \pi 2} \int^{\alpha_2}_{\alpha_2} d \alpha^\prime_2 " + 
            r"\left[\frac{ U^{2\beta}_{\delta_1 \rho_1} " + 
            r"- \alpha^\prime_2U^{1\beta}_{\rho_1 \sigma_2} }{U^{0\beta}_{\rho_1 \sigma_2}}\right]$"))
eqs.append((r"$\frac{d\rho}{d t} + \rho \vec{v}\cdot\nabla\vec{v} = " + 
            r"-\nabla p + \mu\nabla^2 \vec{v} + \rho \vec{g}$"))
eqs.append((r"$\int_{-\infty}^\infty e^{-x^2}dx=\sqrt{\pi}$"))
eqs.append((r"$E = mc^2 = \sqrt{{m_0}^2c^4 + p^2c^2}$"))
eqs.append((r"$F_G = G\frac{m_1m_2}{r^2}$"))

plt.axes([0.025, 0.025, 0.95, 0.95])

for i in range(24):
    index = np.random.randint(0, len(eqs))
    eq = eqs[index]
    size = np.random.uniform(12, 32)
    x,y = np.random.uniform(0, 1, 2)
    alpha = np.random.uniform(0.25, .75)
    plt.text(x, y, eq, ha='center', va='center', color="#11557c", alpha=alpha,
         transform=plt.gca().transAxes, fontsize=size, clip_on=True)
plt.xticks(())
plt.yticks(())

plt.show()