Passiamo dalla teoria alla pratica con un esempio completo.
🚫 COPIA BLOCCATA
# REGRESSIONE LINEARE COMPLETA - CASO PRATICO: PREZZI CASE
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.preprocessing import StandardScaler
import scipy.stats as stats
print("🏠 REGRESSIONE LINEARE - PREZZI CASE")
print("=" * 70)
# 1. CREAZIONE DATASET SINTETICO (simuliamo dati reali)
np.random.seed(42)
n_samples = 500
# Features realistiche per case
metratura = np.random.normal(120, 40, n_samples) # Metratura in m²
metratura = np.clip(metratura, 50, 250) # Min 50m², max 250m²
num_camere = np.random.poisson(3, n_samples) # Numero camere (media 3)
num_camere = np.clip(num_camere, 1, 6) # Min 1, max 6 camere
eta_casa = np.random.exponential(20, n_samples) # Età casa in anni
eta_casa = np.clip(eta_casa, 0, 60) # Max 60 anni
distanza_centro = np.random.exponential(5, n_samples) # Distanza centro in km
distanza_centro = np.clip(distanza_centro, 1, 20)
# Prezzo base (€/m²)
prezzo_base = 3000
# Formula prezzo con relazioni realistiche
prezzo = (
prezzo_base * metratura * 0.8 + # Metratura principale
50000 * num_camere + # Ogni camera aggiunge valore
-2000 * eta_casa + # Case vecchie valgono meno
-10000 * distanza_centro + # Più lontano = meno valore
np.random.normal(0, 50000, n_samples) # Rumore/variazione
)
# Crea DataFrame
df_case = pd.DataFrame({
'metratura': metratura,
'num_camere': num_camere,
'eta_casa': eta_casa,
'distanza_centro': distanza_centro,
'prezzo': prezzo
})
# Assicuriamoci prezzi positivi
df_case['prezzo'] = np.clip(df_case['prezzo'], 100000, 1000000)
print("\n1. 📊 DATASET CASE (prime 10 righe):")
print(df_case.head(10))
print(f"\n📈 DIMENSIONI: {df_case.shape}")
print(f"💰 PREZZO MEDIO: €{df_case['prezzo'].mean():,.0f}")
print(f"📏 METRATURA MEDIA: {df_case['metratura'].mean():.0f} m²")
# 2. ANALISI ESPLORATIVA (EDA)
print("\n\n2. 🔍 ANALISI ESPLORATIVA DEI DATI")
print("-" * 40)
print("\n📋 STATISTICHE DESCRITTIVE:")
print(df_case.describe())
print("\n📊 MATRICE DI CORRELAZIONE:")
correlation_matrix = df_case.corr()
print(correlation_matrix.round(3))
print("\n🎯 CORRELAZIONI CON PREZZO:")
prezzo_corr = correlation_matrix['prezzo'].sort_values(ascending=False)
print(prezzo_corr)
print("\n📈 SU GOOGLE COLAB, SCOMMENTA PER VEDERE I GRAFICI:")
"""
# Pairplot per vedere tutte le relazioni
sns.pairplot(df_case, diag_kind='kde')
plt.suptitle('Analisi Multivariata - Dataset Case', y=1.02)
plt.show()
# Scatter plot prezzo vs metratura
plt.figure(figsize=(10, 6))
sns.scatterplot(x='metratura', y='prezzo', data=df_case, alpha=0.6)
plt.title('Prezzo vs Metratura')
plt.xlabel('Metratura (m²)')
plt.ylabel('Prezzo (€)')
plt.grid(True, alpha=0.3)
plt.show()
# Matrice di correlazione heatmap
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', center=0, fmt='.2f')
plt.title('Matrice di Correlazione')
plt.show()
"""
# 3. PREPARAZIONE DATI PER ML
print("\n\n3. 🛠️ PREPARAZIONE DATI PER REGRESSIONE")
print("-" * 40)
# Separazione features (X) e target (y)
X = df_case[['metratura', 'num_camere', 'eta_casa', 'distanza_centro']]
y = df_case['prezzo']
print(f"Features shape: {X.shape}")
print(f"Target shape: {y.shape}")
# Standardizzazione delle features
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print("✅ Features standardizzate (media=0, dev std=1)")
# Train/Test split
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y,
test_size=0.2, # 20% test
random_state=42
)
print(f"\n🎯 SPLIT DATI:")
print(f"Training set: {X_train.shape[0]} case ({X_train.shape[0]/len(X)*100:.0f}%)")
print(f"Test set: {X_test.shape[0]} case ({X_test.shape[0]/len(X)*100:.0f}%)")
# 4. TRAINING MODELLO DI REGRESSIONE LINEARE
print("\n\n4. 🤖 TRAINING MODELLO REGRESSIONE LINEARE")
print("-" * 40)
# Crea e addestra modello
model = LinearRegression()
model.fit(X_train, y_train)
print("✅ Modello addestrato!")
print(f"\n🎯 COEFFICIENTI DEL MODELLO:")
# Mostra coefficienti in modo leggibile
features = ['metratura', 'num_camere', 'eta_casa', 'distanza_centro']
for feature, coef in zip(features, model.coef_):
print(f" {feature:20} : {coef:10,.0f} €")
print(f"\n📐 INTERCETTA (β₀): {model.intercept_:,.0f} €")
# Interpretazione coefficienti
print("\n📖 INTERPRETAZIONE COEFFICIENTI:")
print("Per ogni aumento di 1 deviazione standard...")
print(f"• Metratura: +{model.coef_[0]:,.0f} € al prezzo")
print(f"• Numero camere: +{model.coef_[1]:,.0f} € al prezzo")
print(f"• Età casa: {model.coef_[2]:,.0f} € al prezzo (negativo = svaluta)")
print(f"• Distanza centro: {model.coef_[3]:,.0f} € al prezzo (negativo = svaluta)")
# 5. PREDIZIONI E VALUTAZIONE
print("\n\n5. 🔮 PREDIZIONI E VALUTAZIONE MODELLO")
print("-" * 40)
# Predizioni sul test set
y_pred = model.predict(X_test)
# Calcola tutte le metriche
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
mae = mean_absolute_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"📊 PERFORMANCE SUL TEST SET:")
print(f"🎯 Mean Squared Error (MSE): {mse:,.0f}")
print(f"🎯 Root Mean Squared Error (RMSE): {rmse:,.0f} €")
print(f"🎯 Mean Absolute Error (MAE): {mae:,.0f} €")
print(f"🎯 R² Score (Coefficiente di Determinazione): {r2:.3f}")
# Interpretazione RMSE e MAE
prezzo_medio = y_test.mean()
print(f"\n📈 INTERPRETAZIONE:")
print(f"Prezzo medio test set: €{prezzo_medio:,.0f}")
print(f"RMSE/Prezzo medio: {(rmse/prezzo_medio*100):.1f}%")
print(f"MAE/Prezzo medio: {(mae/prezzo_medio*100):.1f}%")
# Interpretazione R²
print(f"\n📊 INTERPRETAZIONE R²:")
print(f"Il modello spiega il {r2*100:.1f}% della varianza nei prezzi")
if r2 > 0.7:
print("✅ Ottimo fit del modello!")
elif r2 > 0.5:
print("⚠️ Fit moderato - potrebbero servire più features")
else:
print("❌ Fit scarso - il modello non cattura bene la relazione")
# 6. ANALISI DEI RESIDUI
print("\n\n6. 📉 ANALISI DEI RESIDUI (ERRORI)")
print("-" * 40)
# Calcola residui
residuals = y_test - y_pred
print(f"📊 STATISTICHE RESIDUI:")
print(f"Media residui: €{residuals.mean():,.0f} (dovrebbe essere vicina a 0)")
print(f"Deviazione standard residui: €{residuals.std():,.0f}")
print(f"Min residuo: €{residuals.min():,.0f} (sovrastima più grande)")
print(f"Max residuo: €{residuals.max():,.0f} (sottostima più grande)")
# Test normalità residui (Shapiro-Wilk)
shapiro_stat, shapiro_p = stats.shapiro(residuals[:1000]) # Shapiro max 5000 campioni
print(f"\n🎯 TEST NORMALITÀ RESIDUI (Shapiro-Wilk):")
print(f"Statistica: {shapiro_stat:.3f}, p-value: {shapiro_p:.4f}")
if shapiro_p > 0.05:
print("✅ Non possiamo rifiutare normalità dei residui")
else:
print("⚠️ I residui potrebbero non essere normali")
print("\n📈 SU GOOGLE COLAB, SCOMMENTA PER ANALISI GRAFICA RESIDUI:")
"""
# Plot residui vs predetti
plt.figure(figsize=(12, 10))
plt.subplot(2, 2, 1)
plt.scatter(y_pred, residuals, alpha=0.6)
plt.axhline(y=0, color='red', linestyle='--')
plt.xlabel('Prezzo Predetto (€)')
plt.ylabel('Residui (€)')
plt.title('Residui vs Predetti')
plt.grid(True, alpha=0.3)
plt.subplot(2, 2, 2)
stats.probplot(residuals, dist="norm", plot=plt)
plt.title('QQ-Plot Residui')
plt.subplot(2, 2, 3)
plt.hist(residuals, bins=30, edgecolor='black', alpha=0.7)
plt.xlabel('Residui (€)')
plt.ylabel('Frequenza')
plt.title('Distribuzione Residui')
plt.grid(True, alpha=0.3)
plt.subplot(2, 2, 4)
plt.scatter(y_pred, y_test, alpha=0.6)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('Prezzo Predetto (€)')
plt.ylabel('Prezzo Reale (€)')
plt.title('Predetti vs Reali')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
"""
# 7. PREDIZIONI SU NUOVI DATI
print("\n\n7. 🔮 PREDIZIONI SU NUOVE CASE")
print("-" * 40)
# Crea dati per nuove case
nuove_case = pd.DataFrame({
'metratura': [150, 80, 200, 120],
'num_camere': [3, 2, 4, 3],
'eta_casa': [10, 30, 5, 20],
'distanza_centro': [2, 10, 5, 8]
})
# Standardizza come abbiamo fatto per il training
nuove_case_scaled = scaler.transform(nuove_case)
# Predici prezzi
prezzi_predetti = model.predict(nuove_case_scaled)
print("🏠 PREZZI PREDETTI PER NUOVE CASE:")
for i, (_, casa) in enumerate(nuove_case.iterrows()):
print(f"\nCasa {i+1}:")
print(f" Metratura: {casa['metratura']} m²")
print(f" Camere: {casa['num_camere']}")
print(f" Età: {casa['eta_casa']} anni")
print(f" Distanza centro: {casa['distanza_centro']} km")
print(f" 💰 Prezzo predetto: €{prezzi_predetti[i]:,.0f}")
# 8. CROSS-VALIDATION PER VALUTAZIONE ROBUSTA
print("\n\n8. 🔄 CROSS-VALIDATION")
print("-" * 40)
# 5-fold cross-validation
cv_scores = cross_val_score(
LinearRegression(),
X_scaled, y,
cv=5,
scoring='r2', # Usiamo R² come metrica
n_jobs=-1
)
print("🎯 R² SCORES CON 5-FOLD CROSS-VALIDATION:")
for fold, score in enumerate(cv_scores, 1):
print(f" Fold {fold}: {score:.3f}")
print(f"\n📊 R² medio CV: {cv_scores.mean():.3f} (+/- {cv_scores.std()*2:.3f})")
print(f"📈 R² sul test set: {r2:.3f}")
# Valuta differenza
diff = abs(cv_scores.mean() - r2)
if diff < 0.05:
print("✅ Performance consistenti tra CV e test set")
else:
print(f"⚠️ Differenza tra CV e test: {diff:.3f}")
# 9. ANALISI DELL'INFLUENZA DELLE FEATURES
print("\n\n9. 🎯 FEATURE IMPORTANCE")
print("-" * 40)
# Calcola importanza relativa delle features
feature_importance = np.abs(model.coef_)
feature_importance = 100.0 * (feature_importance / feature_importance.sum())
importance_df = pd.DataFrame({
'Feature': features,
'Coefficiente': model.coef_,
'Importanza (%)': feature_importance
}).sort_values('Importanza (%)', ascending=False)
print("📊 IMPORTANZA RELATIVA DELLE FEATURES:")
print(importance_df.to_string(index=False))
print("\n📈 SU GOOGLE COLAB, SCOMMENTA PER GRAFICO IMPORTANZA:")
"""
plt.figure(figsize=(10, 6))
bars = plt.barh(importance_df['Feature'], importance_df['Importanza (%)'])
plt.xlabel('Importanza Relativa (%)')
plt.title('Importanza delle Features nel Modello')
plt.grid(True, alpha=0.3, axis='x')
# Aggiungi valori sulle barre
for bar in bars:
width = bar.get_width()
plt.text(width + 1, bar.get_y() + bar.get_height()/2,
f'{width:.1f}%', ha='left', va='center')
plt.tight_layout()
plt.show()
"""
# 10. CONCLUSIONI E INSIGHTS
print("\n\n10. 🎓 CONCLUSIONI E INSIGHTS")
print("-" * 40)
print("\n✅ COSA ABBIAMO IMPARATO:")
print("1. Creazione e analisi dataset per regressione")
print("2. Standardizzazione features per regressione lineare")
print("3. Training modello e interpretazione coefficienti")
print("4. Valutazione con metriche MSE, RMSE, MAE, R²")
print("5. Analisi residui e verifica assunzioni")
print("6. Cross-validation per valutazione robusta")
print("7. Predizioni su nuovi dati")
print("8. Analisi feature importance")
print("\n📊 PERFORMANCE FINALI DEL MODELLO:")
metrics_summary = pd.DataFrame({
'Metrica': ['R² Score', 'RMSE', 'MAE', 'MSE'],
'Valore': [r2, f"€{rmse:,.0f}", f"€{mae:,.0f}", f"{mse:,.0f}"],
'Interpretazione': [
f"Spiega {r2*100:.1f}% della varianza",
f"Errore medio €{rmse:,.0f} (±{rmse/prezzo_medio*100:.1f}%)",
f"Errore assoluto medio €{mae:,.0f}",
f"Errore quadratico medio"
]
})
print(metrics_summary.to_string(index=False))
print("\n🎯 INSIGHTS PRATICI:")
print(f"1. La metratura è la feature più importante ({importance_df.iloc[0]['Importanza (%)']:.1f}%)")
print(f"2. Ogni camera in più vale circa €{abs(model.coef_[1]):,.0f}")
print(f"3. Ogni anno di età riduce il valore di €{abs(model.coef_[2]):,.0f}")
print(f"4. Ogni km dal centro riduce il valore di €{abs(model.coef_[3]):,.0f}")
print("\n🚀 PROSSIMI PASSI:")
print("1. Provare regressione polinomiale per relazioni non lineari")
print("2. Aggiungere più features (quartiere, servizi, etc.)")
print("3. Provare altri algoritmi (Random Forest, Gradient Boosting)")
print("4. Ottimizzare con regularizzazione (Ridge, Lasso)")
print("\n🎉 COMPLIMENTI! HAI COMPLETATO LA REGRESSIONE LINEARE!")