Saltar a contenido

Práctoca 7: Feature Engineering con Pandas

🏠 Parte 1: Setup y Carga de Datos

# 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('seaborn-v0_8')  # establecer estilo visual (ej: 'default', 'seaborn', 'classic')
sns.set_palette("husl")  # definir paleta de colores (ej: 'husl', 'Set1', 'viridis')
plt.rcParams['figure.figsize'] = (12, 8)

print("✅ Entorno configurado correctamente")
✅ Entorno configurado correctamente

🏠 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.DataFrame.from_dict(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.head())  # método para mostrar las primeras filas del DataFrame

# 3. Estadísticas básicas
print("\n📊 ESTADÍSTICAS BÁSICAS:")
print(df.describe())  # método que calcula estadísticas descriptivas
🏠 DATASET: Viviendas Sintéticas
   📊 Forma: (1000, 10)
   📋 Columnas: ['price', 'sqft', 'bedrooms', 'bathrooms', 'year_built', 'garage_spaces', 'lot_size', 'distance_to_city', 'school_rating', 'crime_rate']

🔍 Primeras 5 filas:
           price        sqft  bedrooms  bathrooms  year_built  garage_spaces  \
0  224835.707651  161.980663         3          1        2001              3   
1  193086.784941  147.739010         3          3        2011              3   
2  232384.426905  121.788911         3          1        1995              3   
3  276151.492820  100.591897         4          3        1997              0   
4  188292.331264  140.946699         5          1        1984              1   

      lot_size  distance_to_city  school_rating  crime_rate  
0  7436.395239          5.131093       4.402225   12.718119  
1  8196.996696          0.188184       1.926515   39.855922  
2  8308.096155         20.992112       7.471205   17.015264  
3  7915.263179         27.394727       5.534999   25.472050  
4  8133.185144          9.121570       7.378891   26.324617  

📊 ESTADÍSTICAS BÁSICAS:
               price         sqft     bedrooms    bathrooms   year_built  \
count    1000.000000  1000.000000  1000.000000  1000.000000  1000.000000   
mean   200966.602791   122.125087     2.955000     2.039000  2001.671000   
std     48960.796909    29.923631     1.431441     0.817403    12.477758   
min     37936.632997    31.788341     1.000000     1.000000  1980.000000   
25%    167620.484727   101.812749     2.000000     1.000000  1991.750000   
50%    201265.030612   121.892314     3.000000     2.000000  2002.000000   
75%    232397.193774   141.866465     4.000000     3.000000  2012.000000   
max    392636.574533   215.793227     5.000000     3.000000  2023.000000   

       garage_spaces      lot_size  distance_to_city  school_rating  \
count    1000.000000   1000.000000       1000.000000    1000.000000   
mean        1.476000   7964.898433         14.905920       5.317255   
std         1.112952   1962.074655          7.818738       2.563481   
min         0.000000   1449.978732          0.024233       1.002186   
25%         1.000000   6677.962485          9.142445       3.094171   
50%         1.000000   7937.432524         14.685961       5.110074   
75%         2.000000   9246.216116         20.139549       7.468377   
max         3.000000  14029.423419         40.921113       9.964115   

        crime_rate  
count  1000.000000  
mean     49.314724  
std      28.008117  
min       0.005283  
25%      26.411477  
50%      49.367810  
75%      72.735264  
max      99.861732

⚙️ 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")

# Ratios adicionales
df_enhanced['bed_bath_ratio'] = df_enhanced['bedrooms'] / df_enhanced['bathrooms']
df_enhanced['density'] = df_enhanced['sqft'] / df_enhanced['lot_size']
df_enhanced['price_per_bedroom'] = df_enhanced['price'] / df_enhanced['bedrooms']
df_enhanced['distance_school_ratio'] = df_enhanced['distance_to_city'] / (df_enhanced['school_rating'] + 1)

# 2. VARIABLES TEMPORALES
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")

