03 — eda

Análisis Exploratorio de Datos

El EDA es la etapa previa al modelado: permite conocer la estructura del dataset, identificar distribuciones, relaciones entre variables y posibles problemas en los datos antes de ajustar cualquier modelo.

¿Qué es el EDA?

El análisis exploratorio precede al modelado y responde preguntas descriptivas sobre los datos:

Carga y exploración inicial

head, tail y describe

Los primeros métodos que se usan al recibir un dataset nuevo:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_csv("data/csvs/examenes.csv")

df.head()      # primeras 5 filas
df.tail()      # últimas 5 filas
df.shape       # (20000, 13) — filas y columnas
df.info()      # tipos de datos y valores nulos
df.describe()  # estadísticas descriptivas (solo numéricas)

describe() calcula para cada columna numérica: cantidad, media, desviación estándar, mínimo, cuartiles y máximo. Es el resumen estadístico básico.

Tipos de datos

Las columnas pueden ser numéricas (continuas o discretas) o categóricas. Identificarlas correctamente es clave para elegir la visualización adecuada:

df.dtypes   # tipo de cada columna

# Variables numéricas del dataset
numericas = df.select_dtypes(include="number").columns.tolist()

# Variables categóricas
categoricas = ["gender", "course", "internet_access",
               "sleep_quality", "study_method",
               "facility_rating", "exam_difficulty"]

# Valores únicos de una variable categórica
df["study_method"].unique()
df["gender"].value_counts()
Gráfica de barras con la cantidad de estudiantes por género
Distribución de estudiantes por género. Las tres categorías tienen frecuencias muy similares, lo que indica un dataset balanceado en esta variable.

Filtros booleanos

Para analizar subconjuntos de los datos, se crean máscaras booleanas: condiciones que devuelven True o False para cada fila.

# Filtrar un solo género
filtro_mujeres = df["gender"] == "female"
df_mujeres = df[filtro_mujeres]

# Filtrar múltiples condiciones (&, |)
filtro = (df["gender"] == "female") & (df["study_hours"] > 5)
df_filtrado = df[filtro]

# Comparar distribuciones entre grupos
scores_mujeres = df[df["gender"] == "female"]["exam_score"]
scores_hombres = df[df["gender"] == "male"]["exam_score"]

print(scores_mujeres.mean(), scores_hombres.mean())
Scatter plot de horas de estudio vs calificación del examen
Relación entre horas de estudio y calificación del examen (muestra de 500 estudiantes). La tendencia positiva es clara: más horas de estudio se asocian con mejores calificaciones.
Boxplot comparando la distribución de calificaciones por género
Distribución de calificaciones por género. Las medianas son similares entre los tres grupos, lo que sugiere que el género no es un predictor importante de la calificación.
Histogramas superpuestos de calificaciones por género
Histogramas superpuestos: la distribución de calificaciones tiene una forma similar para los tres géneros, con la mayor parte de los datos concentrada entre 40 y 90 puntos.
Importante

Los operadores and, or de Python no funcionan con filtros de Pandas. Hay que usar &, | y envolver cada condición en paréntesis.

Combinación de variables

Crear nuevas columnas combinando variables existentes puede revelar patrones que no son visibles por separado:

# Combinar dos variables categóricas en una sola
df["metodo_calidad"] = df["study_method"] + " - " + df["sleep_quality"]

# Ejemplo de valores resultantes:
# "coaching - poor", "online videos - good", "self-study - average"

# Visualizar la nueva combinación
sns.scatterplot(data=df, x="study_hours", y="exam_score",
                hue="metodo_calidad", alpha=0.5)
plt.show()

Agrupaciones con groupby

groupby() divide el DataFrame en grupos según el valor de una o varias columnas y permite calcular estadísticas por grupo:

# Promedio de calificación por método de estudio y calidad de sueño
resumen = (df.groupby("metodo_calidad")["exam_score"]
             .agg(["mean", "std", "median"])
             .sort_values("mean", ascending=False))

print(resumen.head(5))

Resultado esperado:

#                              mean        std  median
# coaching - good            73.84      17.44   74.60
# coaching - average         69.04      17.54   69.05
# mixed - good               68.29      17.55   68.90
# group study - good         65.86      17.90   66.20
# online videos - good       64.89      18.37   65.20
Ejemplo Comparar promedio por método de estudio con barplot
for col in categoricas:
    sns.barplot(data=df, y="exam_score", hue=col, errorbar="sd")
    plt.title(f"Promedio de calificación por {col}")
    plt.xticks(rotation=30)
    plt.show()

Correlación de Pearson

La correlación de Pearson mide la fuerza y dirección de la relación lineal entre dos variables numéricas. Su valor oscila entre −1 y 1:

\[ r = \frac{\sum (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2 \sum (y_i - \bar{y})^2}} \]
# Seleccionar solo columnas numéricas
df_num = df[["study_hours", "class_attendance",
             "sleep_hours", "exam_score"]]

# Calcular la matriz de correlación
matriz = df_num.corr()  # correlación de Pearson por defecto

# Visualizar con heatmap
sns.heatmap(matriz, annot=True, fmt=".2f",
            cmap="coolwarm", vmin=-1, vmax=1)
plt.title("Matriz de correlación de Pearson")
plt.show()
Heatmap de la matriz de correlación de variables numéricas
Matriz de correlación de las variables numéricas. study_hours tiene la correlación más alta con exam_score. Las demás variables muestran correlaciones bajas, lo que sugiere que el tiempo de estudio es el predictor dominante.
Limitación importante

La correlación de Pearson solo detecta relaciones lineales. Dos variables pueden tener una relación fuerte no lineal y aun así mostrar r ≈ 0. Por eso siempre hay que complementar con scatter plots.

Flujo EDA completo

Un EDA bien estructurado sigue este orden:

  1. Carga y revisión generalshape, info(), head()
  2. Estadísticas descriptivasdescribe() para numéricas, value_counts() para categóricas
  3. Valores faltantesdf.isnull().sum()
  4. Distribuciones individuales — histogramas y boxplots por variable
  5. Relaciones bivariadas — scatter plots, correlación, barplots
  6. Análisis multivariado — combinación de variables, groupby, heatmap
  7. Hipótesis y conclusiones — ¿qué variables parecen predecir la variable objetivo?
Profundización Correlación de Kendall y Spearman

Pandas permite calcular otros tipos de correlación con el parámetro method:

df_num.corr(method="pearson")   # sensible a outliers, asume normalidad
df_num.corr(method="spearman")  # basada en rangos, robusta a outliers
df_num.corr(method="kendall")   # basada en concordancias de pares

Usa Spearman o Kendall cuando los datos tienen muchos valores atípicos o no siguen una distribución normal.