La version originale de ce chapitre a été écrite par Julien Barnier dans le cadre du support de cours Introduction à R, complétée par Joseph Larmarange dans Introduction à l’analyse d’enquêtes avec R.

S’il est tout à fait possible de travailler avec des données pondérées sous R, cette fonctionnalité n’est pas aussi bien intégrée que dans la plupart des autres logiciels de traitement statistique. En particulier, il y a plusieurs manières possibles de gérer la pondération. Cependant, lorsque l’on doit également prendre un compte un plan d’échantillonnage complexe (voir section dédiée ci-après), R fournit tous les outils nécessaires, alors que dans la plupart des logiciels propriétaires, il faut disposer d’une extension adéquate, pas toujours vendue de base avec le logiciel.

Dans ce qui suit, on utilisera le jeu de données tiré de l’enquête Histoire de vie et notamment sa variable de pondération poids1.

library(questionr)
data(hdv2003)
d <- hdv2003
range(d$poids)
[1]    78.07834 31092.14132

Options de certaines fonctions

Tout d’abord, certaines fonctions de R acceptent en argument un vecteur permettant de pondérer les observations (l’option est en général nommée weights ou row.w). C’est le cas par exemple des méthodes d’estimation de modèles linéaires2 (lm) ou de modèles linéaires généralisés 3 (glm) ou dans les analyses de correspondances4 des extensions ade4 ou FactoMineR.

Par contre cette option n’est pas présente dans les fonctions de base comme mean, var, table ou chisq.test.

Fonctions de l’extension questionr

L’extension questionr propose quelques fonctions permettant de calculer des statistiques simples pondérées5 :

  • wtd.mean : moyenne pondérée
  • wtd.var : variance pondérée
  • wtd.table : tris à plat et tris croisés pondérés

On les utilise de la manière suivante :

library(questionr)
mean(d$age)
[1] 48.157
wtd.mean(d$age, weights = d$poids)
[1] 46.34726
wtd.var(d$age, weights = d$poids)
[1] 325.2658

Pour les tris à plat, on utilise la fonction wtd.table à laquelle on passe la variable en paramètre :

wtd.table(d$sexe, weights = d$poids)
  Homme   Femme 
5149382 5921844 

Pour un tri croisé, il suffit de passer deux variables en paramètres :

wtd.table(d$sexe, d$hard.rock, weights = d$poids)
             Non        Oui
Homme 5109366.41   40016.02
Femme 5872596.42   49247.49

Ces fonctions admettent notamment les deux options suivantes :

  • na.rm : si TRUE, on ne conserve que les observations sans valeur manquante.
  • normwt : si TRUE, on normalise les poids pour que les effectifs totaux pondérés soient les mêmes que les effectifs initiaux. Il faut utiliser cette option, notamment si on souhaite appliquer un test sensible aux effectifs comme le χ².

Ces fonctions rendent possibles l’utilisation des statistiques descriptives les plus simples et le traitement des tableaux croisés (les fonctions lprop, cprop ou chisq.test peuvent être appliquées au résultat d’un wtd.table) mais restent limitées en termes de tests statistiques ou de graphiques…

Données pondérées avec l’extension survey

L’extension survey est spécialement dédiée au traitement d’enquêtes ayant des techniques d’échantillonnage et de pondération potentiellement très complexes.

L’extension s’installe comme la plupart des autres :

install.packages("survey")

Le site officiel (en anglais) comporte beaucoup d’informations, mais pas forcément très accessibles :
http://r-survey.r-forge.r-project.org/.

Pour utiliser les fonctionnalités de l’extension, on doit d’abord définir le plan d’échantillonnage ou design de notre enquête, c’est-à-dire indiquer quel type de pondération nous souhaitons lui appliquer.

Dans un premier temps, nous utiliserons le plan d’échantillonnage le plus simple, avec une variable de pondération déjà calculée6. Ceci se fait à l’aide de la fonction svydesign :

library(survey)
dw <- svydesign(ids = ~1, data = d, weights = ~d$poids)

Cette fonction crée un nouvel objet, que nous avons nommé dw. Cet objet n’est pas à proprement parler un tableau de données, mais plutôt un tableau de données plus une méthode de pondération. dw et d sont des objets distincts, les opérations effectuées sur l’un n’ont pas d’influence sur l’autre. On peut cependant retrouver le contenu de d depuis dw en utilisant dw$variables :

str(d$age)
 int [1:2000] 28 23 59 34 71 35 60 47 20 28 ...
str(dw$variables$age)
 int [1:2000] 28 23 59 34 71 35 60 47 20 28 ...

Lorsque notre plan d’échantillonnage est déclaré, on peut lui appliquer une série de fonctions permettant d’effectuer diverses opérations statistiques en tenant compte de la pondération. On citera notamment :

  • svymean, svyvar, svytotal, svyquantile : statistiques univariées (moyenne, variance, total, quantiles)
  • svytable : tri à plat et tableau croisé
  • svychisq : test du χ²
  • svyby : statistiques selon un facteur
  • svyttest : test t de Student de comparaison de moyennes
  • svyciprop : intervalle de confiance d’une proportion
  • svyglm : modèles linéaires généralisés (dont régression logistique)
  • svyplot, svyhist, svyboxplot : fonctions graphiques

D’autres fonctions sont disponibles, comme svyratio, mais elles ne seront pas abordées ici.

