🧹 Preprocessing Dati con Scikit-Learn: La Fondazione del ML
Impara a pulire, trasformare e preparare dati per Machine Learning
Introduzione
Benvenuto nella base fondamentale di ogni progetto di Machine Learning: il preprocessing dei dati! In questa esercitazione imparerai le tecniche essenziali per pulire, trasformare e preparare i tuoi dati prima di applicare algoritmi ML. Il preprocessing è il segreto per modelli accurati e robusti.
Lavorerai come Data Engineer in una startup fintech che ha appena raccolto dati grezzi sui clienti. Il tuo compito è trasformare questi dati sporchi e disorganizzati in un dataset pulito pronto per i modelli ML di valutazione del credito.
⚠️ ATTENZIONE: COPIA BLOCCATA
Il codice è protetto da sistema anti-copia. Dovrai SCRIVERE manualmente tutto il codice per imparare veramente! 🚫📋
Parte 1: Creazione Dataset Realistico “Sporco”
▶ Importazione Librerie e Creazione Dati Grezzi
Crea una nuova cella su Google Colab e importa le librerie:
# IMPORT LIBRERIE PER PREPROCESSING COMPLETO
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import (
StandardScaler, MinMaxScaler, RobustScaler,
LabelEncoder, OneHotEncoder, OrdinalEncoder,
PolynomialFeatures, PowerTransformer,
SimpleImputer, KBinsDiscretizer
)
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.feature_selection import SelectKBest, f_classif
import warnings
warnings.filterwarnings('ignore')
# Configurazione visualizzazione
sns.set_style("whitegrid")
plt.rcParams['figure.figsize'] = (14, 8)
plt.rcParams['font.size'] = 12
print("✅ Librerie per preprocessing importate correttamente!")
print("🧹 Strumenti disponibili: Imputer, Scaler, Encoder, Transformer, Pipeline")
▶ Creazione Dataset Fintech "Sporco" Realistico
Crea un dataset finanziario realistico con problemi comuni:
# 2. CREAZIONE DATASET FINTECH CON PROBLEMI REALISTICI
print("🏦 CREAZIONE DATASET FINTECH 'SPORCO'")
print("="*50)
np.random.seed(42) # Per risultati riproducibili
n_clienti = 200
# === CREAZIONE FEATURES CON PROBLEMI REALISTICI ===
# 1. Feature numerica con outliers e valori mancanti (reddito annuale)
reddito_base = np.random.normal(35000, 15000, n_clienti)
# Aggiungi outliers
outlier_indices = np.random.choice(n_clienti, 10, replace=False)
reddito_base[outlier_indices] = np.random.uniform(200000, 500000, 10)
# Aggiungi valori mancanti (NaN)
nan_indices = np.random.choice(n_clienti, 15, replace=False)
reddito_base[nan_indices] = np.nan
# 2. Feature numerica con distribuzione skew (debito)
debito = np.random.exponential(5000, n_clienti)
debito = np.where(debito > 30000, 30000, debito) # Cap outliers
# 3. Feature categorica con problemi (stato occupazionale)
stati = ['Impiegato', 'Libero Professionista', 'Disoccupato', 'Pensionato', 'Studente',
'impegnato', 'LIBERO PROF.', 'disoccupato', '', 'NaN', 'N/A']
occupazione = np.random.choice(stati, n_clienti, p=[0.3, 0.2, 0.15, 0.1, 0.1, 0.05, 0.03, 0.02, 0.02, 0.02, 0.01])
# 4. Feature ordinale con problemi (rating credito)
rating_valori = ['AAA', 'AA', 'A', 'BBB', 'BB', 'B', 'CCC', 'D', 'NR', 'Unknown']
rating = np.random.choice(rating_valori, n_clienti, p=[0.1, 0.15, 0.2, 0.2, 0.1, 0.08, 0.05, 0.02, 0.05, 0.05])
# 5. Feature numerica con scale diversa (età)
eta = np.random.randint(18, 75, n_clienti)
# Aggiungi alcuni valori impossibili
eta[np.random.choice(n_clienti, 5)] = [150, -5, 200, 0, 1000]
# 6. Feature binaria con problemi (possiede casa)
casa_valori = ['SI', 'NO', 'Si', 'No', 'si', 'no', '1', '0', True, False, np.nan]
casa = np.random.choice(casa_valori, n_clienti)
# 7. Feature temporale con problemi (anni presso banca)
anni_banca = np.random.uniform(0, 30, n_clienti)
# Aggiungi valori negativi
anni_banca[np.random.choice(n_clienti, 3)] = [-2, -1, -5]
# 8. Target variable (rischio credito: 0=basso, 1=alto)
rischio = np.random.choice([0, 1], n_clienti, p=[0.7, 0.3])
# Crea DataFrame
df_fintech = pd.DataFrame({
'id_cliente': range(1001, 1001 + n_clienti),
'reddito_annuale': reddito_base,
'debito_totale': debito,
'occupazione': occupazione,
'rating_credito': rating,
'eta': eta,
'possiede_casa': casa,
'anni_presso_banca': anni_banca,
'rischio_credito': rischio
})
print(f"📊 DATASET CREATO CON PROBLEMI REALISTICI:")
print(f" • Numero clienti: {len(df_fintech)}")
print(f" • Numero features: {len(df_fintech.columns) - 2} (esclusi ID e target)")
print(f" • Target: rischio_credito (0=basso rischio, 1=alto rischio)")
print(f"\n📋 PRIME 10 RIGHE DEL DATASET (SPORCO):")
print(df_fintech.head(10))
print(f"\n⚠️ PROBLEMI INTRODOTTI NEL DATASET:")
print(" 1. Valori mancanti (NaN) in reddito_annuale")
print(" 2. Outliers estremi in reddito_annuale")
print(" 3. Distribuzione skew in debito_totale")
print(" 4. Categoriche inconsistenti (maiuscole/minuscole, valori vuoti)")
print(" 5. Valori impossibili in eta (negativi, troppo alti)")
print(" 6. Dati duplicati in categoriche (SI/Si/si)")
print(" 7. Valori negativi in anni_presso_banca")
print(" 8. Categoriche con troppe categorie (occupazione)")
Parte 2: Analisi Esplorativa Dati (EDA)
▶ Identificazione Problemi e Valori Mancanti
Analizza i dati e identifica tutti i problemi:
# 3. ANALISI ESPLORATIVA DATI (EDA)
print("🔍 ANALISI ESPLORATIVA DATASET FINTECH")
print("="*50)
# 3.1 Informazioni base del dataset
print("\n📊 INFORMAZIONI DATASET:")
print(f"Dimensioni: {df_fintech.shape[0]} righe × {df_fintech.shape[1]} colonne")
print(f"\nTipi di dati:")
print(df_fintech.dtypes)
# 3.2 Valori mancanti
print(f"\n⚠️ VALORI MANCANTI (NaN):")
missing_values = df_fintech.isnull().sum()
missing_percentage = (missing_values / len(df_fintech)) * 100
missing_df = pd.DataFrame({
'Valori Mancanti': missing_values,
'Percentuale': missing_percentage
})
print(missing_df[missing_df['Valori Mancanti'] > 0])
# 3.3 Statistiche descrittive per features numeriche
print(f"\n📈 STATISTICHE DESCRITTIVE (FEATURES NUMERICHE):")
numeric_features = ['reddito_annuale', 'debito_totale', 'eta', 'anni_presso_banca']
print(df_fintech[numeric_features].describe())
# 3.4 Analisi distribuzioni e outliers
print(f"\n🎨 VISUALIZZAZIONE PROBLEMI DATI...")
fig, axes = plt.subplots(2, 3, figsize=(16, 10))
fig.suptitle('Analisi Problemi Dataset Fintech', fontsize=16, fontweight='bold')
# 1. Distribuzione reddito (con outliers)
axes[0, 0].boxplot(df_fintech['reddito_annuale'].dropna())
axes[0, 0].set_title('Reddito Annuale (Boxplot)')
axes[0, 0].set_ylabel('€')
axes[0, 0].grid(True, alpha=0.3)
# 2. Distribuzione debito (skew)
axes[0, 1].hist(df_fintech['debito_totale'], bins=30, edgecolor='black', alpha=0.7, color='skyblue')
axes[0, 1].set_title('Distribuzione Debito (Skewed)')
axes[0, 1].set_xlabel('€')
axes[0, 1].set_ylabel('Frequenza')
axes[0, 1].grid(True, alpha=0.3)
# 3. Distribuzione età (con valori anomali)
axes[0, 2].hist(df_fintech['eta'], bins=30, edgecolor='black', alpha=0.7, color='lightgreen')
axes[0, 2].set_title('Distribuzione Età (Valori Impossibili)')
axes[0, 2].set_xlabel('Anni')
axes[0, 2].set_ylabel('Frequenza')
axes[0, 2].grid(True, alpha=0.3)
# Evidenzia valori anomali
axes[0, 2].axvspan(0, 18, alpha=0.2, color='red')
axes[0, 2].axvspan(100, 1000, alpha=0.2, color='red')
# 4. Valori categorici occupazione
occupazione_counts = df_fintech['occupazione'].value_counts()
axes[1, 0].bar(range(len(occupazione_counts)), occupazione_counts.values, color='salmon')
axes[1, 0].set_title('Valori Occupazione (Inconsistenti)')
axes[1, 0].set_xlabel('Categorie')
axes[1, 0].set_ylabel('Conteggio')
axes[1, 0].set_xticks(range(len(occupazione_counts)))
axes[1, 0].set_xticklabels(occupazione_counts.index, rotation=45, ha='right')
axes[1, 0].grid(True, alpha=0.3)
# 5. Rating credito
rating_counts = df_fintech['rating_credito'].value_counts().sort_index()
axes[1, 1].bar(range(len(rating_counts)), rating_counts.values, color='gold')
axes[1, 1].set_title('Rating Credito (Ordinale)')
axes[1, 1].set_xlabel('Rating')
axes[1, 1].set_ylabel('Conteggio')
axes[1, 1].set_xticks(range(len(rating_counts)))
axes[1, 1].set_xticklabels(rating_counts.index, rotation=45, ha='right')
axes[1, 1].grid(True, alpha=0.3)
# 6. Possiede casa (inconsistenze)
casa_counts = df_fintech['possiede_casa'].value_counts()
axes[1, 2].bar(range(len(casa_counts)), casa_counts.values, color='violet')
axes[1, 2].set_title('Possiede Casa (Binaria Inconsistente)')
axes[1, 2].set_xlabel('Valore')
axes[1, 2].set_ylabel('Conteggio')
axes[1, 2].set_xticks(range(len(casa_counts)))
axes[1, 2].set_xticklabels([str(x) for x in casa_counts.index], rotation=45, ha='right')
axes[1, 2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 3.5 Analisi valori unici per features categoriche
print(f"\n🔍 ANALISI VALORI UNICI FEATURES CATEGORICHE:")
categorical_features = ['occupazione', 'rating_credito', 'possiede_casa']
for feature in categorical_features:
unique_values = df_fintech[feature].unique()
print(f"\n{feature.upper()}:")
print(f" • Numero valori unici: {len(unique_values)}")
print(f" • Valori: {sorted([str(x) for x in unique_values])[:10]}") # Mostra primi 10
if len(unique_values) > 10:
print(f" ... e altri {len(unique_values) - 10} valori")
print(f"\n✅ Analisi EDA completata! Problemi identificati.")
Parte 3: Pulizia Dati Manuale
▶ Correzione Manuale Problemi Evidenti
Pulisci manualmente i problemi più evidenti:
# 4. PULIZIA MANUALE DATI
print("🧹 PULIZIA MANUALE DATASET")
print("="*50)
# Crea una copia per la pulizia
df_clean = df_fintech.copy()
print("🔧 Applicando correzioni manuali...")
# 4.1 Pulizia feature 'occupazione'
print("\n1. 🔄 Normalizzazione 'occupazione':")
# Mappa valori inconsistenti a categorie standard
occupazione_mapping = {
'Impiegato': 'Impiegato',
'impegnato': 'Impiegato', # Typo
'Libero Professionista': 'Libero Professionista',
'LIBERO PROF.': 'Libero Professionista', # Maiuscole
'Disoccupato': 'Disoccupato',
'disoccupato': 'Disoccupato', # Minuscole
'Pensionato': 'Pensionato',
'Studente': 'Studente',
'': 'Altro', # Valore vuoto
'NaN': 'Altro',
'N/A': 'Altro'
}
df_clean['occupazione'] = df_clean['occupazione'].map(occupazione_mapping).fillna('Altro')
print(f" • Categorie ridotte da {len(df_fintech['occupazione'].unique())} a {len(df_clean['occupazione'].unique())}")
# 4.2 Pulizia feature 'possiede_casa'
print("\n2. 🔄 Normalizzazione 'possiede_casa':")
casa_mapping = {
'SI': 'SI',
'Si': 'SI',
'si': 'SI',
'1': 'SI',
True: 'SI',
'NO': 'NO',
'No': 'NO',
'no': 'NO',
'0': 'NO',
False: 'NO',
np.nan: 'NO' # Valori mancanti mappati a NO
}
df_clean['possiede_casa'] = df_clean['possiede_casa'].map(casa_mapping).fillna('NO')
print(f" • Categorie ridotte da {len(df_fintech['possiede_casa'].unique())} a {len(df_clean['possiede_casa'].unique())}")
# 4.3 Correzione valori impossibili in 'eta'
print("\n3. 🎯 Correzione valori impossibili 'eta':")
# Identifica e correggi valori fuori range realistico (18-100)
mask_eta_invalid = (df_clean['eta'] < 18) | (df_clean['eta'] > 100)
invalid_count = mask_eta_invalid.sum()
print(f" • Valori impossibili trovati: {invalid_count}")
# Sostituisci con la mediana degli altri valori
eta_median = df_clean.loc[~mask_eta_invalid, 'eta'].median()
df_clean.loc[mask_eta_invalid, 'eta'] = eta_median
print(f" • Valori corretti con mediana: {eta_median:.0f} anni")
# 4.4 Correzione valori negativi in 'anni_presso_banca'
print("\n4. 🔢 Correzione valori negativi 'anni_presso_banca':")
mask_anni_negativi = df_clean['anni_presso_banca'] < 0
neg_count = mask_anni_negativi.sum()
print(f" • Valori negativi trovati: {neg_count}")
# Sostituisci negativi con 0
df_clean.loc[mask_anni_negativi, 'anni_presso_banca'] = 0
print(f" • Valori negativi impostati a 0")
# 4.5 Verifica risultati pulizia
print(f"\n✅ PULIZIA MANUALE COMPLETATA!")
print(f"\n📊 CONFRONTO PRIMA/DOPO PULIZIA:")
comparison_data = []
for feature in ['occupazione', 'possiede_casa', 'eta', 'anni_presso_banca']:
before_unique = len(df_fintech[feature].dropna().unique())
after_unique = len(df_clean[feature].dropna().unique())
comparison_data.append([feature, before_unique, after_unique])
comparison_df = pd.DataFrame(comparison_data, columns=['Feature', 'Valori Unici (Prima)', 'Valori Unici (Dopo)'])
print(comparison_df.to_string(index=False))
print(f"\n📋 ESEMPIO DATI PULITI (prime 5 righe):")
print(df_clean.head())
print(f"\n🔍 VALORI MANCANTI DOPO PULIZIA MANUALE:")
missing_after = df_clean.isnull().sum()
print(missing_after[missing_after > 0])
Parte 4: Preprocessing Automatico con Scikit-Learn
▶ Imputazione Valori Mancanti e Scaling
Applica preprocessing automatico con Scikit-Learn:
# 5. PREPROCESSING AUTOMATICO CON SCIKIT-LEARN
print("🤖 PREPROCESSING AUTOMATICO CON SCIKIT-LEARN")
print("="*50)
# Separa features e target
X = df_clean.drop(['id_cliente', 'rischio_credito'], axis=1)
y = df_clean['rischio_credito']
print(f"📊 DATI PER PREPROCESSING:")
print(f" • Features (X): {X.shape}")
print(f" • Target (y): {y.shape}")
print(f" • Distribuzione target: {y.value_counts().to_dict()}")
# 5.1 Identifica tipi di features
numeric_features = X.select_dtypes(include=['int64', 'float64']).columns.tolist()
categorical_features = X.select_dtypes(include=['object']).columns.tolist()
print(f"\n🔍 IDENTIFICAZIONE TIPI FEATURES:")
print(f" • Features numeriche ({len(numeric_features)}): {numeric_features}")
print(f" • Features categoriche ({len(categorical_features)}): {categorical_features}")
# 5.2 Definizione transformer per ogni tipo di feature
print(f"\n⚙️ CREAZIONE PIPELINE DI PREPROCESSING...")
# Pipeline per features numeriche
numeric_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='median')), # Imputa con mediana
('scaler', RobustScaler()) # RobustScaler per gestire outliers
])
# Pipeline per features categoriche
categorical_transformer = Pipeline(steps=[
('imputer', SimpleImputer(strategy='most_frequent')), # Imputa con moda
('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False)) # One-hot encoding
])
# 5.3 Combinazione transformer con ColumnTransformer
preprocessor = ColumnTransformer(
transformers=[
('num', numeric_transformer, numeric_features),
('cat', categorical_transformer, categorical_features)
],
remainder='passthrough' # Mantieni altre colonne
)
print(f"✅ Pipeline creata con successo!")
print(f" • Numeriche: Imputazione (mediana) → RobustScaler")
print(f" • Categoriche: Imputazione (moda) → OneHotEncoding")
# 5.4 Applica preprocessing
print(f"\n⚡ Applicando preprocessing ai dati...")
X_preprocessed = preprocessor.fit_transform(X)
# Ottieni nomi features dopo preprocessing
# Per one-hot encoding, otteniamo i nomi delle colonne
numeric_feature_names = numeric_features
categorical_feature_names = []
# Ottieni nomi delle colonne one-hot
if len(categorical_features) > 0:
onehot_encoder = preprocessor.named_transformers_['cat'].named_steps['onehot']
categorical_feature_names = onehot_encoder.get_feature_names_out(categorical_features).tolist()
# Nomi finali delle features
all_feature_names = numeric_feature_names + categorical_feature_names
print(f"\n📊 DATI DOPO PREPROCESSING:")
print(f" • Shape originale: {X.shape}")
print(f" • Shape dopo preprocessing: {X_preprocessed.shape}")
print(f" • Numero features dopo one-hot: {len(all_feature_names)}")
# 5.5 Converti in DataFrame per visualizzazione
X_preprocessed_df = pd.DataFrame(X_preprocessed, columns=all_feature_names)
print(f"\n📋 ESEMPIO DATI PREPROCESSATI (prime 5 righe, prime 10 colonne):")
print(X_preprocessed_df.iloc[:5, :10])
print(f"\n📈 STATISTICHE DATI PREPROCESSATI (features numeriche):")
print(X_preprocessed_df[numeric_feature_names].describe().round(3))
Parte 5: Tecniche Avanzate e Confronto
▶ Tecniche Avanzate e Confronto Metodi
Esplora tecniche avanzate e confronta diversi metodi:
# 6. TECNICHE AVANZATE DI PREPROCESSING
print("🚀 TECNICHE AVANZATE E CONFRONTO METODI")
print("="*50)
# 6.1 Confronto diversi scaler per features numeriche
print("\n📊 CONFRONTO DIVERSI SCALER:")
# Prendiamo solo le features numeriche originali
X_numeric = df_clean[numeric_features].copy()
# Applica diversi scaler
scalers = {
'StandardScaler (media=0, var=1)': StandardScaler(),
'MinMaxScaler (range 0-1)': MinMaxScaler(),
'RobustScaler (resistente outliers)': RobustScaler(),
'PowerTransformer (Yeo-Johnson)': PowerTransformer(method='yeo-johnson')
}
# Visualizzazione confronto
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Confronto Diversi Scaler per Reddito Annuale', fontsize=16, fontweight='bold')
for idx, (scaler_name, scaler) in enumerate(scalers.items()):
ax = axes[idx // 2, idx % 2]
# Applica scaler
X_scaled = scaler.fit_transform(X_numeric[['reddito_annuale']].values)
# Istogramma
ax.hist(X_scaled, bins=30, edgecolor='black', alpha=0.7, color=['skyblue', 'lightgreen', 'salmon', 'gold'][idx])
# Statistiche
ax.axvline(X_scaled.mean(), color='red', linestyle='--', linewidth=2, label=f'Media: {X_scaled.mean():.2f}')
ax.axvline(np.median(X_scaled), color='green', linestyle='--', linewidth=2, label=f'Mediana: {np.median(X_scaled):.2f}')
ax.set_title(scaler_name, fontsize=12)
ax.set_xlabel('Valore Scalato')
ax.set_ylabel('Frequenza')
ax.legend(fontsize=9)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 6.2 Encoding avanzato per features categoriche
print("\n🔤 CONFRONTO METODI ENCODING CATEGORICHE:")
# Esempio con la feature 'occupazione'
occupazione_data = df_clean[['occupazione']].copy()
encoding_methods = {
'Label Encoding': LabelEncoder(),
'One-Hot Encoding': OneHotEncoder(sparse_output=False),
'Ordinal Encoding': OrdinalEncoder(categories=[['Studente', 'Disoccupato', 'Impiegato', 'Libero Professionista', 'Pensionato', 'Altro']])
}
encoding_results = {}
for method_name, encoder in encoding_methods.items():
try:
if method_name == 'One-Hot Encoding':
encoded = encoder.fit_transform(occupazione_data)
encoding_results[method_name] = {
'shape': encoded.shape,
'example': encoded[0] if len(encoded) > 0 else []
}
else:
encoded = encoder.fit_transform(occupazione_data.values.ravel())
encoding_results[method_name] = {
'shape': encoded.shape,
'example': encoded[:5] if len(encoded) > 0 else []
}
except Exception as e:
encoding_results[method_name] = {'error': str(e)}
print("Risultati encoding:")
for method, result in encoding_results.items():
print(f"\n • {method}:")
if 'error' in result:
print(f" Errore: {result['error']}")
else:
print(f" Shape: {result['shape']}")
print(f" Esempio primi valori: {result['example']}")
# 6.3 Discretizzazione (binning) di features numeriche
print("\n📦 DISCRETIZZAZIONE (BINNING) FEATURES NUMERICHE:")
# Esempio con 'eta'
print("\nEsempio discretizzazione 'eta':")
kbins = KBinsDiscretizer(n_bins=5, encode='ordinal', strategy='uniform')
eta_binned = kbins.fit_transform(df_clean[['eta']])
# Mostra bins
bin_edges = kbins.bin_edges_[0]
print(f" • Numero bins: {kbins.n_bins_[0]}")
print(f" • Bin edges: {bin_edges.round(1)}")
print(f" • Esempio valori originali: {df_clean['eta'].iloc[:5].values}")
print(f" • Esempio valori binned: {eta_binned[:5].flatten()}")
# 6.4 Creazione nuove features (feature engineering)
print("\n🎯 CREAZIONE NUOVE FEATURES (FEATURE ENGINEERING):")
# Crea alcune nuove features derivate
df_engineered = df_clean.copy()
# 1. Ratio debito/reddito
df_engineered['debito_reddito_ratio'] = df_engineered['debito_totale'] / (df_engineered['reddito_annuale'] + 1) # +1 per evitare divisione per 0
# 2. Età categorizzata
df_engineered['eta_categoria'] = pd.cut(df_engineered['eta'],
bins=[0, 30, 45, 60, 100],
labels=['Giovane', 'Adulto', 'Maturo', 'Senior'])
# 3. Interazione tra features
df_engineered['eta_anni_banca'] = df_engineered['eta'] * df_engineered['anni_presso_banca']
print("Nuove features create:")
print(f" 1. debito_reddito_ratio: rapporto debito/reddito")
print(f" 2. eta_categoria: età in categorie")
print(f" 3. eta_anni_banca: interazione età × anni banca")
print(f"\n📋 ESEMPIO NUOVE FEATURES (prime 5 righe):")
print(df_engineered[['eta', 'reddito_annuale', 'debito_totale', 'anni_presso_banca',
'debito_reddito_ratio', 'eta_categoria', 'eta_anni_banca']].head())
# 6.5 Pipeline completa per ML
print("\n⚡ PIPELINE COMPLETA PER MACHINE LEARNING:")
# Definisci pipeline completa
from sklearn.ensemble import RandomForestClassifier
ml_pipeline = Pipeline(steps=[
('preprocessor', preprocessor), # Il nostro preprocessor di prima
('classifier', RandomForestClassifier(n_estimators=100, random_state=42))
])
print("✅ Pipeline ML creata con successo!")
print(" Passaggi: Preprocessing → Classificatore (Random Forest)")
# 6.6 Split dati e valutazione rapida
print("\n🎯 SPLIT DATI E VALUTAZIONE RAPIDA:")
# Split train-test
X_final = df_clean.drop(['id_cliente', 'rischio_credito'], axis=1)
y_final = df_clean['rischio_credito']
X_train, X_test, y_train, y_test = train_test_split(
X_final, y_final, test_size=0.2, random_state=42, stratify=y_final
)
print(f" • Training set: {X_train.shape[0]} campioni")
print(f" • Test set: {X_test.shape[0]} campioni")
print(f" • Distribuzione target (train): {pd.Series(y_train).value_counts().to_dict()}")
print(f" • Distribuzione target (test): {pd.Series(y_test).value_counts().to_dict()}")
# Addestra pipeline
print("\n🤖 Addestramento pipeline ML...")
ml_pipeline.fit(X_train, y_train)
# Valutazione
train_score = ml_pipeline.score(X_train, y_train)
test_score = ml_pipeline.score(X_test, y_test)
print(f"\n📊 PERFORMANCE MODELLO:")
print(f" • Accuracy training: {train_score:.3f}")
print(f" • Accuracy test: {test_score:.3f}")
if abs(train_score - test_score) > 0.1:
print(" ⚠️ Attenzione: possibile overfitting!")
else:
print(" ✅ Modello ben bilanciato tra training e test")
print(f"\n🎉 PREPROCESSING COMPLETATO CON SUCCESSO!")
print(" I tuoi dati sono ora pronti per qualsiasi algoritmo di Machine Learning!")
Concetti di Preprocessing Appresi
Congratulazioni! Ora conosci:
- ✓ EDA Completa: Analisi Esplorativa Dati per identificare problemi
- ✓ Pulizia Manuale: Correzione valori inconsistenti e impossibili
- ✓ Imputazione Valori Mancanti: SimpleImputer con strategie median/moda
- ✓ Scaling Features: StandardScaler, MinMaxScaler, RobustScaler
- ✓ Encoding Categoriche: LabelEncoder, OneHotEncoder, OrdinalEncoder
- ✓ ColumnTransformer: Applica transformer diversi a colonne diverse
- ✓ Pipeline: Sequenza automatizzata di passaggi di preprocessing
- ✓ Feature Engineering: Creazione nuove features da quelle esistenti
- ✓ Discretizzazione: KBinsDiscretizer per trasformazione in categorie
- ✓ Power Transformer: Trasformazioni per normalizzare distribuzioni
Prossimi Passi nel Preprocessing
Nella prossima esercitazione imparerai:
- 📊 Feature selection automatica
- 🔍 Dimensionality reduction (PCA, t-SNE)
- 🤖 AutoML per preprocessing automatizzato
- 📈 Time series preprocessing
- 🖼️ Image data preprocessing
Hai padroneggiato le tecniche fondamentali di preprocessing per Machine Learning!