Machine learning · Python

Previsione di opportunità di Cross Sell

Versione integrata nel sito senza iframe. Il notebook ricostruito resta disponibile come file .ipynb.

Codice
import pandas as pd
import numpy as np
import seaborn as sns
import warnings

from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, classification_report, confusion_matrix
from sklearn.model_selection import GridSearchCV
from imblearn.pipeline import Pipeline as ImbPipeline
Codice
BASE_URL = "D:/Anaconda/Cross_Sell/"
Codice
df_cross_sell = pd.read_csv(BASE_URL+"insurance_cross_sell.csv")
Codice
df_cross_sell.shape
Codice
df_cross_sell.count()
Codice
df_cross_sell.head()
Codice
print("\nInformazioni sul dataset:")
print(df_cross_sell.info())
Codice
df_cross_sell.describe()
Codice
df_cross_sell["Gender"].value_counts()
Codice
df_cross_sell["Vehicle_Damage"].value_counts()
Codice
df_cross_sell["Vehicle_Age"].value_counts()
Codice
print("Valori mancanti per colonna:")
print(df_cross_sell.isna().sum())
Codice
print("Valori nulli per colonna:")
print(df_cross_sell.isnull().sum())
Codice
counts_response = df_cross_sell["Response"].value_counts()
counts_response
Codice
print(f"Acquirenti che hanno risposto positivamente alla proposta = {counts_response[1]} ({counts_response[1]/counts_response.sum()*100:.2f}%)")
print(f"Acquirenti che non hanno risposto positivamente alla proposta = {counts_response[0]} ({counts_response[0]/counts_response.sum()*100:.2f}%)")
Codice
class_distribution=(df_cross_sell['Response'].value_counts(normalize=True))
Codice
print(class_distribution)
Codice
plt.figure(figsize=(10, 6))
class_distribution.plot(kind='bar')
plt.title('Distribuzione delle classi (Response)')
plt.xlabel('Classi')
plt.ylabel('Percentuale')
plt.xticks(rotation=0)
plt.show()
Codice
df_cross_sell.drop('id', axis=1, inplace=True)
Codice
X = df_cross_sell.drop('Response', axis=1) # dati in formato pandas
y = df_cross_sell['Response']
Codice
from sklearn.model_selection import train_test_split

# Funzione di valutazione
def evaluate_model(y_true, y_pred, y_pred_proba, model_name):
    results = {
        "Accuracy": accuracy_score(y_true, y_pred),
        "Precision": precision_score(y_true, y_pred),
        "Recall": recall_score(y_true, y_pred),
        "F1-score": f1_score(y_true, y_pred),
        "AUC-ROC": roc_auc_score(y_true, y_pred_proba)
    }
    
    print(f"\nEvaluation for {model_name}:")
    for metric, value in results.items():
        print(f"{metric}: {value:.4f}")
    
    print("\nConfusion Matrix:")
    print(confusion_matrix(y_true, y_pred))
    print("\nClassification Report:")
    print(classification_report(y_true, y_pred))
    
    return results
Codice
RANDOM_SEED = 42
X_train_lr, X_test_lr, y_train_lr, y_test_lr = train_test_split(X, y, test_size=0.15, random_state=RANDOM_SEED)
X_train_lr, X_val_lr, y_train_lr, y_val_lr = train_test_split(X_train_lr, y_train_lr, test_size=0.17, random_state=RANDOM_SEED)
Codice
X_train_lr.shape # set di addestramento
Codice
X_val_lr.shape # set di validazione 
               # serve a valutare le performance del modello durante il processo di addestramento
               # aiuta a prevenire l'overfitting, aiuta a regolare gli iperparametri
               # fornisce una stima delle performance del modello su dati non visti
Codice
X_test_lr.shape # set di test. Usato per la valutazione finale del modello
Codice
numeric_features = ['Age', 'Annual_Premium', 'Vintage', 'Driving_License', 'Region_Code', 'Previously_Insured', 'Policy_Sales_Channel']
categorical_features = ['Gender', 'Vehicle_Age', 'Vehicle_Damage']
Codice
# il preprocessor serve per standardizzare il processo di preparazione, pulizia e trasformazione dei dati
# assicura che i dati siano trattati allo stesso modo in fase di training, validazione e testing
# il one-hot encoding è una tecnica di codifica dei dati categorici
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), numeric_features),
        ('cat', OneHotEncoder(drop='first', sparse_output=False, handle_unknown='ignore'), categorical_features)
    ])
Codice
# la pipeline automatizza il flusso di lavoro
# combina più step di preprocessing e il modello in un unico oggetto
# prevenzione del data leakage, codice più pulito
pipeline_lr = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', LogisticRegression(class_weight='balanced', random_state=RANDOM_SEED, max_iter=1000))
])
Codice
pipeline_lr.fit(X_train_lr, y_train_lr)
Codice
from termcolor import colored

def print_colored_title(text):
    light_green = '\033[38;2;000;204;000m' 
    bold = '\033[1m'
    reset = '\033[0m'
    print(f"{light_green}{bold}{text}{reset}")
Codice
y_pred_train_lr = pipeline_lr.predict(X_train_lr)
y_pred_proba_train_lr = pipeline_lr.predict_proba(X_train_lr)[:, 1]

