Accéder au contenu principal

Visualiser les données manquantes avec le package VIM

Apprenez à utiliser les outils de visualisation du package VIM pour obtenir rapidement des insights sur les schémas de données manquantes.
Actualisé 19 sept. 2026  · 13 min lire

Explorer avec l’IA

ChatGPTClaudePerplexity

Les données manquantes posent problème dans le quotidien de tout data scientist. Faut-il les imputer ? Si oui, quelle méthode choisir ? Ou peut-on simplement supprimer les observations incomplètes ? Pour répondre, il faut comprendre le mécanisme à l’origine des valeurs manquantes. Le détecter par des tests statistiques est complexe et mène souvent à des conclusions floues. Les outils de visualisation, au contraire, sont simples à utiliser et aident non seulement à identifier ces mécanismes, mais aussi à évaluer d’autres aspects de la qualité des données. Dans ce tutoriel, nous présentons un ensemble de méthodes de visualisation disponibles dans le package VIM pour vous aider à comprendre finement les schémas de manquants.

Mécanismes des données manquantes

Un jeu de données peut être incomplet pour de multiples raisons. Il est crucial d’enquêter sur les causes possibles, car elles conditionnent la manière de traiter le problème. Par exemple, dans des enquêtes sur l’emploi, on peut s’attendre à ce que les répondants les plus riches comme les plus pauvres refusent de renseigner leurs revenus : les valeurs manquantes ne sont alors pas réparties uniformément. Dans ce cas, supprimer simplement les observations incomplètes avant l’analyse introduirait un biais. À l’inverse, si des données manquent à cause d’une panne du dispositif de collecte, l’emplacement des manquants peut être purement aléatoire.

On distingue trois schémas selon lesquels les données peuvent manquer, appelés mécanismes de données manquantes.

  • Manquantes complètement au hasard (MCAR)
    Sous MCAR, il n’existe aucun schéma systématique dans l’emplacement des valeurs manquantes : elles surviennent entièrement au hasard. Formellement, la probabilité qu’une observation manque ne dépend ni des autres variables, ni de sa propre valeur. Dans ce cas, supprimer les observations incomplètes n’introduit pas de biais dans l’analyse ultérieure.
    Exemple : La température est mesurée en continu par un capteur qui envoie les données via Internet à une base. Pour des raisons inconnues, la connexion Internet tombe parfois.

  • Manquantes au hasard (MAR)
    Sous MAR, la probabilité qu’une observation manque ne dépend pas de sa propre valeur, mais dépend des valeurs d’autres variables. Ici, supprimer les observations incomplètes rend l’échantillon moins représentatif.
    Exemple : Certaines données des heures nocturnes manquent en raison d’opérations de maintenance du capteur, toujours effectuées la nuit.

  • Manquantes non au hasard (MNAR)
    Sous MNAR, la probabilité qu’une observation manque dépend de sa propre valeur non observée. Dans ce cas encore, supprimer les données incomplètes biaise l’analyse.
    Exemple : Le capteur gèle à -20 °C et ne mesure pas en dessous de ce seuil.

En pratique, il est difficile de déterminer lequel des trois mécanismes s’applique. Pour s’en convaincre, traçons des données MNAR. Nous utiliserons les packages ggplot2 et gridExtra à charger au préalable. Nous chargerons aussi deux autres packages pour la suite : VIM pour l’analyse visuelle des manquants et dplyr pour un peu de prétraitement. N’oubliez pas de les install.packages() si nécessaire.

