🎯 Objetivo del Post: Aprenderás a realizar un análisis exploratorio de datos (EDA) específico para problemas de clasificación, calculando correlaciones entre variables numéricas e identificando las relaciones más fuertes que nos ayudarán a construir mejores modelos.
📊 ¿Por Qué es Importante el EDA en Clasificación?
Antes de construir cualquier modelo, debemos entender nuestros datos. En clasificación, el EDA nos ayuda a:
✅ Identificar relaciones entre variables predictoras y la variable objetivo
✅ Detectar multicolinealidad (variables correlacionadas entre sí)
✅ Descubrir patrones que el modelo podría aprender
✅ Informar decisiones sobre ingeniería de características
✅ Validar suposiciones sobre los datos
🔍 Paso 1: Explorar la Variable Objetivo
Lo primero es entender la distribución de nuestra variable objetivo: converted
Distribución de Conversiones
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# Cargar y limpiar datos (del post anterior)
url = "https://raw.githubusercontent.com/alexeygrigorev/datasets/master/course_lead_scoring.csv"
df = pd.read_csv(url)
# Limpieza rápida
categorical_cols = df.select_dtypes(include=['object']).columns.tolist()
numerical_cols = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
df_clean = df.copy()
for col in categorical_cols:
df_clean[col] = df_clean[col].fillna('NA')
for col in numerical_cols:
if col != 'converted':
df_clean[col] = df_clean[col].fillna(0.0)
# Analizar la variable objetivo
print("ANÁLISIS DE LA VARIABLE OBJETIVO: 'converted'")
print("=" * 60)
# Conteos absolutos
print("\nConteo de conversiones:")
print(df_clean['converted'].value_counts())
# Porcentajes
print("\nPorcentajes:")
print(df_clean['converted'].value_counts(normalize=True) * 100)
# Tasa de conversión
conversion_rate = df_clean['converted'].mean()
print(f"\n📊 Tasa de Conversión: {conversion_rate:.2%}")
Salida:
ANÁLISIS DE LA VARIABLE OBJETIVO: 'converted'
============================================================
Conteo de conversiones:
0 730 ← No convertidos
1 732 ← Convertidos
Name: converted, dtype: int64
Porcentajes:
0 49.93% ← No convertidos
1 50.07% ← Convertidos
Name: converted, dtype: float64
📊 Tasa de Conversión: 50.07%
Visualización de la Distribución
# Visualizar distribución
plt.figure(figsize=(10, 6))
# Gráfico de barras
ax = df_clean['converted'].value_counts().plot(kind='bar', color=['salmon', 'lightgreen'])
plt.title('Distribución de Conversiones', fontsize=16, fontweight='bold')
plt.xlabel('Convertido', fontsize=12)
plt.ylabel('Frecuencia', fontsize=12)
plt.xticks([0, 1], ['No (0)', 'Sí (1)'], rotation=0)
# Añadir valores encima de las barras
for i, v in enumerate(df_clean['converted'].value_counts()):
ax.text(i, v + 10, str(v), ha='center', fontweight='bold')
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\n✅ Dataset Balanceado: Tenemos casi igual cantidad de casos positivos y negativos")
¿Por Qué es Importante el Balance?
Dataset Balanceado (≈50%-50%):
✅ El modelo aprende de ambas clases por igual
✅ No hay sesgo hacia una clase
✅ Métricas como accuracy son confiables
Dataset Desbalanceado (ejemplo: 95%-5%):
❌ El modelo puede ignorar la clase minoritaria
❌ Accuracy puede ser engañoso (95% prediciendo siempre la clase mayoritaria)
❌ Necesita técnicas especiales (resampling, class weights)
Nuestro caso: ¡Dataset perfecto para aprender! 🎉
📈 Paso 2: Matriz de Correlación
La correlación mide la relación lineal entre dos variables numéricas:
- +1: Correlación positiva perfecta (cuando una sube, la otra sube)
- 0: Sin correlación lineal
- -1: Correlación negativa perfecta (cuando una sube, la otra baja)
Calcular la Matriz de Correlación
# Seleccionar solo variables numéricas (sin 'converted')
numerical_features = [col for col in numerical_cols if col != 'converted']
print("\nVARIABLES NUMÉRICAS PARA ANÁLISIS:")
print("=" * 60)
print(numerical_features)
# Calcular matriz de correlación
correlation_matrix = df_clean[numerical_features].corr()
print("\nMATRIZ DE CORRELACIÓN:")
print("=" * 60)
print(correlation_matrix.round(4))
Salida:
VARIABLES NUMÉRICAS PARA ANÁLISIS:
============================================================
['number_of_courses_viewed', 'annual_income',
'interaction_count', 'lead_score']
MATRIZ DE CORRELACIÓN:
============================================================
number_of_courses_viewed annual_income \
number_of_courses_viewed 1.0000 0.0098
annual_income 0.0098 1.0000
interaction_count -0.0236 0.0270
lead_score -0.0049 0.0156
interaction_count lead_score
number_of_courses_viewed -0.0236 -0.0049
annual_income 0.0270 0.0156
interaction_count 1.0000 0.0099
lead_score 0.0099 1.0000
Visualizar la Matriz de Correlación
# Heatmap de correlación
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix,
annot=True, # Mostrar valores
fmt='.3f', # Formato con 3 decimales
cmap='coolwarm', # Escala de colores
center=0, # Centro en 0
square=True, # Celdas cuadradas
linewidths=1, # Líneas de separación
cbar_kws={'label': 'Correlación'})
plt.title('Matriz de Correlación - Variables Numéricas',
fontsize=16, fontweight='bold', pad=20)
plt.tight_layout()
plt.show()
🎯 Question 2 del Homework: Identificar Mayor Correlación
El homework pide identificar cuál par de variables tiene la mayor correlación entre estas opciones:
# Pares específicos del homework
pairs_to_check = [
('interaction_count', 'lead_score'),
('number_of_courses_viewed', 'lead_score'),
('number_of_courses_viewed', 'interaction_count'),
('annual_income', 'interaction_count')
]
print("\nCORRELACIONES DE LOS PARES ESPECIFICADOS:")
print("=" * 70)
correlations = []
for var1, var2 in pairs_to_check:
if var1 in correlation_matrix.columns and var2 in correlation_matrix.columns:
corr_value = correlation_matrix.loc[var1, var2]
correlations.append((var1, var2, corr_value))
print(f"{var1:30} vs {var2:30}: {corr_value:7.4f}")
# Encontrar la mayor correlación (en valor absoluto)
print("\n" + "=" * 70)
max_corr_pair = max(correlations, key=lambda x: abs(x[2]))
print(f"\n🎯 MAYOR CORRELACIÓN:")
print(f" Par: {max_corr_pair[0]} y {max_corr_pair[1]}")
print(f" Correlación: {max_corr_pair[2]:.4f}")
print(f"\n✅ Respuesta Question 2: {max_corr_pair[0]} y {max_corr_pair[1]}")
Salida esperada:
CORRELACIONES DE LOS PARES ESPECIFICADOS:
======================================================================
interaction_count vs lead_score : 0.0099
number_of_courses_viewed vs lead_score :-0.0049
number_of_courses_viewed vs interaction_count :-0.0236
annual_income vs interaction_count : 0.0270
======================================================================
🎯 MAYOR CORRELACIÓN:
Par: annual_income y interaction_count
Correlación: 0.0270
✅ Respuesta Question 2: annual_income y interaction_count
Interpretación de las Correlaciones
# Interpretación automática
print("\nINTERPRETACIÓN DE CORRELACIONES:")
print("=" * 60)
for var1, var2, corr in correlations:
if abs(corr) < 0.1:
strength = "muy débil"
elif abs(corr) < 0.3:
strength = "débil"
elif abs(corr) < 0.7:
strength = "moderada"
else:
strength = "fuerte"
direction = "positiva" if corr > 0 else "negativa"
print(f"\n{var1} vs {var2}:")
print(f" Correlación: {corr:.4f} ({strength} {direction})")
Conclusión:
- 🔍 Todas las correlaciones son muy débiles (< 0.1)
- 📊 Esto significa que las variables numéricas son relativamente independientes
- ✅ Buena noticia: No hay multicolinealidad severa
- ⚠️ Implicación: Cada variable aporta información única
📊 Paso 3: Explorar Variables Numéricas Individualmente
Estadísticas Descriptivas
print("\nESTADÍSTICAS DESCRIPTIVAS - VARIABLES NUMÉRICAS")
print("=" * 80)
for col in numerical_features:
print(f"\n{col.upper()}")
print("-" * 50)
stats = df_clean[col].describe()
print(f" Media: {stats['mean']:.2f}")
print(f" Mediana: {stats['50%']:.2f}")
print(f" Std Dev: {stats['std']:.2f}")
print(f" Min: {stats['min']:.2f}")
print(f" Max: {stats['max']:.2f}")
print(f" Rango: {stats['max'] - stats['min']:.2f}")
Distribuciones por Clase
Analizar cómo se distribuyen las variables numéricas para leads convertidos vs no convertidos:
# Comparar distribuciones por clase
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
axes = axes.ravel()
for idx, col in enumerate(numerical_features):
ax = axes[idx]
# Distribución por clase
df_clean[df_clean['converted'] == 0][col].hist(
bins=30, alpha=0.5, label='No Convertido', ax=ax, color='salmon'
)
df_clean[df_clean['converted'] == 1][col].hist(
bins=30, alpha=0.5, label='Convertido', ax=ax, color='lightgreen'
)
ax.set_title(f'Distribución de {col}', fontweight='bold')
ax.set_xlabel(col)
ax.set_ylabel('Frecuencia')
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
Boxplots por Clase
# Boxplots para detectar diferencias
fig, axes = plt.subplots(1, 4, figsize=(20, 5))
for idx, col in enumerate(numerical_features):
df_clean.boxplot(column=col, by='converted', ax=axes[idx])
axes[idx].set_title(f'{col}')
axes[idx].set_xlabel('Convertido')
axes[idx].set_ylabel(col)
plt.suptitle('Boxplots de Variables Numéricas por Clase',
fontsize=16, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()
📊 Paso 4: Explorar Variables Categóricas
Distribución de Categorías
print("\nDISTRIBUCIÓN DE VARIABLES CATEGÓRICAS")
print("=" * 80)
for col in categorical_cols:
print(f"\n{col.upper()}")
print("-" * 50)
# Conteos
counts = df_clean[col].value_counts()
print(f"Categorías únicas: {len(counts)}")
print(f"\nTop 5 categorías:")
print(counts.head())
# Porcentajes
print(f"\nPorcentajes:")
print(round(counts.head() / len(df_clean) * 100, 2))
Tasa de Conversión por Categoría
# Ver cómo afecta cada categoría a la conversión
print("\nTASA DE CONVERSIÓN POR CATEGORÍA")
print("=" * 80)
for col in categorical_cols:
print(f"\n{col.upper()}")
print("-" * 50)
# Calcular tasa de conversión por categoría
conversion_by_cat = df_clean.groupby(col)['converted'].agg(['mean', 'count'])
conversion_by_cat['conversion_rate %'] = conversion_by_cat['mean'] * 100
conversion_by_cat = conversion_by_cat.sort_values('mean', ascending=False)
print(conversion_by_cat.head())
Ejemplo de salida:
LEAD_SOURCE
--------------------------------------------------
mean count conversion_rate %
referral 0.55 270 55.0 ← Mayor conversión
events 0.52 280 52.0
social_media 0.48 295 48.0
paid_ads 0.47 289 47.0
NA 0.46 128 46.0 ← Menor conversión
organic_search 0.45 200 45.0
Insights:
- 🎯 Referrals tienen la mayor tasa de conversión (55%)
- 📧 Organic search tiene la menor (45%)
- 💡 Implicación: El origen del lead importa
Visualización de Conversión por Categoría
# Gráfico de barras para una variable categórica
col = 'lead_source'
conversion_by_source = df_clean.groupby(col)['converted'].mean().sort_values()
plt.figure(figsize=(12, 6))
conversion_by_source.plot(kind='barh', color='steelblue')
plt.title(f'Tasa de Conversión por {col}', fontsize=16, fontweight='bold')
plt.xlabel('Tasa de Conversión', fontsize=12)
plt.ylabel(col, fontsize=12)
plt.grid(axis='x', alpha=0.3)
# Añadir línea de tasa promedio
avg_rate = df_clean['converted'].mean()
plt.axvline(avg_rate, color='red', linestyle='--',
label=f'Promedio: {avg_rate:.2%}', linewidth=2)
plt.legend()
plt.tight_layout()
plt.show()
🔍 Paso 5: Question 1 del Homework - Moda de Industry
# Question 1: ¿Cuál es la moda de 'industry'?
print("\nQUESTION 1: MODA DE 'INDUSTRY'")
print("=" * 60)
# Calcular la moda
mode_industry = df_clean['industry'].mode()[0]
print(f"\n🎯 La moda de 'industry' es: {mode_industry}")
# Ver distribución completa
print(f"\nDistribución completa de 'industry':")
industry_counts = df_clean['industry'].value_counts()
print(industry_counts)
print(f"\n✅ Respuesta Question 1: {mode_industry}")
Salida esperada:
QUESTION 1: MODA DE 'INDUSTRY'
============================================================
🎯 La moda de 'industry' es: retail
Distribución completa de 'industry':
retail 203 ← Moda (más frecuente)
finance 200
other 198
healthcare 187
education 187
technology 179
manufacturing 174
NA 134
Name: industry, dtype: int64
✅ Respuesta Question 1: retail
💡 Insights Clave del Análisis Exploratorio
✅ Sobre el Dataset
- Balanceado: 50% convertidos, 50% no convertidos
- Correlaciones débiles: Variables numéricas son independientes
- Diversidad: Múltiples categorías en cada variable categórica
✅ Sobre las Variables Numéricas
- annual_income y interaction_count: Mayor correlación (0.027)
- lead_score: Poca correlación con otras variables
- number_of_courses_viewed: Independiente de otras variables
✅ Sobre las Variables Categóricas
- industry: 'retail' es la moda
- lead_source: 'referral' tiene mayor tasa de conversión
- employment_status: Puede afectar la conversión
🎯 Código Completo para Referencia
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# 1. Cargar y limpiar datos
url = "https://raw.githubusercontent.com/alexeygrigorev/datasets/master/course_lead_scoring.csv"
df = pd.read_csv(url)
categorical_cols = df.select_dtypes(include=['object']).columns.tolist()
numerical_cols = df.select_dtypes(include=['int64', 'float64']).columns.tolist()
df_clean = df.copy()
for col in categorical_cols:
df_clean[col] = df_clean[col].fillna('NA')
for col in numerical_cols:
if col != 'converted':
df_clean[col] = df_clean[col].fillna(0.0)
# 2. Analizar variable objetivo
print(f"Tasa de conversión: {df_clean['converted'].mean():.2%}")
# 3. Matriz de correlación
numerical_features = [col for col in numerical_cols if col != 'converted']
correlation_matrix = df_clean[numerical_features].corr()
print("\nMatriz de correlación:")
print(correlation_matrix.round(4))
# 4. Question 1 - Moda de industry
mode_industry = df_clean['industry'].mode()[0]
print(f"\nModa de industry: {mode_industry}")
# 5. Question 2 - Mayor correlación
pairs = [
('interaction_count', 'lead_score'),
('number_of_courses_viewed', 'lead_score'),
('number_of_courses_viewed', 'interaction_count'),
('annual_income', 'interaction_count')
]
for var1, var2 in pairs:
corr = correlation_matrix.loc[var1, var2]
print(f"{var1} vs {var2}: {corr:.4f}")
🚀 Próximos Pasos
En el siguiente post (MLZC25-19), aprenderemos sobre Mutual Information Score, una técnica poderosa para medir la importancia de variables categóricas en relación con nuestra variable objetivo, algo que las correlaciones no pueden capturar.
¿Qué otros patrones encontraste en los datos? ¿Qué visualizaciones te parecen más útiles para entender los datos?
Top comments (0)