Retour à la leçon·Leçon 2 sur 8·Un projet qui se rouvre
Lire un export avec readr et readxl
Le même diaporama que les téléchargements, rendu sous forme de page. Lancez le diaporama pour le présenter en plein écran — les flèches ou un clic avancent d'une diapositive, Échap quitte.
Ce que couvre cette leçon
- readr devine, et vous dit ce qu'il a deviné
- La devinette porte sur les 1 000 premières lignes
- Déclarez les colonnes
mean()renvoie NA, et c'est une qualiténas'applique au fichier, non à une colonneproblems()est une habitude, non un sauvetage- Vérifier la lecture avant d'en faire quoi que ce soit
- Séparateurs, encodages et milliers
- Excel
- La fonction de lecture, assemblée
- Ce qui vient ensuite
Notes du présentateur
Des spécifications de colonnes plutôt que des devinettes, problems() comme habitude et non comme sauvetage, et les pièges d'Excel — feuilles multiples, en-tête qui n'est pas la ligne un, cellules fusionnées et dates en numéro de série.readr devine, et vous dit ce qu'il a deviné — En R
library(readr) library(here) PATH <- here("data", "raw", "muac-screening-artibonite-2024.v1.csv") muac <- read_csv(PATH)readr devine, et vous dit ce qu'il a deviné — Exemple
child_id character commune character screening_date date age_months numeric sex character muac_mm numeric oedema character outcome characterNotes du présentateur
read_csv()affiche la spécification qu'il a inférée. Ce message n'est pas un bruit à masquer — c'est le seul moment où le lecteur vous dit ce qu'il a décidé, et sur ce registre il décide huit choses : L'essentiel est juste. Deux points méritent que vous en repreniez le contrôle.La devinette porte sur les 1 000 premières lignes — En R
# Deliberement : tout lire en texte, regarder, puis convertir. peek <- read_csv(PATH, col_types = cols(.default = col_character()), n_max = 200) print(peek)Notes du présentateur
guess_maxvaut 1 000 par défaut. Une colonne vide sur les mille premières lignes puis numérique ensuite est devinée comme logique — carNAest logique — et chaque valeur ultérieure devientNA. C'est l'équivalent readr de l'avertissement de types mixtes de pandas, et il échoue plus discrètement : vous obtenez une colonne pleine de valeurs manquantes et un tableauproblems()que vous n'avez pas regardé. Deux cents lignes suffisent à voir la forme et coûtent peu sur un gros fichier.Déclarez les colonnes — En R
muac <- read_csv( PATH, col_types = cols( child_id = col_character(), commune = col_character(), screening_date = col_date(format = "%Y-%m-%d"), age_months = col_integer(), sex = col_character(), muac_mm = col_integer(), oedema = col_character(), outcome = col_character() ), na = c("", "NA", "-99") )Déclarez les colonnes
- Les zéros initiaux survivent —
col_character()sur un identifiant est toute la leçon : un code de formation sanitaire… - Le format de date est énoncé — Un format inféré peut changer d'un fichier à l'autre selon la composition des valeurs
- La sentinelle est déclarée — Ce registre code un PB non mesuré par
-99
Notes du présentateur
Trois avantages. Les zéros initiaux survivent.col_character()sur un identifiant est toute la leçon : un code de formation sanitaire007lu comme un nombre devient7, la jointure à la liste des formations échoue exactement pour les codes qui portaient un zéro initial, et la défaillance ressemble à des formations manquantes plutôt qu'à une erreur de type. La règle se généralise : si vous n'allez jamais faire d'arithmétique dessus, ce n'est pas un nombre. Codes de formations, numéros de téléphone, identifiants de grappes, numéros de ménages et codes administratifs sont du texte qui s'écrit avec des chiffres. Le format de date est énoncé. Un format inféré peut changer d'un fichier à l'autre selon la composition des valeurs.01/02/2024est soit le 1er février, soit le 2 janvier, et le fichier ne vous dira pas lequel. La sentinelle est déclarée. Ce registre code un PB non mesuré par-99. Lu sansna, c'est un nombre :- Les zéros initiaux survivent —
Déclarez les colonnes — En R
mean(muac$muac_mm) #> [1] NANotes du présentateur
Ce qui nous amène à la différence la plus importante avec pandas.mean()renvoie NA, et c'est une qualité — En Rmean(muac$muac_mm) #> [1] NA mean(muac$muac_mm, na.rm = TRUE) #> [1] 139.92Notes du présentateur
pandas ignore les valeurs manquantes par défaut ; R non.mean()renvoie NA, et c'est une qualité- R est bruyant là où pandas est silencieux — Le
NAest une question : *savez-vous qu'il y a ici des valeurs…
Notes du présentateur
R est bruyant là où pandas est silencieux. LeNAest une question : savez-vous qu'il y a ici des valeurs manquantes, et avez-vous décidé de ce qu'elles signifient ? Y répondre parna.rm = TRUEest parfaitement légitime — y répondre sans avoir remarqué qu'on vous interrogeait, c'est ainsi qu'un dénominateur change en silence.na.rm = TRUEretire les manquants du numérateur et du dénominateur. C'est généralement juste pour une moyenne et généralement faux pour un taux de couverture, où l'enfant non mesuré reste un enfant dépisté. Comptez-les :- R est bruyant là où pandas est silencieux — Le
nas'applique au fichier, non à une colonne — En Rmuac <- read_csv(PATH, na = c("", "NA", "-99"))nas'applique au fichier, non à une colonne — En Rmuac <- read_csv(PATH, na = c("", "NA")) |> dplyr::mutate(muac_mm = dplyr::na_if(muac_mm, -99L))Notes du présentateur
readr appliquenaà toutes les colonnes. pandas sait restreindre une sentinelle à une colonne avecna_values = {"muac_mm": ["-99"]}; readr non. Si une autre colonne porte légitimement-99, traitez-la ensuite :na_if()est la forme restreinte, à préférer dès que plus d'une colonne est numérique.problems()est une habitude, non un sauvetage — En Rissues <- problems(muac) nrow(issues) #> [1] 0Notes du présentateur
Quand une valeur ne correspond pas à son type déclaré, readr ne s'arrête pas. Il consigne la ligne, la colonne, ce qu'il attendait et ce qu'il a trouvé.problems()est une habitude, non un sauvetage — En Rif (nrow(problems(muac)) > 0) { print(problems(muac)) stop("L'entree ne correspond pas a sa specification de colonnes.") }Notes du présentateur
Zéro sur ce fichier, et c'est justement l'intérêt de la vérification : le chiffre n'a de sens que si vous le regardez à chaque fois. Une exécution qui a discrètement transformé 200 valeurs enNAressemble en tout point à une exécution propre tant que vous ne demandez pas. Dans un script produisant un chiffre rapporté, cestop()est correct. Refuser de s'exécuter vaut mieux que produire un tableau à colonne silencieusement vide.Vérifier la lecture avant d'en faire quoi que ce soit — En R
library(dplyr) check <- function(df, expected_rows = NULL) { cat("lignes/colonnes ", nrow(df), ncol(df), "\n") print(dplyr::glimpse(df)) print(colSums(is.na(df))) if (!is.null(expected_rows)) { stopifnot(nrow(df) == expected_rows) } } check(muac, expected_rows = 4218)Notes du présentateur
Quatre contrôles, une minute, et ils attrapent presque tout : L'assertion sur le nombre de lignes est celle qu'on saute. Un fichier arrivant avec 4 190 lignes au lieu de 4 218 a perdu quelque chose entre le serveur et vous, et le script doit le dire plutôt que de rapporter discrètement une charge de cas plus faible.Séparateurs, encodages et milliers — En R
epi <- read_csv2(PATH) # separateur ; decimale , epi <- read_delim(PATH, delim = "\t") # tabulationsNotes du présentateur
Un fichier produit sur une machine Windows française ou espagnole est très souvent séparé par des points-virgules, car la virgule y est le séparateur décimal.Séparateurs, encodages et milliers — En R
muac <- read_csv(PATH, locale = locale(encoding = "UTF-8")) muac <- read_csv(PATH, locale = locale(encoding = "latin1")) # ce qu'Excel ecrit souventNotes du présentateur
read_csv2()est la variante européenne — point-virgule et virgule — et elle existe précisément parce que le cas est assez courant pour mériter sa propre fonction. L'encodage se manifeste dans les noms de communes : Gonaïves, L'Estère, Anse-Rouge.Séparateurs, encodages et milliers — En R
epi <- read_csv(PATH, locale = locale(grouping_mark = ","))Notes du présentateur
latin1ne lève jamais d'erreur, car tout octet est un caractère latin1 valide. Cela en fait un mauvais diagnostic et un repli correct : si un fichier lu enlatin1afficheGonaïves, il était en UTF-8 depuis le début et vous avez dit le contraire au lecteur. Les séparateurs de milliers transforment un nombre en texte : Sans cela,target_populationarrive en"1,480"— une colonne de caractères sur laquelle toute opération arithmétique échoue ou concatène.Excel — En R
library(readxl) excel_sheets(path) #> [1] "Cover" "Data" "Codebook" "Sheet3" data <- read_excel(path, sheet = "Data")Excel
- L'en-tête n'est pas la ligne un
Notes du présentateur
read_excel()sanssheetrenvoie la première feuille, qui dans un classeur de programme est le plus souvent une page de garde. Listez toujours les feuilles d'abord.Excel
- Les cellules fusionnées
Notes du présentateur
Les classeurs de programme portent couramment un titre, une ligne de logo et une ligne vide au-dessus du véritable en-tête. Le symptôme, ce sont des noms de colonnes comme...1,...2. Une cellule fusionnée porte sa valeur en haut à gauche et rien dans le reste. Quand un nom de district est fusionné sur ses formations, seule la première ligne de chaque district en porte un :Excel
- Les dates en numéro de série
Notes du présentateur
fill()est correct ici et dangereux en général : il n'est juste que parce que les blancs viennent d'une fusion, non d'une non-réponse. Ne l'appliquez jamais à une colonne où un blanc pourrait signifier « non renseigné ». Excel stocke les dates comme un nombre de jours depuis le 30/12/1899.read_excel()les convertit généralement, mais une colonne typée en texte dans le classeur arrive sous forme de numéro de série brut.Excel — En R
data$screening_date <- as.Date(as.numeric(data$screening_date), origin = "1899-12-30")Excel
- Les types sous Excel
Notes du présentateur
Si une colonne de dates arrive en nombres à cinq chiffres, voilà pourquoi.Excel — En R
data <- read_excel( path, sheet = "Data", col_types = c("text", "text", "date", "numeric", "text", "numeric", "text", "text") )Notes du présentateur
readxlattend un vecteur positionnel plutôt qu'une spécification nommée : une colonne ajoutée à l'export décale donc tout ce qui suit. Vérifiezncol()face à la longueur de votre vecteur, ou lisez en texte et convertissez avec dplyr.La fonction de lecture, assemblée — En R (suite)
# R/read_register.R read_register <- function(path) { muac <- readr::read_csv( path, col_types = readr::cols( child_id = readr::col_character(), commune = readr::col_character(), screening_date = readr::col_date(format = "%Y-%m-%d"), age_months = readr::col_integer(), sex = readr::col_character(), muac_mm = readr::col_integer(), oedema = readr::col_character(), outcome = readr::col_character() ), na = c("", "NA", "-99") )Notes du présentateur
Tout ce qui précède appartient à une fonction unique que les scripts partagent :La fonction de lecture, assemblée — En R (suite)
if (nrow(readr::problems(muac)) > 0) { print(readr::problems(muac)) stop("L'entree ne correspond pas a sa specification de colonnes.") } stopifnot(!any(is.na(muac$child_id))) muac }Notes du présentateur
Une fonction, un seul endroit à corriger quand le prochain export changera.Ce qui vient ensuite
- Le CSV est entré et ses types sont justes.
Notes du présentateur
Le CSV est entré et ses types sont justes. La leçon suivante traite les fichiers qui transportent plus que des valeurs — les exports Stata et SPSS, où1 = Ouivoyage avec les données et oùhavenest la raison de les lire sous R même si vous les analyserez ailleurs.