print("Performance sul set di training:")
train_results_lr = evaluate_model(y_train_lr, y_pred_train_lr, y_pred_proba_train_lr, "Logistic Regression (Training)")
Codice
y_pred_val_lr = pipeline_lr.predict(X_val_lr)
y_pred_proba_val_lr = pipeline_lr.predict_proba(X_val_lr)[:, 1]

print("\nPerformance sul set di validazione:")
val_results_lr = evaluate_model(y_val_lr, y_pred_val_lr, y_pred_proba_val_lr, "Logistic Regression (Validation)")
Codice
y_pred_test_lr = pipeline_lr.predict(X_test_lr)
y_pred_proba_test_lr = pipeline_lr.predict_proba(X_test_lr)[:, 1]

print("Logistic Regression:")
test_results_lr = evaluate_model(y_test_lr, y_pred_test_lr, y_pred_proba_test_lr, "Logistic Regression")
Codice
import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

def plot_confusion_matrix(y_true, y_pred, title):
    fig, ax = plt.subplots(figsize=(10, 6))
    ConfusionMatrixDisplay.from_predictions(y_true, y_pred, ax=ax, cmap='Blues', values_format='d')
    
    precision = precision_score(y_true, y_pred)
    recall = recall_score(y_true, y_pred)
    
    plt.text(0, -0.15, f"Precision: {precision:.4f}", transform=ax.transAxes)
    plt.text(1, -0.15, f"Recall: {recall:.4f}", transform=ax.transAxes, ha='right')
    
    plt.title(title)
    plt.tight_layout()
    plt.show()

# Matrice di confusione per il set di training
plot_confusion_matrix(y_train_lr, y_pred_train_lr, 'Matrice di Confusione (Training)')

# Matrice di confusione per il set di validazione
plot_confusion_matrix(y_val_lr, y_pred_val_lr, 'Matrice di Confusione (Validazione)')

# Matrice di confusione per il set di test
plot_confusion_matrix(y_test_lr, y_pred_test_lr, 'Matrice di Confusione (Test)')
Codice
y_pred_train_lr_mod = np.where(y_pred_proba_train_lr>0.65,1,0)
y_pred_proba_train_lr_mod = pipeline_lr.predict_proba(X_train_lr)[:, 1]

print("Performance sul set di training:")
train_results_lr_mod = evaluate_model(y_train_lr, y_pred_train_lr_mod, y_pred_proba_train_lr_mod, "Logistic Regression (Training)")
Codice
y_pred_val_lr_mod = np.where(y_pred_proba_val_lr>0.65,1,0)
y_pred_proba_val_lr_mod = pipeline_lr.predict_proba(X_val_lr)[:, 1]

print("\nPerformance sul set di validazione:")
val_results_lr_mod = evaluate_model(y_val_lr, y_pred_val_lr_mod, y_pred_proba_val_lr_mod, "Logistic Regression (Validation)")
Codice
y_pred_test_lr_mod = np.where(y_pred_proba_test_lr>0.65,1,0)
y_pred_proba_test_lr_mod = pipeline_lr.predict_proba(X_test_lr)[:, 1]

print("Logistic Regression:")
test_results_lr_mod = evaluate_model(y_test_lr, y_pred_test_lr_mod, y_pred_proba_test_lr_mod, "Logistic Regression")
Codice
def plot_confusion_matrix(y_true, y_pred, title):
    fig, ax = plt.subplots(figsize=(10, 6))
    ConfusionMatrixDisplay.from_predictions(y_true, y_pred, ax=ax, cmap='Blues', values_format='d')
    
    precision = precision_score(y_true, y_pred)
    recall = recall_score(y_true, y_pred)
    
    plt.text(0, -0.15, f"Precision: {precision:.4f}", transform=ax.transAxes)
    plt.text(1, -0.15, f"Recall: {recall:.4f}", transform=ax.transAxes, ha='right')
    
    plt.title(title)
    plt.tight_layout()
    plt.show()

plot_confusion_matrix(y_train_lr, y_pred_train_lr_mod, 'Matrice di Confusione (Training)')

plot_confusion_matrix(y_val_lr, y_pred_val_lr_mod, 'Matrice di Confusione (Validazione)')

