Saltar a contenido

Assignment UT3-7-1: Feature Engineering con Pandas - Fill in the Blanks

📋 Lo que necesitas saber ANTES de empezar

  • Conceptos básicos de Python y pandas
  • Idea general de qué es feature engineering
  • Curiosidad por crear nuevas variables a partir de datos existentes

🏠 Parte 1: Setup y Carga de Datos

📋 CONTEXTO DE NEGOCIO (CRISP-DM: Business Understanding)

🔗 Referencias oficiales:

🏠 Caso de negocio:

  • Problema: Una empresa inmobiliaria necesita predecir precios de viviendas con mayor precisión
  • Objetivo: Crear features derivadas que capturen patrones no obvios en los datos
  • Variables: Precio, superficie, habitaciones, año construcción, ubicación, etc.
  • Valor para el negocio: Mejorar predicciones de precios para optimizar inversiones
# Importar librerías que vamos a usar
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.feature_selection import mutual_info_regression
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
import warnings
warnings.filterwarnings('ignore')

# Configuración
np.random.seed(42)
plt.style.use('_______')  # establecer estilo visual (ej: 'default', 'seaborn', 'classic')
sns.set_palette("_______")  # definir paleta de colores (ej: 'husl', 'Set1', 'viridis')
plt.rcParams['figure.figsize'] = (12, 8)

print("✅ Entorno configurado correctamente")

💡 PISTAS:

🏠 Paso 2: Crear Dataset Sintético de Viviendas

# === CREAR DATASET SINTÉTICO DE VIVIENDAS ===

# 1. Generar datos base
np.random.seed(42)
n_samples = 1000

data = {
    'price': np.random.normal(200000, 50000, n_samples),
    'sqft': np.random.normal(120, 30, n_samples),
    'bedrooms': np.random.choice([1, 2, 3, 4, 5], n_samples),
    'bathrooms': np.random.choice([1, 2, 3], n_samples),
    'year_built': np.random.choice(range(1980, 2024), n_samples),
    'garage_spaces': np.random.choice([0, 1, 2, 3], n_samples),
    'lot_size': np.random.normal(8000, 2000, n_samples),
    'distance_to_city': np.random.normal(15, 8, n_samples),
    'school_rating': np.random.uniform(1, 10, n_samples),
    'crime_rate': np.random.uniform(0, 100, n_samples)
}

df = pd._______(data)  # función para crear DataFrame desde diccionario

# Asegurar valores positivos
df['price'] = np.abs(df['price'])
df['sqft'] = np.abs(df['sqft'])
df['lot_size'] = np.abs(df['lot_size'])
df['distance_to_city'] = np.abs(df['distance_to_city'])

print("🏠 DATASET: Viviendas Sintéticas")
print(f"   📊 Forma: {df.shape}")
print(f"   📋 Columnas: {list(df.columns)}")

# 2. Explorar los datos básicamente
print("\n🔍 Primeras 5 filas:")
print(df._____())  # método para mostrar las primeras filas del DataFrame

# 3. Estadísticas básicas
print("\n📊 ESTADÍSTICAS BÁSICAS:")
print(df._____())  # método que calcula estadísticas descriptivas

💡 PISTAS:

  • 📚 ¿Qué función de pandas crea DataFrame desde diccionario? Documentación
  • 👀 ¿Cuál método muestra las primeras filas? Documentación
  • 📊 ¿Cuál método calcula estadísticas descriptivas? Documentación

⚙️ Paso 3: Crear Features Derivadas

# === CREAR FEATURES DERIVADAS ===

print("⚙️ CREANDO FEATURES DERIVADAS")
print("-" * 50)

# Crear copia del dataset para trabajar
df_enhanced = df.copy()

# 1. RATIOS Y PROPORCIONES (Ejemplos básicos)
print("🔢 1. CREANDO RATIOS Y PROPORCIONES")

# Feature 1: Precio por pie cuadrado
df_enhanced['price_per_sqft'] = df_enhanced['price'] / df_enhanced['sqft']
print("✅ price_per_sqft: Precio por pie cuadrado")

# Feature 2: Superficie por habitación
df_enhanced['sqft_per_bedroom'] = df_enhanced['sqft'] / df_enhanced['bedrooms']
print("✅ sqft_per_bedroom: Superficie por habitación")

