Regresión lineal multivariable

import numpy as np # Operaciones numéricas y arrays
import pandas as pd # Manejo de tablas de datos (DataFrames)
import matplotlib.pyplot as plt # Gráficas
from sklearn.model_selection import train_test_split # Dividir datos en tren/test
from sklearn.linear_model import LinearRegression # Nuestro modelo
from sklearn.preprocessing import StandardScaler # Normalizar datos
from sklearn.metrics import mean_squared_error, r2_score

np.random.seed(42)  # Fijamos semilla para reproducibilidad
n = 200         # Número de pisos en nuestro dataset

# Cada columna representa una característica del piso
data = pd.DataFrame({
    'metros2'     : np.random.randint(30, 150, n),   # Entre 30 y 150 m²
    'habitaciones': np.random.randint(1,  6, n),   # Entre 1 y 5 habitaciones
    'planta'      : np.random.randint(0,  10, n),   # Entre planta 0 y 9
    'antiguedad'  : np.random.randint(0,  50, n),   # Años de antigüedad
})

# Fórmula ficticia para el precio (así sabemos cuál es la verdad)
# precio = 50k + 2000×m² + 8000×hab + 500×planta - 300×antigüedad + ruido
ruido = np.random.normal(0, 10000, n)  # Variación aleatoria realista
data['precio'] = (
    50000
    + 2000 * data['metros2']
    + 8000 * data['habitaciones']
    +  500 * data['planta']
    -  300 * data['antiguedad']
    + ruido
)

print(data.head())  # Ver las primeras filas

# Aquí tenemos un dataset de 200 pisos con sus precios aleatorio pero siguiendo
# una fórmula que podemos predecir

# ─── Separar variables de entrada (X) y salida (y) ─────────────
X = data.drop(columns=['precio'])  # Todo excepto el precio
y = data['precio']          # Solo el precio (lo que queremos predecir)

# ─── Dividir en entrenamiento (80%) y test (20%) ─────────────
X_train, X_test, y_train, y_test = train_test_split(
    X, y,                   # Datos de entrada y salida
    test_size=0.2,          # 20% para test
    random_state=42          # Semilla para resultados reproducibles
)

print(f'Datos de entrenamiento: {len(X_train)} pisos')
print(f'Datos de test:          {len(X_test)} pisos')
# → Datos de entrenamiento: 160 pisos
# → Datos de test:          40 pisos

# ─── Normalizar los datos ──────────────────────────────────────
scaler = StandardScaler()  # Creamos el normalizador

# fit_transform: aprende la media y std DE LOS DATOS DE ENTRENAMIENTO
# y luego transforma. NUNCA uses fit en los datos de test.
X_train_scaled = scaler.fit_transform(X_train)

# transform solo: aplica la misma transformación al test
# (sin volver a aprender — eso sería trampa)
X_test_scaled  = scaler.transform(X_test)

modelo = LinearRegression()  # Instanciar el modelo

# .fit() es donde ocurre el 'aprendizaje':
# el modelo encuentra los mejores coeficientes β
modelo.fit(X_train_scaled, y_train)

# Ver los coeficientes aprendidos
print('Intercepto (β₀):', modelo.intercept_)
for nombre, coef in zip(X.columns, modelo.coef_):
    print(f'  {nombre:<15}: {coef:>10.2f}')

y_pred = modelo.predict(X_test_scaled)  # Predicciones

# R² (R-cuadrado): qué porcentaje de la variación explica el modelo
# Va de 0 a 1. Cuanto más cerca de 1, mejor.
r2  = r2_score(y_test, y_pred)

# RMSE: error promedio en las mismas unidades que y (euros)
# Fácil de interpretar: 'me equivoco en promedio X euros'
mse  = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)

print(f'R²   = {r2:.4f}')  # p.ej. R² = 0.9812
print(f'RMSE = {rmse:.2f} €')  # p.ej. RMSE = 9.847,23 €
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# --- Gráfico 1: Predicciones vs Valores reales ---
axes[0].scatter(y_test, y_pred, alpha=0.5, color='steelblue')
# La línea perfecta (y = x) sirve como referencia visual
axes[0].plot([y_test.min(), y_test.max()],
         [y_test.min(), y_test.max()], 'r--', label='Predicción perfecta')
axes[0].set_xlabel('Precio real (€)')
axes[0].set_ylabel('Precio predicho (€)')
axes[0].set_title(f'Predicciones vs. Valores reales\nR² = {r2:.4f}')
axes[0].legend()

# --- Gráfico 2: Residuos (errores del modelo) ---
residuos = y_test - y_pred  # Diferencia entre real y predicho
axes[1].scatter(y_pred, residuos, alpha=0.5, color='coral')
axes[1].axhline(0, color='black', linestyle='--')  # Línea en 0
axes[1].set_xlabel('Precio predicho (€)')
axes[1].set_ylabel('Residuo (real - predicho)')
axes[1].set_title('Análisis de Residuos\n(idealmente dispersos sin patrón)')

plt.tight_layout()  # Ajusta márgenes automáticamente
plt.savefig('resultados_regresion.png', dpi=150, bbox_inches='tight')
plt.show()

# ─── Predecir el precio de un piso nuevo ───────────────────────

# Creamos un DataFrame con las características del piso nuevo
# ¡Importante: usar los mismos nombres de columna que en el entrenamiento!
piso_nuevo = pd.DataFrame([{
    'metros2'     : 90,   # 90 metros cuadrados
    'habitaciones': 3,   # 3 habitaciones
    'planta'      : 5,   # Planta 5
    'antiguedad'  : 10,  # 10 años de antigüedad
}])

# Normalizar usando el MISMO scaler que entrenamos (no fit_transform)
piso_nuevo_scaled = scaler.transform(piso_nuevo)

# Predecir
precio_estimado = modelo.predict(piso_nuevo_scaled)[0]
print(f'Precio estimado: {precio_estimado:,.0f} €')
# → Precio estimado: 250.500 €  (resultado aproximado)

Publicado por

Juan Pablo Fuentes

Formador de programación y bases de datos