plot_confusion_matrix(y_test_lr, y_pred_test_lr_mod, 'Matrice di Confusione (Test)')
Codice
results_lr = f"""
{'Set':<12}{'Accuracy':<12}{'F1-score':<12}{'AUC-ROC':<12}{'Precision':<12}{'Recall':<12}
{'-'*70}
{'Training   ':<12}{train_results_lr['Accuracy']:<12.4f}{train_results_lr['F1-score']:<12.4f}{train_results_lr['AUC-ROC']:<12.4f}{train_results_lr['Precision']:<12.4f}{train_results_lr['Recall']:<12.4f}
{'Validation ':<12}{val_results_lr['Accuracy']:<12.4f}{val_results_lr['F1-score']:<12.4f}{val_results_lr['AUC-ROC']:<12.4f}{val_results_lr['Precision']:<12.4f}{val_results_lr['Recall']:<12.4f}
{'Test       ':<12}{test_results_lr['Accuracy']:<12.4f}{test_results_lr['F1-score']:<12.4f}{test_results_lr['AUC-ROC']:<12.4f}{test_results_lr['Precision']:<12.4f}{test_results_lr['Recall']:<12.4f}
{'Train 0.65 ':<12}{train_results_lr_mod['Accuracy']:<12.4f}{train_results_lr_mod['F1-score']:<12.4f}{train_results_lr_mod['AUC-ROC']:<12.4f}{train_results_lr_mod['Precision']:<12.4f}{train_results_lr_mod['Recall']:<12.4f}
{'Val 0.65   ':<12}{val_results_lr_mod['Accuracy']:<12.4f}{val_results_lr_mod['F1-score']:<12.4f}{val_results_lr_mod['AUC-ROC']:<12.4f}{val_results_lr_mod['Precision']:<12.4f}{val_results_lr_mod['Recall']:<12.4f}
{'Test 0.65  ':<12}{test_results_lr_mod['Accuracy']:<12.4f}{test_results_lr_mod['F1-score']:<12.4f}{test_results_lr_mod['AUC-ROC']:<12.4f}{test_results_lr_mod['Precision']:<12.4f}{test_results_lr_mod['Recall']:<12.4f}
"""
print(results_lr)
Codice
from sklearn.metrics import roc_curve, auc


                                     # Curva ROC soglia 0.5

def plot_roc_curve(y_true, y_pred_proba, label, color):
    fpr, tpr, thresholds = roc_curve(y_true, y_pred_proba)
    roc_auc = auc(fpr, tpr)
    plt.plot(fpr, tpr, label=f'{label} (AUC = {roc_auc:.2f})', color=color)
    return fpr, tpr, thresholds

plt.figure(figsize=(10, 6))

# Curve ROC per i diversi set con soglia a 0.5
fpr_train, tpr_train, thresholds_train = plot_roc_curve(y_train_lr, y_pred_proba_train_lr, 'Training', 'darkblue')
fpr_val, tpr_val, thresholds_val = plot_roc_curve(y_val_lr, y_pred_proba_val_lr, 'Validation', 'darkorange')
fpr_test, tpr_test, thresholds_test = plot_roc_curve(y_test_lr, y_pred_proba_test_lr, 'Test', 'purple')

# Linea di riferimento (random classifier)
plt.plot([0, 1], [0, 1], color='#C0C0C0', linestyle='--')

# Funzione per trovare il punto più vicino a una data soglia
def find_nearest(array, value):
    return (np.abs(array - value)).argmin()

# Punti per le soglie specifiche
soglie = [0.5, 0.65]  # Aggiungo le soglie che ho usato
colori = ['#A7C7E7', '#FFCC99'] 

for soglia, colore in zip(soglie, colori):
    # Trovo il punto più vicino alla soglia per ogni set
    idx_train = find_nearest(thresholds_train, soglia)
    idx_val = find_nearest(thresholds_val, soglia)
    idx_test = find_nearest(thresholds_test, soglia)
    
    # Plotto i punti
    plt.plot(fpr_test[idx_test], tpr_test[idx_test], 'o', color=colore, markersize=10, label=f'Soglia {soglia}')

plt.xlabel('Falsi Positivi')
plt.ylabel('Veri Positivi')
plt.title('ROC Curve con Soglie')
plt.legend(loc="lower right")
plt.show()




                                        # Curva ROC soglia 0.65

def plot_roc_curve(y_true, y_pred_proba, label, color):
    fpr, tpr, thresholds = roc_curve(y_true, y_pred_proba)
    roc_auc = auc(fpr, tpr)
    plt.plot(fpr, tpr, label=f'{label} (AUC = {roc_auc:.2f})', color=color)
    return fpr, tpr, thresholds

plt.figure(figsize=(10, 6))

# Curve ROC per i diversi set con soglia a 0.65
fpr_train, tpr_train, thresholds_train = plot_roc_curve(y_train_lr, y_pred_proba_train_lr_mod, 'Training', 'blue')
fpr_val, tpr_val, thresholds_val = plot_roc_curve(y_val_lr, y_pred_proba_val_lr_mod, 'Validation', 'orange')
fpr_test, tpr_test, thresholds_test = plot_roc_curve(y_test_lr, y_pred_proba_test_lr_mod, 'Test', 'green')

# Linea di riferimento (random classifier)
plt.plot([0, 1], [0, 1], color='#C0C0C0', linestyle='--')

# Funzione per trovare il punto più vicino a una data soglia
def find_nearest(array, value):
    return (np.abs(array - value)).argmin()

# Punti per le soglie specifiche
soglie = [0.5, 0.65]  # Aggiungo le soglie che ho usato
colori = ['#A7C7E7', '#FFCC99'] 

for soglia, colore in zip(soglie, colori):
    # Trovo il punto più vicino alla soglia per ogni set
    idx_train = find_nearest(thresholds_train, soglia)
    idx_val = find_nearest(thresholds_val, soglia)
    idx_test = find_nearest(thresholds_test, soglia)
    
    # Plotto i punti
    plt.plot(fpr_test[idx_test], tpr_test[idx_test], 'o', color=colore, markersize=10, label=f'Soglia {soglia}')