# TODO: Crea al menos 2 ratios más que consideres relevantes
# PISTAS: ¿Qué otros ratios podrían ser útiles?
# - ¿Ratio entre habitaciones y baños?
# - ¿Densidad de construcción (superficie/lote)?
# - ¿Precio por habitación?
# - ¿Ratio entre distancia a ciudad y rating de escuela?

# Tu código aquí:
# df_enhanced['mi_ratio_1'] = _______
# df_enhanced['mi_ratio_2'] = _______

# 2. VARIABLES TEMPORALES (Ejemplo básico)
print("\n📅 2. CREANDO VARIABLES TEMPORALES")

# Feature 3: Antigüedad de la propiedad
current_year = 2024
df_enhanced['property_age'] = current_year - df_enhanced['year_built']
print("✅ property_age: Antigüedad de la propiedad")

# TODO: Crea variables temporales adicionales
# PISTAS: ¿Qué información temporal podría ser útil?
# - ¿Categorías de antigüedad (nuevo, moderno, antiguo)?
# - ¿Es propiedad nueva? (binario)
# - ¿Década de construcción?
# - ¿Es construcción reciente vs histórica?

# Tu código aquí:
# df_enhanced['age_category'] = pd._______(...)
# df_enhanced['is_new_property'] = _______

# 3. TRANSFORMACIONES MATEMÁTICAS (Ejemplo básico)
print("\n🧮 3. APLICANDO TRANSFORMACIONES MATEMÁTICAS")

# Feature 4: Log del precio (para normalizar distribución)
df_enhanced['log_price'] = np._______(df_enhanced['price'])  # función para logaritmo natural
print("✅ log_price: Logaritmo del precio")

# TODO: Aplica otras transformaciones matemáticas
# PISTAS: ¿Qué transformaciones podrían ayudar?
# - ¿Raíz cuadrada de superficie?
# - ¿Superficie al cuadrado?
# - ¿Log de otras variables?
# - ¿Transformaciones para normalizar distribuciones sesgadas?

# Tu código aquí:
# df_enhanced['sqrt_sqft'] = _______
# df_enhanced['sqft_squared'] = _______

# 4. FEATURES COMPUESTAS (Tu turno!)
print("\n🎯 4. CREANDO FEATURES COMPUESTAS")

# TODO: Crea features que combinen múltiples variables
# PISTAS: ¿Qué conceptos complejos podrías capturar?
# - ¿Score de lujo (precio alto + superficie grande + amenities)?
# - ¿Score de ubicación (distancia baja + escuela alta + crimen bajo)?
# - ¿Score de eficiencia (superficie por habitación + precio por m²)?
# - ¿Indicadores de calidad (antigüedad + amenities + ubicación)?

# Tu código aquí:
# df_enhanced['luxury_score'] = _______
# df_enhanced['location_score'] = _______

print(f"\n📊 RESUMEN DE FEATURES CREADAS:")
print(f"Dataset original: {df.shape[1]} columnas")
print(f"Dataset con features: {df_enhanced.shape[1]} columnas")
print(f"Features creadas: {df_enhanced.shape[1] - df.shape[1]}")

💡 PISTAS:

  • 📊 ¿Qué función de pandas crea categorías? Documentación
  • 🔢 ¿Qué tipo de datos usar para variables binarias? ('int', 'float', 'bool')
  • 📈 ¿Qué función de numpy calcula logaritmo natural? Documentación
  • 🔢 ¿Qué función de numpy calcula raíz cuadrada? Documentación

📊 Paso 4: Análisis de Distribución de Features

# === ANÁLISIS DE DISTRIBUCIÓN DE FEATURES ===

print("📊 ANÁLISIS DE DISTRIBUCIÓN DE FEATURES")
print("-" * 50)

# 1. ANÁLISIS DE DISTRIBUCIÓN DE NUEVAS FEATURES
print("🔍 1. DISTRIBUCIÓN DE FEATURES DERIVADAS")

# Seleccionar solo las nuevas features creadas
new_features = ['price_per_sqft', 'sqft_per_bedroom', 'bedroom_bathroom_ratio', 
                'building_density', 'property_age', 'log_price', 'sqrt_sqft', 'sqft_squared']