# Categoría de antigüedad
df_enhanced['age_category'] = pd.cut(df_enhanced['property_age'], bins=[0, 10, 30, 100], labels=['Nuevo', 'Moderno', 'Antiguo'])
# Es propiedad nueva (binario)
df_enhanced['is_new_property'] = (df_enhanced['property_age'] < 5).astype(int)
# Década de construcción
df_enhanced['decade_built'] = (df_enhanced['year_built'] // 10) * 10

# 3. TRANSFORMACIONES MATEMÁTICAS
print("\n🧮 3. APLICANDO TRANSFORMACIONES MATEMÁTICAS")

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

# Otras transformaciones matemáticas
df_enhanced['sqrt_sqft'] = np.sqrt(df_enhanced['sqft'])
df_enhanced['sqft_squared'] = df_enhanced['sqft'] ** 2
df_enhanced['log_sqft'] = np.log(df_enhanced['sqft'])
df_enhanced['log_lot_size'] = np.log(df_enhanced['lot_size'])

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

# Score de lujo
df_enhanced['luxury_score'] = (df_enhanced['price_per_sqft'] * df_enhanced['sqft'] * (df_enhanced['garage_spaces'] + 1))
# Score de ubicación
df_enhanced['location_score'] = (1 / (df_enhanced['distance_to_city'] + 1)) * df_enhanced['school_rating'] / (df_enhanced['crime_rate'] + 1)
# Score de eficiencia
df_enhanced['efficiency_score'] = df_enhanced['sqft_per_bedroom'] + df_enhanced['price_per_sqft']
# Indicador de calidad
df_enhanced['quality_indicator'] = (1 / (df_enhanced['property_age'] + 1)) * (df_enhanced['garage_spaces'] + 1) * df_enhanced['school_rating']

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]}")
⚙️ CREANDO FEATURES DERIVADAS
--------------------------------------------------
🔢 1. CREANDO RATIOS Y PROPORCIONES
✅ price_per_sqft: Precio por pie cuadrado
✅ sqft_per_bedroom: Superficie por habitación

📅 2. CREANDO VARIABLES TEMPORALES
✅ property_age: Antigüedad de la propiedad

🧮 3. APLICANDO TRANSFORMACIONES MATEMÁTICAS
✅ log_price: Logaritmo del precio

🎯 4. CREANDO FEATURES COMPUESTAS

📊 RESUMEN DE FEATURES CREADAS:
Dataset original: 10 columnas
Dataset con features: 29 columnas
Features creadas: 19

📊 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', 'bed_bath_ratio', 
                'density', 'property_age', 'log_price', 'sqrt_sqft', 'sqft_squared']

print("📈 Estadísticas de nuevas features:")
print(df_enhanced[new_features].describe().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}]")
📊 ANÁLISIS DE DISTRIBUCIÓN DE FEATURES
--------------------------------------------------
🔍 1. DISTRIBUCIÓN DE FEATURES DERIVADAS
📈 Estadísticas de nuevas features:
       price_per_sqft  sqft_per_bedroom  bed_bath_ratio  density  \
count         1000.00           1000.00         1000.00  1000.00   
mean          1776.39             57.15            1.77     0.02   
std            726.68             39.58            1.25     0.01   
min            278.61              6.36            0.33     0.00   
25%           1287.19             28.99            1.00     0.01   
50%           1646.13             41.55            1.50     0.02   
75%           2112.68             74.27            2.50     0.02   
max           5521.03            199.33            5.00     0.08   

       property_age  log_price  sqrt_sqft  sqft_squared  
count       1000.00    1000.00    1000.00       1000.00  
mean          22.33      12.18      10.96      15809.07  
std           12.48       0.26       1.40       7376.84  
min            1.00      10.54       5.64       1010.50  
25%           12.00      12.03      10.09      10365.84  
50%           22.00      12.21      11.04      14857.74  
75%           32.25      12.36      11.91      20126.10  
max           44.00      12.88      14.69      46566.72  

📊 2. VISUALIZANDO DISTRIBUCIONES
🚨 3. DETECCIÓN DE OUTLIERS
🔍 price_per_sqft:
   📊 Total outliers: 37 (3.7%)
   📏 Rango normal: [48.94, 3350.93]
🔍 sqft_per_bedroom:
   📊 Total outliers: 45 (4.5%)
   📏 Rango normal: [-38.94, 142.20]
🔍 property_age:
   📊 Total outliers: 0 (0.0%)
   📏 Rango normal: [-18.38, 62.62]

🎯 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', 'bed_bath_ratio',
                   '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))
🎯 EVALUACIÓN DE IMPORTANCIA DE FEATURES
--------------------------------------------------
🔧 1. PREPARANDO DATOS PARA EVALUACIÓN
📊 Features evaluadas: 17
📊 Muestras: 1000

