21  Données manquantes et imputation multiple

ImportantQuestion de départ

Lorsque le revenu manque pour une partie des répondants, vaut-il mieux supprimer ces observations, remplir les cases par une valeur unique, ou représenter explicitement l’incertitude créée par ce que l’on n’a pas observé ?

21.1 Objectifs d’apprentissage

À la fin de ce chapitre, vous saurez :

  • distinguer une non-réponse imputable d’une absence structurelle liée au questionnaire ;
  • diagnostiquer qui manque avant de choisir une méthode ;
  • expliquer les hypothèses MCAR, MAR et MNAR sans les transformer en diagnostics automatiques ;
  • construire un modèle d’imputation cohérent avec l’analyse substantielle ;
  • utiliser réellement le workflow mice()with()pool() ;
  • comparer cas complets et imputations multiples avec estimations et intervalles ;
  • articuler imputation multiple et plan d’enquête ;
  • rédiger ce que l’imputation change — et ce qu’elle ne résout pas.

21.2 Toutes les valeurs manquantes ne sont pas à imputer

L’imputation commence par le questionnaire, pas par mice().

Dans une enquête, une cellule vide peut représenter un refus, « ne sait pas », une panne de collecte, une valeur supprimée lors du nettoyage, mais aussi une question hors univers. heures_travail, par exemple, n’a pas à être inventée pour une personne retraitée uniquement parce que la cellule est NA.

AvertissementRègle fondamentale

N’imputez jamais mécaniquement une valeur qui n’aurait pas dû exister selon le questionnaire. Une absence structurelle et une non-réponse à une question effectivement posée sont deux objets différents.

21.3 Commencer par mesurer le problème

Dans le jeu pédagogique, le revenu est l’une des variables les plus touchées par la non-réponse. Calculons un audit compact.

Afficher le code R
tab_na <- enqueter |>
  summarise(
    Revenu = mean(is.na(revenu_mensuel)),
    Âge = mean(is.na(age)),
    Diplôme = mean(is.na(diplome_4)),
    Santé = mean(is.na(sante_f)),
    `Satisfaction de vie` = mean(is.na(satisfaction_vie))
  ) |>
  tidyr::pivot_longer(everything(), names_to = "Variable", values_to = "Part") |>
  mutate(`Part manquante` = scales::percent(Part, accuracy = 0.1, decimal.mark = ",")) |>
  select(Variable, `Part manquante`)

knitr::kable(tab_na, align = c("l", "r"))
Variable Part manquante
Revenu 9,1%
Âge 0,7%
Diplôme 1,8%
Santé 1,1%
Satisfaction de vie 1,4%

Le taux ne suffit pas. Il faut aussi demander si les personnes sans revenu diffèrent des autres sur des caractéristiques observées.

Afficher le code R
enqueter_na <- enqueter |>
  mutate(
    revenu_manquant = factor(
      if_else(is.na(revenu_mensuel), "Revenu manquant", "Revenu observé"),
      levels = c("Revenu observé", "Revenu manquant")
    )
  )

survey::svydesign(
  ids = ~psu, strata = ~strate, weights = ~poids_final,
  data = enqueter_na, nest = TRUE
) |>
  gtsummary::tbl_svysummary(
    by = revenu_manquant,
    include = c(age, diplome_4, sexe_f, satisfaction_vie, sante_f),
    missing = "no",
    label = list(
      age ~ "Âge",
      diplome_4 ~ "Niveau de diplôme",
      sexe_f ~ "Sexe / genre",
      satisfaction_vie ~ "Satisfaction de vie",
      sante_f ~ "État de santé"
    )
  ) |>
  gtsummary::bold_labels()
Caractéristique Revenu observé
N = 3 2581
Revenu manquant
N = 3341
Âge 46 (35 – 57) 46 (36 – 58)
Niveau de diplôme

    Inférieur au bac 1 466 (46%) 157 (48%)
    Bac 629 (20%) 64 (20%)
    Bac+2 499 (16%) 43 (13%)
    Supérieur long 603 (19%) 65 (20%)
Sexe / genre

    Homme 1 578 (49%) 158 (48%)
    Femme 1 599 (50%) 172 (52%)
    Autre / non-binaire 48 (1,5%) 3 (0,9%)
Satisfaction de vie 7,00 (6,00 – 8,00) 7,00 (6,00 – 8,00)
État de santé

    Très bonne 406 (13%) 46 (14%)
    Bonne 1 360 (42%) 132 (40%)
    Moyenne 1 167 (36%) 112 (34%)
    Mauvaise 270 (8,4%) 36 (11%)
    Très mauvaise 19 (0,6%) 4 (1,2%)