Pour ne rien arranger, ces fonctions prennent leurs arguments sous forme de formules7, c’est-à-dire pas de la manière habituelle. En général l’appel de fonction se fait en spécifiant d’abord les variables d’intérêt sous forme de formule, puis l’objet survey.design.

Voyons tout de suite quelques exemples8 :

svymean(~age, dw)
      mean     SE
age 46.347 0.5284
svyquantile(~age, dw, quantile = c(0.25, 0.5, 0.75), 
  ci = TRUE)
$quantiles
    0.25 0.5 0.75
age   31  45   60

$CIs
, , age

       0.25 0.5 0.75
(lower   30  43   58
upper)   32  47   62
svyvar(~heures.tv, dw, na.rm = TRUE)
          variance     SE
heures.tv   2.9886 0.1836

Les tris à plat se déclarent en passant comme argument le nom de la variable précédé d’un tilde (~), tandis que les tableaux croisés utilisent les noms des deux variables séparés par un signe plus (+) et précédés par un tilde (~).

svytable(~sexe, dw)
sexe
  Homme   Femme 
5149382 5921844 
svytable(~sexe + clso, dw)
       clso
sexe           Oui        Non Ne sait pas
  Homme 2658744.04 2418187.64    72450.75
  Femme 2602031.76 3242389.36    77422.79

La fonction freq peut être utilisée si on lui passe en argument non pas la variable elle-même, mais son tri à plat obtenu avec svytable :

tab <- svytable(~peche.chasse, dw)
freq(tab, total = TRUE)
             n     %  val%
Non    9716683  87.8  87.8
Oui    1354544  12.2  12.2
Total 11071226 100.0 100.0

On peut également récupérer le tableau issu de svytable dans un objet et le réutiliser ensuite comme n’importe quel tableau croisé :

tab <- svytable(~sexe + clso, dw)
tab
       clso
sexe           Oui        Non Ne sait pas
  Homme 2658744.04 2418187.64    72450.75
  Femme 2602031.76 3242389.36    77422.79

Les fonctions lprop et cprop de questionr sont donc tout à fait compatibles avec l’utilisation de survey.

lprop(tab)
          clso
sexe       Oui   Non   Ne sait pas Total
  Homme     51.6  47.0   1.4       100.0
  Femme     43.9  54.8   1.3       100.0
  Ensemble  47.5  51.1   1.4       100.0

Le principe de la fonction svyby est similaire à celui de tapply9. Elle permet de calculer des statistiques selon plusieurs sous-groupes définis par un facteur. Par exemple :

svyby(~age, ~sexe, dw, svymean)
       sexe      age        se
Homme Homme 45.20200 0.7419450
Femme Femme 47.34313 0.7420836

survey est également capable de produire des graphiques à partir des données pondérées. Quelques exemples :

par(mfrow = c(2, 2))
svyplot(~age + heures.tv, dw, col = "red", main = "Bubble plot")
svyhist(~heures.tv, dw, col = "peachpuff", main = "Histogramme")
svyboxplot(age ~ 1, dw, main = "Boxplot simple", ylab = "Âge")
svyboxplot(age ~ sexe, dw, main = "Boxplot double", 
  ylab = "Âge", xlab = "Sexe")
Fonctions graphiques de l’extension survey

Extraire un sous-échantillon

Si l’on souhaite travailler sur un sous-échantillon tout en gardant les informations d’échantillonnage, on utilisera la fonction subset présentée en détail dans le chapitre Sous-ensembles.

sous <- subset(dw, sexe == "Femme" & age >= 40)

Conclusion

Si, la gestion de la pondération sous R n’est sans doute pas ce qui se fait de plus pratique et de plus simple, on pourra quand même donner les conseils suivants :

  • utiliser les options de pondération des fonctions usuelles ou les fonctions d’extensions comme questionr pour les cas les plus simples ;
  • si on utilise survey, effectuer autant que possible tous les recodages et manipulations sur les données non pondérées ;
  • une fois les recodages effectués, on déclare le design et on fait les analyses en tenant compte de la pondération ;
  • surtout ne jamais modifier les variables du design. Toujours effectuer recodages et manipulations sur les données non pondérées, puis redéclarer le design pour que les mises à jour effectuées soient disponibles pour l’analyse.

  1. On notera que cette variable est utilisée à titre purement illustratif. Le jeu de données étant un extrait d’enquête et la variable de pondération n’ayant pas été recalculée, elle n’a ici à proprement parler aucun sens.

  2. Voir le chapitre régression linéaire.

  3. Voir le chapitre sur la régression logistique.

  4. Voir le chapitre dédié à l’analyse des correspondances.

  5. Les fonctions wtd.mean et wtd.var sont des copies conformes des fonctions du même nom de l’extension Hmisc de Frank Harrel. Hmisc étant une extension « de taille », on a préféré recopié les fonctions pour limiter le poids des dépendances.

  6. Pour d’autres types de plan d’échantillonnage, voir la chapitre sur les plans d’échantillonnage complexes.

  7. Pour plus de détails sur les formules, voir le chapitre dédié.

  8. Pour d’autres exemples, voir http://www.ats.ucla.edu/stat/r/faq/svy_r_oscluster.htm (en anglais).

  9. La fonction tapply est présentée plus en détails dans le chapitre Manipulation de données.