plt.xlabel('Falsi Positivi')
plt.ylabel('Veri Positivi')
plt.title('ROC Curve con Soglie')
plt.legend(loc="lower right")
plt.show()
Codice
warnings.filterwarnings('ignore')

plt.figure(figsize=(10, 6))
sns.kdeplot(y_pred_proba_train_lr_mod, fill=True, color="blue", label="Train")
sns.kdeplot(y_pred_proba_val_lr_mod, fill=True, color="orange", label="Validation")
sns.kdeplot(y_pred_proba_test_lr_mod, fill=True, color="black", label="Test")

plt.axvline(x=0.65, color='red', linestyle='--', label='Soglia 0.65')

plt.xlabel('Probabilità predette')
plt.ylabel('Density')
plt.title('Distribuzione delle probabilità predette')
plt.legend()
plt.show()
Codice
def plot_probability_scatter(y_true, y_pred_proba):
    plt.figure(figsize=(10, 6))
    
    # Creo un array di indici
    indices = np.arange(len(y_true))
    
    # Scatter plot per la classe negativa (0)
    plt.scatter(indices[y_true == 0], y_pred_proba[y_true == 0], 
                color='blue', alpha=0.5, label='Classe Negativa')
    
    # Scatter plot per la classe positiva (1)
    plt.scatter(indices[y_true == 1], y_pred_proba[y_true == 1], 
                color='red', alpha=0.5, label='Classe Positiva')
    
    # Linea per la soglia
    plt.axhline(y=0.65, color='black', linestyle='--', label='Soglia 0.65')
    
    plt.title('Scatter Plot delle Probabilità Predette')
    plt.xlabel('Indice dell\'istanza')
    plt.ylabel('Probabilità Predetta')
    plt.legend()

    plt.show()

plot_probability_scatter(y_test_lr, y_pred_proba_test_lr_mod)
Codice
from sklearn.metrics import precision_recall_curve

def plot_precision_recall_vs_threshold(y_true, y_pred, test_name):
    precisions, recalls, thresholds = precision_recall_curve(y_true, y_pred)
    
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, precisions[:-1], "blue", label="Precision")
    plt.plot(thresholds, recalls[:-1], "orange", label="Recall")
    plt.xlabel("Threshold")
    plt.legend(loc="upper left")
    plt.ylim([0, 1])
    plt.title(f"Precision and Recall vs. Threshold ({test_name})")

plot_precision_recall_vs_threshold(y_train_lr, y_pred_proba_train_lr_mod, "Train")
plot_precision_recall_vs_threshold(y_val_lr, y_pred_proba_val_lr_mod, "Validation")
plot_precision_recall_vs_threshold(y_test_lr, y_pred_proba_test_lr_mod, "Test")
plt.show()
Codice
def analyze_errors(y_true, y_pred, test_name):
    errors = y_true != y_pred
    error_rate = errors.sum() / len(y_true)
    print(f"{test_name} - Numero di errori: {errors.sum()} su {len(y_true)} esempi ({error_rate:.2%})")
    return errors

train_errors_lr = analyze_errors(y_train_lr, y_pred_train_lr_mod, "Training")
val_errors_lr = analyze_errors(y_val_lr, y_pred_val_lr_mod, "Validation")
test_errors_lr = analyze_errors(y_test_lr, y_pred_test_lr_mod, "Test")
Codice
def analyze_error_types(y_true, y_pred, test_name):
    cm = confusion_matrix(y_true, y_pred)
    tn, fp, fn, tp = cm.ravel()
    print(f"\n{test_name} Error Analysis:")
    print(f"False Positives: {fp} ({fp/(fp+tn):.2%} of actual negatives)")
    print(f"False Negatives: {fn} ({fn/(fn+tp):.2%} of actual positives)")

analyze_error_types(y_train_lr, y_pred_train_lr_mod, "Training")
analyze_error_types(y_val_lr, y_pred_val_lr_mod, "Validation")
analyze_error_types(y_test_lr, y_pred_test_lr_mod, "Test")
Codice
plt.figure(figsize=(10, 6))
bars = plt.bar(['Training', 'Validation', 'Test'], 
                [train_errors_lr.mean(), val_errors_lr.mean(), test_errors_lr.mean()],
                yerr=[train_errors_lr.std(), val_errors_lr.std(), test_errors_lr.std()],
                capsize=5, color=['#A7C7E7', '#FFCC99', '#C0C0C0'])

plt.title('Confronto del Tasso di Errore tra i test')
plt.ylabel('Tasso di Errore')

# Aggiungo etichette con i valori sopra ciascuna barra
for i, bar in enumerate(bars):
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2., height,
             f'{height:.4f}',
             ha='center', va='bottom')



plt.tight_layout()  # Assicura che tutto si adatti bene nella figura
plt.show()
Codice
from scipy.stats import norm

accuracy_lr = accuracy_score(y_test_lr, y_pred_test_lr_mod)
n_lr = len(y_test_lr)
std_error_lr = np.sqrt(accuracy_lr * (1 - accuracy_lr) / n_lr)
confidence_interval_lr = norm.interval(0.95, loc=accuracy_lr, scale=std_error_lr)