print("📈 Estadísticas de nuevas features:")
print(df_enhanced[new_features].______().round(2))  # método para estadísticas descriptivas

# 2. VISUALIZACIÓN DE DISTRIBUCIONES
print("\n📊 2. VISUALIZANDO DISTRIBUCIONES")

# Crear subplots para visualizar distribuciones
fig, axes = plt.subplots(2, 4, figsize=(16, 8))
axes = axes.ravel()  # convertir a array 1D

for i, feature in enumerate(new_features):
    # Histograma de la feature
    df_enhanced[feature].hist(bins=30, ax=axes[i], alpha=0.7, color='skyblue')
    axes[i].set_title(f'Distribución de {feature}')
    axes[i].set_xlabel(feature)
    axes[i].set_ylabel('Frecuencia')
    axes[i].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 3. DETECCIÓN DE OUTLIERS
print("\n🚨 3. DETECCIÓN DE OUTLIERS")

def detect_outliers_iqr(df, column):
    """Detectar outliers usando método IQR"""
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
    return outliers, lower_bound, upper_bound

# Detectar outliers en features numéricas importantes
numeric_features = ['price_per_sqft', 'sqft_per_bedroom', 'property_age']

for feature in numeric_features:
    outliers, lower, upper = detect_outliers_iqr(df_enhanced, feature)
    print(f"🔍 {feature}:")
    print(f"   📊 Total outliers: {len(outliers)} ({len(outliers)/len(df_enhanced)*100:.1f}%)")
    print(f"   📏 Rango normal: [{lower:.2f}, {upper:.2f}]")

💡 PISTAS:


🎯 Paso 5: Evaluación de Importancia de Features

# === EVALUACIÓN DE IMPORTANCIA DE FEATURES ===

print("🎯 EVALUACIÓN DE IMPORTANCIA DE FEATURES")
print("-" * 50)

# 1. PREPARAR DATOS PARA EVALUACIÓN
print("🔧 1. PREPARANDO DATOS PARA EVALUACIÓN")

# Seleccionar features numéricas para evaluación
numeric_features = ['sqft', 'bedrooms', 'bathrooms', 'year_built', 'garage_spaces',
                   'lot_size', 'distance_to_city', 'school_rating', 'crime_rate',
                   'price_per_sqft', 'sqft_per_bedroom', 'bedroom_bathroom_ratio',
                   'building_density', 'property_age', 'log_price', 'sqrt_sqft', 'sqft_squared']

# Preparar X e y
X = df_enhanced[numeric_features].fillna(0)  # llenar valores faltantes con 0
y = df_enhanced['price']

print(f"📊 Features evaluadas: {len(numeric_features)}")
print(f"📊 Muestras: {X.shape[0]}")

# 2. MUTUAL INFORMATION
print("\n📈 2. CALCULANDO MUTUAL INFORMATION")

# Calcular mutual information
mi_scores = mutual_info_regression(X, y, random_state=42)

# Crear DataFrame con resultados
mi_df = pd.DataFrame({
    'feature': numeric_features,
    'mutual_info': mi_scores
}).sort_values('mutual_info', ascending=False)

print("🔝 Top 10 features por Mutual Information:")
print(mi_df.head(10).round(4))

# 3. RANDOM FOREST IMPORTANCE
print("\n🌲 3. CALCULANDO IMPORTANCIA CON RANDOM FOREST")

# Entrenar Random Forest
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X, y)

# Obtener importancia de features
rf_importance = pd.DataFrame({
    'feature': numeric_features,
    'importance': rf.feature_importances_
}).sort_values('importance', ascending=False)

print("🔝 Top 10 features por Random Forest:")
print(rf_importance.head(10).round(4))

# 4. VISUALIZACIÓN COMPARATIVA
print("\n📊 4. VISUALIZACIÓN COMPARATIVA")

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))

# Mutual Information
top_mi = mi_df.head(10)
ax1.barh(range(len(top_mi)), top_mi['mutual_info'], color='skyblue')
ax1.set_yticks(range(len(top_mi)))
ax1.set_yticklabels(top_mi['feature'])
ax1.set_xlabel('Mutual Information')
ax1.set_title('Top 10 Features - Mutual Information')
ax1.grid(True, alpha=0.3)

