4.3. Descriptivos básicos#

En libretas anteriores ya vimos que los datasets en pandas tiene un método llamado describe que puede generar algunos estadísticos claves para comenzar a describir los datos, pero hoy iremos más profundo en cómo analizarlos. En esta libreta usaremos una librería nueva matplotlib para comenzar a elaborar gráficos exploratorios.

Nota

Aunque utilizamos matplotlib en esta libreta vamos a dejar que pandas se encargue de los gráficos, en otras lecciones veremos cómo usar matplotlib de forma más directa.

4.3.1. Cargar las librerías#

Truco

Recuerda que cada celda que se ejecuta deposita sus valores en la memoria del kernel de python que estamos utilizando. Eso quiere decir que podemos importar las librerías en una sola celda para organizar nuestro código. Comenzaremos cargando todo lo necesario desde el principio.

Importante

En python las importaciones pueden ser procesos tardados, como lo verás en esta libreta, pero python importa librerías una sola vez. Esto quiere decir, que si tienes dos importaciones de lo mismo, python no volverá a importar la librería.

import pandas as pd # aquí python importa la libería
import pandas as pd # aquí python prácticamente se salta la línea

Ejecuta la siguiente celda.

# nota que a todas les cambiamos el nombre
# esto es estándar en el análisis de datos
import pandas as pd
import matplotlib.pyplot as plt
print('Importaciones listas')
Importaciones listas

4.3.2. Cargar los datos desde la url#

Esto es igual que en la libreta previa, pero si hiciste el ejercicio final, intenta cambiar el código para importar la librería desde el archivo csv.

4.3.2.1. Nota#

Si tienes el archivo csv puedes cargarlo así.

path = 'Documentos/cursos/pingüinos.csv' # ejemplo
df = pd.read_csv(path)
url = 'https://raw.githubusercontent.com/raphaelvallat/pingouin/refs/heads/main/src/pingouin/datasets/penguins.csv'

df = pd.read_csv(url)
df.head() # solo las primeras 5 filas
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex
0 Adelie Biscoe 37.8 18.3 174.0 3400.0 female
1 Adelie Biscoe 37.7 18.7 180.0 3600.0 male
2 Adelie Biscoe 35.9 19.2 189.0 3800.0 female
3 Adelie Biscoe 38.2 18.1 185.0 3950.0 male
4 Adelie Biscoe 38.8 17.2 180.0 3800.0 male

4.3.3. Análisis descriptivo#

Como debiste revisar en la libreta previa el dataset contiene tres especies diferentes de pingüinos, Adelie, Chinstrap y Gentoo; y contiene algunos datos de ellos, de qué isla provienen, las dimenciones de sus picos, aletas y su peso corporal, así como su sexo. Comenzaremos analizando los datos categóricos.

4.3.3.1. Información del dataset#