📈 2. CALCULANDO MUTUAL INFORMATION
🔝 Top 10 features por Mutual Information:
             feature  mutual_info
14         log_price       5.4759
9     price_per_sqft       0.4459
12           density       0.0303
1           bedrooms       0.0189
15         sqrt_sqft       0.0092
0               sqft       0.0078
16      sqft_squared       0.0065
10  sqft_per_bedroom       0.0060
2          bathrooms       0.0000
8         crime_rate       0.0000

🌲 3. CALCULANDO IMPORTANCIA CON RANDOM FOREST
🔝 Top 10 features por Random Forest:
             feature  importance
14         log_price      0.9985
9     price_per_sqft      0.0005
7      school_rating      0.0002
10  sqft_per_bedroom      0.0001
0               sqft      0.0001
15         sqrt_sqft      0.0001
11    bed_bath_ratio      0.0001
16      sqft_squared      0.0001
12           density      0.0001
6   distance_to_city      0.0001

📊 4. VISUALIZACIÓN COMPARATIVA
🔗 5. CORRELACIÓN CON PRECIO
🔝 Top 10 correlaciones con precio:
log_price         0.9789
price_per_sqft    0.5968
crime_rate        0.0706
sqft             -0.0404
sqft_squared     -0.0399
sqrt_sqft        -0.0377
garage_spaces    -0.0328
school_rating     0.0326
density          -0.0312
property_age      0.0311
Name: price, dtype: float64

🧪 Paso 6: 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())

# 🎯 APLICANDO LAS TÉCNICAS APRENDIDAS AL DATASET REAL
print("\n🎯 APLICANDO FEATURE ENGINEERING AL DATASET REAL")
print("-" * 50)

# 1. FEATURES BÁSICAS - Aplicando las mismas técnicas del dataset sintético
print("🔢 1. CREANDO FEATURES BÁSICAS")

# Feature 1: Precio por pie cuadrado (equivalente a price_per_sqft)
ames_df['price_per_sqft'] = ames_df['SalePrice'] / ames_df['GrLivArea']
print("✅ price_per_sqft: Precio por pie cuadrado de área habitable")

# Feature 2: Antigüedad de la propiedad (equivalente a property_age)
current_year = 2024
ames_df['property_age'] = current_year - ames_df['YearBuilt']
print("✅ property_age: Antigüedad de la propiedad")

# Feature 3: Eficiencia del espacio (surface efficiency - superficie vs lote)
ames_df['space_efficiency'] = ames_df['GrLivArea'] / ames_df['LotArea']
print("✅ space_efficiency: Aprovechamiento del lote (área habitable/área del lote)")

# 2. FEATURES ADICIONALES ESPECÍFICAS PARA AMES HOUSING
print("\n🏗️ 2. FEATURES ESPECÍFICAS PARA AMES HOUSING")

# Feature 4: Área habitable por habitación
ames_df['sqft_per_bedroom'] = ames_df['GrLivArea'] / ames_df['BedroomAbvGr']
print("✅ sqft_per_bedroom: Superficie por habitación")

# Feature 5: Relación habitaciones/baños
ames_df['bed_bath_ratio'] = ames_df['BedroomAbvGr'] / ames_df['FullBath']
print("✅ bed_bath_ratio: Relación habitaciones por baño")

# Feature 6: Score de comodidad (basado en garaje y baños)
ames_df['comfort_score'] = (ames_df['GarageCars'] + 1) * (ames_df['FullBath'] + 1)
print("✅ comfort_score: Índice de comodidad (garaje × baños)")

# Feature 7: Densidad habitacional
ames_df['room_density'] = ames_df['BedroomAbvGr'] / ames_df['GrLivArea'] * 1000  # habitaciones por 1000 sqft
print("✅ room_density: Densidad de habitaciones por 1000 sqft")

# 3. FEATURES CATEGÓRICAS - Trabajando con 'Neighborhood'
print("\n🏘️ 3. FEATURES CATEGÓRICAS - NEIGHBORHOOD")

# Feature 8: Codificación de vecindario por precio promedio (target encoding simulado)
neighborhood_avg_price = ames_df.groupby('Neighborhood')['SalePrice'].mean()
ames_df['neighborhood_price_level'] = ames_df['Neighborhood'].map(neighborhood_avg_price)
print("✅ neighborhood_price_level: Nivel de precio por vecindario")

