cassionAnalyse de données

Leçon 3 sur 8

Unité · Des données qui gardent leur sens

Les fichiers Stata et SPSS gardent leurs étiquettes

haven, la classe labelled, et pourquoi le .dta d'un institut de sondage doit être lu sous R même si l'analyse se fait ailleurs — plus les deux façons de perdre un dictionnaire de codes sans s'en apercevoir.

R75 min

Ce qu’un CSV jette

Un institut de sondage livre un .dta ou un .sav. À l’intérieur, une colonne n’est pas seulement des valeurs — elle porte une étiquette de variable (« Le chef de ménage a consenti à l’entretien ») et des étiquettes de valeurs (1 = Oui, 2 = Non, 9 = Ne sait pas).

Exportez-le en CSV et les deux disparaissent. Vous obtenez une colonne de 1, 2 et 9, un dictionnaire de codes en PDF que quelqu’un doit conserver, et une moyenne de 1,7 qui ne signifie rien.

library(haven)

survey <- read_dta(here("data", "raw", "household-survey.dta"))

haven lit les étiquettes en même temps que les valeurs. C’est la raison d’être de cette leçon, et la raison de lire un fichier d’enquête sous R même si l’analyse se fera en Python : R préserve davantage du fichier que pandas, et un CSV accompagné d’un dictionnaire que vous avez écrit vous-même est un moins bon artefact que l’original.

La classe labelled

read_dta() vous donne des colonnes de classe haven_labelled : les valeurs sous-jacentes, avec leurs étiquettes attachées.

class(survey$consent)
#> [1] "haven_labelled" "vctrs_vctr" "double"

attr(survey$consent, "labels")
#>        Oui        Non Ne sait pas
#>          1          2           9

attr(survey$consent, "label")
#> [1] "Le chef de menage a consenti a l'entretien"

Les valeurs sont toujours là — l’arithmétique fonctionne — et c’est justement le piège. Une colonne étiquetée est un nombre du point de vue de mean() :

mean(survey$consent, na.rm = TRUE)
#> [1] 1.7

Ce chiffre est la moyenne de 1, 2 et 9. Il n’a aucun sens et il n’avertit de rien.

Convertir délibérément

haven propose trois gestes, et le bon dépend de ce qu’est la colonne.

Une variable catégorielle devient un facteur.

library(dplyr)

survey <- survey |>
  mutate(consent = as_factor(consent))

levels(survey$consent)
#> [1] "Oui" "Non" "Ne sait pas"

as_factor() — celui de haven, non le as.factor() de base — utilise les étiquettes de valeurs comme niveaux, dans l’ordre que le fichier déclare plutôt qu’alphabétiquement. Cet ordre est un cadeau : celui du dictionnaire de codes est généralement celui qu’un rapport attend.

Un véritable nombre perd ses étiquettes.

survey <- survey |> mutate(hh_size = zap_labels(hh_size))

zap_labels() retire l’étiquetage et laisse les valeurs. À employer là où la colonne est réellement une quantité qui portait accessoirement une étiquette.

Tout d’un coup, quand le fichier est uniformément catégoriel :

survey <- read_dta(path) |> as_factor()

Appliquer as_factor() au tibble entier convertit chaque colonne étiquetée. Rapide, et faux pour tout fichier mêlant catégories et quantités — vérifiez avec glimpse() avant d’y recourir.

Les deux façons de perdre un dictionnaire de codes

Lire avec les étiquettes déjà appliquées

survey <- read_dta(path, .name_repair = "unique")
survey <- as_factor(survey)          # les valeurs ont disparu, restent les etiquettes

Les codes sous-jacents sont désormais indisponibles. Cela compte plus qu’il n’y paraît : le dictionnaire, le questionnaire, le plan de tabulation et toute analyse antérieure renvoient tous à 1 et 2. À un collègue qui demande « combien ont été codés 9 ? », un facteur ne répond pas.

Gardez les deux :

survey <- read_dta(path)

codes <- survey |> mutate(across(where(is.labelled), zap_labels))
labels <- survey |> as_factor()

Écrire le CSV et passer à autre chose

readr::write_csv(as_factor(survey), "survey.csv")

Le CSV porte désormais "Oui" et "Non", ce qui est lisible et perd dans une autre direction : les codes ont disparu, et avec eux la distinction entre « Ne sait pas » et une véritable valeur manquante.

Si vous devez remettre un CSV à une analyse Python, remettez un dictionnaire avec lui, généré depuis le fichier plutôt que saisi :

codebook <- tibble::tibble(
  variable = names(survey),
  label    = vapply(survey, function(x) attr(x, "label") %||% NA_character_, character(1))
)

readr::write_csv(codebook, "survey-codebook.csv")

Les étiquettes de valeurs demandent une ligne par niveau, ce que labelled::look_for() produit directement si vous disposez de ce paquet. Dans tous les cas, le dictionnaire est dérivé, non transcrit — un dictionnaire transcrit s’écarte du fichier qu’il décrit.

Les valeurs manquantes définies par l’utilisateur

Stata et SPSS distinguent des sortes de manquant : refus, sans objet, non posé. haven les préserve sous forme de NA étiquetés plutôt que de les fondre en un seul.

survey$income
#> <labelled<double>[3]>
#> [1]  4500 NA(a) NA(b)

NA(a) et NA(b) restent des NA pour is.na() : rien ne devient donc silencieusement un nombre. Mais la distinction survit, et elle est souvent le constat — une question refusée par 40 % des répondants est un problème différent d’une question qui ne les concernait pas.

survey <- survey |> mutate(income = zap_missing(income))

zap_missing() les aplatit en NA ordinaires une fois que vous les avez comptés. Comptez d’abord.

SPSS et les autres formats

read_sav(path)     # SPSS .sav
read_por(path)     # SPSS portable
read_dta(path)     # Stata
read_xpt(path)     # SAS transport

Tous renvoient la même structure étiquetée : tout ce qui précède s’applique tel quel.

L’écriture est symétrique, et parfois nécessaire quand un ministère attend un .dta :

write_dta(survey, "cleaned.dta")

Stata impose aux noms de variables des règles que R n’a pas — pas de points, 32 caractères — et write_dta() lèvera une erreur plutôt que de les déformer silencieusement. C’est le comportement souhaitable.

Où cela rejoint le cours Python

Le cours Python pour les données de programme dit, dans sa leçon sur les codes et les sentinelles, que c’est le seul endroit où la réponse est « utilisez l’autre langage ». C’est cette leçon-ci qu’il désigne.

pandas lit un .dta et vous donne soit les codes, soit les étiquettes, jamais les deux, et ne préserve pas du tout les valeurs manquantes étiquetées. Si les étiquettes comptent — et sur un fichier d’enquête, c’est toujours le cas — lisez sous R, convertissez délibérément, et exportez à la fois les codes et un dictionnaire dérivé.

Ce qui vient ensuite

Les étiquettes sont attachées et converties en facteurs. La leçon suivante porte sur ce qu’est réellement un facteur en R, et sur le problème d’ordre que as_factor() a résolu ici pour vous et ne résoudra pas pour une colonne que vous construisez vous-même.

Animer cette leçon

La leçon en diaporama, la prose étant reléguée dans les notes du présentateur plutôt que projetée. Produit à partir de cette page, dont il ne peut donc pas s'écarter.

Lancer le diaporamaLire les diapositives

Le PDF ne requiert aucun logiciel et se projette depuis n'importe quel poste. Le fichier PowerPoint est fait pour être modifié : appliquez la charte de votre organisation, retirez une section pour une séance plus courte, ou fusionnez deux leçons en atelier.