Le pipeline automatizzano il flusso di lavoro ML, prevenendo errori e data leakage.
🚫 COPIA BLOCCATA
# PIPELINE COMPLETA CON COLUMNTRANSFORMER
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.preprocessing import StandardScaler, OneHotEncoder, MinMaxScaler
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import warnings
warnings.filterwarnings('ignore')
print("🔧 PIPELINE AVANZATA SCKIT-LEARN")
print("=" * 80)
# ------------------------------------------------------------
# PASSO 1: CREIAMO DATASET COMPLESSO (SIMULAZIONE DATI REALI)
# ------------------------------------------------------------
print("\n📊 PASSO 1: CREAZIONE DATASET COMPLESSO")
print("-" * 50)
np.random.seed(42)
n_samples = 1000
# Features di vario tipo
data = {
# Numeriche continue
'età': np.random.normal(35, 10, n_samples),
'reddito': np.random.lognormal(10, 0.5, n_samples),
'spese_mensili': np.random.exponential(1000, n_samples),
# Numeriche discrete
'figli': np.random.poisson(1.5, n_samples),
'anni_esperienza': np.random.randint(0, 40, n_samples),
# Categoriche nominali
'città': np.random.choice(['Milano', 'Roma', 'Napoli', 'Torino', 'Bologna', 'Firenze'], n_samples),
'titolo_studio': np.random.choice(['Diploma', 'Laurea', 'Master', 'Dottorato', 'Nessuno'], n_samples),
# Categoriche ordinali
'rating_cliente': np.random.choice([1, 2, 3, 4, 5], n_samples, p=[0.1, 0.2, 0.3, 0.25, 0.15]),
# Booleane
'possiede_casa': np.random.choice([0, 1], n_samples, p=[0.6, 0.4]),
'ha_mutuo': np.random.choice([0, 1], n_samples, p=[0.7, 0.3]),
# Con valori mancanti (simulazione dati reali)
'risparmi': np.where(np.random.random(n_samples) > 0.1,
np.random.exponential(5000, n_samples),
np.nan)
}
# Target: approvato credito (1) o rifiutato (0)
# Logica complessa per simulare decisione di credito
reddito_norm = (data['reddito'] - np.mean(data['reddito'])) / np.std(data['reddito'])
spese_norm = (data['spese_mensili'] - np.mean(data['spese_mensili'])) / np.std(data['spese_mensili'])
rating_norm = data['rating_cliente'] / 5.0
# Probabilità di approvazione basata su multiple features
prob_approvazione = (
0.4 * (reddito_norm > 0).astype(int) +
0.3 * (spese_norm < 1).astype(int) +
0.2 * rating_norm +
0.1 * data['possiede_casa']
)
# Aggiungiamo rumore
prob_approvazione += np.random.normal(0, 0.2, n_samples)
# Target binario
data['credito_approvato'] = (prob_approvazione > 0.5).astype(int)
# Creiamo DataFrame
df = pd.DataFrame(data)
print("✅ Dataset creato con successo!")
print(f"📊 Dimensioni: {df.shape[0]} campioni × {df.shape[1]} colonne")
print("\n🔍 PRIME 5 RIGHE:")
print(df.head())
print("\n📈 DISTRIBUZIONE TARGET:")
print(df['credito_approvato'].value_counts())
print(f"Percentuale approvati: {(df['credito_approvato'].mean()*100):.1f}%")
print("\n🎯 VALORI MANCANTI:")
print(df.isnull().sum())
# ------------------------------------------------------------
# PASSO 2: ANALISI TIPI DI FEATURE
# ------------------------------------------------------------
print("\n\n🔍 PASSO 2: IDENTIFICAZIONE TIPI DI FEATURE")
print("-" * 50)
# Classifichiamo le features
numeric_features = ['età', 'reddito', 'spese_mensili', 'risparmi']
discrete_features = ['figli', 'anni_esperienza', 'rating_cliente']
categorical_features = ['città', 'titolo_studio']
binary_features = ['possiede_casa', 'ha_mutuo']
target = 'credito_approvato'
print("🎯 CLASSIFICAZIONE FEATURES:")
print(f"• Numeriche continue: {len(numeric_features)}")
print(f"• Numeriche discrete: {len(discrete_features)}")
print(f"• Categoriche nominali: {len(categorical_features)}")
print(f"• Binarie: {len(binary_features)}")
print(f"• Target: {target}")
# Separiamo features e target
X = df.drop(target, axis=1)
y = df[target]
print(f"\n📊 X shape: {X.shape}")
print(f"📊 y shape: {y.shape}")
# ------------------------------------------------------------
# PASSO 3: PIPELINE DI PREPROCESSING AVANZATA
# ------------------------------------------------------------
print("\n\n🔧 PASSO 3: CREAZIONE PIPELINE DI PREPROCESSING")
print("-" * 50)
print("Creiamo pipeline separate per ogni tipo di feature:")
# 1. Pipeline per features numeriche continue
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')), # Riempie valori mancanti con mediana
('scaler', StandardScaler()) # Standardizza (media=0, dev=1)
])
# 2. Pipeline per features numeriche discrete
discrete_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')), # Riempie con moda
('scaler', MinMaxScaler()) # Scala tra 0 e 1
])
# 3. Pipeline per features categoriche
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='constant', fill_value='missing')), # Riempie con 'missing'
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)) # One-hot encoding
])
# 4. Pipeline per features binarie (nessuna trasformazione necessaria)
binary_transformer = 'passthrough' # Mantiene così come sono
# ------------------------------------------------------------
# PASSO 4: COLUMNTRANSFORMER - APPLICA TRASFORMAZIONI DIVERS PER COLONNE DIVERS
# ------------------------------------------------------------
print("\n\n🎯 PASSO 4: COLUMNTRANSFORMER PER GESTIRE TUTTE LE FEATURE")
print("-" * 50)
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('disc', discrete_transformer, discrete_features),
('cat', categorical_transformer, categorical_features),
('binary', binary_transformer, binary_features)
],
remainder='drop' # Scarta colonne non specificate
)
print("✅ Preprocessor creato con successo!")
print("\n🎯 COSA FA IL PREPROCESSOR:")
print("1. Features numeriche: Imputazione + Standardizzazione")
print("2. Features discrete: Imputazione + MinMax scaling")
print("3. Features categoriche: Imputazione + One-hot encoding")
print("4. Features binarie: Mantiene così come sono")
# Test del preprocessor
print("\n🔧 TEST DEL PREPROCESSOR (prime 5 righe trasformate):")
X_transformed = preprocessor.fit_transform(X.head())
print(f"Shape dopo trasformazione: {X_transformed.shape}")
print(f"Numero features dopo one-hot: {X_transformed.shape[1]}")
# ------------------------------------------------------------
# PASSO 5: PIPELINE COMPLETA (PREPROCESSING + MODELLO)
# ------------------------------------------------------------
print("\n\n🤖 PASSO 5: PIPELINE COMPLETA ML")
print("-" * 50)
# Creiamo pipeline completa
pipeline = Pipeline(steps=[
('preprocessor', preprocessor),
('classifier', RandomForestClassifier(
n_estimators=100,
random_state=42,
n_jobs=-1 # Usa tutti i core CPU
))
])
print("✅ Pipeline completa creata!")
print("\n🎯 STRUTTURA PIPELINE:")
for step_name, step in pipeline.steps:
print(f"• {step_name}: {step}")
# ------------------------------------------------------------
# PASSO 6: TRAIN/TEST SPLIT E TRAINING
# ------------------------------------------------------------
print("\n\n🎯 PASSO 6: TRAINING DELLA PIPELINE")
print("-" * 50)
# Split dei dati
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y # Mantiene distribuzione target
)
print(f"📊 SPLIT DATI:")
print(f"• Training: {X_train.shape[0]} campioni")
print(f"• Test: {X_test.shape[0]} campioni")
print(f"• Distribuzione target training: {y_train.value_counts().to_dict()}")
print(f"• Distribuzione target test: {y_test.value_counts().to_dict()}")
# Training della pipeline
print("\n🚀 TRAINING DELLA PIPELINE...")
pipeline.fit(X_train, y_train)
print("✅ Training completato!")
# ------------------------------------------------------------
# PASSO 7: VALUTAZIONE DEL MODELLO
# ------------------------------------------------------------
print("\n\n📊 PASSO 7: VALUTAZIONE COMPLETA DEL MODELLO")
print("-" * 50)
# Predizioni
y_pred = pipeline.predict(X_test)
y_pred_proba = pipeline.predict_proba(X_test)[:, 1]
# Metriche
from sklearn.metrics import (accuracy_score, precision_score, recall_score,
f1_score, roc_auc_score, confusion_matrix)
print("🎯 METRICHE DI PERFORMANCE:")
print(f"• Accuracy: {accuracy_score(y_test, y_pred):.3f}")
print(f"• Precision: {precision_score(y_test, y_pred):.3f}")
print(f"• Recall: {recall_score(y_test, y_pred):.3f}")
print(f"• F1-Score: {f1_score(y_test, y_pred):.3f}")
print(f"• ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.3f}")
# Matrice di confusione
print("\n📋 MATRICE DI CONFUSIONE:")
cm = confusion_matrix(y_test, y_pred)
print(f" Predetto NO Predetto SI")
print(f"Reale NO (TN): {cm[0,0]:8} (FP): {cm[0,1]:8}")
print(f"Reale SI (FN): {cm[1,0]:8} (TP): {cm[1,1]:8}")
# Classification report
print("\n📄 CLASSIFICATION REPORT:")
print(classification_report(y_test, y_pred, target_names=['Rifiutato', 'Approvato']))
# ------------------------------------------------------------
# PASSO 8: CROSS-VALIDATION PER VALUTAZIONE ROBUSTA
# ------------------------------------------------------------
print("\n\n🔄 PASSO 8: CROSS-VALIDATION CON PIPELINE")
print("-" * 50)
# Cross-validation con la pipeline completa
cv_scores = cross_val_score(
pipeline,
X, y,
cv=5, # 5-fold CV
scoring='roc_auc', # Usiamo ROC-AUC
n_jobs=-1 # Parallelizza
)
print("🎯 ROC-AUC SCORES CON 5-FOLD CV:")
for fold, score in enumerate(cv_scores, 1):
print(f" Fold {fold}: {score:.3f}")
print(f"\n📊 Media CV: {cv_scores.mean():.3f} (+/- {cv_scores.std()*2:.3f})")
print(f"📈 ROC-AUC sul test set: {roc_auc_score(y_test, y_pred_proba):.3f}")
# ------------------------------------------------------------
# PASSO 9: FEATURE IMPORTANCE (anche dopo preprocessing!)
# ------------------------------------------------------------
print("\n\n🎯 PASSO 9: ANALISI FEATURE IMPORTANCE")
print("-" * 50)
print("Anche con pipeline complessa, possiamo estrarre feature importance:")
# Estraiamo il classificatore dalla pipeline
classifier = pipeline.named_steps['classifier']
# Otteniamo feature names dopo preprocessing
preprocessor = pipeline.named_steps['preprocessor']
preprocessor.fit(X_train)
# Costruiamo feature names
feature_names = []
# Numeriche
feature_names.extend(numeric_features)
# Discrete
feature_names.extend(discrete_features)
# Categoriche (dopo one-hot)
cat_encoder = preprocessor.named_transformers_['cat'].named_steps['onehot']
for i, cat in enumerate(categorical_features):
categories = cat_encoder.categories_[i]
feature_names.extend([f'{cat}_{c}' for c in categories])
# Binarie
feature_names.extend(binary_features)
print(f"📊 Numero totale features dopo preprocessing: {len(feature_names)}")
# Feature importance dal Random Forest
if hasattr(classifier, 'feature_importances_'):
importance_df = pd.DataFrame({
'Feature': feature_names,
'Importance': classifier.feature_importances_
}).sort_values('Importance', ascending=False)
print("\n🎯 TOP 10 FEATURES PIÙ IMPORTANTI:")
print(importance_df.head(10).to_string(index=False))
print(f"\n📈 IMPORTANZA CUMULATIVA TOP 5 FEATURES: "
f"{(importance_df.head(5)['Importance'].sum()*100):.1f}%")
# ------------------------------------------------------------
# PASSO 10: PREDIZIONI SU NUOVI DATI
# ------------------------------------------------------------
print("\n\n🔮 PASSO 10: PREDIZIONI SU NUOVI DATI CON LA PIPELINE")
print("-" * 50)
print("Creiamo nuovi candidati per il credito:")
nuovi_candidati = pd.DataFrame({
'età': [25, 45, 32, 55, 28],
'reddito': [30000, 60000, 45000, 80000, 35000],
'spese_mensili': [1500, 3000, 2000, 4000, 1800],
'figli': [0, 2, 1, 3, 0],
'anni_esperienza': [3, 20, 8, 30, 5],
'città': ['Milano', 'Roma', 'Napoli', 'Torino', 'Bologna'],
'titolo_studio': ['Laurea', 'Master', 'Diploma', 'Dottorato', 'Laurea'],
'rating_cliente': [4, 5, 3, 5, 4],
'possiede_casa': [0, 1, 0, 1, 0],
'ha_mutuo': [0, 1, 0, 1, 0],
'risparmi': [5000, 20000, 8000, 50000, 3000]
})
print("📊 NUOVI CANDIDATI:")
print(nuovi_candidati)
# Predizioni
predizioni = pipeline.predict(nuovi_candidati)
probabilità = pipeline.predict_proba(nuovi_candidati)[:, 1]
print("\n💰 RISULTATI PREDIZIONI:")
for i in range(len(nuovi_candidati)):
decisione = "APPROVATO" if predizioni[i] == 1 else "RIFIUTATO"
prob = probabilità[i] * 100
print(f"\nCandidato {i+1}:")
print(f" • Decisione: {decisione}")
print(f" • Probabilità approvazione: {prob:.1f}%")
print(f" • Età: {nuovi_candidati.iloc[i]['età']}, Reddito: €{nuovi_candidati.iloc[i]['reddito']:,.0f}")
print(f" • Città: {nuovi_candidati.iloc[i]['città']}, Titolo studio: {nuovi_candidati.iloc[i]['titolo_studio']}")
# ------------------------------------------------------------
# PASSO 11: SALVATAGGIO E CARICAMENTO PIPELINE
# ------------------------------------------------------------
print("\n\n💾 PASSO 11: SALVATAGGIO E CARICAMENTO DELLA PIPELINE")
print("-" * 50)
import joblib
# Salva la pipeline completa
joblib.dump(pipeline, 'pipeline_credito.pkl')
print("✅ Pipeline salvata come 'pipeline_credito.pkl'")
# Simulazione di caricamento in un'altra sessione
print("\n🔧 SIMULAZIONE CARICAMENTO IN NUOVA SESSIONE:")
loaded_pipeline = joblib.load('pipeline_credito.pkl')
# Test con un nuovo candidato
nuovo_candidato = pd.DataFrame([{
'età': 30,
'reddito': 40000,
'spese_mensili': 1800,
'figli': 1,
'anni_esperienza': 7,
'città': 'Milano',
'titolo_studio': 'Laurea',
'rating_cliente': 4,
'possiede_casa': 0,
'ha_mutuo': 0,
'risparmi': 10000
}])
predizione_caricata = loaded_pipeline.predict(nuovo_candidato)[0]
probabilità_caricata = loaded_pipeline.predict_proba(nuovo_candidato)[0, 1] * 100
print(f"🎯 PREDIZIONE CON PIPELINE CARICATA:")
print(f"• Decisione: {'APPROVATO' if predizione_caricata == 1 else 'RIFIUTATO'}")
print(f"• Probabilità: {probabilità_caricata:.1f}%")
print("✅ La pipeline funziona perfettamente anche dopo il salvataggio!")
# ------------------------------------------------------------
# RIEPILOGO FINALE
# ------------------------------------------------------------
print("\n\n🎉 PIPELINE SCKIT-LEARN MASTERATA!")
print("=" * 80)
print("\n✅ COSA HAI IMPARATO OGGI:")
print("1. Gestione di dataset con tipi di feature diversi")
print("2. Creazione pipeline con ColumnTransformer")
print("3. Trasformazioni specifiche per ogni tipo di feature")
print("4. Cross-validation con pipeline completa")
print("5. Feature importance anche dopo preprocessing complesso")
print("6. Salvataggio e caricamento di pipeline complete")
print("7. Predizioni su nuovi dati con tutto automatizzato")
print("\n🎯 VANTAGGI DELLE PIPELINE:")
print("• ✅ Prevenzione data leakage")
print("• ✅ Riproducibilità completa")
print("• ✅ Codice più pulito e mantenibile")
print("• ✅ Facile deployment")
print("• ✅ Cross-validation corretta")
print("\n🚀 PROSSIMI PASSI:")
print("1. Sperimenta con diversi preprocessori")
print("2. Prova GridSearchCV con pipeline")
print("3. Aggiungi feature selection nella pipeline")
print("4. Crea pipeline per regressione (non solo classificazione)")
print("\n📊 PERFORMANCE FINALE DEL MODELLO:")
print(f"• ROC-AUC: {roc_auc_score(y_test, y_pred_proba):.3f}")
print(f"• F1-Score: {f1_score(y_test, y_pred):.3f}")
print(f"• Cross-validation: {cv_scores.mean():.3f} (+/- {cv_scores.std()*2:.3f})")
print("\n🌟 COMPLIMENTI! HAI MASTERIZZATO LE PIPELINE DI SCKIT-LEARN!")