8  Recoder et construire des variables

ImportantQuestion de départ

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 ?

8.1 Objectifs d’apprentissage

À la fin de ce chapitre, vous saurez :

  • recoder une variable tout en conservant sa version source ;
  • utiliser mutate(), case_when() et {forcats} pour créer des catégories ;
  • définir explicitement l’ordre des modalités et les catégories de référence ;
  • créer un indicateur binaire à partir d’une variable catégorielle ;
  • construire une classe d’âge en comprenant la perte d’information qu’elle implique ;
  • regrouper des modalités sans confondre simplification et neutralité ;
  • construire un score simple à partir d’une batterie d’items ;
  • résumer une question à réponses multiples ;
  • vérifier systématiquement un recodage avec des tableaux avant/après ;
  • sauvegarder une base dérivée séparée du fichier brut.

8.2 Prérequis

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.

8.3 Un recodage est une décision analytique

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 :

  • justifiées ;
  • reproductibles ;
  • vérifiées ;
  • et, si possible, réversibles à partir des variables sources.
NotePrincipe directeur

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).

8.4 Préparer l’objet de travail

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 :

Afficher le code R
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.

8.5 mutate() : ajouter sans perdre les autres variables

mutate() crée ou modifie des colonnes (Wickham et al. 2026) :

Afficher le code R
enqueter <- enqueter |>
  mutate(age_centre = age - mean(age, na.rm = TRUE))

Ici, age reste disponible et age_centre est ajouté.

8.5.1 Pourquoi éviter l’écrasement systématique ?

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.

8.6 Créer des classes d’âge avec cut()

Nous créons quatre groupes :

Afficher le code R
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
    )
  )

8.6.1 Lire les bornes

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 :

Afficher le code R
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

8.6.2 Pourquoi ne pas analyser uniquement l’âge classé ?

La transformation en classes :

  • facilite certains tableaux ;
  • rend parfois l’interprétation plus intuitive ;
  • mais perd de l’information ;
  • crée des seuils qui peuvent être arbitraires ;
  • peut masquer une relation continue ou non linéaire.

Pour les modèles, nous conserverons souvent age numérique et n’utiliserons age_classe que lorsque la question le justifie.

AvertissementLes classes ne sont pas plus ‘objectives’ que la variable continue

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.

8.7 Regrouper le diplôme avec case_when()

Le diplôme brut comporte sept niveaux substantifs. Pour certains tableaux, nous créons quatre groupes :

Afficher le code R
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).

8.7.1 Définir l’ordre des groupes

Afficher le code R
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.

AvertissementOrdonné ne signifie pas toujours ‘meilleur pour la régression’

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.

8.8 Vérifier un recodage par un tableau de correspondance

Une règle essentielle : ne jamais considérer un recodage comme terminé avant d’avoir croisé source et résultat.

Afficher le code R
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 :

  • qu’aucun niveau attendu n’est oublié ;
  • qu’aucune modalité n’arrive dans deux groupes ;
  • que les NA restent cohérents.

8.8.1 Vérifier les effectifs totaux

Afficher le code R
sum(!is.na(enqueter$diplome))
[1] 3528
Afficher le code R
sum(!is.na(enqueter$diplome_4))
[1] 3528

Sauf choix explicite, les deux nombres devraient être identiques.

8.9 Recoder des modalités avec {forcats}

Le package {forcats} est conçu pour le travail sur les facteurs (Wickham 2026).

8.9.1 Changer une catégorie de référence

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.

8.9.2 Regrouper des modalités

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.

8.10 Créer un indicateur binaire d’emploi

À partir de statut_emploi :

Afficher le code R
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.

8.11 Binaire numérique ou facteur ?

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.

8.12 Construire un score à partir d’une batterie d’items

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.

8.12.1 Score moyen complet

Une règle très stricte consiste à calculer le score uniquement si les cinq réponses sont observées :

