Annexe D — Corrigés transversaux des exercices

Les chapitres contiennent directement des corrections repliables pour les mini-exercices et des éléments de correction pour les exercices de synthèse. Cette annexe ajoute des corrigés transversaux qui mobilisent plusieurs chapitres à la fois. Ils sont particulièrement utiles pour la version PDF ou pour préparer un examen.

E Comment utiliser les corrigés

Pour chaque problème :

  1. essayez d’abord de rédiger le raisonnement sans R ;
  2. identifiez l’univers et le type des variables ;
  3. écrivez le code ;
  4. vérifiez les résultats ;
  5. comparez ensuite avec la correction.

Une correction n’est pas un script à copier : elle montre une démarche possible.

F Corrigé 1 — Auditer un fichier brut

F.1 Consigne

À partir de data/raw/enqueter_brut.csv :

  • vérifier les dimensions ;
  • vérifier l’unicité de id ;
  • repérer les âges hors champ ;
  • repérer les valeurs invalides de info_reseaux ;
  • proposer des corrections sans modifier le fichier source.

F.2 Démarche

Le dictionnaire indique que l’âge théorique est compris entre 18 et 79 ans et que 999 est un code spécial. info_reseaux est une indicatrice binaire ; ses valeurs valides sont 0/1, auxquelles peuvent s’ajouter les codes spéciaux documentés.

F.3 Code

Afficher le code R
library(tidyverse)

brut <- read_csv(
  "data/raw/enqueter_brut.csv",
  show_col_types = FALSE
)

dim(brut)
n_distinct(brut$id)

brut %>%
  count(id) %>%
  filter(n > 1)

brut %>%
  filter(!age %in% c(999), age < 18 | age > 79) %>%
  select(id, age)

brut %>%
  filter(!info_reseaux %in% c(0, 1, 8, 9)) %>%
  select(id, info_reseaux)

F.4 Interprétation

L’objectif n’est pas de « supprimer les outliers ». Les valeurs hors plage documentée sont des anomalies de validité. Le code 999, lui, est une information de non-réponse et doit être transformé en NA tout en conservant, si nécessaire, la raison de la non-réponse.

Le fichier propre doit être construit dans un nouvel objet puis écrit dans data/derived/.

G Corrigé 2 — Recoder sans perdre le sens

G.1 Consigne

Créer :

  • une classe d’âge 18–29 / 30–44 / 45–59 / 60–79 ;
  • une variable sport_regulier égale à 1 pour une pratique au moins hebdomadaire ;
  • une variable sexe_f lisible.

G.2 Code

Afficher le code R
enqueter2 <- enqueter %>%
  mutate(
    age_classe2 = cut(
      age,
      breaks = c(18, 30, 45, 60, 80),
      right = FALSE,
      labels = c("18-29", "30-44", "45-59", "60-79")
    ),
    sport_regulier = case_when(
      sport_freq %in% c(3, 4) ~ 1,
      sport_freq %in% c(0, 1, 2) ~ 0,
      TRUE ~ NA_real_
    ),
    sexe_f = factor(
      sexe,
      levels = c(1, 2, 3),
      labels = c("Homme", "Femme", "Autre/non-binaire")
    )
  )

G.3 Vérifications

Afficher le code R
table(enqueter2$age_classe2, useNA = "ifany")
with(enqueter2, table(sport_freq, sport_regulier, useNA = "ifany"))
table(enqueter2$sexe, enqueter2$sexe_f, useNA = "ifany")

G.4 Point clé

La correction inclut le contrôle du recodage. Un mutate() qui s’exécute sans erreur n’est pas une preuve que la nouvelle variable correspond à la définition voulue.

H Corrigé 3 — Brut ou pondéré ?

H.1 Consigne

Comparer la moyenne d’âge brute et la moyenne d’âge estimée avec le design.

H.2 Code

Afficher le code R
mean(enqueter$age, na.rm = TRUE)

design <- svydesign(
  ids = ~psu,
  strata = ~strate,
  weights = ~poids_final,
  data = enqueter,
  nest = TRUE
)

age_pond <- svymean(~age, design, na.rm = TRUE)
age_pond
confint(age_pond)

H.3 Interprétation

La première moyenne décrit les lignes observées avec le même poids implicite pour chaque répondant. La seconde est une estimation pour la population cible définie par le dispositif de sondage, en tenant compte des poids et de la structure utilisée pour la variance.