1 Médiane (Q1 – Q3); n (%)
NoteCe que montre ce tableau

Une différence entre cas complets et incomplets n’identifie pas à elle seule un mécanisme MAR ou MNAR. Elle montre en revanche pourquoi supprimer automatiquement les lignes incomplètes peut modifier la composition de l’échantillon analytique.

21.4 MCAR, MAR et MNAR : trois hypothèses

MCAR — Missing Completely At Random. La probabilité d’absence ne dépend ni des informations observées ni de la valeur manquante.

MAR — Missing At Random. Conditionnellement aux variables observées incluses dans le modèle d’imputation, le mécanisme de manque ne dépend plus de la valeur non observée.

MNAR — Missing Not At Random. Même après conditionnement, l’absence dépend encore de la valeur non observée ou d’une information non disponible.

Un refus de revenu peut être associé à l’âge, au diplôme ou à l’emploi — informations observées — mais également à un revenu extrêmement élevé ou faible précisément parce que cette valeur est extrême. Les données observées ne permettent généralement pas de « tester MAR » de façon définitive.

NotePrincipe d’interprétation

L’imputation multiple ne prouve pas que MAR est vrai. Elle produit une analyse cohérente sous un modèle et des hypothèses explicites. Lorsque MNAR est plausible, une analyse de sensibilité est plus informative qu’une confiance excessive dans un seul scénario.

21.5 Pourquoi une valeur unique est une mauvaise représentation de l’incertitude

Imputer la moyenne est facile :

Afficher le code R
revenu_moyen <- mean(enqueter$revenu_mensuel, na.rm = TRUE)

enqueter_simple <- enqueter |>
  mutate(
    revenu_imp_simple = if_else(
      is.na(revenu_mensuel), revenu_moyen, revenu_mensuel
    )
  )

Mais toutes les valeurs imputées sont alors identiques. La distribution est artificiellement resserrée et l’incertitude liée au manque disparaît du calcul final.

L’imputation multiple adopte une autre logique : elle génère plusieurs valeurs plausibles, ajuste le même modèle dans plusieurs jeux complétés, puis combine les estimations et leurs variances (Buuren et Groothuis-Oudshoorn 2011).

21.6 Construire un exemple reproductible

Pour que le rendu du livre reste raisonnable tout en exécutant réellement l’analyse, nous utilisons ici cinq imputations et cinq itérations. Ce n’est pas une recommandation universelle. Dans un projet de recherche, le nombre d’imputations doit être augmenté lorsque l’incertitude liée au manque est importante et la stabilité Monte-Carlo doit être vérifiée.

Nous concentrons l’imputation sur revenu_mensuel. Les autres variables utilisées comme prédicteurs sont requises comme observées dans ce mini-exemple. Cela permet d’isoler clairement ce que fait l’algorithme.

Afficher le code R
data_imp <- enqueter |>
  filter(
    !is.na(age),
    !is.na(sexe_f),
    !is.na(diplome_4),
    !is.na(satisfaction_vie),
    !is.na(sante_f),
    !is.na(statut_emploi_f)
  ) |>
  mutate(strate_f = factor(strate)) |>
  select(
    id, satisfaction_vie, revenu_mensuel, age,
    sexe_f, diplome_4, sante_f, statut_emploi_f,
    territoire_f, mode_collecte_f, strate_f,
    poids_final, psu, strate
  )

ini <- mice::mice(data_imp, maxit = 0, printFlag = FALSE)
meth <- ini$method
pred <- ini$predictorMatrix

# Une seule cible d'imputation : le revenu.
meth[] <- ""
meth["revenu_mensuel"] <- "pmm"

# Les identifiants et variables de design ne sont pas des cibles.
pred[,] <- 0
pred[
  "revenu_mensuel",
  c(
    "satisfaction_vie", "age", "sexe_f", "diplome_4",
    "sante_f", "statut_emploi_f", "territoire_f",
    "mode_collecte_f", "strate_f", "poids_final"
  )
] <- 1

La méthode pmm (predictive mean matching) impute ici des valeurs de revenu à partir de valeurs effectivement observées chez des profils proches. Cela évite notamment de fabriquer directement des revenus hors du support empirique par une simple prédiction linéaire.

Nous incluons aussi des informations du dispositif (strate_f, mode, territoire, poids) parce qu’elles peuvent être liées à la non-réponse et à l’analyse finale. Nous n’introduisons pas ici 72 indicatrices de PSU dans le modèle d’imputation : ce serait disproportionné pour cet exemple pédagogique. Dans une enquête réelle fortement structurée en grappes, la manière de représenter cette structure dans l’imputation doit être discutée explicitement.