print(f"Accuratezza: {accuracy_lr:.4f}")
print(f"Intervallo di confidenza al 95%: {confidence_interval_lr}")
Codice
accuracy_lr = 0.7526
confidence_interval_lr = (0.7490648538668198, 0.7561391974215109) 

plt.figure(figsize=(10, 6))

# Calcolo l'errore come la differenza tra il valore di accuratezza e i limiti dell'intervallo di confidenza
yerr = [[accuracy_lr - confidence_interval_lr[0]], [confidence_interval_lr[1] - accuracy_lr]]

plt.bar(['Accuratezza'], [accuracy_lr], yerr=yerr, capsize=5)

plt.title('Accuratezza con Intervallo di Confidenza al 95%')
plt.ylabel('Accuratezza')
plt.ylim(0, 1)  # Assumendo che l'accuratezza sia tra 0 e 1

# Aggiungo l'etichetta con il valore esatto sopra la barra
plt.text(0, accuracy_lr, f'{accuracy_lr:.4f}', ha='center', va='bottom')

plt.show()
Codice
feature_names_lr = (numeric_features + 
                 pipeline_lr.named_steps['preprocessor']
                 .named_transformers_['cat']
                 .get_feature_names_out(categorical_features).tolist())

feature_importance_lr = pd.DataFrame({
    'feature': feature_names_lr,
    'importance': abs(pipeline_lr.named_steps['classifier'].coef_[0])
}).sort_values('importance', ascending=False)

print("\nFeature Importance:")
print(feature_importance_lr)

N = 10  # Numero di top feature da visualizzare
plt.figure(figsize=(10, 6))
sns.barplot(x='importance', y='feature', data=feature_importance_lr.head(N))
plt.title(f'Top {N} Feature Importances (Logistic Regression 0.65)')
plt.tight_layout()
plt.show()
Codice
# Le feature che voglio visualizzare
feature1 = 'Annual_Premium'
feature2 = 'Vintage'

# Griglia di punti
x0, x1 = X[feature1], X[feature2]
xx, yy = np.meshgrid(np.linspace(x0.min()-1, x0.max()+1, 100),
                     np.linspace(x1.min()-1, x1.max()+1, 100))

# Preparo i dati per la predizione
X_plot = np.c_[xx.ravel(), yy.ravel()]

# Creo un DataFrame con le stesse colonne di X originale
X_plot_df_lr = pd.DataFrame(X_plot, columns=[feature1, feature2])
for col in X.columns:
    if col not in X_plot_df_lr.columns:
        X_plot_df_lr[col] = X[col].mode()[0]  # Uso il valore più frequente per le altre feature

# Predizioni
Z = pipeline_lr.predict_proba(X_plot_df_lr)[:, 1]
Z = Z.reshape(xx.shape)

plt.figure(figsize=(10, 6))
plt.contourf(xx, yy, Z, alpha=0.4, cmap='RdYlBu')
scatter = plt.scatter(X[feature1], X[feature2], c=y, alpha=0.8, cmap='RdYlBu')
plt.xlabel(feature1)
plt.ylabel(feature2)
plt.title(f'Decision Boundary - {feature1} vs {feature2}')
plt.colorbar(scatter)
plt.show()
Codice
X_train_rf, X_test_rf, y_train_rf, y_test_rf = train_test_split(X, y, test_size=0.15, random_state=RANDOM_SEED)
X_train_rf, X_val_rf, y_train_rf, y_val_rf = train_test_split(X_train_rf, y_train_rf, test_size=0.17, random_state=RANDOM_SEED)
Codice
pipeline_rf = Pipeline([
    ('preprocessor', preprocessor),  # Usa il preprocessor esistente
    ('classifier', RandomForestClassifier(class_weight='balanced', random_state=RANDOM_SEED, n_jobs=-1))
])
Codice
pipeline_rf.fit(X_train_rf, y_train_rf)
Codice
y_pred_train_rf = pipeline_rf.predict(X_train_rf)
y_pred_proba_train_rf = pipeline_rf.predict_proba(X_train_rf)[:, 1]

print("Performance sul set di training:")
train_results_rf = evaluate_model(y_train_rf, y_pred_train_rf, y_pred_proba_train_rf, "Random Forest (Training)")
Codice
y_pred_val_rf = pipeline_rf.predict(X_val_rf)
y_pred_proba_val_rf = pipeline_rf.predict_proba(X_val_rf)[:, 1]

print("\nPerformance sul set di validazione:")
val_results_rf = evaluate_model(y_val_rf, y_pred_val_rf, y_pred_proba_val_rf, "Random Forest (Validation)")
Codice
y_pred_test_rf = pipeline_rf.predict(X_test_rf)
y_pred_proba_test_rf = pipeline_rf.predict_proba(X_test_rf)[:, 1]

