Afficher le code R
enqueter <- readr::read_csv(
"data/derived/enqueter_clean.csv",
show_col_types = FALSE
)Comment transformer des réponses brutes en variables d’analyse — classes d’âge, niveau de diplôme regroupé, indicateur d’emploi ou score de confiance — sans perdre le lien avec les questions d’origine ?
À la fin de ce chapitre, vous saurez :
mutate(), case_when() et {forcats} pour créer des catégories ;Le fichier doit avoir été diagnostiqué et ses codes spéciaux transformés en valeurs manquantes lorsque cela est nécessaire. Les variables sources doivent rester disponibles.
Recoder n’est pas uniquement changer une syntaxe. Lorsque sept modalités de diplôme deviennent quatre groupes, on décide que certaines différences sont moins importantes pour l’analyse. Lorsque l’âge est transformé en classes, on remplace une information précise par des catégories. Lorsque cinq items deviennent un score moyen, on suppose qu’ils peuvent être résumés ensemble.
Ces décisions doivent être :
Dans une base d’analyse, préférer la création d’une nouvelle variable (diplome_4) à l’écrasement immédiat de la variable source (diplome).
Nous partons ici d’un objet où les principales anomalies et codes spéciaux ont déjà été traités. Le script complet correspondant est fourni dans R/01-construire-enqueter-clean.R.
Dans une session de travail, on peut soit exécuter les étapes des chapitres 6–7, soit charger la version dérivée fournie :
enqueter <- readr::read_csv(
"data/derived/enqueter_clean.csv",
show_col_types = FALSE
)Dans le reste du livre, un fichier .rds sera généralement préféré pour conserver les classes R des facteurs et autres objets.
mutate() : ajouter sans perdre les autres variablesmutate() crée ou modifie des colonnes (Wickham et al. 2026) :
enqueter <- enqueter |>
mutate(age_centre = age - mean(age, na.rm = TRUE))Ici, age reste disponible et age_centre est ajouté.
Comparer :
# Risque de perdre l'information d'origine
enqueter <- enqueter |>
mutate(age = if_else(age >= 50, 1, 0))et :
enqueter <- enqueter |>
mutate(age_50plus = if_else(age >= 50, 1, 0))La seconde version conserve l’âge précis, ce qui permet de tester ultérieurement une autre définition.
cut()Nous créons quatre groupes :
enqueter <- enqueter |>
mutate(
age_classe = cut(
age,
breaks = c(17, 29, 44, 59, 79),
labels = c("18-29", "30-44", "45-59", "60-79"),
ordered_result = TRUE
)
)Par défaut, cut() construit ici des intervalles cohérents avec les bornes fournies. Il est important de vérifier la classification autour des seuils :
enqueter |>
filter(age %in% c(18, 29, 30, 44, 45, 59, 60, 79)) |>
count(age, age_classe)# A tibble: 8 × 3
age age_classe n
<dbl> <ord> <int>
1 18 18-29 134
2 29 18-29 46
3 30 30-44 45
4 44 30-44 103
5 45 45-59 88
6 59 45-59 68
7 60 60-79 71
8 79 60-79 85
La transformation en classes :
Pour les modèles, nous conserverons souvent age numérique et n’utiliserons age_classe que lorsque la question le justifie.
Créer des groupes n’est pas une obligation méthodologique. Il faut pouvoir expliquer pourquoi 29 et 30 ans appartiennent à des catégories différentes alors que 30 et 44 ans sont regroupés.
case_when()Le diplôme brut comporte sept niveaux substantifs. Pour certains tableaux, nous créons quatre groupes :
enqueter <- enqueter |>
mutate(
diplome_4 = case_when(
diplome %in% 1:3 ~ "Inférieur au bac",
diplome == 4 ~ "Bac",
diplome == 5 ~ "Bac+2",
diplome %in% 6:7 ~ "Supérieur long",
.default = NA_character_
)
)case_when() évalue les conditions dans l’ordre et attribue la valeur correspondante (Wickham et al. 2026).
enqueter <- enqueter |>
mutate(
diplome_4 = factor(
diplome_4,
levels = c(
"Inférieur au bac",
"Bac",
"Bac+2",
"Supérieur long"
),
ordered = TRUE
)
)L’ordre est ici substantiel. Il sera utilisé dans les tableaux et peut influencer le comportement de certaines fonctions.
Un facteur ordered = TRUE utilise par défaut des contrastes polynomiaux dans plusieurs modèles R. Lorsque l’objectif est de comparer chaque niveau de diplôme à une catégorie de référence, les chapitres de régression utiliseront une version factorielle non ordonnée dont les niveaux restent présentés dans le même ordre. Le type de contraste est une décision de modélisation, pas un simple détail d’affichage.
Une règle essentielle : ne jamais considérer un recodage comme terminé avant d’avoir croisé source et résultat.
enqueter |>
count(diplome, diplome_4) |>
arrange(diplome)# A tibble: 8 × 3
diplome diplome_4 n
<dbl> <ord> <int>
1 1 Inférieur au bac 468
2 2 Inférieur au bac 446
3 3 Inférieur au bac 687
4 4 Bac 703
5 5 Bac+2 548
6 6 Supérieur long 370
7 7 Supérieur long 306
8 NA <NA> 64
Le tableau permet de vérifier :
NA restent cohérents.sum(!is.na(enqueter$diplome))[1] 3528
sum(!is.na(enqueter$diplome_4))[1] 3528
Sauf choix explicite, les deux nombres devraient être identiques.
{forcats}Le package {forcats} est conçu pour le travail sur les facteurs (Wickham 2026).
Pour une variable sexe_f, imaginons que l’on souhaite « Femme » comme première modalité :
sexe_f <- forcats::fct_relevel(sexe_f, "Femme")La première modalité joue souvent le rôle de référence dans une régression. Ce choix doit être substantif, pas dicté par l’ordre alphabétique.
statut_simplifie <- forcats::fct_collapse(
statut,
"Actif" = c("En emploi", "Au chômage"),
"Inactif" = c("Étudiant", "Retraité", "Au foyer", "Autre inactif")
)Attention : ce regroupement n’est pas forcément pertinent pour toutes les questions de recherche. {forcats} facilite l’opération technique ; il ne fournit pas la justification sociologique.
À partir de statut_emploi :
enqueter <- enqueter |>
mutate(
en_emploi = case_when(
statut_emploi == 1 ~ 1,
!is.na(statut_emploi) ~ 0,
.default = NA_real_
)
)Cette syntaxe distingue trois situations :
1 : en emploi ;0 : autre statut observé ;NA : statut d’emploi lui-même manquant.Une écriture comme :
as.numeric(statut_emploi == 1)peut être compacte, mais il faut vérifier son comportement en présence de NA. Pour un manuel, case_when() rend la logique explicite.
en_emploi peut être conservé en 0/1 pour certains calculs :
mean(enqueter$en_emploi, na.rm = TRUE)La moyenne correspond alors à la proportion de 1 parmi les valeurs observées.
Pour un tableau lisible :
en_emploi_f <- factor(
enqueter$en_emploi,
levels = c(0, 1),
labels = c("Non", "Oui")
)Il n’est pas nécessaire de choisir une représentation unique pour tous les usages. L’important est de connaître la signification des codes.
Le jeu comporte cinq questions de confiance :
conf_parlement
conf_justice
conf_police
conf_mairie
conf_associations
Chaque item est codé de 1 à 5 après retrait des codes spéciaux.
Une règle très stricte consiste à calculer le score uniquement si les cinq réponses sont observées :
enqueter <- enqueter |>
mutate(
score_confiance = if_else(
if_all(
c(conf_parlement, conf_justice, conf_police, conf_mairie, conf_associations),
~ !is.na(.x)
),
rowMeans(
pick(conf_parlement, conf_justice, conf_police, conf_mairie, conf_associations)
),
NA_real_
)
)Le score reste sur une échelle de 1 à 5.
Dans certains projets, on peut accepter un score si au moins quatre items sur cinq sont renseignés :
items_confiance <- c(
"conf_parlement", "conf_justice", "conf_police",
"conf_mairie", "conf_associations"
)
mat <- as.matrix(enqueter[items_confiance])
n_items_valides <- rowSums(!is.na(mat))
score_partiel <- rowMeans(mat, na.rm = TRUE)
score_partiel[n_items_valides < 4] <- NACette règle change la comparabilité et la quantité de données disponibles. Elle doit être annoncée.
Avant de combiner des items, il faut vérifier qu’ils mesurent bien un concept commun, que leur orientation est cohérente et que la règle de traitement des manquants est défendable. Les analyses de fiabilité et de structure seront discutées dans les approfondissements.
Supposons une échelle de 1 à 5 où un item est orienté dans le sens opposé aux autres. Une inversion classique :
item_inverse <- 6 - item_originalcar :
Cette formule dépend des bornes. Pour une échelle 0–10, elle serait différente. L’inversion doit être guidée par le libellé du questionnaire.
Dans notre enquête, chaque source d’information est une colonne binaire :
info_tv
info_presse
info_radio
info_reseaux
info_podcasts
Après correction du code invalide de info_reseaux :
enqueter <- enqueter |>
mutate(
nb_sources_info = rowSums(
pick(info_tv, info_presse, info_radio, info_reseaux, info_podcasts),
na.rm = FALSE
)
)Pourquoi na.rm = FALSE ? Parce que si une composante est réellement inconnue, nous ne voulons pas automatiquement considérer cette absence comme 0 = non.
Selon le questionnaire, on pourrait choisir une règle différente. L’important est de ne pas confondre NA et « modalité non cochée » sans connaître le codage de la collecte.
recode() : prudence et alternativesPour quelques remplacements simples, on peut utiliser les outils de recodage de {dplyr} ou {forcats} (Wickham et al. 2026; Wickham 2026). Pour des règles complexes, case_when() est souvent plus lisible car chaque condition est explicitement écrite.
Exemple :
mode <- factor(c("Web", "Téléphone", "Face-à-face"))
mode_court <- forcats::fct_recode(
mode,
"F2F" = "Face-à-face"
)Renommer une modalité pour un graphique n’implique pas nécessairement de modifier la variable analytique principale.
Les facteurs ont un ordre de niveaux :
levels(enqueter$diplome_4)[1] "Inférieur au bac" "Bac" "Bac+2" "Supérieur long"
Dans de nombreux modèles, le premier niveau sert de référence. Pour le modifier :
diplome_4 <- forcats::fct_relevel(diplome_4, "Bac")Ce choix modifie l’interprétation des coefficients, pas les observations elles-mêmes. Nous y reviendrons aux chapitres 17 et 18.
Le dictionnaire ne doit pas rester figé au fichier brut. Pour une analyse sérieuse, on peut créer un dictionnaire dérivé :
| Variable dérivée | Variables sources | Règle | Commentaire |
|---|---|---|---|
age_classe |
age |
18–29, 30–44, 45–59, 60–79 | pour tableaux |
diplome_4 |
diplome |
regroupement 1–3 / 4 / 5 / 6–7 | ordonné |
en_emploi |
statut_emploi |
1 si code 1, 0 autres statuts observés | binaire |
score_confiance |
5 items | moyenne si 5/5 observés | échelle 1–5 |
nb_sources_info |
5 items binaires | somme si tous connus | 0–5 |
Le code reste la source d’exécution ; ce tableau sert de documentation humaine.
enqueter |>
count(diplome, diplome_4)# A tibble: 8 × 3
diplome diplome_4 n
<dbl> <ord> <int>
1 1 Inférieur au bac 468
2 2 Inférieur au bac 446
3 3 Inférieur au bac 687
4 4 Bac 703
5 5 Bac+2 548
6 6 Supérieur long 370
7 7 Supérieur long 306
8 NA <NA> 64
range(enqueter$score_confiance, na.rm = TRUE)[1] 1 5
range(enqueter$nb_sources_info, na.rm = TRUE)[1] 1 5
table(enqueter$statut_emploi, enqueter$en_emploi, useNA = "ifany")
0 1 <NA>
1 0 2143 0
2 239 0 0
3 173 0 0
4 442 0 0
5 196 0 0
6 363 0 0
<NA> 0 0 36
sum(is.na(enqueter$diplome_4))[1] 64
sum(is.na(enqueter$score_confiance))[1] 240
Un recodage peut être syntaxiquement correct tout en créant trop de NA parce qu’une modalité a été oubliée. Les contrôles avant/après détectent ce type d’erreur.
readr::write_csv(
enqueter,
"data/derived/enqueter_clean.csv"
)saveRDS(
enqueter,
"data/derived/enqueter_clean.rds"
)Puis :
enqueter <- readRDS("data/derived/enqueter_clean.rds")Le RDS est particulièrement utile lorsque la base contient des facteurs ordonnés, dates ou autres classes que l’on souhaite retrouver telles quelles.
Le fichier dérivé doit être régénérable par le script. Si vous devez le modifier manuellement après l’exécution, le workflow n’est pas encore complet.
Créez une variable avec trois groupes : 18–34, 35–54, 55–79. Vérifiez ensuite les âges 34, 35, 54 et 55.
enqueter <- enqueter |>
mutate(
age_3 = cut(
age,
breaks = c(17, 34, 54, 79),
labels = c("18-34", "35-54", "55-79"),
ordered_result = TRUE
)
)
enqueter |>
filter(age %in% c(34, 35, 54, 55)) |>
count(age, age_3)Créez sport_regulier = 1 si sport_freq >= 3, 0 si une autre fréquence valide est observée, et NA si sport_freq manque.
enqueter <- enqueter |>
mutate(
sport_regulier = case_when(
sport_freq >= 3 ~ 1,
!is.na(sport_freq) ~ 0,
.default = NA_real_
)
)Reproduisez age_classe, diplome_4, en_emploi et nb_sources_info. Pour chaque variable, produisez au moins un contrôle de correspondance ou de plage.
Vous souhaitez étudier le lien entre âge et satisfaction de vie. Discutez les avantages et limites de trois représentations de l’âge : numérique linéaire, classes, et transformation non linéaire. Laquelle choisiriez-vous pour une première description ? pour une régression ?
Construisez un score de confiance à partir des cinq items. Comparez :
Comparez le nombre de scores non manquants, leur moyenne et leur dispersion. Discutez ce que change la règle de construction.
Les classes facilitent une description mais créent des seuils et perdent de l’information. Une variable numérique conserve l’information et permet une interprétation par unité, mais un modèle linéaire suppose une forme particulière de relation. Une transformation non linéaire ou des splines peuvent être envisagées si la relation ne semble pas linéaire. Pour une première description, classes et distribution continue peuvent être présentées ensemble ; pour une régression, il est souvent préférable de ne pas catégoriser automatiquement l’âge.
Le score strict est plus comparable d’une personne à l’autre mais perd davantage d’observations. La règle « au moins quatre items » conserve plus de personnes mais autorise des scores calculés sur des compositions légèrement différentes. Il faut vérifier que les résultats substantiels ne dépendent pas fortement de cette décision et documenter la règle retenue.
case_when() rend explicites les règles de regroupement (Wickham et al. 2026).{forcats} facilite leur manipulation (Wickham 2026).NA lorsque le statut source est inconnu.Les verbes de transformation sont documentés dans {dplyr} (Wickham et al. 2026), et les opérations sur facteurs dans {forcats} (Wickham 2026). Dans les chapitres suivants, ces variables dérivées seront utilisées pour les estimations, tableaux et modèles.
Nous avons maintenant accompli la première grande étape du workflow : passer d’un fichier brut documenté à une base d’analyse traçable. Avant de décrire les résultats, il reste une question spécifiquement liée aux enquêtes : les observations doivent-elles toutes compter de la même manière ? Le chapitre 9 introduira les poids et le plan d’échantillonnage.