# Random Forest Importance
top_rf = rf_importance.head(10)
ax2.barh(range(len(top_rf)), top_rf['importance'], color='lightgreen')
ax2.set_yticks(range(len(top_rf)))
ax2.set_yticklabels(top_rf['feature'])
ax2.set_xlabel('Feature Importance')
ax2.set_title('Top 10 Features - Random Forest')
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

# 5. ANÁLISIS DE CORRELACIÓN CON TARGET
print("\n🔗 5. CORRELACIÓN CON PRECIO")

# Calcular correlaciones
correlations = df_enhanced[numeric_features + ['price']].corr()['price'].drop('price')
correlations = correlations.sort_values(key=abs, ascending=False)

print("🔝 Top 10 correlaciones con precio:")
print(correlations.head(10).round(4))

💡 PISTAS:

  • 📊 ¿Qué función calcula mutual information? Documentación
  • 🌲 ¿Qué atributo del Random Forest contiene la importancia? Documentación
  • 🔗 ¿Qué método calcula correlaciones? Documentación
  • 📈 ¿Qué función de matplotlib crea gráficos de barras horizontales? Documentación

🚀 Paso 6: Investigación Libre - Tu Turno

# === INVESTIGACIÓN LIBRE ===
# Aquí tienes libertad para explorar y crear tus propias features

print("🚀 INVESTIGACIÓN LIBRE - CREA TUS PROPIAS FEATURES")
print("=" * 60)

# 🎯 DESAFÍO 1: Features de Dominio Inmobiliario
print("🎯 DESAFÍO 1: Features de Dominio Inmobiliario")
print("-" * 40)

# TODO: Crea al menos 3 features nuevas basadas en tu conocimiento del mercado inmobiliario
# PISTAS ESPECÍFICAS:
# - ¿Cómo afecta la relación entre superficie y lote? (¿es eficiente el uso del espacio?)
# - ¿Qué pasa con propiedades que tienen muchas habitaciones pero poca superficie?
# - ¿Cómo combinar distancia a ciudad, crimen y rating de escuela en un score de ubicación?
# - ¿Hay patrones relacionados con la década de construcción vs precio?

# Tu código aquí:
# df_enhanced['space_efficiency'] = _______  # ¿superficie/lote?
# df_enhanced['crowded_property'] = _______  # ¿habitaciones/superficie?
# df_enhanced['location_score'] = _______   # ¿combinar distancia, crimen, escuela?

print("💡 PISTAS AVANZADAS:")
print("- Piensa en outliers: ¿qué hace única a una propiedad?")
print("- Considera interacciones: ¿cómo se relacionan múltiples variables?")
print("- Usa conocimiento del dominio: ¿qué buscan los compradores?")

# 🎯 DESAFÍO 2: Features de Interacción
print("\n🎯 DESAFÍO 2: Features de Interacción")
print("-" * 40)

# TODO: Crea features que capturen interacciones entre variables
# PISTAS:
# - ¿Cómo interactúa el precio por m² con la antigüedad?
# - ¿Qué pasa cuando una propiedad es nueva Y tiene muchas habitaciones?
# - ¿Cómo afecta la combinación de distancia y rating de escuela?

# Tu código aquí:
# df_enhanced['price_age_interaction'] = _______
# df_enhanced['new_large_property'] = _______
# df_enhanced['distance_school_interaction'] = _______

# 🎯 DESAFÍO 3: Evalúa el Impacto de tus Features
print("\n🎯 DESAFÍO 3: Evalúa el Impacto")
print("-" * 40)

# TODO: Analiza qué tan útiles son tus nuevas features
# 1. Calcula correlaciones con el precio
# 2. Visualiza distribuciones
# 3. Compara con features originales

# Tu código aquí:
# nuevas_features = ['space_efficiency', 'crowded_property', 'location_score']
# correlaciones = df_enhanced[nuevas_features + ['price']].corr()['price']
# print("Correlaciones de mis features:")
# print(correlaciones)

# Visualizar distribuciones
# fig, axes = plt.subplots(1, 3, figsize=(15, 5))
# for i, feature in enumerate(nuevas_features):
#     df_enhanced[feature].hist(bins=30, ax=axes[i], alpha=0.7)
#     axes[i].set_title(f'Distribución de {feature}')
# plt.tight_layout()
# plt.show()