print("Random Forest Regression:")
test_results_rf = evaluate_model(y_test_rf, y_pred_test_rf, y_pred_proba_test_rf, "Random Forest Regression")
Codice
results_rf = f"""
Riepilogo delle metriche principali:
{'Set':<10}{'Accuracy':<12}{'F1-score':<12}{'AUC-ROC':<12}{'Precision':<12}{'Recall':<12}
{'-'*70}
{'Training':<10}{train_results_rf['Accuracy']:<12.4f}{train_results_rf['F1-score']:<12.4f}{train_results_rf['AUC-ROC']:<12.4f}{train_results_rf['Precision']:<12.4f}{train_results_rf['Recall']:<12.4f}
{'Validation':<10}{val_results_rf['Accuracy']:<12.4f}{val_results_rf['F1-score']:<12.4f}{val_results_rf['AUC-ROC']:<12.4f}{val_results_rf['Precision']:<12.4f}{val_results_rf['Recall']:<12.4f}
{'Test':<10}{test_results_rf['Accuracy']:<12.4f}{test_results_rf['F1-score']:<12.4f}{test_results_rf['AUC-ROC']:<12.4f}{test_results_rf['Precision']:<12.4f}{test_results_rf['Recall']:<12.4f}
"""
print(results_rf)
Codice
plot_confusion_matrix(y_train_rf, y_pred_train_rf, 'Matrice di Confusione (Training)')

plot_confusion_matrix(y_val_rf, y_pred_val_rf, 'Matrice di Confusione (Validazione)')

plot_confusion_matrix(y_test_rf, y_pred_test_rf, 'Matrice di Confusione (Test)')
Codice
plt.figure(figsize=(10, 6))

# Curve ROC per i diversi set
fpr_train, tpr_train, thresholds_train = plot_roc_curve(y_train_rf, y_pred_proba_train_rf, 'Training', 'darkblue')
fpr_val, tpr_val, thresholds_val = plot_roc_curve(y_val_rf, y_pred_proba_val_rf, 'Validation', 'darkorange')
fpr_test, tpr_test, thresholds_test = plot_roc_curve(y_test_rf, y_pred_proba_test_rf, 'Test', 'purple')

# Linea di riferimento (random classifier)
plt.plot([0, 1], [0, 1], color='#C0C0C0', linestyle='--')

# Funzione per trovare il punto più vicino a una data soglia
def find_nearest(array, value):
    return (np.abs(array - value)).argmin()

    
    # Plotto i punti
    plt.plot(fpr_test[idx_test], tpr_test[idx_test], 'o', color=colore, markersize=10, label=f'Soglia {soglia}')

plt.xlabel('Falsi Positivi')
plt.ylabel('Veri Positivi')
plt.title('ROC Curve con Soglie')
plt.legend(loc="lower right")
plt.show()
Codice
warnings.filterwarnings('ignore')

plt.figure(figsize=(10, 6))
sns.kdeplot(y_pred_proba_train_rf, fill=True, color="blue", label="Train")
sns.kdeplot(y_pred_proba_val_rf, fill=True, color="orange", label="Validation")
sns.kdeplot(y_pred_proba_test_rf, fill=True, color="black", label="Test")

plt.xlabel('Predicted Probability')
plt.ylabel('Density')
plt.title('Distribution of Predicted Probabilities')
plt.legend()
plt.show()
Codice
from sklearn.metrics import precision_recall_curve

def plot_precision_recall_vs_threshold(y_true, y_scores, test_name):
    precisions, recalls, thresholds = precision_recall_curve(y_true, y_scores)
    
    plt.figure(figsize=(10, 6))
    plt.plot(thresholds, precisions[:-1], "b--", label="Precision")
    plt.plot(thresholds, recalls[:-1], "g-", label="Recall")
    plt.xlabel("Threshold")
    plt.legend(loc="upper left")
    plt.ylim([0, 1])
    plt.title(f"Precision and Recall vs. Threshold ({test_name})")

plot_precision_recall_vs_threshold(y_train_rf, y_pred_proba_train_rf, "Train")
plot_precision_recall_vs_threshold(y_val_rf, y_pred_proba_val_rf, "Validation")
plot_precision_recall_vs_threshold(y_test_rf, y_pred_proba_test_rf, "Test")
plt.show()
Codice
def analyze_errors(y_true, y_pred, test_name):
    errors = y_true != y_pred
    error_rate = errors.sum() / len(y_true)
    print(f"{test_name} - Numero di errori: {errors.sum()} su {len(y_true)} esempi ({error_rate:.2%})")
    return errors

train_errors_rf = analyze_errors(y_train_rf, y_pred_train_rf, "Training")
val_errors_rf = analyze_errors(y_val_rf, y_pred_val_rf, "Validation")
test_errors_rf = analyze_errors(y_test_rf, y_pred_test_rf, "Test")
Codice
def analyze_error_types(y_true, y_pred, test_name):
    cm = confusion_matrix(y_true, y_pred)
    tn, fp, fn, tp = cm.ravel()
    print(f"\n{test_name} Error Analysis:")
    print(f"False Positives: {fp} ({fp/(fp+tn):.2%} of actual negatives)")
    print(f"False Negatives: {fn} ({fn/(fn+tp):.2%} of actual positives)")

