Nel momento in cui andrai a strutturare un progetto di data mining o di machine learning non è difficile incappare nella situazione in cui il dataset di lavoro contenga valori mancanti.

I valori mancanti nei dataset possono derivare da diverse cause, come ad esempio un errore nella raccolta dei dati, la perdita di dati durante il trasferimento da un sistema di archiviazione all’altro, informazioni non applicabili per una determinata osservazione, ecc…

Ci sono vari modi per gestire i valori mancanti in un dataset:

  • Rimozione delle tuple o delle colonne;
  • Imputazione;
  • Utilizzare algoritmi non sensibili ai valori Nan (Not a number) come RandomForest o XGBoost (entrambi classificatori ensemble).

In questo articolo, mi concentrerò sulla gestione dei valori Nan in un dataset tramite l’imputazione. Quindi, vedremo insieme cos’è e come funziona un imputer, quante tipologie ne esistono, in che modo implementarlo in Python.

Cos’è un imputer

Nel momento in cui andiamo a lavorare su un dataset, una delle prime cose da controllare è la presenza di eventuali valori Nan (o nulli) che potrebbe falsare i risultati del nostro progetto oppure addirittura causare problemi durante l’eventuale addestramento di modelli di machine learning.

L’uso del termine imputazione nel data mining deriva dal concetto generale di attribuire o assegnare valori laddove questi non siano disponibili. Infatti, l’imputer risolve il problema dei valori mancanti, cioè sostituendoli con valori sostitutivi (spesso stimati). Ci sono diversi metodi di imputazione, tra cui:

  • Imputazione con la media: i valori mancanti vengono sostituiti con la media dei valori disponibili per quella specifica variabile (potrebbe non rappresentare adeguatamente la distribuzione dei dati);
  • Imputazione con la mediana: i valori mancanti vengono sostituiti con la mediana dei valori disponibili;
  • Imputazione con la moda: i valori mancanti vengono sostituiti con il valore che appare più frequentemente nei dati (soluzione valida per dati categorici);
  • Imputazione con valori costanti: puoi sostituire i valori mancanti con un valore costante (ad esempio 0);
  • Imputazione tramite algoritmi di Machine Learning: vengono utilizzati dei modelli di machine learning per prevedere e sostituire i valori mancanti.

Quale imputazione conviene utilizzare? Risposta da ingegnere: dipende! Ovviamente, dipende dal contesto del tuo specifico progetto. Non è possibile stabilirlo a priori.

Come fare imputazione in Python

In Python puoi fare imputazione tramite la libreria scikit-learn, in particolare con SimpleImputer e IterativeImputer.

SimpleImputer

Il SimpleImputer utilizza semplici statistiche (come media, mediana, ecc…) per imputare dei valori Nan. Ecco un esempio di codice.

from sklearn.impute import SimpleImputer
import numpy as np
import pandas as pd

# Supponiamo di avere un DataFrame con alcune colonne contenenti valori mancanti
data = {
'Age': [25, np.nan, 27, 29, 32],
'Salary': [50000, 55000, np.nan, np.nan, 60000],
'Gender': ['M', 'F', 'F', 'M', np.nan]
}
df = pd.DataFrame(data)

# Creazione di un SimpleImputer che imputa i valori mancanti utilizzando la media per le colonne numeriche
imputer_num = SimpleImputer(strategy='mean')

# Puoi applicare l'imputer alle colonne numeriche
df['Age'] = imputer_num.fit_transform(df[['Age']])
df['Salary'] = imputer_num.fit_transform(df[['Salary']])

IterativeImputer

A differenza del metodo precedente si tratta di uno strumento più avanzato per imputare i valori mancanti, l’IterativeImputer stima i valori mancanti in modo iterativo, modellando ogni feature con valori mancanti come una funzione delle altre feature.

Ecco un esempio di codice in si fa uso di IterativeImputer:

from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer
import numpy as np
import pandas as pd

# Creiamo un DataFrame con alcuni valori mancanti
data = {
'Age': [25, np.nan, 27, 29, 32],
'Salary': [50000, 55000, np.nan, np.nan, 60000],
'YearsOfExperience': [5, 2, 7, 10, np.nan]
}
df = pd.DataFrame(data)

# Utilizziamo IterativeImputer con un numero massimo di 19 iterazioni
imputer = IterativeImputer(max_iter=10, random_state=0)

# Fitting e trasformazione del dataframe
df_imputed = imputer.fit_transform(df)

# Convertiamo il risultato nuovamente in un DataFrame e conserviamo i nomi delle colonne originali
df_imputed = pd.DataFrame(df_imputed, columns=df.columns)

print(df_imputed)

Dove con max_iter si intende il numero massimo di iterazioni da fare per ogni attributo con valori Nan. Infatti, ad ogni iterazione l’algoritmo cerca di prevedere i valori mancanti di un attributo tramite i valori attuali, ovvero quelli imputati nelle iterazioni precedenti.

Nel codice di esempio, verranno eseguite 10 iterazioni e in ognuna di esse verranno predetti alcuni valori. I risultati della decima iterazione saranno quelli definitivi. Si raggiunge la convergenza nel momento in cui delle iterazioni successive non portano miglioramenti significativi dei risultati già predetti. Quindi, se ad esempio facendo anche 11/12 iterazioni i risultati dell’imputazioni sono molto vicini a quelli già ottenuti nella decima iterazione non ha senso iterare oltre 10.

Ovviamente, se l’imputazione converge prima delle iterazioni massime, l’algoritmo si ferma in anticipo senza consumare altre risorse computazionali. Per calcolare la convergenza si confrontano i valori imputati tra le varie iterazioni.

Potrebbe succedere che per un attributo l’imputazione converga prima rispetto ad un altro? Si, ad esempio per l’attributo A bastano 4 iterazioni mentre per l’attributo B sono necessarie 9 iterazioni. Tuttavia, l’imputer calcola la convergenza complessiva e non sul singolo attributo. Di conseguenza, se il sospetto è che ci siano degli attributi che convergano prima di altri, la soluzione migliore è raggruppare gli attributi per il quale si prevedano serva lo stesso numero di attributi e lanciare un imputer singolarmente per ogni gruppo. Tramite il parametro verbose siamo in grado di avere dei messaggi di log ad ogni iterazione, ergo possiamo avere informazioni anche sul raggiungimento o meno della convergenza.

Per prevedere i valori mancanti sulla base degli altri valori di tutta la riga, l’IterativeImputer utilizza l’estimatore. Ne esistono di vari tipi da utilizzare in base ai dati che abbiamo a disposizione:

  • BayesianRidge (utilizzato di default);
  • LinearRegression;
  • LogisticRegression;
  • DecisionTreeClassifier;
  • RandomForestClassifier.
Categorie: Informatica