21.7 Exécuter les imputations

Afficher le code R
set.seed(2026)

imp <- mice::mice(
  data_imp,
  m = 5,
  maxit = 5,
  method = meth,
  predictorMatrix = pred,
  printFlag = FALSE
)

resume_imp <- tibble::tibble(
  Élément = c("Imputations", "Itérations", "Variables imputées", "Événements consignés"),
  Valeur = c(
    imp$m,
    imp$iteration,
    sum(imp$method != ""),
    if (is.null(imp$loggedEvents)) 0 else nrow(imp$loggedEvents)
  )
)
knitr::kable(resume_imp, align = c("l", "r"))
Élément Valeur
Imputations 5
Itérations 5
Variables imputées 1
Événements consignés 0

21.7.1 Vérifier la convergence

Figure — Trace des chaînes d’imputation pour le revenu

Afficher le code R
plot(imp)

Évolution des statistiques de la chaîne d'imputation du revenu au fil des itérations pour cinq imputations.

Une trace n’est pas une certification automatique. On recherche surtout une absence de dérive persistante et un mélange raisonnable des chaînes (Buuren 2018).

21.7.2 Comparer valeurs observées et imputées

Afficher le code R
imp_long <- mice::complete(imp, "long", include = FALSE)
manquant_initial <- is.na(data_imp$revenu_mensuel)

imp_values <- imp_long |>
  mutate(
    ligne = as.integer(.id),
    origine_manquante = manquant_initial[ligne]
  ) |>
  filter(origine_manquante) |>
  transmute(revenu_mensuel, Source = "Valeurs imputées")

obs_values <- data_imp |>
  filter(!is.na(revenu_mensuel)) |>
  transmute(revenu_mensuel, Source = "Valeurs observées")

bind_rows(obs_values, imp_values) |>
  ggplot(aes(x = revenu_mensuel, colour = Source, fill = Source)) +
  geom_density(alpha = 0.12, linewidth = 0.9) +
  scale_colour_manual(values = palette_enqueter[c(1, 3)]) +
  scale_fill_manual(values = palette_enqueter[c(1, 3)]) +
  labs(
    x = "Revenu mensuel (€)", y = "Densité",
    title = "L'imputation doit rester plausible sur l'échelle substantielle",
    subtitle = "Les valeurs imputées concernent uniquement les revenus initialement manquants",
    caption = "Données synthétiques EnquêteR ; cinq imputations par predictive mean matching."
  ) +
  theme_enqueter()

Deux densités comparent les revenus observés aux revenus imputés pour les cas initialement manquants.

L’objectif n’est pas d’obtenir deux courbes identiques. Si les personnes sans revenu observé ont un profil différent, leurs valeurs imputées peuvent aussi avoir une distribution différente. En revanche, une masse de valeurs impossibles ou une distribution totalement étrangère aux données observées doit conduire à revoir le modèle.

21.8 Analyser puis combiner : les règles de Rubin

Ajustons dans chaque jeu imputé le même modèle de satisfaction.

Afficher le code R
fit_imp <- with(
  imp,
  lm(
    satisfaction_vie ~ I(revenu_mensuel / 1000) + age +
      sexe_f + diplome_4 + sante_f
  )
)

res_pool <- mice::pool(fit_imp)

pool_table <- broom::tidy(res_pool, conf.int = TRUE)

pool_focus <- pool_table |>
  dplyr::filter(
    term %in% c(
      "I(revenu_mensuel/1000)",
      "age",
      "diplome_4Supérieur long"
    )
  ) |>
  dplyr::mutate(
    Terme = dplyr::recode(
      term,
      "I(revenu_mensuel/1000)" = "Revenu (+1 000 €)",
      "age" = "Âge (+1 an)",
      "diplome_4Supérieur long" = "Supérieur long vs inférieur au bac"
    ),
    Estimation = fmt_num(estimate, .001),
    `IC 95 %` = paste0("[", fmt_num(conf.low, .001), " ; ", fmt_num(conf.high, .001), "]"),
    `Information manquante` = fmt_pct(fmi, .1)
  ) |>
  dplyr::select(Terme, Estimation, `IC 95 %`, `Information manquante`)

