cassionAnalyse de données

Retour à la leçonLeçon 5 sur 8Les verbes

Les verbes dplyr, employés délibérément

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 / 23

    Ce que couvre cette leçon

    • Cinq verbes et un tube
    • filter() écarte les valeurs manquantes, en silence
    • select() et ses assistants
    • mutate() et les deux fonctions conditionnelles
    • across() : une règle, plusieurs colonnes
    • arrange()
    • distinct() et le comptage
    • Les jointures, et le contrôle qui les accompagne
    • Ce qui vient ensuite
    Notes du présentateur
    filter, select, mutate et arrange — ce que chacun fait d'une valeur manquante, pourquoi if_else() refuse ce que ifelse() accepte en silence, et le across() qui applique une règle à plusieurs colonnes.
  2. Diapositive 2 / 23

    Cinq verbes et un tube — En R

    library(dplyr)
    
    muac |>
      filter(!is.na(muac_mm)) |>
      mutate(gam = muac_mm < 125) |>
      group_by(commune) |>
      summarise(assessed = n(), cases = sum(gam)) |>
      arrange(desc(cases / assessed))
    Notes du présentateur
    Chaque verbe prend un tableau de données et en renvoie un. Le tube passe le résultat au suivant. Voilà toute la grammaire ; le reste de cette leçon porte sur ce que fait chaque verbe quand les données ne sont pas propres — c'est-à-dire toujours. |> est le tube natif de R, disponible depuis la 4.1. %>% de magrittr le précède et se comporte de façon quasi identique pour ce type de code ; si les scripts de votre équipe l'emploient, tout ce qui suit fonctionne tel quel.
  3. Diapositive 3 / 23

    filter() écarte les valeurs manquantes, en silence — En R

    nrow(muac)
    #> [1] 4218
    
    nrow(filter(muac, muac_mm < 125))
    #> [1] 319
    
    sum(is.na(muac$muac_mm))
    #> [1] 72
  4. Diapositive 4 / 23

    filter() écarte les valeurs manquantes, en silence — En R

    # Enfants mesures en dessous de 125 mm.
    filter(muac, muac_mm < 125)
    
    # Enfants dont on ne sait pas qu'ils sont a 125 mm ou plus — non mesures compris.
    filter(muac, is.na(muac_mm) | muac_mm < 125)
    #> 391 lignes
    Notes du présentateur
    Une comparaison avec NA vaut NA, et filter() ne conserve que les lignes TRUE. Ces 72 enfants non mesurés ont donc disparu, sans que rien ne le dise. C'est le même comportement que pandas, et cela mérite d'être énoncé dans les deux langages parce que c'est la façon la plus rapide de changer un dénominateur par accident. Soyez explicite sur ce que vous vouliez dire : Soixante-douze lignes séparent ces deux questions. Laquelle vous voulez dépend de savoir si un enfant non mesuré est un non-cas ou un inconnu, et c'est une décision qui relève d'un journal de nettoyage, non d'un comportement par défaut.
  5. Diapositive 5 / 23

    select() et ses assistants — En R

    muac |> select(child_id, commune, muac_mm)
    muac |> select(-oedema)
    muac |> select(starts_with("muac"), ends_with("_date"))
    muac |> select(where(is.numeric))
  6. Diapositive 6 / 23

    select() et ses assistants — En R

    epi |> select(facility = facility_id, month = period, doses = doses_administered)
    Notes du présentateur
    select() renomme en sélectionnant, ce qui est la manière tidy de corriger une fois pour toutes les noms de colonnes d'un export : Deux habitudes à prendre. Sélectionnez avant de joindre, pour que le résultat ne traîne pas quarante colonnes dont vous ne vouliez pas. Et ne sélectionnez jamais par position — select(3:5) casse le jour où l'export gagne une colonne, et il casse en silence puisque le code s'exécute toujours.
  7. Diapositive 7 / 23

    mutate() et les deux fonctions conditionnelles — En R

    muac <- muac |>
      mutate(
        gam = muac_mm < 125,
        sam = muac_mm < 115,
        band = case_when(
          muac_mm < 115 ~ "severe",
          muac_mm < 125 ~ "moderate",
          .default = "normal"
        )
      )
  8. Diapositive 8 / 23

    mutate() et les deux fonctions conditionnelles — En R

    case_when(c(110, 130, NA) < 125 ~ "case", .default = "not")
    #> [1] "case" "not"  "not"
    Notes du présentateur
    case_when() évalue dans l'ordre et la première correspondance l'emporte : < 115 doit donc venir en premier. Son .default attrape tout le reste, y compris les NA, ce qui n'est presque jamais souhaité :
  9. Diapositive 9 / 23

    mutate() et les deux fonctions conditionnelles — En R

    band = case_when(
      is.na(muac_mm) ~ NA_character_,
      muac_mm < 115  ~ "severe",
      muac_mm < 125  ~ "moderate",
      .default = "normal"
    )
    Notes du présentateur
    La mesure manquante est devenue "not". Traitez-la explicitement :
  10. Diapositive 10 / 23

    mutate() et les deux fonctions conditionnelles

    • if_else() plutôt que ifelse()
    Notes du présentateur
    Placer la branche is.na() en premier est l'habitude à prendre. C'est le même piège que le default de np.select dans le cours Python, et il vaut d'être reconnu aux deux endroits.
  11. Diapositive 11 / 23

    mutate() et les deux fonctions conditionnelles — En R

    ifelse(c(TRUE, FALSE), 1L, "x")
    #> [1] "1" "x"
  12. Diapositive 12 / 23

    mutate() et les deux fonctions conditionnelles — En R

    if_else(c(TRUE, FALSE), 1L, "x")
    #> Error: Can't combine `true` <integer> and `false` <character>.
    Notes du présentateur
    Le ifelse() de R de base a silencieusement fondu un entier et une chaîne en un vecteur de caractères. Une colonne que vous croyiez numérique est désormais du texte, et vous l'apprendrez trois étapes plus loin quand une somme échouera. Le if_else() de dplyr refuse. Préférez-le partout, pour la raison sur laquelle ce cours revient sans cesse : une erreur là où la faute est commise vaut mieux qu'une mauvaise réponse trois étapes plus loin. if_else() accepte aussi un argument missing =, qui permet de dire ce qu'une condition NA doit produire plutôt que de la laisser se propager.
  13. Diapositive 13 / 23

    across() : une règle, plusieurs colonnes — En R

    muac |>
      summarise(across(c(muac_mm, age_months), ~ mean(.x, na.rm = TRUE)))
    #> # A tibble: 1 × 2
    #>   muac_mm age_months
    #> 1    140.       30.9
  14. Diapositive 14 / 23

    across() : une règle, plusieurs colonnes — En R

    survey |> mutate(across(where(is.character), stringr::str_squish))
    
    survey |> mutate(across(starts_with("fcs_"), ~ if_else(.x > 7, NA_real_, .x)))
    Notes du présentateur
    across() est ce qui évite qu'un script de nettoyage soit quinze lignes presque identiques.
  15. Diapositive 15 / 23

    across() : une règle, plusieurs colonnes — En R

    survey |> mutate(across(starts_with("fcs_"), \(x) if_else(x > 7, NA_real_, x)))
    Notes du présentateur
    Cette seconde ligne est la règle sécurité alimentaire du cours Python pour les données de programme, en une instruction : une valeur de consommation supérieure à sept jours est impossible et doit devenir manquante plutôt qu'être plafonnée, sur les huit groupes alimentaires à la fois. La forme ~ .x abrège une fonction à un argument. \(x) ... est l'écriture plus récente de R de base et fonctionne à l'identique :
  16. Diapositive 16 / 23

    arrange() — En R

    muac |> arrange(commune, desc(screening_date))
    Notes du présentateur
    NA se classe en dernier quel que soit le sens, ce qui est généralement souhaitable et masque parfois un problème — une colonne entièrement manquante paraît triée. Trier range des lignes. Cela ne dit rien de la réalité des écarts entre lignes voisines, point que développe le projet Tableau de bord de programme nutritionnel, où neuf communes sur douze ont des intervalles de confiance qui se recouvrent.
  17. Diapositive 17 / 23

    distinct() et le comptage — En R

    n_distinct(muac$commune)
    #> [1] 12
    
    muac |> distinct(child_id, .keep_all = TRUE)
  18. Diapositive 18 / 23

    distinct() et le comptage — En R

    muac |> count(commune, sort = TRUE)
    Notes du présentateur
    distinct() sans .keep_all = TRUE ne renvoie que les colonnes nommées, ce qui surprend qui attendait des lignes dédupliquées. Avec, c'est la première ligne de chaque groupe qui est conservée — triez donc d'abord si le choix de la survivante compte. count() équivaut à group_by() |> summarise(n = n()) |> ungroup(), et c'est le bon outil chaque fois que c'est tout ce que vous voulez.
  19. Diapositive 19 / 23

    Les jointures, et le contrôle qui les accompagne — En R

    daily <- attendance |>
      left_join(roster, by = "student_id")
  20. Diapositive 20 / 23

    Les jointures, et le contrôle qui les accompagne — En R

    before <- nrow(attendance)
    daily <- attendance |> left_join(roster, by = "student_id")
    stopifnot(nrow(daily) == before)
    Notes du présentateur
    La défaillance à prévenir est une jointure qui multiplie les lignes parce que le côté droit a des clés dupliquées. dplyr avertit, mais un avertissement dans un long script défile :
  21. Diapositive 21 / 23

    Les jointures, et le contrôle qui les accompagne — En R

    attendance |> anti_join(roster, by = "student_id") |> distinct(student_id)
    Notes du présentateur
    Cette assertion est l'équivalent R du validate="many_to_one" de pandas, et elle a attrapé deux élèves inscrits deux fois dans le projet Assiduité scolaire — un transfert non enregistré ayant laissé les deux inscriptions actives. anti_join() est la façon la plus rapide de voir ce qui n'a pas correspondu : Un résultat vide signifie que chaque ligne de présence a trouvé son élève. Un résultat non vide est une liste à rapporter à qui tient la liste des élèves.
  22. Diapositive 22 / 23

    Ce qui vient ensuite

    • Les verbes sont acquis.
    Notes du présentateur
    Les verbes sont acquis. La leçon suivante est celle vers laquelle ils mènent et celle où naissent la plupart des erreurs d'indicateurs : group_by() et summarise(), et les trois choses que R fait des clés manquantes, des niveaux inutilisés et du regroupement résiduel — à chaque fois à l'inverse de pandas.
  23. Diapositive 23 / 23

    La suite

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