Comencemos analizando el dataset como un todo, qué columnas tiene, cuántas filas, datos faltantes, etcétera.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 344 entries, 0 to 343
Data columns (total 7 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   species            344 non-null    object 
 1   island             344 non-null    object 
 2   bill_length_mm     342 non-null    float64
 3   bill_depth_mm      342 non-null    float64
 4   flipper_length_mm  342 non-null    float64
 5   body_mass_g        342 non-null    float64
 6   sex                333 non-null    object 
dtypes: float64(4), object(3)
memory usage: 18.9+ KB

Podemos ver que tenemos siete columnas, tres variables categóricas y cuatro numéricas float. Además, podemos ver que tenemos información de 344 pingüinos, pero no tenemos todos los datos de cada uno, solo la columna de especie e isla están completas y en la columna de sexo nos faltan 11 datos.

4.3.3.2. Descriptivos categóricos#

4.3.3.2.1. Generales#

Iniciemos utilizando el método describe con las variables categóricas, en el output the info aparecen como «object».

df.describe(include='object')
species island sex
count 344 344 333
unique 3 3 2
top Adelie Biscoe male
freq 152 168 168

De aquí podemos ver que en especie, tenemos las tres ya comentadas y que la más frecuente aparentemente es la Adelie, la mayoría de los pingüinos provienen de la isla Biscoe y hay en total tres islas, solo tenemos dos categorías en la variable sex y la más frecuente es «male».

4.3.3.2.2. Cuentas#

Esta información es útil pero es limitada, nos gustaría poder ver, de cada variable, su cuenta de valores y su porcentaje relativo, como es habitual. Comencemos por obtener la cuenta de cada variable, eso lo hacemos con el método value_counts

# iniciaremos por "especie"
especie = df['species']
especie.value_counts()
species
Adelie       152
Gentoo       124
Chinstrap     68
Name: count, dtype: int64

Para «especie» tenemos entonces los 152 Adelie que ya describe nos había dato, pero ahora podemos ver la cuenta de las otras dos especies, parece que la más rara en este dataset es Chinstrap.

Este código está bien, epero habría que analizar las otras dos categóricas y eso multiplicaría nuestro código, así que podemos utilizar eficientemente un simple loop.

for variable in ['species', 'island', 'sex']: # hacemos un loop para ahorrar código
    print(df[variable].value_counts())
species
Adelie       152
Gentoo       124
Chinstrap     68
Name: count, dtype: int64
island
Biscoe       168
Dream        124
Torgersen     52
Name: count, dtype: int64
sex
male      168
female    165
Name: count, dtype: int64

Como puedes ver en dos líneas de código obtuvimos los datos que nos interesaban. Ahora, veamos cómo podemos calcular las frecuencias relativas. La fórmula es muy básica

\(\text{frecuencia relativa} = \frac{n_{cat}}{n_{total}}\)

cuenta = df['species'].value_counts() # obtenemos la cuenta
total = cuenta.sum() # suma todos los valores
relativos = cuenta / total # dividie cada cuenta entre el total
porcentaje = relativos * 100 # si lo queremos como porcentaje
pd.DataFrame({ # utilizamos un dataframe para que mejore el formato del output.
    'cuenta': cuenta,
    'p': relativos,
    '%': porcentaje
})
cuenta p %
species
Adelie 152 0.441860 44.186047
Gentoo 124 0.360465 36.046512
Chinstrap 68 0.197674 19.767442

observa lo fácil que podemos hacer todos los cálculos. Es como una calculadora.

4.3.3.2.3. Ejercicio#

Calcula ahora tú las frecuencias relativas de las otras dos variables categóricas island y sex.

'tu código aquí'
'tu código aquí'

4.3.3.3. Descriptivos numéricos#

Veamos ahora cómo describir nuestros datos numéricos. Iniciemos como en la libreta previa con describe.

4.3.3.3.1. Descriptivos básicos#

df.describe() # por default solo muestra las numéricas
              # no es necesario especificarlo
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
count 342.000000 342.000000 342.000000 342.000000
mean 43.921930 17.151170 200.915205 4201.754386
std 5.459584 1.974793 14.061714 801.954536
min 32.100000 13.100000 172.000000 2700.000000
25% 39.225000 15.600000 190.000000 3550.000000
50% 44.450000 17.300000 197.000000 4050.000000
75% 48.500000 18.700000 213.000000 4750.000000
max 59.600000 21.500000 231.000000 6300.000000

La tabla está bien, pero seguro te parece raro el formato, generalmente estos gráficos se muestran al revés, con las varialbes en las filas y los descriptivos en las columnas. «Voltear la tabla» en python, es tribial. El término técnico es Trasponer y se logra de la siguiente manera.

df.describe().T # solo usamos el atributo T.
count mean std min 25% 50% 75% max
bill_length_mm 342.0 43.921930 5.459584 32.1 39.225 44.45 48.5 59.6
bill_depth_mm 342.0 17.151170 1.974793 13.1 15.600 17.30 18.7 21.5
flipper_length_mm 342.0 200.915205 14.061714 172.0 190.000 197.00 213.0 231.0
body_mass_g 342.0 4201.754386 801.954536 2700.0 3550.000 4050.00 4750.0 6300.0

También puede ser que sean demasiados decimales y eso dificulte la lectura, nuevamente arreglarlo es tribial, redondearemos a 2 decimales.

df.describe().T.round(2)
count mean std min 25% 50% 75% max
bill_length_mm 342.0 43.92 5.46 32.1 39.22 44.45 48.5 59.6
bill_depth_mm 342.0 17.15 1.97 13.1 15.60 17.30 18.7 21.5
flipper_length_mm 342.0 200.92 14.06 172.0 190.00 197.00 213.0 231.0
body_mass_g 342.0 4201.75 801.95 2700.0 3550.00 4050.00 4750.0 6300.0

Otro de los principales descriptivos en variables numéricas son las correlaciones. En pandas es igualmente tribial generar una matriz de correlación.

(
    df
    .select_dtypes(exclude='object') # excluimos a las variables categóricas
    .corr() # corremos la matriz de correlación
            # por default utilizará pearson
)
bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
bill_length_mm 1.000000 -0.235053 0.656181 0.595110
bill_depth_mm -0.235053 1.000000 -0.583851 -0.471916
flipper_length_mm 0.656181 -0.583851 1.000000 0.871202
body_mass_g 0.595110 -0.471916 0.871202 1.000000

Nota

El atributo T está disponible en cualquier arreglo de datos, no solo en los descriptivos. Lo mismo para el método round.

4.3.3.4. Visualizaciones básicas#

Para continuar nuestro análisis descriptivo básico, utilizaremos algunos gráficos comunes. Para comenzar a visualizar nuestros datos utilizaremos matplotlib

Importante

Por favor tomate un momento para revisar la documentación de matplotlib. También revisa los acordeones.

4.3.3.4.1. Distribuciones#

4.3.3.4.1.1. Histogramas#

Los histogramas nos dan información de la frecuencia de cada valor posible de nuestras variables numéricas. Exploremos el comportamiento del peso. Es posible generar un histograma directamente de la columna de peso con pandas y el método hist.

df['body_mass_g'].hist()
<Axes: >
../_images/f1355e9eaee3161c5cd91ba67667445e9f62bee823487f9de4a3a0c63ce3c67b.png

Si solo lo dejamos así, es poco informativo. En un momento veremos cómo darle más estilo a la imagen, pero antes, es importante resaltar algo notable que está sucediendo. No es pandas quien acaba de hacer el histograma, en realidad fue matplotlib.

Sí, matplotlib es la base de muchas librerías que ayudan con la visualización de los datos, incluyendo pandas. Eso quiere decir que podemos utilizar matplotlib para interactuar más con la imagen, por ejemplo:

df['body_mass_g'].hist()
plt.title('Histograma de peso') # para el título
plt.xlabel('Peso en gramos')    # rótulo X
plt.ylabel('Frecuencia')        # rótulo Y
plt.show()                      # para el formato de salida de la imagen
../_images/2d4ff74a6709f9bcc393626c3cf2e78438169f4c70f308d9751ce4f5e6bdcc34.png
4.3.3.4.1.1.1. Ejercicio#
  1. Revisa la documentación de pd.Series.hist.

  2. Utiliza la documentación y los ejemplos para generar nuevas versiones del histograma.

  3. Revisa también pd.DataFrame.hist

# ejemplo de lo que se puede hacer si utilizamos hist desde el dataframe en lugar de la serie.
df.hist(
    column='body_mass_g', # dato que va al histograma
    by='species',  # cómo separar los datos
    xrot=0
)
plt.show()
../_images/1ea41526da31d95100666307da923e915102254b1f91a139c567c6a280884bfb.png

4.3.3.4.2. Densidad#

Los gráficos de densidad son también muy útiles y frecuentemente utilizados para analizar la distribución de los datos. El método estándar para analizar la densidad de datos empíricos es con el estimado del kernel, en inglés kde.

df['body_mass_g'].plot.kde()
plt.title('Densidad de peso')
plt.xlabel('Peso en gramos')
plt.ylabel('Densidad')
plt.show()
../_images/752f2730d1e59660fa2a508e7fe94bafc182d9442a439fa0d8d771a69e91ac25.png

4.3.3.4.3. Boxplot (Caja y brazos)#

Estos gráficos también son uno de los principales estándares para visualizar la distribución de los datos.

df.boxplot(
    column='body_mass_g',
    by='species'
)
plt.show()
../_images/b8bd743a9e9b7af9733258ac4f25a5aa02c5a5ed16fa776cf7259fdcf30bad2e.png

4.3.3.4.4. Ejercicio#

Utiliza los tres tipos de gráficos que hemos analizado hasta ahora para analizar el resto de las variables

'tu código aquí'
'tu código aquí'

4.3.3.5. Dispersión#

Otro de los principales gráficos en el análisis de datos es el de dispersión, que muestra la relación entre dos principales variables.

df.plot.scatter(
    x='bill_length_mm',
    y='bill_depth_mm',
)
plt.title('Dispersión entre el largo y el ancho del pico')
plt.show()
../_images/ab19206dcd11d8e8ee6c429c8f62c482da8e501e16bc559ff2ddab9f0c857364.png

Si observas con atención, existen tres diferentes clusters o «conglomerados» podemos ver qué pasa si asignamos colore por especie.

colors = {'Adelie': 'blue', 'Chinstrap': 'green', 'Gentoo': 'red'}
df.plot.scatter(
    x='bill_length_mm',
    y='bill_depth_mm',
    c=df['species'].map(colors)
)
plt.title('Dispersión entre el largo y el ancho del pico por especie')
plt.show()
../_images/f8f9eca8b981a4d3312044a865623b893f441074ffd0e828088891af964ce7d3.png

Los colores muestran que hay diferencias por especies, pero en el gráfico no tenemos claro qué especie es qué color, podemos agregarlo pero veremos luego cómo hacerlo.

4.3.3.6. Ejercicio final#

Utiliza lo aprendido y termina de analizar los datos y las diferentes asociaciones que creas que existan.

4.3.3.7. Práctica personal#

Abre otro dataset que conozcas y aplica lo que practicamos.