Afficher le code R
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.

8.12.2 Autoriser un nombre minimal d’items

Dans certains projets, on peut accepter un score si au moins quatre items sur cinq sont renseignés :

Afficher le code R
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] <- NA

Cette règle change la comparabilité et la quantité de données disponibles. Elle doit être annoncée.

AvertissementUn score n’est pas justifié uniquement parce qu’une moyenne est calculable

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.

8.13 Items inversés

Supposons une échelle de 1 à 5 où un item est orienté dans le sens opposé aux autres. Une inversion classique :

item_inverse <- 6 - item_original

car :

  • 1 devient 5 ;
  • 2 devient 4 ;
  • 3 reste 3.

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.

8.14 Questions à réponses multiples

Dans notre enquête, chaque source d’information est une colonne binaire :

info_tv
info_presse
info_radio
info_reseaux
info_podcasts

8.14.1 Nombre de sources déclarées

Après correction du code invalide de info_reseaux :

Afficher le code R
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.

8.14.2 Variante : nombre minimal de réponses connues

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.

8.15 Recoder du texte avec recode() : prudence et alternatives

Pour 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.

8.16 Catégories de référence en régression

Les facteurs ont un ordre de niveaux :

Afficher le code R
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.

8.17 Documenter les variables dérivées

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.

8.18 Vérification globale des recodages

8.18.1 Distribution avant/après

Afficher le code R
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

8.18.2 Plages des variables dérivées

Afficher le code R
range(enqueter$score_confiance, na.rm = TRUE)
[1] 1 5
Afficher le code R
range(enqueter$nb_sources_info, na.rm = TRUE)
[1] 1 5

8.18.3 Cohérence du binaire

Afficher le code R
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

8.18.4 Nombre de manquants créés

Afficher le code R
sum(is.na(enqueter$diplome_4))
[1] 64
Afficher le code R
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.

8.19 Sauvegarder la base dérivée

8.19.1 CSV : interopérable mais pauvre en classes R

Afficher le code R
readr::write_csv(
  enqueter,
  "data/derived/enqueter_clean.csv"
)

8.19.2 RDS : préserver l’objet R

Afficher le code R
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.

AstuceBon réflexe

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.

8.20 Mini-exercices

8.20.1 Mini-exercice 1 — Vérifier une classe d’âge

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)

8.20.2 Mini-exercice 2 — Vérifier un binaire

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_
    )
  )

8.21 Exercice de synthèse

8.21.1 Niveau A — Appliquer

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.

8.21.2 Niveau B — Choisir

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 ?

8.21.3 Niveau C — Analyser

Construisez un score de confiance à partir des cinq items. Comparez :

  1. un score disponible uniquement si les cinq items sont renseignés ;
  2. un score disponible si au moins quatre items sont renseignés.

Comparez le nombre de scores non manquants, leur moyenne et leur dispersion. Discutez ce que change la règle de construction.

8.21.4 Correction détaillée

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.

8.22 À retenir

  • Un recodage est une décision analytique, pas uniquement une opération informatique.
  • Conserver les variables sources facilite les vérifications et analyses de sensibilité.
  • case_when() rend explicites les règles de regroupement (Wickham et al. 2026).
  • Les facteurs doivent avoir des niveaux et un ordre choisis consciemment ; {forcats} facilite leur manipulation (Wickham 2026).
  • Catégoriser une variable continue entraîne une perte d’information.
  • Les indicateurs binaires doivent préserver les NA lorsque le statut source est inconnu.
  • Un score nécessite une justification conceptuelle et une règle explicite de traitement des items manquants.
  • Une question à réponses multiples ne doit pas confondre absence de réponse et réponse « non ».
  • Chaque recodage doit être vérifié par des tableaux de correspondance, plages ou effectifs.
  • Les fichiers dérivés doivent être produits par le code et séparés des données brutes.

8.23 Pour aller plus loin

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.