Travaux pratiques - Données manquantes¶
(correspond à 1 séance de TP)
Références externes utiles :
L’objectif de cette séance de TP est de présenter l’utilisation de méthodes simples d’imputation des données manquantes, ainsi que de contribuer à une meilleure compréhension de la problématique associée.
L’imputation dans Scikit-learn¶
La description de l’implémentation de l’imputation de données manquantes se trouve dans http://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html#sklearn.impute.SimpleImputer. On peut constater que pour le moment seules sont directement disponibles les méthodes basées sur l’imputation par une valeur unique.
Pour définir un imputer on appelle SimpleImputer(missing_values=nan, strategy=’mean’, fill_value=None, verbose=0, copy=True). Les paramètres d’appel sont (pour des explications plus détaillées voir le lien ci-dessus) :
missing_values: codage des valeurs manquantes dans le tableau de données. Ce codage peut être fait de différentes façons, par ex. par « NaN » (Not a Number),np.nan, parfois par0ou par des valeurs qui sont éloignées du domaine de variation de la variable concernée, par ex.-999. En général, le même code est employé pour les valeurs manquantes dans tout le tableau de données.
strategy: méthode employée pour l’imputation :'mean'(par défaut) ou'median','most_frequent'ou'constant'; en général,'most_frequent'n’a pas de sens pour des variables continues ; le calcul de la moyenne, respectivement de la médiane ou de la valeur la plus fréquente est réalisé sur les observations complètes ; si'constant', alors c’est la valeur indiquée dans'fill_value'qui est systématiquement employée.
fill_value: indique la valeur constante (chaîne de caractères ou valeur numérique, suivant le cas) utilisée pour compléter les valeurs manquantes (par défautNone).
verbose: contrôle la verbosité (0ou1, par défaut0c’est à dire moins verbeux).
copy: siFalse, la matrice de données est écrasée (si cela est possible) par les données transformées (par défautTrue).
Le seul attribut accessible est :
statistics_: array[n_features]dans lequel on trouve les valeurs calculées pour réaliser l’imputation.
Les méthodes qui peuvent être employées :
fit(X, y=None): calcul des valeurs à utiliser pour l’imputation.
transform(X): imputation des données manquantes en utilisant les valeurs obtenues lors de l’appel àfit.
fit_transform(X, y=None): calcul des valeurs à utiliser pour l’imputation et application de l’imputation.
get_params([deep]): lire les valeurs des paramètres de l’estimateur employé.
set_params(**params): donner des valeurs aux paramètres de l’estimateur employé.
Imputation par une valeur unique, pour des données générées¶
Nous examinerons d’abord le résultat de l’application de méthodes d’imputation proposées par Scikit-learn à des données simples bidimensionnelles. Après avoir généré les données complètes, nous supprimons les valeurs de la seconde variable pour certaines observations afin d’obtenir des données incomplètes.
# importations préalables >>> import numpy as np >>> from sklearn.impute import SimpleImputer # Génération des données bidimensionnelles complètes >>> n_base = 100 >>> data1 = np.random.randn(n_base,2) + [5,5] >>> data2 = np.random.randn(n_base,2) + [3,2] >>> data3 = np.random.randn(n_base,2) + [1,5] >>> data = np.concatenate((data1,data2,data3)) >>> data.shape # vérification (300, 2) >>> np.random.shuffle(data) >>> n_samples = data.shape[0] # visualisation (optionnelle) des données générées >>> import matplotlib.pyplot as plt >>> plt.plot(data[:,0],data[:,1],'r+') >>> plt.show() # Suppréssion de certaines valeurs de la seconde variable (colonne) # pour obtenir des données manquantes # # définition du taux de lignes à valeurs manquantes >>> missing_rate = 0.3 >>> n_missing_samples = int(np.floor(n_samples * missing_rate)) >>> n_missing_samples 90 # choix des lignes à valeurs manquantes >>> missing_samples = np.hstack((np.zeros(n_samples - n_missing_samples, dtype=np.bool), np.ones(n_missing_samples, dtype=np.bool))) >>> np.random.shuffle(missing_samples) >>> missing_samples ... # obtenir la matrice avec données manquantes : manque indiqué par # valeurs NaN dans la seconde colonne pour les lignes True dans # missing_samples >>> data_missing = data.copy() >>> data_missing[np.where(missing_samples), 1] = np.nan >>> data_missing ... # imputation par la moyenne >>> imp = SimpleImputer(missing_values=np.nan, strategy='mean') >>> data_imputed = imp.fit_transform(data_missing) >>> data_imputed ... # calculer l'"erreur" d'imputation >>> from sklearn.metrics import mean_squared_error >>> mean_squared_error(data[missing_samples,1],data_imputed[missing_samples,1]) ...
Question :
Comment caractériser ici le mécanisme par lequel des données manquent : MCAR, MAR, MNAR ?
Question :
Affichez les valeurs calculées (les moyennes) employées pour l’imputation.
Question :
Appliquez une imputation par la médiane et examinez l’erreur résultante.
Question :
Appliquez une imputation par 0 et examinez l’erreur résultante. Pourquoi est-elle nettement supérieure à celle obtenue lors de l’imputation par la moyenne ou par la médiane ?
Imputation par le centre du groupe, pour des données générées¶
Cette méthode n’est pas directement disponible dans Scikit-learn mais nous pouvons néanmoins l’appliquer de façon assez simple. Pour cela, nous ferons d’abord une classification automatique avec K-means des observations sans données manquantes. Nous trouverons ensuite, pour chaque observation incomplète (à données manquantes), quel est le centre de groupe le plus proche de cette observation (en utilisant une distance partielle, calculée seulement sur les coordonnées renseignées pour cette observation) ; nous nous servirons pour cela de la méthode de classement Nearest centroid, voir http://scikit-learn.org/stable/modules/neighbors.html#nearest-centroid-classifier. Enfin, pour chaque observation incomplète nous complèterons la valeur manquante par la coordonnée correspondante du centre de groupe le plus proche.
# obtenir le tableau composé des seules observations complètes >>> data_filtered = data[~missing_samples, :] >>> data_filtered.shape # vérification (210, 2) # application de la classification automatique aux observations complètes >>> from sklearn.cluster import KMeans >>> kmeans = KMeans(n_clusters=3).fit(data_filtered) >>> kmeans.cluster_centers_ # affichage des centres obtenus pour les groupes ... # pour chaque observation incomplète, détermination du centre le plus proche >>> from sklearn.neighbors.nearest_centroid import NearestCentroid >>> y = np.array([1, 2, 3]) # les étiquettes des groupes >>> ncPredictor = NearestCentroid() # les centres calculés par k-means sont associés aux 3 étiquettes des groupes # (les 'observations' pour NearestCentroid sont les centres issus de k-means) # seules les coordonnées des centres sur l'axe 1 sont employées >>> ncPredictor.fit(kmeans.cluster_centers_[:,0].reshape(-1, 1), y) # l'index du centre le plus proche est déterminé pour chaque observation à # donnée manquante (à partir de la valeur de la variable non manquante, axe 1 >>> whichClust = ncPredictor.predict(data_missing[missing_samples, 0].reshape(-1,1)) # détermination des valeurs à utiliser pour l'imputation : pour chaque # observation, la valeur sur l'axe 2 du centre correspondant >>> estimated = np.zeros(n_missing_samples) >>> indices = range(n_missing_samples) >>> for i in indices: estimated[i] = kmeans.cluster_centers_[whichClust[i]-1,1] >>> data_imputed = data_missing.copy() # initialisation de data_imputed # imputation avec les valeurs obtenues >>> data_imputed[missing_samples, 1] = estimated # calcul de l'erreur moyenne d'imputation >>> mean_squared_error(data[missing_samples,1],data_imputed[missing_samples,1]) ...
L’erreur moyenne peut être supérieure à celle obtenue lors de l’imputation par la moyenne ou par la médiane, même si les données forment des groupes « naturels », bien identifiés par la classification automatique. Les données complètes et le choix des données manquantes étant issus de tirages aléatoires, pour d’autres tirages il est possible qu’une autre des méthodes d’imputation donne de meilleurs résultats.
Question :
Que faudrait-il changer dans la génération des données pour que l’imputation par les centres fonctionne mieux que l’imputation par la moyenne ou la médiane ?
Imputation par les k plus proches voisins, pour des données générées¶
Cette méthode n’est pas directement disponible dans Scikit-learn mais nous pouvons employer la librairie fancyimpute (qu’il faudra d’abord installer, si elle n’est pas disponible, avec pip install fancyimpute). L’utilisation est simple :
>>> from fancyimpute import KNN # imputation sur la base des 3 plus proches voisins sans données manquantes # obtenus à partir de distances calculées avec les seules données présentes >>> data_imputed = KNN(k=3).fit_transform(data_missing) # évaluation des résultats >>> mean_squared_error(data[missing_samples,1],data_imputed[missing_samples,1]) ...
D’autres méthodes sont disponibles dans cette librairie, voir la liste sur fancyimpute.
Imputation pour les données « textures » projetées sur les 2 premiers axes principaux¶
L’objectif de cette section est de traiter des données dont la distribution est plus asymétrique et donc pour lesquelles l’écart entre la médiane et la moyenne est plus fort. Pour cela, nous nous servirons de la projection des données « textures » sur les deux premiers axes principaux. Les données ainsi obtenues sont bidimensionnelles et peuvent être facilement visualisées.
Pour rappel, ces données correspondent à 5500 observations décrites par 40 variables. Chaque observation appartient à une des 11 classes de textures ; chaque classe est représentée par 500 observations. Les données sont issues de https://www.elen.ucl.ac.be/neural-nets/Research/Projects/ELENA/databases/REAL/texture/. Nous appliquerons K-means à ces données, avec n_clusters = 11, et examinerons dans quelle mesure les groupes issus de la classification automatique se rapprochent des classes présentes.
Si les données ne sont pas dans le répertoire de travail il est nécessaire de les chercher d’abord :
[nom@machine ~]$ wget http://cedric.cnam.fr/~crucianm/src/texture.dat
Question :
Projetez ces données sur les deux premiers axes principaux, choisissez aléatoirement 25% de valeurs manquantes sur le premier axe (les projections sur les deux premiers axes sont des vecteurs à 2 dimensions) et appliquez l’imputation par la moyenne, ensuite par la médiane.
Question :
Comparez les résultats obtenus avec la médiane à ceux obtenus avec la moyenne.
Question :
Appliquer aux données « textures » l’imputation par les centres des groupes vous semble être un choix pertinent ?