Il ne faut pas dire que la moyenne pondérée est « vraie » et la brute « fausse ». Elles répondent à des objets descriptifs différents.

I Corrigé 4 — Choisir le bon dénominateur

I.1 Consigne

Répondre à deux questions :

  1. Parmi chaque niveau de diplôme, quelle proportion pratique régulièrement un sport ?
  2. Parmi les sportifs réguliers, quelle est la répartition des diplômes ?

I.2 Code descriptif non pondéré

Afficher le code R
# Pourcentages en ligne : P(sport | diplôme)
enqueter2 %>%
  filter(!is.na(diplome_4), !is.na(sport_regulier)) %>%
  count(diplome_4, sport_regulier) %>%
  group_by(diplome_4) %>%
  mutate(pct = n / sum(n))

# Distribution du diplôme parmi sport = 1
enqueter2 %>%
  filter(sport_regulier == 1, !is.na(diplome_4)) %>%
  count(diplome_4) %>%
  mutate(pct = n / sum(n))

I.3 Point clé

Les deux tableaux contiennent les mêmes variables, mais ne répondent pas à la même question. Le choix ligne/colonne n’est donc pas cosmétique.

J Corrigé 5 — Test d’association et taille du résultat

J.1 Consigne

Tester l’association entre diplôme et sport régulier en tenant compte du design, puis produire les proportions pondérées par diplôme.

J.2 Code

Afficher le code R
design2 <- update(
  design,
  sport_regulier = enqueter2$sport_regulier,
  diplome_4 = enqueter2$diplome_4
)

svychisq(
  ~diplome_4 + sport_regulier,
  design2,
  statistic = "F"
)

svyby(
  ~sport_regulier,
  ~diplome_4,
  design2,
  svymean,
  na.rm = TRUE,
  vartype = c("se", "ci")
)

J.3 Interprétation

Le test répond à une question globale de compatibilité avec l’indépendance. Les proportions et leurs intervalles montrent où se situent les différences et leur ordre de grandeur. Un rapport ne doit donc pas se limiter à la p-value.

K Corrigé 6 — Régression logistique

K.1 Consigne

Expliquer la pratique sportive régulière par le diplôme, l’âge et le sexe.

K.2 Code

Afficher le code R
mod <- svyglm(
  sport_regulier ~ diplome_4 + age + sexe_f,
  design = design2,
  family = quasibinomial()
)

summary(mod)

tbl_regression(
  mod,
  exponentiate = TRUE
)

K.3 Lecture

  • La catégorie de référence du diplôme doit être identifiée.
  • exp(beta) donne un odds ratio.
  • L’intervalle de confiance doit être lu avec l’estimation.
  • Un odds ratio n’est pas directement une différence de probabilités.

K.4 Restitution plus lisible

Afficher le code R
profils <- expand_grid(
  diplome_4 = levels(enqueter2$diplome_4),
  age = 45,
  sexe_f = factor("Femme", levels = levels(enqueter2$sexe_f))
)

predict(
  mod,
  newdata = profils,
  type = "response",
  se.fit = TRUE
)

L Corrigé 7 — Interaction

L.1 Consigne

Tester si l’association diplôme–sport diffère selon le sexe.

L.2 Code

Afficher le code R
mod_inter <- svyglm(
  sport_regulier ~ diplome_4 * sexe_f + age,
  design = design2,
  family = quasibinomial()
)

regTermTest(
  mod_inter,
  ~diplome_4:sexe_f
)

L.3 Interprétation

Une interaction se lit difficilement à partir de coefficients isolés. Il faut produire des prédictions pour les combinaisons diplôme × sexe et représenter les probabilités prévues avec leur incertitude.

Une interaction statistiquement peu précise ne signifie pas que les groupes sont identiques ; elle peut aussi refléter des cellules petites, notamment pour les catégories peu fréquentes.

M Corrigé 8 — Valeurs manquantes et cas complets

M.1 Consigne

Étudier si les personnes sans revenu observé diffèrent des autres selon l’âge et le diplôme.

M.2 Code

Afficher le code R
enqueter %>%
  mutate(revenu_manquant = is.na(revenu_mensuel)) %>%
  group_by(revenu_manquant) %>%
  summarise(
    n = n(),
    age_moyen = mean(age, na.rm = TRUE),
    .groups = "drop"
  )