analyze_error_types(y_train_rf, y_pred_train_rf, "Training")
analyze_error_types(y_val_rf, y_pred_val_rf, "Validation")
analyze_error_types(y_test_rf, y_pred_test_rf, "Test")
Codice
plt.figure(figsize=(10, 6))
bars = plt.bar(['Training', 'Validation', 'Test'], 
                [train_errors_rf.mean(), val_errors_rf.mean(), test_errors_rf.mean()],
                yerr=[train_errors_rf.std(), val_errors_rf.std(), test_errors_rf.std()],
                capsize=5, color=['#A7C7E7', '#FFCC99', '#C0C0C0'])

plt.title('Confronto del Tasso di Errore tra i test')
plt.ylabel('Tasso di Errore')

# Aggiungoe etichette con i valori sopra ciascuna barra
for i, bar in enumerate(bars):
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2., height,
             f'{height:.4f}',
             ha='center', va='bottom')

plt.tight_layout()
plt.show()
Codice
accuracy_rf = accuracy_score(y_test_rf, y_pred_test_rf)
n_rf = len(y_test_rf)
std_error_rf = np.sqrt(accuracy_rf * (1 - accuracy_rf) / n_rf)
confidence_interval_rf = norm.interval(0.95, loc=accuracy_rf, scale=std_error_rf)

print(f"Accuratezza: {accuracy_rf:.4f}")
print(f"Intervallo di confidenza al 95%: {confidence_interval_rf}")
Codice
plt.figure(figsize=(10, 6))

# Calcolo l'errore come la differenza tra il valore di accuratezza e i limiti dell'intervallo di confidenza
yerr_rf = [[accuracy_rf - confidence_interval_rf[0]], [confidence_interval_rf[1] - accuracy_rf]]

plt.bar(['Accuratezza'], [accuracy_rf], yerr=yerr, capsize=5)

plt.title('Accuratezza con Intervallo di Confidenza al 95%')
plt.ylabel('Accuratezza')
plt.ylim(0, 1)  # Assumendo che l'accuratezza sia tra 0 e 1

# Aggiungo l'etichetta con il valore esatto sopra la barra
plt.text(0, accuracy_rf, f'{accuracy_rf:.4f}', ha='center', va='bottom')

plt.show()
Codice
feature_names_rf = (numeric_features + 
                    pipeline_rf.named_steps['preprocessor']
                    .named_transformers_['cat']
                    .get_feature_names_out(categorical_features).tolist())

feature_importance_rf = pd.DataFrame({
    'feature': feature_names_rf,
    'importance': pipeline_rf.named_steps['classifier'].feature_importances_
}).sort_values('importance', ascending=False)

print("\nFeature Importance:")
print(feature_importance_rf)


N = 10  # Numero di top feature da visualizzare
plt.figure(figsize=(10, 6))
sns.barplot(x='importance', y='feature', data=feature_importance_rf.head(N))
plt.title(f'Top {N} Feature Importances (Random Forest)')
plt.tight_layout()
plt.show()
Codice
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.15, random_state=RANDOM_SEED)
X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size=0.17, random_state=RANDOM_SEED)

def evaluate_model_due(y_true, y_pred, y_pred_proba):
    return {
        'Accuracy': accuracy_score(y_true, y_pred),
        'Precision': precision_score(y_true, y_pred),
        'Recall': recall_score(y_true, y_pred),
        'F1-score': f1_score(y_true, y_pred),
        'AUC-ROC': roc_auc_score(y_true, y_pred_proba)
    }

def create_and_evaluate_models(X_train, y_train, X_val, y_val, X_test, y_test, model_class, model_name):
    base_model = model_class(random_state=RANDOM_SEED) # Istanza del modello base
                                                       # base_model è il parametro che verrà passato
                                                       # alle funzioni LogisticRegression e RandomForestClassifier
                                                       # durante la creazione e valutazione dei modelli successivamente
    
    # Creo i modelli
    smote = ImbPipeline([
        ('preprocessor', preprocessor),
        ('smote', SMOTE(random_state=RANDOM_SEED)), # Bilanciamento
        ('classifier', base_model) # Classificatore
    ])
    
    under = ImbPipeline([
        ('preprocessor', preprocessor),
        ('undersampler', RandomUnderSampler(random_state=RANDOM_SEED)),
        ('classifier', base_model)
    ])

    # Dizionario 
    models = {
        f'{model_name} SMOTE': smote,
        f'{model_name} Undersampling': under
    }

    # Inizializzazione del dizionario vuoto
    results = {}
    
    # Iterazione su ciascun modello del dizionario
    for name, model in models.items():
        model.fit(X_train, y_train) # Addestramento
        
        y_train_pred = model.predict(X_train) # Previsioni e calcolo delle probabilità
        y_train_pred_proba = model.predict_proba(X_train)[:, 1]
        train_results = evaluate_model_due(y_train, y_train_pred, y_train_pred_proba) # Valutazione delle prestazioni
        
        y_val_pred = model.predict(X_val)
        y_val_pred_proba = model.predict_proba(X_val)[:, 1]
        val_results = evaluate_model_due(y_val, y_val_pred, y_val_pred_proba)
        
        y_test_pred = model.predict(X_test)
        y_test_pred_proba = model.predict_proba(X_test)[:, 1]
        test_results = evaluate_model_due(y_test, y_test_pred, y_test_pred_proba)

        # Memorizzazione dei risultati nel dizionario
        results[name] = {
            'model': model,
            'training': train_results,
            'validation': val_results,
            'test': test_results
        }
    
