Afficher le code R
library(readr)
enquete_csv <- read_csv(
"data/raw/enqueter_brut.csv",
show_col_types = FALSE
)Lorsque le même fichier d’enquête existe en CSV, Excel et Stata, obtient-on réellement les mêmes informations après importation dans R ?
À la fin de ce chapitre, vous saurez :
{haven} ;Il faut connaître la logique d’un projet, les chemins relatifs, les objets R et les fonctions dim(), names(), class() et glimpse().
L’importation semble parfois se résumer à une ligne :
enquete <- readr::read_csv("data/raw/enquete.csv")Mais cette ligne contient déjà plusieurs décisions : quel séparateur est utilisé ? quelle est la première ligne ? comment les nombres sont-ils écrits ? comment les valeurs vides sont-elles interprétées ? quelles colonnes R déduit-il comme numériques ou textuelles ? les labels d’un fichier statistique ont-ils été conservés ?
Les formats d’enquête ne transportent pas tous la même quantité d’information. Un CSV conserve essentiellement un tableau de valeurs ; un fichier Stata, SPSS ou SAS peut en plus contenir des labels de variables, des labels de valeurs et des informations sur des valeurs manquantes définies par l’utilisateur (Wickham, Miller, et al. 2026).
Le but de l’importation n’est pas d’obtenir un objet R « sans erreur ». Le but est d’obtenir un objet dont on sait ce qui a été lu, comment cela a été interprété et quelles informations ont été perdues ou conservées.
Le dossier data/raw/ contient plusieurs versions du jeu pédagogique :
data/raw/
├── enqueter_brut.csv
├── enqueter_brut.xlsx
├── enqueter_brut.dta
└── enqueter_vague2.csv
Le fichier principal contient 3 600 lignes et 36 colonnes avant traitement. Les anomalies qu’il contient sont volontaires : il ne faut donc pas « corriger » les données pendant l’importation.
Une version SPSS peut également être générée localement à partir du fichier Stata avec le script R/00-creer-fichiers-riches.R. Cette étape est séparée pour que le dépôt reste utilisable même si tous les formats ne sont pas nécessaires.
{readr}{readr} fournit plusieurs fonctions pour lire des données rectangulaires textuelles (Wickham, Hester, et al. 2026) :
read_csv() pour les fichiers séparés par des virgules ;read_csv2() pour le format courant en Europe avec ; comme séparateur et , comme marque décimale ;read_tsv() pour les tabulations ;read_delim() lorsque le séparateur doit être indiqué explicitement.library(readr)
enquete_csv <- read_csv(
"data/raw/enqueter_brut.csv",
show_col_types = FALSE
)Juste après l’import :
dim(enquete_csv)[1] 3600 36
head(names(enquete_csv), 12) [1] "id" "strate" "psu"
[4] "poids_base" "poids_final" "territoire"
[7] "mode_collecte" "sexe" "age"
[10] "diplome" "statut_emploi" "situation_familiale"
enquete_csv |>
dplyr::select(id, age, sexe, diplome, revenu_mensuel, poids_final) |>
dplyr::glimpse()Rows: 3,600
Columns: 6
$ id <dbl> 100001, 100002, 100003, 100004, 100005, 100006, 100007,…
$ age <dbl> 70, 64, 30, 41, 48, 63, 51, 27, 35, 34, 31, 28, 33, 54,…
$ sexe <dbl> 2, 1, 2, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, 2, 2…
$ diplome <dbl> 2, 2, 3, 7, 1, 2, 7, 7, 5, 3, 1, 2, 5, 3, 2, 4, 1, 6, 4…
$ revenu_mensuel <dbl> 1750, 99997, 2600, 3400, 2200, 2350, 99998, 2500, 2350,…
$ poids_final <dbl> 1.2692, 0.7122, 1.2943, 0.8282, 1.3215, 0.9727, 0.8254,…
glimpse(enquete_csv) reste la commande à utiliser sur votre machine. Le livre n’imprime ici qu’un sous-ensemble de colonnes représentatives afin d’éviter plusieurs dizaines de lignes de console qui n’ajouteraient pas d’information pédagogique.
Nous attendons 3 600 lignes et 36 colonnes. Si le résultat est 3600 × 1, le séparateur est probablement mal interprété.
enquete <- read_delim(
"data/raw/fichier.txt",
delim = ";"
)Ne pas choisir ; parce que « les fichiers français utilisent des points-virgules » : ouvrir quelques lignes du fichier ou consulter sa documentation permet de vérifier le délimiteur réel.
readr essaie d’inférer le type des colonnes. En cas de problème, on peut inspecter :
problems(enquete_csv)Cette fonction est particulièrement utile lorsqu’une valeur ne correspond pas au type attendu.
Supposons qu’un identifiant comme 00125 doive conserver ses zéros initiaux :
read_csv(
"fichier.csv",
col_types = cols(
identifiant = col_character()
)
)La décision doit venir de la signification de la variable, pas uniquement de l’apparence des premières lignes.
Un code postal, un numéro de ménage ou un identifiant peuvent être composés de chiffres sans être des variables quantitatives. Les convertir automatiquement en nombres peut supprimer des zéros initiaux ou encourager des calculs sans sens.
On peut indiquer à read_csv() certaines chaînes représentant des valeurs manquantes :
read_csv(
"fichier.csv",
na = c("", "NA")
)Mais il faut être prudent avec les codes spéciaux d’enquête. Dans enqueter_brut.csv, 98, 99, 99997 ou 99998 peuvent avoir des significations comme « ne sait pas », « refus » ou « sans objet ». Les convertir tous en NA dès l’import peut faire disparaître une information utile sur la nature de la non-réponse.
Dans ce livre, nous préférons donc :
Cette séparation est essentielle pour le chapitre 7.
{readxl}{readxl} lit les fichiers .xls et .xlsx (Wickham et Bryan 2026).
library(readxl)
enquete_excel <- read_excel(
"data/raw/enqueter_brut.xlsx",
sheet = "enqueter_brut"
)Pour connaître les feuilles :
excel_sheets("data/raw/enqueter_brut.xlsx")Une feuille Excel peut contenir :
L’import peut donc nécessiter skip, range ou col_types.
read_excel(
"fichier.xlsx",
sheet = 2,
skip = 3
)Dans le kit EnquêteR, le fichier Excel est volontairement simple : une feuille rectangulaire contenant les mêmes valeurs que le CSV. Il sert à apprendre la mécanique d’importation, pas à reproduire toutes les difficultés possibles d’un classeur réel.
{haven}Le package {haven} lit les principaux formats utilisés par les logiciels statistiques :
haven::read_dta("fichier.dta")
haven::read_sav("fichier.sav")
haven::read_sas("fichier.sas7bdat")Ces formats ont un avantage important pour les données d’enquête : ils peuvent conserver des métadonnées qui ne sont pas présentes dans un CSV (Wickham, Miller, et al. 2026).
library(haven)
enquete_dta <- read_dta("data/raw/enqueter_brut.dta")
dim(enquete_dta)[1] 3600 36
enquete_dta |>
dplyr::select(id, age, sexe, diplome, revenu_mensuel, poids_final) |>
dplyr::glimpse()Rows: 3,600
Columns: 6
$ id <dbl> 100001, 100002, 100003, 100004, 100005, 100006, 100007,…
$ age <dbl> 70, 64, 30, 41, 48, 63, 51, 27, 35, 34, 31, 28, 33, 54,…
$ sexe <dbl+lbl> 2, 1, 2, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, …
$ diplome <dbl+lbl> 2, 2, 3, 7, 1, 2, 7, 7, 5, 3, 1, 2, 5, 3, 2, 4, 1, …
$ revenu_mensuel <dbl> 1750, 99997, 2600, 3400, 2200, 2350, 99998, 2500, 2350,…
$ poids_final <dbl> 1.2692, 0.7122, 1.2943, 0.8282, 1.3215, 0.9727, 0.8254,…
Certaines colonnes apparaissent avec une classe de type haven_labelled. Ce n’est pas une erreur. Cela signifie que la variable peut contenir une valeur numérique et, en parallèle, un dictionnaire associant certains codes à des libellés.
attributes(enquete_dta$diplome)$label
[1] "Plus haut diplôme obtenu"
$format.stata
[1] "%12.0g"
$class
[1] "haven_labelled" "vctrs_vctr" "double"
$labels
Sans diplôme Brevet/CEP CAP/BEP Baccalauréat Bac+2
1 2 3 4 5
Licence Master/doctorat Ne sait pas Refus
6 7 98 99
ou, avec {labelled} :
labelled::val_labels(enquete_dta$diplome) Sans diplôme Brevet/CEP CAP/BEP Baccalauréat Bac+2
1 2 3 4 5
Licence Master/doctorat Ne sait pas Refus
6 7 98 99
labelled::var_label(enquete_dta$diplome)[1] "Plus haut diplôme obtenu"
Nous n’allons pas convertir immédiatement cette variable en facteur. Le chapitre suivant explique pourquoi.
Si le fichier .sav n’est pas déjà présent, le script fourni peut le produire :
source("R/00-creer-fichiers-riches.R")Puis :
if (file.exists("data/raw/enqueter_brut.sav")) {
enquete_spss <- haven::read_sav("data/raw/enqueter_brut.sav")
enquete_spss |>
dplyr::select(id, age, sexe, diplome, revenu_mensuel, poids_final) |>
dplyr::glimpse()
}Rows: 3,600
Columns: 6
$ id <dbl> 100001, 100002, 100003, 100004, 100005, 100006, 100007,…
$ age <dbl> 70, 64, 30, 41, 48, 63, 51, 27, 35, 34, 31, 28, 33, 54,…
$ sexe <dbl+lbl> 2, 1, 2, 2, 1, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 1, …
$ diplome <dbl+lbl> 2, 2, 3, 7, 1, 2, 7, 7, 5, 3, 1, 2, 5, 3, 2, 4, 1, …
$ revenu_mensuel <dbl> 1750, 99997, 2600, 3400, 2200, 2350, 99998, 2500, 2350,…
$ poids_final <dbl> 1.2692, 0.7122, 1.2943, 0.8282, 1.3215, 0.9727, 0.8254,…
L’utilisation de if (file.exists(...)) évite qu’un rendu Quarto échoue uniquement parce qu’un format optionnel n’a pas été généré.
Comparons conceptuellement :
class(enquete_csv$diplome)[1] "numeric"
class(enquete_excel$diplome)[1] "character"
class(enquete_dta$diplome)[1] "haven_labelled" "vctrs_vctr" "double"
Les valeurs visibles peuvent être les mêmes, mais le fichier Stata peut porter des labels supplémentaires. Cette différence devient importante lorsque l’on produit des tableaux ou que l’on doit distinguer un code 98 d’une modalité substantive.
rbind(
CSV = c(nrow(enquete_csv), ncol(enquete_csv)),
Excel = c(nrow(enquete_excel), ncol(enquete_excel)),
Stata = c(nrow(enquete_dta), ncol(enquete_dta))
) [,1] [,2]
CSV 3600 36
Excel 3600 36
Stata 3600 36
Les dimensions sont un premier contrôle, pas une preuve d’identité.
Pour une sélection de colonnes :
head(enquete_csv[c("id", "age", "diplome")])# A tibble: 6 × 3
id age diplome
<dbl> <dbl> <dbl>
1 100001 70 2
2 100002 64 2
3 100003 30 3
4 100004 41 7
5 100005 48 1
6 100006 63 2
head(enquete_dta[c("id", "age", "diplome")])# A tibble: 6 × 3
id age diplome
<dbl> <dbl> <dbl+lbl>
1 100001 70 2 [Brevet/CEP]
2 100002 64 2 [Brevet/CEP]
3 100003 30 3 [CAP/BEP]
4 100004 41 7 [Master/doctorat]
5 100005 48 1 [Sans diplôme]
6 100006 63 2 [Brevet/CEP]
Pour une comparaison plus rigoureuse, il peut être nécessaire de retirer temporairement les attributs de labels ou d’harmoniser les classes. Le but ici est de comprendre qu’égalité des valeurs et égalité des métadonnées sont deux questions distinctes.
Un fichier texte peut avoir été enregistré avec différents encodages. Un symptôme classique est l’affichage incorrect des caractères accentués.
Avec readr, on peut préciser une locale :
read_csv(
"fichier.csv",
locale = locale(encoding = "Latin1")
)Il ne faut pas choisir un encodage au hasard jusqu’à ce que le résultat « ait l’air correct ». Idéalement, l’encodage est documenté ou identifié à partir du fichier source.
UTF-8 est aujourd’hui un choix courant et robuste pour les projets reproductibles, mais les fichiers historiques d’enquête peuvent utiliser d’autres encodages.
Un fichier peut contenir :
revenu;score
1 850,50;7,2
2 100,00;8,0
Dans ce cas, read_csv() n’est pas adapté. read_csv2() utilise les conventions ; et , prévues pour ce type de fichier :
read_csv2("fichier.csv")On peut aussi expliciter la locale pour des cas particuliers.
Lorsque l’import semble incohérent, vérifier d’abord quatre choses : séparateur, décimales, encodage, ligne d’en-tête. Ce sont des causes fréquentes avant même de considérer un problème statistique.
Après chaque importation, nous allons adopter la même routine.
dim(enquete)Sont-elles compatibles avec la documentation ?
names(enquete)Les variables attendues sont-elles présentes ? Y a-t-il des colonnes vides ou des noms modifiés ?
dplyr::glimpse(enquete)Les identifiants, dates, variables catégorielles et quantitatives semblent-ils avoir des classes plausibles ?
head(enquete)
tail(enquete)Cette inspection ne remplace pas un diagnostic, mais permet de détecter une ligne d’en-tête répétée, une note importée comme observation ou une ligne vide inhabituelle.
unique(enquete$sexe)
unique(enquete$diplome)Sur un fichier très volumineux, dplyr::count() est souvent plus utile :
dplyr::count(enquete, sexe)Pour readr :
problems(enquete_csv)Cette routine doit devenir automatique. Elle est l’équivalent d’un contrôle de réception avant de commencer le nettoyage.
dataR possède déjà une fonction data(). Écrire :
data <- read_csv("...")fonctionne souvent, mais crée une ambiguïté inutile. Des noms comme enquete_brute, enquete_csv ou donnees_importees sont plus informatifs.
Un script R/01-import.R peut contenir :
library(readr)
library(dplyr)
# Import ----
enquete_brute <- read_csv(
"data/raw/enqueter_brut.csv",
show_col_types = FALSE
)
# Contrôles minimaux ----
stopifnot(nrow(enquete_brute) == 3600)
stopifnot(ncol(enquete_brute) == 36)
stopifnot("id" %in% names(enquete_brute))
glimpse(enquete_brute)stopifnot() interrompt le script si une hypothèse essentielle est fausse. C’est utile lorsque l’on sait précisément ce que le fichier doit contenir.
Si une nouvelle livraison contient 3 599 lignes au lieu de 3 600, le script doit attirer l’attention. Continuer silencieusement peut propager une erreur jusqu’aux résultats finaux.
Associez chaque fichier à une fonction :
enquete.csv séparé par virgules ;enquete_europe.csv séparé par ; avec virgule décimale ;enquete.xlsx feuille repondants ;enquete.dta ;enquete.sav.readr::read_csv("enquete.csv")
readr::read_csv2("enquete_europe.csv")
readxl::read_excel("enquete.xlsx", sheet = "repondants")
haven::read_dta("enquete.dta")
haven::read_sav("enquete.sav")Vous attendiez 40 variables mais dim(enquete) retourne 2500 1. Quelle hypothèse testez-vous en premier ?
Le séparateur est l’une des premières hypothèses. Ouvrir quelques lignes du fichier comme texte permet de voir s’il est séparé par ;, tabulation ou un autre caractère. Ensuite utiliser read_csv2(), read_tsv() ou read_delim() selon le cas.
Importez enqueter_brut.csv. Vérifiez :
age, diplome et revenu_mensuel ;sexe ;Importez le CSV et le fichier Stata. Comparez diplome dans les deux objets. Expliquez pourquoi les deux colonnes peuvent porter les mêmes codes tout en n’étant pas des objets R strictement équivalents.
Rédigez un script d’importation qui :
id, age, poids_final et diplome sont présents ;Expliquez en cinq lignes pourquoi vous séparez l’importation du nettoyage.
library(readr)
library(dplyr)
enquete <- read_csv(
"data/raw/enqueter_brut.csv",
show_col_types = FALSE
)
dim(enquete)
head(names(enquete), 10)
class(enquete$age)
class(enquete$diplome)
class(enquete$revenu_mensuel)
count(enquete, sexe)
problems(enquete)Le fichier attendu comporte 3 600 lignes et 36 colonnes. Le fait que diplome soit numérique dans le CSV ne permet pas de conclure qu’il s’agit d’une variable quantitative.
library(readr)
library(dplyr)
enquete_brute <- read_csv(
"data/raw/enqueter_brut.csv",
show_col_types = FALSE
)
variables_obligatoires <- c("id", "age", "poids_final", "diplome")
stopifnot(all(variables_obligatoires %in% names(enquete_brute)))
stopifnot(nrow(enquete_brute) >= 3000)
glimpse(enquete_brute)La séparation est méthodologique : l’import doit permettre de reproduire fidèlement la lecture du fichier source. Les règles de nettoyage dépendent ensuite du questionnaire, des codes et des incohérences identifiées. Les mélanger rend plus difficile de savoir si une valeur a disparu au moment de la lecture ou lors d’une transformation analytique.
{haven} conserve les labels dans des objets haven_labelled lorsque le format source les contient (Wickham, Miller, et al. 2026).NA dès l’import.La documentation de {readr} décrit les lecteurs de fichiers délimités et le diagnostic des problèmes de parsing (Wickham, Hester, et al. 2026). {readxl} fournit les fonctions d’import Excel (Wickham et Bryan 2026), tandis que {haven} assure l’interface avec SPSS, Stata et SAS (Wickham, Miller, et al. 2026).
Le chapitre suivant porte sur une difficulté spécifique aux données d’enquête : comprendre ce que signifient réellement les colonnes que nous venons d’importer, leurs labels, leurs codes et leur type conceptuel.