---
title: "Échantillonnage, poids et objet de design"
---
```{r}
#| label: setup-analyse
#| include: false
source("R/04-initialiser-analyse.R")
```
::: {.callout-important title="Question de départ"}
Dans une enquête où certaines personnes ont eu plus de chances d'être sélectionnées que d'autres, une ligne doit-elle nécessairement compter exactement autant qu'une autre ?
:::
## Objectifs d'apprentissage
À la fin de ce chapitre, vous saurez :
- expliquer la différence entre échantillon observé et population cible ;
- interpréter intuitivement une probabilité d'inclusion et un poids de sondage ;
- reconnaître le rôle de la stratification et des grappes ;
- distinguer poids de base, ajustement de non-réponse et calibration ;
- créer un objet `survey.design` avec `{survey}` ;
- comparer une statistique brute et une statistique pondérée ;
- comprendre pourquoi pondérer une moyenne ne suffit pas toujours pour obtenir une erreur standard correcte ;
- conserver ensemble données et informations de plan pour les analyses suivantes.
## Pourquoi ce chapitre arrive avant les statistiques descriptives
Dans beaucoup de cours de R, les poids apparaissent à la fin, comme une option supplémentaire. Dans un livre consacré spécifiquement aux **données d'enquête**, ce serait trompeur. Le plan de sondage influence non seulement les estimations ponctuelles, mais également leur incertitude [@lumley2010; @survey2026].
Nous devons donc définir l'objet d'enquête **avant** de produire systématiquement des pourcentages, intervalles de confiance ou tests.
## Échantillon et population
Supposons une population de 100 000 adultes. Une enquête interroge 2 000 personnes. Les 2 000 lignes décrivent d'abord **les personnes observées**. Pour utiliser ces données afin d'estimer une caractéristique de la population, il faut comprendre comment ces personnes sont entrées dans l'échantillon.
Si chaque adulte avait exactement la même probabilité d'être sélectionné, la situation est relativement simple. Mais de nombreuses enquêtes utilisent :
- des probabilités différentes selon les groupes ;
- une stratification ;
- plusieurs degrés de tirage ;
- des grappes géographiques ;
- des ajustements de non-réponse ;
- une calibration sur des marges de population.
## Probabilité d'inclusion et poids
Si une unité a une probabilité d'inclusion $\pi_i$, son poids de base est intuitivement :
$$
w_i = \frac{1}{\pi_i}.
$$
Une personne sélectionnée avec une probabilité de 1/100 porte un poids de base 100 ; une personne sélectionnée avec une probabilité de 1/50 porte un poids 50.
Cela ne signifie pas que la première « vaut deux fois plus humainement ». Le poids représente sa contribution statistique dans le mécanisme d'estimation.
### Poids normalisés
Dans le jeu pédagogique, `poids_final` a été normalisé autour de 1. Il est utile pour illustrer les différences relatives de pondération, mais sa somme n'est pas conçue pour représenter la taille réelle d'une population française.
```{r}
resume_poids <- tibble::tibble(
Minimum = min(enqueter$poids_final),
Q1 = unname(quantile(enqueter$poids_final, .25)),
Médiane = median(enqueter$poids_final),
Moyenne = mean(enqueter$poids_final),
Q3 = unname(quantile(enqueter$poids_final, .75)),
Maximum = max(enqueter$poids_final)
) |>
dplyr::mutate(dplyr::across(everything(), ~round(.x, 3)))
knitr::kable(resume_poids, align = rep("r", 6))
```
La moyenne est très proche de 1, avec des poids qui varient approximativement de 0,50 à 2,18.
::: {.callout-note title="Poids normalisé ou poids d'extrapolation"}
Deux jeux de poids peuvent produire les mêmes proportions pondérées tout en ayant des sommes différentes. Il faut consulter la documentation pour savoir si un poids est calibré pour totaliser une population, un échantillon, ou une autre constante.
:::
## Stratification
Une population peut être divisée en **strates** avant le tirage : par exemple grandes zones, catégories de communes ou classes définies pour assurer une représentation suffisante.
Dans `EnquêteR`, le plan pédagogique comporte six strates et plusieurs unités primaires d'échantillonnage (PSU) dans chacune d'elles. Une table compacte permet de voir cette architecture sans imprimer deux sorties séparées.
```{r}
#| label: ch09-plan-structure
structure_plan <- enqueter |>
dplyr::group_by(strate) |>
dplyr::summarise(
`Répondants` = dplyr::n(),
`PSU distinctes` = dplyr::n_distinct(psu),
.groups = "drop"
)
knitr::kable(structure_plan, align = c("c", "r", "r"))
```
La stratification peut améliorer la précision lorsqu'elle est bien conçue. Elle doit néanmoins être déclarée au logiciel : sinon, le calcul de variance suppose implicitement un autre mécanisme de sélection.
## Grappes et PSU
Dans un sondage en plusieurs degrés, on peut d'abord tirer des zones, établissements, communes ou autres **unités primaires d'échantillonnage** (*Primary Sampling Units*, PSU), puis sélectionner des individus à l'intérieur.
Les personnes d'une même grappe peuvent se ressembler davantage que deux personnes tirées indépendamment dans toute la population. Cette dépendance affecte l'incertitude. Ici, les 72 couples strate–PSU laissent **`r survey::degf(design)` degrés de liberté de plan** (`PSU − strates`) pour une partie de l'inférence. Ce nombre, et pas seulement les 3 592 lignes du fichier, intervient dans certains tests et intervalles.
## Pourquoi `weighted.mean()` ne suffit pas
Pour une moyenne pondérée, on pourrait écrire :
```{r}
weighted.mean(
enqueter$satisfaction_vie,
w = enqueter$poids_final,
na.rm = TRUE
)
```
Cette commande peut donner une estimation ponctuelle utile. Mais elle ne sait rien de `strate` ni de `psu`. Elle ne suffit donc pas pour calculer une variance ou un intervalle de confiance tenant compte du plan complexe.
C'est précisément le rôle du package `{survey}` [@survey2026].
## Créer l'objet de design
Le code suivant construit explicitement l'objet afin de comprendre ses composantes. Pour reprendre directement les exemples des chapitres suivants dans une nouvelle session R, le projet fournit aussi un script d'initialisation :
```r
source("R/04-initialiser-analyse.R")
```
Ce script charge `enqueter` puis crée `design` avec exactement les informations de plan utilisées ci-dessous. La syntaxe essentielle est :
```r
design <- survey::svydesign(
ids = ~psu,
strata = ~strate,
weights = ~poids_final,
data = enqueter,
nest = TRUE
)
```
Dans le rendu du livre, cet objet a déjà été créé par le chunk d'initialisation invisible :
```{r}
#| label: ch09-design-compact
resume_design <- tibble::tibble(
Composante = c(
"Observations", "Strates", "PSU distinctes", "Poids final (min–max)"
),
Valeur = c(
format(nrow(enqueter), big.mark = " "),
dplyr::n_distinct(enqueter$strate),
dplyr::n_distinct(paste(enqueter$strate, enqueter$psu, sep = "-")),
paste0(
fmt_num(min(enqueter$poids_final, na.rm = TRUE), .01),
" – ",
fmt_num(max(enqueter$poids_final, na.rm = TRUE), .01)
)
)
)
knitr::kable(resume_design, align = c("l", "r"))
```
Les principaux arguments sont :
- `ids` : identifiants de grappes / PSU ;
- `strata` : strates ;
- `weights` : poids ;
- `data` : tableau contenant les variables ;
- `nest = TRUE` : indique ici que les identifiants de PSU doivent être considérés comme imbriqués dans les strates.
La documentation de `svydesign()` définit cet objet comme le conteneur des données et métadonnées nécessaires à l'analyse d'échantillons complexes [@survey2026].
### Un plan sans grappe
Pour une enquête pondérée mais sans information de stratification ou de grappe :
```r
design_simple <- svydesign(
ids = ~1,
weights = ~poids_final,
data = enqueter
)
```
Il ne faut pas choisir `~1` par simplicité si un véritable plan plus complexe est connu.
## Première comparaison brute / pondérée
Avant de multiplier les tableaux, comparons deux statistiques simples. Le tableau juxtapose le résumé des répondants et l'estimation obtenue avec le plan d'enquête.
```{r}
#| label: ch09-premiere-comparaison
est_sat <- survey::svymean(~satisfaction_vie, design, na.rm = TRUE)
est_emp <- survey::svymean(~en_emploi, design, na.rm = TRUE)
premiere_comparaison <- tibble::tibble(
Indicateur = c("Satisfaction de vie (moyenne)", "En emploi (proportion)"),
Brut = c(
mean(enqueter$satisfaction_vie, na.rm = TRUE),
mean(enqueter$en_emploi, na.rm = TRUE)
),
Pondéré = c(as.numeric(coef(est_sat)), as.numeric(coef(est_emp))),
`Erreur standard survey` = c(as.numeric(SE(est_sat)), as.numeric(SE(est_emp)))
) |>
dplyr::mutate(
Brut = c(fmt_num(Brut[1], .01), fmt_pct(Brut[2], .1)),
Pondéré = c(fmt_num(Pondéré[1], .01), fmt_pct(Pondéré[2], .1)),
`Erreur standard survey` = c(
fmt_num(`Erreur standard survey`[1], .001),
fmt_pct(`Erreur standard survey`[2], .1)
)
)
knitr::kable(premiere_comparaison, align = c("l", "r", "r", "r"))
```
La pondération peut déplacer peu le point estimé tout en modifiant son incertitude. Une petite différence brut–pondéré sur **un** indicateur ne permet donc pas de conclure que le plan est négligeable.
### Voir simultanément brut et pondéré
Le même jeu de variables peut être résumé avec un tableau brut et un tableau survey. Cela rend visible la différence d'objet statistique.
```{r}
#| label: tab-ch09-brut-pondere
tbl_brut <- enqueter |>
gtsummary::tbl_summary(
include = c(sexe_f, diplome_4, age_classe_f),
missing = "no",
label = list(
sexe_f ~ "Sexe / genre",
diplome_4 ~ "Niveau de diplôme",
age_classe_f ~ "Classe d'âge"
)
) |>
gtsummary::bold_labels()
tbl_pond <- design |>
gtsummary::tbl_svysummary(
include = c(sexe_f, diplome_4, age_classe_f),
missing = "no",
label = list(
sexe_f ~ "Sexe / genre",
diplome_4 ~ "Niveau de diplôme",
age_classe_f ~ "Classe d'âge"
)
) |>
gtsummary::bold_labels()
gtsummary::tbl_merge(
list(tbl_brut, tbl_pond),
tab_spanner = c("**Échantillon brut**", "**Estimation pondérée**")
)
```
::: {.callout-note title="Ce que montre la comparaison"}
La pondération ne transforme pas un échantillon en population réelle observée. Elle change la contribution des lignes afin d'estimer des paramètres sous le dispositif documenté. Le tableau brut reste utile pour connaître les répondants effectivement disponibles ; le tableau pondéré vise un autre objet.
:::
## Deux usages complémentaires des tableaux survey
Pour une vérification rapide, `svytable()` reste très pratique : `prop.table(svytable(~diplome_4, design))` renvoie directement la composition pondérée par diplôme. Pour les tableaux destinés au lecteur, nous privilégierons ensuite `svymean()` pour les estimations et `gtsummary::tbl_svysummary()` pour la mise en forme [@gtsummary2021].
Cette distinction évite de confondre **objet de calcul** et **objet de communication** : le premier doit être facilement inspectable, le second doit rendre le dénominateur, les unités et l'incertitude compréhensibles.
## Sous-populations : ne pas filtrer naïvement le design
Supposons que l'on souhaite analyser les personnes de 60 ans et plus. Avec un objet survey, la méthode recommandée est généralement de définir un **domaine** :
```{r}
design_60plus <- subset(design, age >= 60)
svymean(~satisfaction_vie, design_60plus, na.rm = TRUE)
```
Cette opération conserve l'information du design nécessaire au calcul de variance dans le domaine. La logique des sous-populations sera reprise plus loin.
## Poids de base, non-réponse et calibration : panorama
Un « poids final » peut résulter de plusieurs étapes.
### Poids de base
Inverse de la probabilité d'inclusion issue du plan de tirage.
### Ajustement de non-réponse
Certains groupes répondent moins que d'autres. Le poids peut être ajusté pour compenser partiellement ce déséquilibre, selon une méthode documentée.
### Calibration / redressement
Les poids sont modifiés afin que certaines distributions pondérées concordent avec des totaux externes connus : âge, sexe, région, diplôme, etc.
Le chapitre 22 abordera ces techniques plus en détail. Ici, l'objectif est de comprendre que `poids_final` n'est pas un coefficient mystérieux que l'on applique sans lire sa documentation.
::: {.callout-warning title="Un poids ne répare pas tout"}
La pondération ne corrige pas automatiquement les erreurs de mesure, les questions mal formulées, une couverture déficiente sur des dimensions non observées ou toute forme de biais de non-réponse. Elle fait partie d'un dispositif d'estimation, pas d'une garantie générale de représentativité.
:::
## Effet de plan et taille effective : deux diagnostics à ne pas confondre
Deux idées différentes sont souvent mélangées.
La première concerne **la dispersion des poids**. Si quelques observations portent beaucoup plus de poids que les autres, l'information disponible est moins équilibrée. La taille effective de Kish résume uniquement cette perte liée aux poids :
$$
n_{eff,w} = \frac{(\sum_i w_i)^2}{\sum_i w_i^2}.
$$
La seconde concerne **le plan complet** : stratification et corrélation à l'intérieur des PSU peuvent à leur tour augmenter ou réduire la variance. L'effet de plan (`DEFF`) est donc propre à une estimation donnée.
Comme `poids_final` est normalisé et n'est pas l'inverse brut d'une probabilité d'inclusion, nous demandons à `{survey}` le DEFF avec remise (`deff = "replace"`). La documentation du package recommande cette référence lorsque les poids ont été rescalés [@survey2026].
```{r}
#| label: tab-ch09-diagnostics-plan
n_eff_poids <- kish_neff(enqueter$poids_final)
sat_deff <- survey::svymean(
~satisfaction_vie, design, na.rm = TRUE, deff = "replace"
)
sport_deff <- survey::svymean(
~sport_regulier, design, na.rm = TRUE, deff = "replace"
)
diag_plan <- tibble::tibble(
Diagnostic = c(
"N observé",
"N effectif — dispersion des poids seulement",
"DEFF — satisfaction de vie",
"DEFF — sport régulier"
),
Valeur = c(
fmt_num(nrow(enqueter), 1),
fmt_num(n_eff_poids, 1),
fmt_num(as.numeric(survey::deff(sat_deff)), .01),
fmt_num(as.numeric(survey::deff(sport_deff)), .01)
)
)
knitr::kable(diag_plan, align = c("l", "r"))
```
Ici, la dispersion des poids seule ramène la taille effective d'environ **`r fmt_num(nrow(enqueter), 1)`** à **`r fmt_num(n_eff_poids, 1)`** observations. Ce nombre ne tient compte ni des strates ni des grappes : il ne doit donc pas être présenté comme « la » taille effective universelle de l'enquête.
::: {.callout-note title="Pourquoi garder les deux diagnostics ?"}
La taille effective de Kish est une propriété des **poids**. Le DEFF de `svymean()` est une propriété de **l'estimation sous le plan**. Deux variables peuvent avoir le même jeu de poids mais des effets de plan différents parce que leur corrélation intra-grappe n'est pas la même.
:::
Le chapitre 14 reviendra sur la variance, les degrés de liberté et l'effet de plan.
## Vérifier les poids avant de les utiliser
Plutôt qu'une longue sortie, retenons cinq percentiles et la distribution graphique.
```{r}
quantiles_poids <- tibble::tibble(
Percentile = c("1 %", "5 %", "50 %", "95 %", "99 %"),
Poids = as.numeric(
quantile(enqueter$poids_final, probs = c(.01, .05, .5, .95, .99))
)
) |>
dplyr::mutate(Poids = round(Poids, 3))
knitr::kable(quantiles_poids, align = c("l", "r"))
```
```{r}
#| label: plot-ch09-poids
#| fig-alt: "Histogramme des poids finaux, concentrés autour de un mais avec une dispersion visible."
ggplot(enqueter, aes(x = poids_final)) +
geom_histogram(
bins = 30,
fill = couleurs_enqueter[["principal"]],
colour = "white",
linewidth = 0.2
) +
geom_vline(xintercept = 1, linetype = 2, colour = couleurs_enqueter[["accent"]]) +
labs(
x = "Poids final", y = "Nombre d'observations",
title = "Les poids ne sont pas tous égaux à 1",
subtitle = "La ligne verticale repère le poids 1",
caption = "Données synthétiques EnquêteR."
) +
theme_enqueter()
```
Des poids très dispersés peuvent augmenter la variance. Leur diagnostic est une étape méthodologique, pas uniquement technique.
## Conserver l'objet de design dans le workflow
L'objet `design` devient la porte d'entrée des chapitres 10 à 19 :
```text
enqueter_clean.rds
↓
svydesign()
↓
design
↙ ↓ ↘
svymean svytable svyglm ...
```
Cette architecture réduit le risque d'appliquer les poids à certaines analyses mais de les oublier dans d'autres.
## Mini-exercices
### Mini-exercice 1 — Probabilité et poids
Deux individus ont des probabilités d'inclusion respectives de 0,02 et 0,01. Quels sont leurs poids de base ?
::: {.callout-caution collapse="true" title="Correction"}
Les poids sont `1/0,02 = 50` et `1/0,01 = 100`. Le second individu représente davantage d'unités de population dans l'estimation parce qu'il avait une probabilité d'inclusion plus faible.
:::
### Mini-exercice 2 — Point estimé versus variance
Pourquoi une moyenne calculée avec `weighted.mean()` n'est-elle pas équivalente à une analyse complète avec `svymean()` lorsque l'enquête est stratifiée et en grappes ?
::: {.callout-caution collapse="true" title="Correction"}
`weighted.mean()` utilise les poids pour le point estimé mais ne connaît pas la structure des strates et grappes. `svymean()` appliqué à un `survey.design` utilise les informations de design pour l'estimation de variance et l'erreur standard.
:::
## Exercice de synthèse
### Niveau A — Appliquer
Créez l'objet `design` du livre et calculez :
1. la moyenne pondérée de `satisfaction_vie` ;
2. la proportion pondérée de personnes en emploi ;
3. la distribution pondérée de `diplome_4`.
### Niveau B — Choisir
Vous recevez un fichier avec une colonne `poids`, mais aucune information sur la façon dont elle a été construite. Quelles questions posez-vous avant de l'utiliser ?
### Niveau C — Analyser
Comparez, pour `sexe`, `diplome_4` et `en_emploi`, les distributions brutes et pondérées. Identifiez les catégories les plus modifiées par la pondération et rédigez une courte interprétation qui n'assimile pas automatiquement écart de pondération et « biais corrigé ».
### Correction détaillée
::: {.callout-caution collapse="true" title="Éléments de correction — Niveau B"}
Il faut au minimum savoir : population cible ; plan de sondage ; probabilité d'inclusion ; éventuelle normalisation ; ajustement de non-réponse ; variables de calibration ; présence de strates et grappes ; objectif du poids ; éventuels poids de réplication. Sans ces informations, la colonne ne peut pas être interprétée correctement.
:::
## À retenir
- Une ligne d'enquête n'a pas nécessairement la même probabilité d'inclusion qu'une autre.
- Le poids de base est lié à l'inverse de la probabilité d'inclusion.
- Stratification et grappes concernent aussi l'estimation de l'incertitude.
- `weighted.mean()` ne remplace pas un objet de plan complexe.
- `{survey}` rassemble données, poids, strates et PSU dans un `survey.design` [@survey2026].
- Les analyses descriptives et modèles peuvent ensuite utiliser ce même objet.
- Un poids final peut combiner plusieurs corrections ; sa construction doit être documentée.
- Pondération et représentativité ne sont pas synonymes.
- Les poids doivent eux-mêmes être diagnostiqués.
## Pour aller plus loin
L'ouvrage de Lumley fournit une présentation complète de l'analyse de sondages complexes avec R [@lumley2010]. Le package `{survey}` implémente les principales statistiques, tests, modèles et méthodes de calibration pour ce type de données [@survey2026].
Nous pouvons maintenant décrire les variables. À partir du chapitre suivant, nous distinguerons systématiquement **description brute de l'échantillon** et **estimation pondérée associée au plan d'enquête**.