# 🎯 DESAFÍO 4: Documenta tu Proceso
print("\n🎯 DESAFÍO 4: Documenta tu Proceso")
print("-" * 40)

# TODO: Explica el razonamiento detrás de cada feature
print("📝 REFLEXIÓN OBLIGATORIA:")
print("1. ¿Qué features creaste y cuál fue tu razonamiento?")
print("2. ¿Qué patrones esperabas encontrar?")
print("3. ¿Los resultados coinciden con tus expectativas?")
print("4. ¿Cuál fue tu feature más creativa y por qué?")
print("5. ¿Qué otras features podrías crear con más tiempo?")

# TODO: Escribe tus respuestas aquí:
# print("Mis respuestas:")
# print("1. ...")
# print("2. ...")
# print("3. ...")
# print("4. ...")
# print("5. ...")

💡 PISTAS PARA INVESTIGACIÓN:

  • 🏠 Dominio inmobiliario: Piensa en qué factores realmente afectan el precio de una casa
  • 📊 Visualización: Usa plt.scatter() o plt.hist() para explorar tus features
  • 🔍 Correlación: Usa .corr() para medir la relación con el precio
  • 📚 Documentación: Pandas Feature Engineering
  • 🎯 Creatividad: No hay respuestas "correctas", solo diferentes perspectivas


🧪 Paso 7: Dataset de Prueba - Ames Housing

# === PROBAR TUS SKILLS CON DATOS REALES ===

print("🧪 PROBANDO CON DATOS REALES - AMES HOUSING")
print("=" * 60)

# Cargar dataset real de Ames Housing (más pequeño para práctica)
# Este dataset tiene características similares pero datos reales
ames_data = {
    'SalePrice': [215000, 105000, 172000, 244000, 189900],
    'GrLivArea': [1710, 856, 1262, 1710, 1362],
    'BedroomAbvGr': [3, 3, 3, 3, 3],
    'FullBath': [2, 1, 2, 2, 1],
    'YearBuilt': [2003, 1961, 1958, 2000, 1992],
    'GarageCars': [2, 1, 2, 2, 1],
    'LotArea': [8450, 9600, 11250, 9550, 10140],
    'Neighborhood': ['CollgCr', 'Veenker', 'Crawfor', 'NoRidge', 'Mitchel']
}

ames_df = pd.DataFrame(ames_data)

print("🏠 DATASET REAL: Ames Housing (muestra)")
print(f"   📊 Forma: {ames_df.shape}")
print(f"   📋 Columnas: {list(ames_df.columns)}")
print("\n🔍 Primeras filas:")
print(ames_df.head())

# TODO: Aplica las técnicas que aprendiste a este dataset real
# 1. Crea las mismas features que hiciste antes
# 2. ¿Qué features adicionales podrías crear?
# 3. ¿Cómo se comparan los resultados?

# Tu código aquí:
# ames_df['price_per_sqft'] = _______
# ames_df['property_age'] = _______
# ames_df['space_efficiency'] = _______

print("\n💡 DESAFÍO:")
print("- ¿Qué features funcionan mejor con datos reales?")
print("- ¿Hay diferencias entre datos sintéticos y reales?")
print("- ¿Qué nuevas features podrías crear con 'Neighborhood'?")

🎯 Resumen y Reflexión Final

Lo que aprendiste:

  1. Crear features derivadas usando operaciones matemáticas básicas
  2. Evaluar distribución de nuevas variables con estadísticas descriptivas
  3. Calcular importancia usando mutual information y random forest
  4. Visualizar patrones en los datos con histogramas y gráficos
  5. Documentar proceso de creación de features
  6. Aplicar técnicas a datasets reales

🤔 Preguntas para reflexionar:

  1. ¿Cuáles fueron las features más importantes según tu análisis?
  2. ¿Qué sorpresas encontraste en los datos?
  3. ¿Cómo podrías mejorar el proceso de feature engineering?
  4. ¿Qué otras técnicas de feature engineering conoces?
  5. ¿Qué diferencias notaste entre datos sintéticos y reales?