library(\"VIM\")
library(\"dplyr\")
library(\"ggplot2\")
library(\"gridExtra\")

Créons un jeu de données avec deux variables non corrélées et distribuées normalement, x et y, avec des valeurs manquantes dans y dans la queue droite de sa distribution, c’est-à-dire parmi ses valeurs les plus élevées. Notez que nous conservons aussi les valeurs réelles, marquées comme manquantes dans une variable séparée.

set.seed(2)
mnar_data <- data.frame(x = rnorm(100), y = rnorm(100)) %>%
    mutate(y_miss = ifelse(y > 1, y, NA),
           y = ifelse(is.na(y_miss), y, NA),
           x_miss = ifelse(is.na(y), x, NA))

Traçons maintenant deux nuages de points de x contre y : l’un incluant les points dont nous supposons la valeur de y manquante, et l’autre avec uniquement les données observées.

grid.arrange(
  # Plot mettant en évidence les données manquantes
  ggplot(mnar_data, aes(x, y)) +
    geom_point(size = 4, alpha = 0.6) +
    geom_point(aes(x, y_miss), col = \"red\", size = 4, alpha = 0.6) +
    geom_hline(aes(yintercept = y_miss), col = \"red\", alpha = 0.6,
               linetype = \"dashed\") +
    ylim(NA, max(mnar_data$y_miss, na.rm = TRUE)) +
    ggtitle(\"Where the data points are missing\"),
  # Ce que le data scientist peut voir
  ggplot(mnar_data, aes(x, y)) +
    geom_point(size = 4, alpha = 0.6) +
    geom_vline(aes(xintercept = x_miss), col = \"red\", alpha = 0.6,
               linetype = \"dashed\") +
    ylim(NA, max(mnar_data$y_miss, na.rm = TRUE)) +
    ggtitle(\"What the data scientist can see\"),
  # Disposer les deux graphiques côte à côte
  ncol = 2
)
\"scatter

Alors que le premier graphique montre clairement un mécanisme MNAR, le data scientist ne voit que les abscisses des points manquants. Quelles conclusions tirer du second graphique ? Les données sont-elles MCAR ? Possiblement, car l’emplacement des valeurs manquantes semble assez uniforme sur l’axe des x. Ou sont-elles MAR ? À y regarder de plus près, il semble y avoir plus de manquants dans y pour des x négatifs que positifs, ce qui est aussi plausible. Enfin, on ne peut jamais exclure MNAR, car par définition il faudrait observer les valeurs non observées.

Heureusement, nous ne sommes pas totalement démunis. Les outils de visualisation peuvent révéler des schémas dans les données manquantes. Nous allons analyser plusieurs graphiques utiles pour détecter ces schémas en nous appuyant sur le jeu biopics du package fivethirtyeight, qui contient des informations sur des films biographiques.

Nettoyage des données

Avant de passer aux graphiques, effectuons un prétraitement. Le bloc de code ci-dessous s’en charge. L’appel à select au début du pipeline dplyr extrait les variables d’intérêt et le mutate suivant regroupe des catégories de races pour disposer de suffisamment d’observations par groupe. Ensuite, nous ajustons la classe de certaines variables : facteurs pour les chaînes et entier pour la variable logique. Enfin, nous renommons les variables pour des noms plus courts, afin d’améliorer la lisibilité des graphiques.

data(biopics, package = \"fivethirtyeight\")

biopics <- biopics %>%
  select(country, year_release, box_office, number_of_subjects,
         type_of_subject, subject_race, person_of_color, subject_sex) %>%
  mutate(subject_race = ifelse(grepl(\"^Hispanic\", subject_race), \"Hispanic\",
                               subject_race),
         subject_race = ifelse(grepl(\"^African\", subject_race), \"African\",
                               subject_race),
         subject_race = ifelse(grepl(\"^Middle\", subject_race), \"Mid Eastern\",
                               subject_race),
         subject_race = ifelse(subject_race %in% c(\"White\", \"Asian\", \"African\",
                                                   \"Hispanic\", \"Mid Eastern\",
                                                   \"Multi racial\", NA),
                               subject_race, \"other\")) %>%
  mutate(country = as.factor(country),
         type_of_subject = as.factor(type_of_subject),
         subject_race = as.factor(subject_race),
         subject_sex = as.factor(subject_sex),
         person_of_color = as.integer(person_of_color)) %>%
      as.data.frame()

colnames(biopics) <- c(\"country\", \"year\", \"earnings\", \"sub_num\",
                       \"sub_type\", \"sub_race\", \"non_white\", \"sub_sex\")

Nous obtenons un jeu de huit variables :

  • country - pays ou pays d’origine du film,
  • year - année de sortie,
  • earnings - recettes brutes au box-office américain,
  • sub_num - nombre de personnages mis en scène,
  • sub_type - métier ou raison de la notoriété du personnage,
  • sub_race - race du personnage,
  • non_white - variable indicatrice signalant une personne de couleur,
  • sub_sex - sexe du personnage.

Graphiques d’agrégation

Première question essentielle : dans quelles variables y a‑t‑il des manquants, et combien ? Les graphiques d’agrégation sont très utiles pour y répondre. La ligne suivante suffit.

aggr(biopics, numbers = TRUE, prop = c(TRUE, FALSE))
\"aggregation

Nous passons numbers = TRUE pour afficher les effectifs au-dessus des barres. L’argument prop indique s’il faut utiliser les proportions de valeurs manquantes et de combinaisons plutôt que les totaux. Nous le mettons à TRUE pour le premier graphique et à FALSE pour le second.

On voit que des manquants n’apparaissent que dans deux variables : plus de 40 % pour earnings et environ 25 % pour sub_race. Dans le graphique des combinaisons à droite, la grille présente toutes les combinaisons de valeurs manquantes (rouge) et observées (bleu). Il y a 317 observations complètes, et dans 77 lignes les deux variables manquent simultanément.

Lire deux graphiques en parallèle peut dérouter ; regroupons la même information dans une seule visualisation avec la ligne suivante.

aggr(biopics, combined = TRUE, numbers = TRUE)

L’argument combined fusionne les deux graphiques. Ici, les barres horizontales à droite de la grille montrent les fréquences des combinaisons correspondantes, tandis que les barres verticales au-dessus indiquent les proportions de manquants par variable. En additionnant les valeurs des combinaisons pour earnings (0,32 et 0,10), on obtient une proportion globale de manquants de 42 %, confirmant l’ordre de grandeur vu précédemment.

Spinogramme et spineplot

Nous avons pris de la hauteur ; regardons maintenant les interactions entre variables. Le spinogramme et le spineplot permettent d’étudier le pourcentage de valeurs manquantes d’une variable selon les valeurs d’une autre. Si cette dernière est numérique, on parle de spinogramme ; si elle est catégorielle, de spineplot.

Les deux se génèrent avec spineMiss(), qui prend en entrée un data frame à deux colonnes. La première variable indiquée sert au découpage et est mappée sur l’axe horizontal. La seconde est celle dont on observe le schéma de manquants. Voyons un spineplot : nous indiquons d’abord la variable catégorielle sub_race, puis earnings, ce qui revient à : quel est le pourcentage de manquants dans earnings pour chaque catégorie de sub_race ?

spineMiss(biopics[, c(\"sub_race\", \"earnings\")])
\"Spinogram

La largeur relative des barres par catégorie de sub_race reflète la fréquence de cette catégorie : par exemple, la grande majorité des films ont un personnage principal blanc. Dans chaque barre, la proportion de manquants de earnings est indiquée, tandis que la barre grisée à droite montre cette proportion sur l’ensemble du jeu. Il semble que lorsque le personnage principal est africain, nous ayons le plus souvent des informations complètes sur les recettes.

En inversant l’ordre des variables comme ci-dessous, on produit un spinogramme répondant à la question inversée : quel est le pourcentage de manquants dans sub_race selon les valeurs de earnings ?

spineMiss(biopics[, c(\"earnings\", \"sub_race\")])
\"Spinogram

Comme earnings est numérique, ses valeurs sont regroupées en classes, dont les largeurs reflètent la distribution de la variable. Le spinogramme montre que earnings est fortement asymétrique à droite : seuls quelques films réalisent les profits les plus élevés. Fait intéressant, pour ces blockbusters, la race du personnage manque plus souvent, comme l’indique la barre rouge la plus haute pour les plus grandes valeurs d’earnings.

Diagramme en mosaïque

Le spinogramme et le spineplot étudient l’interaction entre deux variables. Cette idée se généralise avec le diagramme en mosaïque. Le graphique est un assemblage de tuiles, chacune correspondant à une combinaison de catégories (pour des variables catégorielles) ou de classes (pour des variables numériques) de deux variables ou plus. Dans chaque tuile, on affiche le pourcentage de manquants d’une autre variable. En principe, on peut utiliser autant de variables qu’on veut, mais le graphique devient vite chargé. Il est plus lisible si les variables de découpage sont des facteurs avec peu de modalités.

Observons la proportion de manquants dans earnings selon sub_sex et US_movie. Cette dernière, créée ci-dessous, est un booléen indiquant si les États‑Unis ont participé à la production. On fournit le data frame à trois variables comme premier argument de mosaicMiss(). plotvars mis à 1:2 signifie que l’on découpe selon les deux premières colonnes. highlight à 3 indique que l’on affiche dans les tuiles la proportion de manquants de la troisième variable.

biopics <- biopics %>%
    mutate(US_movie = ifelse(grepl(\"US\", country), TRUE, FALSE))
mosaicMiss(biopics[, c(\"sub_sex\", \"US_movie\", \"earnings\")], highlight = 3,
           plotvars = 1:2, miss.labels = FALSE)
\"Mosaic

De nouveau, la taille des tuiles reflète la fréquence des combinaisons. Par exemple, la grande tuile en bas à droite signifie que la majorité des films ont un personnage masculin et ont été produits, au moins en partie, aux États‑Unis.

Pour les films avec un personnage masculin, l’information de recettes manque plus souvent lorsque le film n’est pas américain. À l’inverse, lorsque le personnage principal est féminin, les films américains présentent légèrement plus de manquants sur les recettes. Ces différences semblent toutefois faibles.

Boxplot parallèle

Autre type de visualisation : le boxplot parallèle. L’idée est de scinder le jeu en deux sous‑ensembles : l’un avec uniquement les valeurs observées d’une variable incomplète, l’autre avec uniquement ses valeurs manquantes. On trace pour chacun un boxplot d’une variable numérique choisie. Cela permet de vérifier si la distribution de la variable choisie est affectée par le caractère manquant de la variable de découpage.

Pour produire un boxplot parallèle, utilisez pbox(). On passe un data frame à deux colonnes : la première est tracée, la seconde sert au découpage. Dans l’exemple ci‑dessous, on prend le logarithme de earnings car la variable est très asymétrique ; sans cette transformation, le boxplot serait écrasé.

biopics <- biopics %>%
  mutate(log_earnings = log(earnings))
pbox(biopics[, c(\"log_earnings\", \"sub_race\")])
\"Parallel

La boîte blanche à gauche montre la distribution globale de log_earnings, tandis que les boîtes bleue et rouge montrent sa distribution pour les sous‑ensembles avec valeurs observées et manquantes de sub_race. La largeur relative reflète la taille des sous‑ensembles : la boîte bleue plus large indique plus de valeurs observées que manquantes dans sub_race. À part cela, les deux boîtes se ressemblent, ainsi que la boîte globale. Cela suggère que l’absence d’information sur la race n’affecte pas la distribution des recettes.

Coordonnées parallèles

Nous avons examiné des variables isolées et leurs interactions. Passons à l’analyse simultanée de toutes les variables avec le graphe de coordonnées parallèles. Chaque variable est transformée sur la même échelle et représentée par un axe parallèle. Pour les variables catégorielles, l’axe est découpé en points équidistants, un par modalité. Les valeurs manquantes sont placées au‑dessus des axes, hors de la zone de tracé. Chaque ligne correspond à une observation, et sa couleur indique les manquants dans la variable sélectionnée.

On produit ce graphe avec parcoordMiss(). Par défaut, il utilise toutes les variables du data frame. En mettant highlight à earnings, on colore les observations dont earnings est manquante. Un alpha à 0,6 rend les lignes légèrement transparentes, améliorant la lisibilité.

# Supprimer les variables créées pour les graphiques précédents
biopics <- biopics %>%
  select(- US_movie, - log_earnings)

parcoordMiss(biopics, highlight = 'earnings', alpha = 0.6)
\"Parallel

Sur le graphique, les lignes rouge foncé représentent les observations avec earnings manquante. Elles semblent se comporter différemment du reste des données. En particulier, très peu correspondent au deuxième niveau le plus élevé de country, qui regroupe pourtant de nombreuses observations ! Il s’agit de films US/UK (pour le voir, exécutez levels(biopics$country) et repérez l’avant‑dernière modalité).

De plus, un coup d’œil à l’axe year révèle une zone, à environ deux tiers de sa hauteur, où passent moins de lignes rouge foncé. Il semble qu’il y ait eu une période récente où les recettes étaient mieux renseignées. Cela suggère que country et year peuvent aider à expliquer la distribution des manquants dans earnings.

Graphe matrice

Dernier outil : le graphe matrice. Il visualise toutes les cellules de la matrice de données par des rectangles. Les données observées sont affichées sur une palette continu gris‑noir (plus c’est sombre, plus la valeur est élevée), tandis que les manquants sont en rouge. Il est recommandé de trier les données par une variable incomplète pour faciliter l’interprétation. Le code ci‑dessous trie par earnings.

matrixplot(biopics, sortby = c('earnings'))
\"Matrix

Le graphique confirme certains constats : les observations avec earnings manquante proviennent plutôt d’époques anciennes (faible year) et de faibles niveaux de country – indiqué par des teintes plus claires sur ces deux variables dans les lignes où earnings est en rouge. Il semble aussi y avoir moins de films avec un personnage non blanc lorsque earnings manque.

Conclusions

En résumé, les données manquantes du jeu biopics ne sont pas MCAR. L’emplacement des manquants dans earnings et sub_race s’explique mutuellement, comme le montrent le spineplot et le spinogramme. Par ailleurs, le graphe matrice et le graphe de coordonnées parallèles suggèrent que country, year et non_white peuvent aussi expliquer la distribution des manquants dans earnings. Il n’est donc pas recommandé de supprimer les observations incomplètes, sous peine d’introduire un biais.

Dernières réflexions

Beau parcours ! Vous connaissez désormais les mécanismes des données manquantes, leurs différences, et surtout comment en inférer la nature grâce à diverses visualisations. Bravo ! Vous avez tout ce qu’il faut pour analyser vos propres jeux de données incomplets.

Si vous souhaitez aller plus loin avec R, suivez le cours Data Visualization with ggplot2 (Part 1) sur DataCamp et consultez notre R Formula Tutorial.

Sujets
Visualisation des données
Science des données

Approfondissez R et la visualisation de données

Cours

Introduction à la visualisation de données avec ggplot2

4 h
188.2K
Créez des visualisations impactantes et esthétiques avec ggplot2 en maîtrisant la grammaire des graphiques.
Afficher les détailsRight Arrow
Commencer Le Cours
Voir plusRight Arrow