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)