# Feature 9: Indicador de vecindario premium (basado en precio)
price_threshold = ames_df['SalePrice'].median()
ames_df['is_premium_neighborhood'] = (ames_df['neighborhood_price_level'] > price_threshold).astype(int)
print("✅ is_premium_neighborhood: Indicador de vecindario premium")

# Feature 10: Longitud del nombre del vecindario (puede indicar exclusividad)
ames_df['neighborhood_name_length'] = ames_df['Neighborhood'].str.len()
print("✅ neighborhood_name_length: Longitud del nombre del vecindario")

# 4. TRANSFORMACIONES MATEMÁTICAS
print("\n🧮 4. TRANSFORMACIONES MATEMÁTICAS")

# Feature 11: Log del precio (para normalizar distribución)
ames_df['log_price'] = np.log(ames_df['SalePrice'])
print("✅ log_price: Logaritmo natural del precio")

# Feature 12: Raíz cuadrada del área (otra transformación común)
ames_df['sqrt_area'] = np.sqrt(ames_df['GrLivArea'])
print("✅ sqrt_area: Raíz cuadrada del área habitable")

# 5. MOSTRAR RESULTADOS
print(f"\n📊 RESUMEN FINAL:")
print(f"Dataset original: {len(ames_data)} columnas")
print(f"Dataset con features: {ames_df.shape[1]} columnas")
print(f"Features creadas: {ames_df.shape[1] - len(ames_data)}")

print("\n🔍 DATASET FINAL CON NUEVAS FEATURES:")
print(ames_df.round(2))

# 6. ANÁLISIS COMPARATIVO
print("\n📈 ANÁLISIS DE CORRELACIONES CON PRECIO:")
numeric_features = ['price_per_sqft', 'property_age', 'space_efficiency', 'sqft_per_bedroom', 
                   'bed_bath_ratio', 'comfort_score', 'room_density', 'neighborhood_price_level']

correlations = ames_df[numeric_features + ['SalePrice']].corr()['SalePrice'].drop('SalePrice')
correlations_sorted = correlations.sort_values(key=abs, ascending=False)
print(correlations_sorted.round(4))

print("\n💡 INSIGHTS DEL DATASET REAL:")
print("- ¿Qué features funcionan mejor con datos reales?")
print(f"  🔹 Feature más correlacionada: {correlations_sorted.index[0]} ({correlations_sorted.iloc[0]:.3f})")
print(f"  🔹 Feature menos correlacionada: {correlations_sorted.index[-1]} ({correlations_sorted.iloc[-1]:.3f})")

print("\n- ¿Hay diferencias entre datos sintéticos y reales?")
print("  🔹 Los datos reales muestran patrones más específicos")
print("  🔹 Las correlaciones pueden ser más fuertes o débiles")
print("  🔹 Los vecindarios aportan información valiosa")

print("\n- ¿Qué nuevas features podrías crear con 'Neighborhood'?")
print("  🔹 Target encoding por vecindario (ya implementado)")
print("  🔹 Clustering de vecindarios similares")
print("  🔹 Distancia a vecindarios premium")
print("  🔹 Indicadores de tipo de vecindario (urbano/suburbano)")

print("\n🎯 PRÓXIMOS PASOS SUGERIDOS:")
print("- Obtener más datos para validar patrones")
print("- Probar feature selection automática")
print("- Crear features de interacción entre variables")
print("- Implementar feature scaling para modelos ML")
🧪 PROBANDO CON DATOS REALES - AMES HOUSING
============================================================
🏠 DATASET REAL: Ames Housing (muestra)
   📊 Forma: (5, 8)
   📋 Columnas: ['SalePrice', 'GrLivArea', 'BedroomAbvGr', 'FullBath', 'YearBuilt', 'GarageCars', 'LotArea', 'Neighborhood']

🔍 Primeras filas:
   SalePrice  GrLivArea  BedroomAbvGr  FullBath  YearBuilt  GarageCars  \
0     215000       1710             3         2       2003           2   
1     105000        856             3         1       1961           1   
2     172000       1262             3         2       1958           2   
3     244000       1710             3         2       2000           2   
4     189900       1362             3         1       1992           1   

   LotArea Neighborhood  