knitr::kable(pool_focus, align = c("l", "r", "c", "r"))
Terme Estimation IC 95 % Information manquante
2 Revenu (+1 000 €) 0,333 [0,257 ; 0,409] 4,3%
3 Âge (+1 an) -0,002 [-0,006 ; 0,002] 0,5%
8 Supérieur long vs inférieur au bac 0,020 [-0,140 ; 0,179] 1,0%

pool() combine la variabilité moyenne à l’intérieur des imputations et la variabilité entre imputations. La colonne « information manquante » (fmi) indique, pour chaque paramètre, quelle part de l’incertitude est attribuable au manque sous le modèle d’imputation. Les cinq jeux complétés ne sont donc jamais traités comme cinq fois plus de personnes.

AvertissementErreur très fréquente

Concaténer les jeux imputés puis ajuster un seul modèle produit une pseudo-taille d’échantillon artificielle. Les imputations représentent plusieurs versions plausibles des mêmes observations, pas de nouveaux répondants.

21.9 Cas complets versus imputations multiples

La comparaison doit porter sur des estimations, pas sur le nombre d’étoiles de significativité.

Afficher le code R
fit_cc <- lm(
  satisfaction_vie ~ I(revenu_mensuel / 1000) + age +
    sexe_f + diplome_4 + sante_f,
  data = data_imp
)

coef_cc <- coef(summary(fit_cc))["I(revenu_mensuel/1000)", ]
ci_cc <- confint(fit_cc)["I(revenu_mensuel/1000)", ]

ligne_mi <- pool_table |>
  filter(term == "I(revenu_mensuel/1000)")

comparaison_imp <- tibble(
  Analyse = c("Cas complets", "Imputation multiple"),
  estimation = c(unname(coef_cc["Estimate"]), ligne_mi$estimate),
  bas = c(ci_cc[1], ligne_mi$conf.low),
  haut = c(ci_cc[2], ligne_mi$conf.high)
)
Afficher le code R
ggplot(comparaison_imp, aes(x = estimation, y = forcats::fct_rev(Analyse))) +
  geom_vline(xintercept = 0, linetype = 2, colour = couleurs_enqueter[["gris"]]) +
  geom_errorbarh(aes(xmin = bas, xmax = haut), height = 0.12, linewidth = 0.8) +
  geom_point(size = 3, colour = couleurs_enqueter[["principal"]]) +
  labs(
    x = "Variation de satisfaction associée à +1 000 € de revenu",
    y = NULL,
    title = "L'imputation est une analyse de sensibilité, pas une machine à créer de la significativité",
    subtitle = "Modèle ajusté sur âge, sexe, diplôme et santé",
    caption = "Données synthétiques EnquêteR."
  ) +
  theme_enqueter()

Deux estimations avec intervalles de confiance comparent l'effet d'une hausse de mille euros de revenu sur la satisfaction selon deux traitements des données manquantes.

NoteLecture du résultat

Si les deux estimations restent proches, la conclusion substantielle est relativement robuste à ce traitement des revenus manquants sous le modèle d’imputation retenu. Un écart marqué demanderait au contraire de comprendre quels profils ont été réintroduits par l’imputation et pourquoi.

21.10 Articuler imputation multiple et plan d’enquête

L’exemple précédent isolait la logique de {mice} avec lm(). Dans une enquête complexe, l’analyse finale doit également utiliser les poids, strates et grappes.

Une manière reproductible consiste à transformer les jeux complétés en imputationList, à construire un design identique dans chaque jeu, puis à combiner les modèles survey avec MIcombine().

Afficher le code R
imp_list <- mitools::imputationList(
  lapply(seq_len(imp$m), function(i) mice::complete(imp, action = i))
)

designs_imp <- survey::svydesign(
  ids = ~psu,
  strata = ~strate,
  weights = ~poids_final,
  data = imp_list,
  nest = TRUE
)

mods_svy_imp <- with(
  designs_imp,
  survey::svyglm(
    satisfaction_vie ~ I(revenu_mensuel / 1000) + age +
      sexe_f + diplome_4 + sante_f
  )
)

design_cc_imp <- survey::svydesign(
  ids = ~psu,
  strata = ~strate,
  weights = ~poids_final,
  data = data_imp,
  nest = TRUE
)

pool_svy <- mitools::MIcombine(
  mods_svy_imp,
  df.complete = survey::degf(design_cc_imp)
)

coef_svy_mi <- pool_svy$coefficients
se_svy_mi <- sqrt(diag(pool_svy$variance))
terme_revenu <- grep("revenu_mensuel", names(coef_svy_mi), value = TRUE)[1]
crit_svy_mi <- stats::qt(.975, df = pool_svy$df[terme_revenu])