enqueter %>%
  mutate(revenu_manquant = is.na(revenu_mensuel)) %>%
  count(revenu_manquant, diplome_4) %>%
  group_by(revenu_manquant) %>%
  mutate(pct = n / sum(n))

M.3 Interprétation

Des différences observées signalent que l’analyse en cas complets peut sélectionner une sous-population particulière. Elles ne permettent pas, à elles seules, de déterminer si le mécanisme est MAR ou MNAR.

N Corrigé 9 — Imputation multiple

N.1 Consigne

Construire un workflow d’imputation pour le revenu puis estimer satisfaction_vie ~ revenu + âge + diplôme.

N.2 Code essentiel

Afficher le code R
data_imp <- enqueter %>%
  select(
    satisfaction_vie,
    revenu_mensuel,
    age,
    sexe,
    diplome_4,
    statut_emploi,
    sante
  )

set.seed(2026)
imp <- mice(
  data_imp,
  m = 20,
  maxit = 20,
  printFlag = FALSE
)

fit <- with(
  imp,
  lm(
    satisfaction_vie ~ revenu_mensuel + age + diplome_4
  )
)

pool(fit) %>% summary(conf.int = TRUE)

N.3 Ce que la copie doit expliquer

  • pourquoi ces variables ont été retenues ;
  • comment les variables structurellement non applicables ont été exclues ;
  • quels diagnostics d’imputation ont été examinés ;
  • pourquoi on combine les estimations, pas les jeux de données empilés.

O Corrigé 10 — Raking

O.1 Consigne

Calibrer le design sur les marges fictives sexe, âge et territoire.

O.2 Solution

Voir le chapitre 22 pour la préparation des trois objets de population. La partie décisive est :

Afficher le code R
design_rake <- rake(
  design_cal,
  sample.margins = list(
    ~sexe_f,
    ~age_classe,
    ~territoire
  ),
  population.margins = list(
    pop_sexe,
    pop_age,
    pop_territoire
  )
)

Puis vérifier :

Afficher le code R
svytable(~sexe_f, design_rake)
svytable(~age_classe, design_rake)
svytable(~territoire, design_rake)
summary(weights(design_rake))

Une correction sans diagnostic des poids est incomplète.

P Corrigé 11 — Attrition longitudinale

P.1 Consigne

Comparer les personnes présentes et absentes de la vague 2 puis construire un panel long.

P.2 Code

Afficher le code R
vague2 <- read_csv(
  "data/raw/enqueter_vague2.csv",
  show_col_types = FALSE
)

statut_panel <- enqueter %>%
  mutate(repond_v2 = id %in% vague2$id)

statut_panel %>%
  group_by(repond_v2) %>%
  summarise(
    n = n(),
    age_moyen = mean(age, na.rm = TRUE),
    satisfaction = mean(satisfaction_vie, na.rm = TRUE),
    .groups = "drop"
  )

Dans le jeu synthétique, les non-répondants de vague 2 sont en moyenne plus âgés. Le panel observé n’est donc pas une simple réduction aléatoire de la vague 1.

Q Corrigé 12 — Audit de reproductibilité

Q.1 Consigne

Un projet contient :

  • un fichier brut modifié manuellement ;
  • trois scripts utilisant setwd() vers des chemins personnels ;
  • un fichier .RData requis mais non documenté ;
  • des données personnelles dans un dépôt Git public ;
  • des figures collées manuellement dans Word.

Proposer un plan de correction.

Q.2 Correction

Priorité critique :

  1. retirer et traiter l’exposition des données personnelles selon les procédures institutionnelles ;
  2. rétablir une source brute immuable et documentée ;
  3. supprimer la dépendance à des objets cachés dans .RData.

Priorité importante :

  1. créer un projet RStudio et des chemins relatifs ;
  2. transformer les manipulations manuelles en scripts ;
  3. produire tableaux et figures depuis le code.

Amélioration :

  1. ajouter renv, README, Git pour le code, Quarto pour le rapport et un journal des transformations.

R Auto-évaluation finale

Pour chaque exercice, attribuez-vous 1 point par critère :

Un exercice techniquement correct mais sans justification méthodologique n’est pas complètement corrigé.