---
title: "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.
# 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.
# Corrigé 1 — Auditer un fichier brut
## 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.
## 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.
## Code
```{r, eval=FALSE}
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)
```
## 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/`.
# Corrigé 2 — Recoder sans perdre le sens
## 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.
## Code
```{r, eval=FALSE}
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")
)
)
```
## Vérifications
```{r, eval=FALSE}
table(enqueter2$age_classe2, useNA = "ifany")
with(enqueter2, table(sport_freq, sport_regulier, useNA = "ifany"))
table(enqueter2$sexe, enqueter2$sexe_f, useNA = "ifany")
```
## 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.
# Corrigé 3 — Brut ou pondéré ?
## Consigne
Comparer la moyenne d'âge brute et la moyenne d'âge estimée avec le design.
## Code
```{r, eval=FALSE}
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)
```
## 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.
# Corrigé 4 — Choisir le bon dénominateur
## 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 ?
## Code descriptif non pondéré
```{r, eval=FALSE}
# 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))
```
## 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.
# Corrigé 5 — Test d'association et taille du résultat
## 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.
## Code
```{r, eval=FALSE}
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")
)
```
## 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.
# Corrigé 6 — Régression logistique
## Consigne
Expliquer la pratique sportive régulière par le diplôme, l'âge et le sexe.
## Code
```{r, eval=FALSE}
mod <- svyglm(
sport_regulier ~ diplome_4 + age + sexe_f,
design = design2,
family = quasibinomial()
)
summary(mod)
tbl_regression(
mod,
exponentiate = TRUE
)
```
## 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.
## Restitution plus lisible
```{r, eval=FALSE}
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
)
```
# Corrigé 7 — Interaction
## Consigne
Tester si l'association diplôme–sport diffère selon le sexe.
## Code
```{r, eval=FALSE}
mod_inter <- svyglm(
sport_regulier ~ diplome_4 * sexe_f + age,
design = design2,
family = quasibinomial()
)
regTermTest(
mod_inter,
~diplome_4:sexe_f
)
```
## 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.
# Corrigé 8 — Valeurs manquantes et cas complets
## Consigne
Étudier si les personnes sans revenu observé diffèrent des autres selon l'âge et le diplôme.
## Code
```{r, eval=FALSE}
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))
```
## 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.
# Corrigé 9 — Imputation multiple
## Consigne
Construire un workflow d'imputation pour le revenu puis estimer `satisfaction_vie ~ revenu + âge + diplôme`.
## Code essentiel
```{r, eval=FALSE}
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)
```
## 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.
# Corrigé 10 — Raking
## Consigne
Calibrer le design sur les marges fictives sexe, âge et territoire.
## Solution
Voir le chapitre 22 pour la préparation des trois objets de population. La partie décisive est :
```{r, eval=FALSE}
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 :
```{r, eval=FALSE}
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.
# Corrigé 11 — Attrition longitudinale
## Consigne
Comparer les personnes présentes et absentes de la vague 2 puis construire un panel long.
## Code
```{r, eval=FALSE}
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.
# Corrigé 12 — Audit de reproductibilité
## 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.
## 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 :
4. créer un projet RStudio et des chemins relatifs ;
5. transformer les manipulations manuelles en scripts ;
6. produire tableaux et figures depuis le code.
Amélioration :
7. ajouter `renv`, README, Git pour le code, Quarto pour le rapport et un journal des transformations.
# Auto-évaluation finale
Pour chaque exercice, attribuez-vous 1 point par critère :
- [ ] j'ai défini l'univers ;
- [ ] j'ai vérifié les types/codes ;
- [ ] j'ai pris en compte le design lorsque nécessaire ;
- [ ] j'ai produit une mesure d'incertitude adaptée ;
- [ ] j'ai interprété l'ordre de grandeur, pas uniquement la p-value ;
- [ ] j'ai formulé les limites ;
- [ ] mon code peut être rejoué depuis une session propre.
Un exercice techniquement correct mais sans justification méthodologique n'est pas complètement corrigé.
E Comment utiliser les corrigés
Pour chaque problème :
Une correction n’est pas un script à copier : elle montre une démarche possible.