cassionAnalyse de données

Retour à la leçonLeçon 2 sur 8Un 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.

Diapositives · PDFDiapositives · PowerPoint

  1. Diapositive 1 / 31

    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é
    • na s'applique au fichier, non à une colonne
    • problems() 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.
  2. Diapositive 2 / 31

    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)
  3. Diapositive 3 / 31

    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        character
    Notes 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.
  4. Diapositive 4 / 31

    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_max vaut 1 000 par défaut. Une colonne vide sur les mille premières lignes puis numérique ensuite est devinée comme logique — car NA est logique — et chaque valeur ultérieure devient NA. 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 tableau problems() que vous n'avez pas regardé. Deux cents lignes suffisent à voir la forme et coûtent peu sur un gros fichier.
  5. Diapositive 5 / 31

    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")
    )
  6. Diapositive 6 / 31

    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 sanitaire 007 lu comme un nombre devient 7, 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/2024 est 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 sans na, c'est un nombre :
  7. Diapositive 7 / 31

    Déclarez les colonnes — En R

    mean(muac$muac_mm)
    #> [1] NA
    Notes du présentateur
    Ce qui nous amène à la différence la plus importante avec pandas.
  8. Diapositive 8 / 31

    mean() renvoie NA, et c'est une qualité — En R

    mean(muac$muac_mm)
    #> [1] NA
    
    mean(muac$muac_mm, na.rm = TRUE)
    #> [1] 139.92
    Notes du présentateur
    pandas ignore les valeurs manquantes par défaut ; R non.
  9. Diapositive 9 / 31

    mean() renvoie NA, et c'est une qualité

    • R est bruyant là où pandas est silencieux — Le NA est une question : *savez-vous qu'il y a ici des valeurs…
    Notes du présentateur
    R est bruyant là où pandas est silencieux. Le NA est 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 par na.rm = TRUE est 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 = TRUE retire 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 :
  10. Diapositive 10 / 31

    mean() renvoie NA, et c'est une qualité — En R

    sum(is.na(muac$muac_mm))
    #> [1] 72
  11. Diapositive 11 / 31

    na s'applique au fichier, non à une colonne — En R

    muac <- read_csv(PATH, na = c("", "NA", "-99"))
  12. Diapositive 12 / 31

    na s'applique au fichier, non à une colonne — En R

    muac <- read_csv(PATH, na = c("", "NA")) |>
      dplyr::mutate(muac_mm = dplyr::na_if(muac_mm, -99L))
    Notes du présentateur
    readr applique na à toutes les colonnes. pandas sait restreindre une sentinelle à une colonne avec na_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.
  13. Diapositive 13 / 31

    problems() est une habitude, non un sauvetage — En R

    issues <- problems(muac)
    nrow(issues)
    #> [1] 0
    Notes 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é.
  14. Diapositive 14 / 31

    problems() est une habitude, non un sauvetage — En R

    if (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 en NA ressemble en tout point à une exécution propre tant que vous ne demandez pas. Dans un script produisant un chiffre rapporté, ce stop() est correct. Refuser de s'exécuter vaut mieux que produire un tableau à colonne silencieusement vide.
  15. Diapositive 15 / 31

    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.
  16. Diapositive 16 / 31

    Séparateurs, encodages et milliers — En R

    epi <- read_csv2(PATH)                       # separateur ; decimale ,
    epi <- read_delim(PATH, delim = "\t")        # tabulations
    Notes 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.
  17. Diapositive 17 / 31

    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 souvent
    Notes 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.
  18. Diapositive 18 / 31

    Séparateurs, encodages et milliers — En R

    epi <- read_csv(PATH, locale = locale(grouping_mark = ","))
    Notes du présentateur
    latin1 ne 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 en latin1 affiche Gonaï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_population arrive en "1,480" — une colonne de caractères sur laquelle toute opération arithmétique échoue ou concatène.
  19. Diapositive 19 / 31

    Excel — En R

    library(readxl)
    
    excel_sheets(path)
    #> [1] "Cover" "Data" "Codebook" "Sheet3"
    
    data <- read_excel(path, sheet = "Data")
  20. Diapositive 20 / 31

    Excel

    • L'en-tête n'est pas la ligne un
    Notes du présentateur
    read_excel() sans sheet renvoie la première feuille, qui dans un classeur de programme est le plus souvent une page de garde. Listez toujours les feuilles d'abord.
  21. Diapositive 21 / 31

    Excel — En R

    data <- read_excel(path, sheet = "Data", skip = 3)
  22. Diapositive 22 / 31

    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 :
  23. Diapositive 23 / 31

    Excel — En R

    data <- data |> tidyr::fill(district, .direction = "down")
  24. Diapositive 24 / 31

    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.
  25. Diapositive 25 / 31

    Excel — En R

    data$screening_date <- as.Date(as.numeric(data$screening_date), origin = "1899-12-30")
  26. Diapositive 26 / 31

    Excel

    • Les types sous Excel
    Notes du présentateur
    Si une colonne de dates arrive en nombres à cinq chiffres, voilà pourquoi.
  27. Diapositive 27 / 31

    Excel — En R

    data <- read_excel(
      path,
      sheet = "Data",
      col_types = c("text", "text", "date", "numeric", "text", "numeric", "text", "text")
    )
    Notes du présentateur
    readxl attend 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érifiez ncol() face à la longueur de votre vecteur, ou lisez en texte et convertissez avec dplyr.
  28. Diapositive 28 / 31

    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 :
  29. Diapositive 29 / 31

    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.
  30. Diapositive 30 / 31

    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 = Oui voyage avec les données et où haven est la raison de les lire sous R même si vous les analyserez ailleurs.
  31. Diapositive 31 / 31

    La suite

    Lire la leçon complète, avec le code exécutable Retour à la leçon