resume_svy_mi <- tibble::tibble(
  Terme = "Revenu (+1 000 €)",
  Estimation = unname(coef_svy_mi[terme_revenu]),
  `Erreur standard` = unname(se_svy_mi[terme_revenu]),
  bas = Estimation - crit_svy_mi * `Erreur standard`,
  haut = Estimation + crit_svy_mi * `Erreur standard`,
  `Information manquante` = unname(pool_svy$missinfo[terme_revenu])
) |>
  dplyr::transmute(
    Terme,
    Estimation = fmt_num(Estimation, .001),
    `Erreur standard` = fmt_num(`Erreur standard`, .001),
    `IC 95 %` = paste0("[", fmt_num(bas, .001), " ; ", fmt_num(haut, .001), "]"),
    `Information manquante` = fmt_pct(`Information manquante`, .1)
  )

knitr::kable(resume_svy_mi, align = c("l", "r", "r", "c", "r"))
Terme Estimation Erreur standard IC 95 % Information manquante
Revenu (+1 000 €) 0,324 0,037 [0,251 ; 0,397] 7,3%

Cette étape combine deux sources d’incertitude : celle créée par le plan d’enquête et celle créée par les données manquantes. MIcombine() reçoit ici les degrés de liberté du plan complet, ce qui est préférable à une approximation normale automatique dans une application survey. Poids et variables auxiliaires ne sont donc pas une décoration ajoutée après coup : ils font partie de la définition de l’analyse.

21.11 Lecture sociologique : les absents font partie du problème de recherche

Dans les sciences sociales, la non-réponse n’est pas seulement une nuisance informatique. Refuser de déclarer son revenu, ne pas connaître précisément ses ressources ou interrompre un questionnaire peut être socialement structuré.

Pour une analyse réelle, on demanderait donc :

  • la non-réponse au revenu varie-t-elle selon l’âge, la position sociale ou la situation d’emploi ?
  • les variables utilisées dans le modèle d’imputation capturent-elles une partie de cette structuration ?
  • les résultats substantiels changent-ils lorsque l’on modifie le modèle d’imputation ?
AvertissementCe que nous ne pouvons pas conclure

Une stabilité entre cas complets et imputations multiples ne démontre ni que les données sont MAR, ni que le biais de non-réponse a disparu. Elle montre seulement que, sous les scénarios analysés, la conclusion étudiée change peu.

21.12 Mini-exercice

Parmi ces absences, lesquelles sont candidates à une imputation ?

  1. revenu_mensuel = NA après un refus ;
  2. heures_travail = NA pour une personne retraitée ;
  3. satisfaction_vie = NA après non-réponse ;
  4. teletravail_jours = NA pour une personne sans emploi.

Les cas 1 et 3 peuvent être candidats à une imputation si le modèle et les hypothèses sont justifiés. Les cas 2 et 4 sont des absences structurelles : il serait incohérent de fabriquer une valeur pour une question hors univers.

21.13 Exercice de synthèse

21.13.1 Niveau A — Appliquer

Reproduisez cinq imputations du revenu. Comparez graphiquement distribution observée et imputée, puis estimez le même modèle en cas complets et après imputation.

21.13.2 Niveau B — Choisir

Un collègue propose de remplacer chaque revenu manquant par la médiane de son niveau de diplôme. Expliquez pourquoi cette solution ne représente pas correctement l’incertitude et proposez un modèle d’imputation plus cohérent.

21.13.3 Niveau C — Analyser

Modifiez la matrice de prédicteurs de l’imputation. Comparez les estimations poolées du revenu et rédigez un paragraphe distinguant : résultat stable, hypothèse MAR, sensibilité au modèle et risque MNAR.

21.14 À retenir

  • Une cellule NA n’est pas automatiquement imputable.
  • Il faut analyser qui manque avant de remplir les cases.
  • MCAR, MAR et MNAR sont des hypothèses sur le mécanisme de manque.
  • L’imputation multiple combine des estimations et des variances, pas des pseudo-observations supplémentaires.
  • Les diagnostics portent sur convergence, plausibilité et sensibilité.
  • Dans une enquête complexe, imputation et design doivent être articulés explicitement.
  • Comparer cas complets et imputations multiples est une analyse de robustesse, pas un concours de p-values.

21.15 Pour aller plus loin

La référence logicielle centrale pour {mice} est van Buuren et Groothuis-Oudshoorn (Buuren et Groothuis-Oudshoorn 2011). Pour les diagnostics, la sélection de variables et les analyses de sensibilité, voir van Buuren (Buuren 2018).