Leçon 5 sur 8
Unité · Estimer correctement
Déclarez le plan une seule fois
Un objet de plan, et toute estimation ultérieure hérite des pondérations, des strates et des grappes. Le paquet survey de R, l'arithmétique équivalente en Python, et les quatre erreurs qui produisent en silence une simple moyenne.
Cessez de porter le plan à la main
Tout ce qui précède a été calculé explicitement, et c’était voulu — il faut savoir ce qu’est l’arithmétique. À partir d’ici, le plan doit être déclaré une fois et hérité, car l’alternative consiste à penser à appliquer pondérations, strates et grappes à chaque estimation d’un script de cinquante lignes, et l’estimation que vous oublierez est celle qui ira dans le résumé.
L’objet de plan sous R
library(survey)
design <- svydesign(
ids = ~ea_id, # the primary sampling unit
strata = ~stratum, # the stratification
weights = ~weight, # the weight built in lesson 2
data = survey,
nest = TRUE # cluster ids are nested within strata
)
design
Quatre arguments et chacun porte.
idsest l’unité primaire de sondage — la zone de dénombrement, non le ménage. Passer~household_idici est l’erreur la plus fréquente de ce cours et produit silencieusement une variance de sondage aléatoire simple.strataretire la variance inter-strates, qui est le gain mesuré à la leçon précédente.weightsrend l’estimation ponctuelle sans biais.nest = TRUEindique au paquet que les identifiants de zone ne sont uniques qu’à l’intérieur d’une strate. Omettez-le là où ils sont réutilisés entre strates et les zones seront fusionnées.
Ensuite, chaque estimation sort du plan.
svymean(~I(food_insecure == "true"), design, deff = TRUE)
svytotal(~I(food_insecure == "true"), design)
svyby(~I(food_insecure == "true"), ~stratum, design, svymean, vartype = c("se", "ci"))
svyciprop(~I(food_insecure == "true"), design, method = "logit")
svyby est le cheval de trait — n’importe quelle estimation, par n’importe quel
regroupement, avec le plan propagé. Et svyciprop est ce qu’il faut pour une
proportion : la leçon suivante explique pourquoi l’intervalle de svymean a la
mauvaise forme près de 0 et de 1.
La même chose en Python
Python n’a pas d’équivalent de survey sur lequel ce secteur se soit fixé, si
bien que les exemples calculent l’estimateur directement. C’est un coût et aussi
un avantage — l’arithmétique reste visible.
import numpy as np
import pandas as pd
class SurveyDesign:
"""Stratified two-stage design, ultimate-cluster variance."""
def __init__(self, data, ids, strata, weights):
self.data, self.ids, self.strata, self.weights = data, ids, strata, weights
def mean(self, indicator):
d = self.data
y = indicator(d).astype(float)
w = d[self.weights]
p = np.average(y, weights=w)
variance, total_w = 0.0, w.sum()
for _, stratum in d.groupby(self.strata):
residual = stratum[self.weights] * (indicator(stratum).astype(float) - p)
totals = residual.groupby(stratum[self.ids]).sum()
n = len(totals)
if n < 2:
continue
variance += n / (n - 1) * ((totals - totals.mean()) ** 2).sum()
se = np.sqrt(variance) / total_w
srs = np.sqrt(p * (1 - p) / len(d))
return {"estimate": p, "se": se, "deff": (se / srs) ** 2,
"ci_low": p - 1.96 * se, "ci_high": p + 1.96 * se}
design = SurveyDesign(survey, ids="ea_id", strata="stratum", weights="weight")
print(design.mean(lambda d: d["food_insecure"] == "true"))
# The R equivalent of the whole class above:
svymean(~I(food_insecure == "true"), design, deff = TRUE)
La classe Python fait trente lignes et l’appel R une seule. Cette asymétrie est réelle et c’est pourquoi l’analyse d’enquêtes se fait d’ordinaire en R dans ce secteur. Employez R pour l’estimation d’enquête quand vous avez le choix ; la voie Python est pour les équipes qui ne l’ont pas, et pour comprendre ce que fait l’appel R.
L’approximation par grappe ultime
Les deux implémentations emploient le même raccourci, et il vaut la peine de le nommer car il ressemble à une simplification sans en être une.
La variance se calcule à partir des totaux des unités primaires de sondage, en ignorant entièrement le second degré. C’est exact lorsque le premier degré est tiré avec remise, et légèrement conservateur sinon — cela surestime très légèrement la variance en ignorant la correction de population finie au second degré.
Être légèrement conservateur est le bon sens d’erreur, et c’est pourquoi tous les
grands paquets font cela par défaut. survey l’appelle l’approximation par grappe
ultime, et tous les rapports EDS aussi.
Les quatre façons d’obtenir une simple moyenne par accident
Chacune produit un nombre d’allure correcte qui est une estimation de sondage aléatoire simple.
Passer le ménage comme grappe. ids = ~household_id dit que chaque ménage est
sa propre unité primaire, donc qu’il n’y a pas de mise en grappes à prendre en
compte, et l’erreur type s’effondre.
Oublier les pondérations. svydesign(..., weights = NULL) donne à chaque
ménage le même poids, ce qui est le 32,8 % de la leçon 1.
Filtrer le tableau au lieu de sous-ensembler le plan. Celle-ci est subtile et fréquente.
# WRONG: the design object no longer knows about the dropped strata
rural <- svydesign(ids = ~ea_id, strata = ~stratum, weights = ~weight,
data = filter(survey, stratum != "urban"), nest = TRUE)
# RIGHT
rural <- subset(design, stratum != "urban")
# Same rule: subset the design, keeping the full strata structure in view
rural = SurveyDesign(survey[survey["stratum"] != "urban"],
ids="ea_id", strata="stratum", weights="weight")
Sous-ensembler un plan préserve la structure des strates et des grappes pour l’estimation de variance. Le reconstruire depuis un tableau filtré jette les zones sans ménage restant, ce qui change les degrés de liberté et peut produire en silence une strate à une seule grappe — où la contribution à la variance est indéfinie et discrètement écartée.
Calculer sur un tableau joint. Joignez l’enquête à quoi que ce soit en un-à-plusieurs et les pondérations deviennent fausses, car un ménage apparaît plusieurs fois. Agrégez au ménage avant d’estimer : c’est la leçon sur la granularité du cours de jointures qui revient dans un autre décor.
Des estimations autres que des proportions
svymean(~household_size, design, na.rm = TRUE)
svytotal(~I(food_insecure == "true"), design)
svyquantile(~minutes_to_water, design, quantiles = c(0.5, 0.9), na.rm = TRUE)
svyratio(~I(food_insecure == "true"), ~I(children_under5 > 0), design)
print(design.mean(lambda d: d["household_size"] > 7))
Deux remarques. Un total pondéré est l’estimation que l’on veut le plus souvent
et qu’on ne peut le plus souvent pas obtenir, car elle exige des pondérations à
l’échelle de la population — les pondérations normalisées de la leçon 2 donneraient
un total de 996. Et na.rm = TRUE sur une moyenne d’enquête est une décision et
non une commodité : cela suppose que les ménages manquants ressemblent aux ménages
observés de leur strate, hypothèse identique à celle de l’ajustement pour
non-réponse, et à énoncer une fois pour les deux.
Le plan du sous-échantillon
Les enfants mesurés forment un sous-échantillon avec sa propre pondération, et ils exigent leur propre objet de plan — non un filtre sur celui des ménages.
children <- subset(design, !is.na(child_muac_mm))
children <- update(children, child_weight = weight * children_under5)
child_design <- svydesign(ids = ~ea_id, strata = ~stratum,
weights = ~child_weight,
data = subset(survey, !is.na(child_muac_mm)), nest = TRUE)
svyciprop(~I(child_muac_mm < 125), child_design, method = "logit")
measured = survey[survey["child_muac_mm"].notna()].copy()
measured["child_weight"] = measured["weight"] * measured["children_under5"]
child_design = SurveyDesign(measured, ids="ea_id", strata="stratum",
weights="child_weight")
print(child_design.mean(lambda d: d["child_muac_mm"] < 125))
Les grappes sont les mêmes zones, donc la mise en grappes s’applique toujours. Seuls la pondération et la population changent.
Écrivez le plan à côté des estimations
Design Stratified two-stage cluster
Strata urban, rural-accessible, rural-remote
PSU enumeration area (75 sampled, 25 per stratum)
Stage 1 PPS, size measure = households at frame listing
Stage 2 SRS, 14 households per area
Weights base = M_h / (25 * 14), non-response adjusted within area
Variance ultimate cluster, Taylor linearisation
Software R survey 4.x / equivalent direct computation in Python
Sept lignes. Tout lecteur peut désormais reproduire vos erreurs types, et tout relecteur peut voir en dix secondes si vous avez fait les choses correctement — ce qu’on ne peut pas dire de la plupart des annexes d’enquête.
La suite
L’objet de plan suppose que tous les ménages tirés ont été enquêtés et que toutes les zones tirées ont été visitées. Ni l’un ni l’autre n’est vrai ici. La leçon suivante traite les 54 ménages non enquêtés et les deux zones qu’il a fallu remplacer.