return results
Codice
# Creo un dizionario con i risultati per Logistic Regression 0.65 e Random Forest Standard che poi verranno
# usati per fare le comparazioni
results_lr_065 = {
    'Accuracy': test_results_lr_mod['Accuracy'],
    'F1-score': test_results_lr_mod['F1-score'],
    'AUC-ROC': test_results_lr_mod['AUC-ROC'],
    'Precision': test_results_lr_mod['Precision'],
    'Recall': test_results_lr_mod['Recall']
}


results_rf = {
    'Accuracy': test_results_rf['Accuracy'],
    'F1-score': test_results_rf['F1-score'],
    'AUC-ROC': test_results_rf['AUC-ROC'],
    'Precision': test_results_rf['Precision'],
    'Recall': test_results_rf['Recall']
}
Codice
# Creazione e valutazione dei modelli di Regressione Logistica e Random Forest
lr_results = create_and_evaluate_models(X_train, y_train, X_val, y_val, X_test, y_test, 
                                        LogisticRegression, "Logistic Regression")

rf_results = create_and_evaluate_models(X_train, y_train, X_val, y_val, X_test, y_test, 
                                        RandomForestClassifier, "Random Forest")
Codice
# Combino i risultati
all_results = {**lr_results, **rf_results}

comparison_train = pd.DataFrame({name: results['training'] for name, results in all_results.items()}).transpose()

# Creo il confronto per il set di validazione
comparison_val = pd.DataFrame({name: results['validation'] for name, results in all_results.items()}).transpose()

# Creo il DataFrame per i risultati del test
comparison_test = pd.DataFrame({name: results['test'] for name, results in all_results.items()}).transpose()

# Aggiungo le righe di summary per Random Forest e Logistic Regression 0.65
comparison_test.loc['Logistic Regression 0.65'] = pd.Series(results_lr_065)
comparison_test.loc['Random Forest Standard'] = pd.Series(results_rf)
Codice
from termcolor import colored

train_text = "Risultati sul set di training:"
val_text = "Risultati sul set di validazione:"
test_text = "Risultati sul set di test di tutti i modelli esaminati:"

def print_colored_title(text):
    light_green = '\033[38;2;000;204;000m' 
    bold = '\033[1m'
    reset = '\033[0m'
    print(f"{light_green}{bold}{text}{reset}")


print_colored_title(train_text)
print(comparison_train)
print("\n") 

print_colored_title(val_text)
print(comparison_val)
print("\n") 

print_colored_title(test_text)
print(comparison_test)
Codice
def plot_roc_curve(y_true, y_pred_proba, label, color):
    fpr, tpr, thresholds = roc_curve(y_true, y_pred_proba)
    roc_auc = auc(fpr, tpr)
    plt.plot(fpr, tpr, label=f'{label} (AUC = {roc_auc:.2f})', color=color)
    return fpr, tpr, thresholds

plt.figure(figsize=(10, 6))

# Curve ROC per i modelli SMOTE e Undersampling
for name, results in all_results.items():
    try:
        model = results['model']
        y_pred_proba = model.predict_proba(X_test)[:, 1]
        plot_roc_curve(y_test, y_pred_proba, name, None)  # None per colore casuale
    except Exception as e:
        pass  # Silenziosamente ignora eventuali errori

# Curve ROC per RF standard e LR standard (solo test)
fpr_rf, tpr_rf, _ = plot_roc_curve(y_test_rf, y_pred_proba_test_rf, 'Random Forest Standard (Test)', 'purple')
fpr_lr, tpr_lr, _ = plot_roc_curve(y_test_lr, y_pred_proba_test_lr, 'Logistic Regression 0.65 (Test)', 'darkgreen')

# Linea di riferimento (random classifier)
plt.plot([0, 1], [0, 1], color='#C0C0C0', linestyle='--', label='Random Chance')

# Funzione per trovare il punto più vicino a una data soglia
def find_nearest(array, value):
    return (np.abs(array - value)).argmin()

# Aggiungo il punto per la soglia specifica (solo per LR standard)
soglie =  [0.65]
colori = ['#A7C7E7', '#FFCC99']
for soglia, colore in zip(soglie, colori):
    idx_lr = find_nearest(_, soglia)  # _ contiene i thresholds dell'ultima chiamata a plot_roc_curve (LR)
    plt.plot(fpr_lr[idx_lr], tpr_lr[idx_lr], 'o', color=colore, markersize=10, label=f'LR Soglia {soglia}')

plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curves - All Models')
plt.legend(loc="lower right")
plt.grid(True)
plt.show()

print("\nValori AUC-ROC:")
for name, results in all_results.items():
    model = results['model']
    y_pred_proba = model.predict_proba(X_test)[:, 1]
    roc_auc = auc(roc_curve(y_test, y_pred_proba)[0], roc_curve(y_test, y_pred_proba)[1])
    print(f"{name}: {roc_auc:.4f}")

print(f"Random Forest Standard: {auc(fpr_rf, tpr_rf):.4f}")
print(f"Logistic Regression 0.65: {auc(fpr_lr, tpr_lr):.4f}")