0     8450      CollgCr  
1     9600      Veenker  
2    11250      Crawfor  
3     9550      NoRidge  
4    10140      Mitchel  

🎯 APLICANDO FEATURE ENGINEERING AL DATASET REAL
--------------------------------------------------
🔢 1. CREANDO FEATURES BÁSICAS
✅ price_per_sqft: Precio por pie cuadrado de área habitable
✅ property_age: Antigüedad de la propiedad
✅ space_efficiency: Aprovechamiento del lote (área habitable/área del lote)

🏗️ 2. FEATURES ESPECÍFICAS PARA AMES HOUSING
✅ sqft_per_bedroom: Superficie por habitación
✅ bed_bath_ratio: Relación habitaciones por baño
✅ comfort_score: Índice de comodidad (garaje × baños)
✅ room_density: Densidad de habitaciones por 1000 sqft

🏘️ 3. FEATURES CATEGÓRICAS - NEIGHBORHOOD
✅ neighborhood_price_level: Nivel de precio por vecindario
✅ is_premium_neighborhood: Indicador de vecindario premium
✅ neighborhood_name_length: Longitud del nombre del vecindario

🧮 4. TRANSFORMACIONES MATEMÁTICAS
✅ log_price: Logaritmo natural del precio
✅ sqrt_area: Raíz cuadrada del área habitable

📊 RESUMEN FINAL:
Dataset original: 8 columnas
Dataset con features: 20 columnas
Features creadas: 12

🔍 DATASET FINAL CON NUEVAS FEATURES:
   SalePrice  GrLivArea  BedroomAbvGr  FullBath  YearBuilt  GarageCars  \
0     215000       1710             3         2       2003           2   
1     105000        856             3         1       1961           1   
2     172000       1262             3         2       1958           2   
3     244000       1710             3         2       2000           2   
4     189900       1362             3         1       1992           1   

   LotArea Neighborhood  price_per_sqft  property_age  space_efficiency  \
0     8450      CollgCr          125.73            21              0.20   
1     9600      Veenker          122.66            63              0.09   
2    11250      Crawfor          136.29            66              0.11   
3     9550      NoRidge          142.69            24              0.18   
4    10140      Mitchel          139.43            32              0.13   

   sqft_per_bedroom  bed_bath_ratio  comfort_score  room_density  \
0            570.00             1.5              9          1.75   
1            285.33             3.0              4          3.50   
2            420.67             1.5              9          2.38   
3            570.00             1.5              9          1.75   
4            454.00             3.0              4          2.20   

   neighborhood_price_level  is_premium_neighborhood  \
0                  215000.0                        1   
1                  105000.0                        0   
2                  172000.0                        0   
3                  244000.0                        1   
4                  189900.0                        0   

   neighborhood_name_length  log_price  sqrt_area  
0                         7      12.28      41.35  
1                         7      11.56      29.26  
2                         7      12.06      35.52  
3                         7      12.40      41.35  
4                         7      12.15      36.91  

📈 ANÁLISIS DE CORRELACIONES CON PRECIO:
neighborhood_price_level    1.0000
sqft_per_bedroom            0.9755
room_density               -0.9738
space_efficiency            0.8750
property_age               -0.8220
comfort_score               0.6576
bed_bath_ratio             -0.6576
price_per_sqft              0.6389
Name: SalePrice, dtype: float64

💡 INSIGHTS DEL DATASET REAL:
- ¿Qué features funcionan mejor con datos reales?
  🔹 Feature más correlacionada: neighborhood_price_level (1.000)
  🔹 Feature menos correlacionada: price_per_sqft (0.639)

- ¿Hay diferencias entre datos sintéticos y reales?
  🔹 Los datos reales muestran patrones más específicos
  🔹 Las correlaciones pueden ser más fuertes o débiles
  🔹 Los vecindarios aportan información valiosa

- ¿Qué nuevas features podrías crear con 'Neighborhood'?
  🔹 Target encoding por vecindario (ya implementado)
  🔹 Clustering de vecindarios similares
  🔹 Distancia a vecindarios premium
  🔹 Indicadores de tipo de vecindario (urbano/suburbano)

🎯 PRÓXIMOS PASOS SUGERIDOS:
- Obtener más datos para validar patrones
- Probar feature selection automática
- Crear features de interacción entre variables
- Implementar feature scaling para modelos ML