Retour à la leçon·Leçon 5 sur 8·Les 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.
Ce que couvre cette leçon
- Cinq verbes et un tube
filter()écarte les valeurs manquantes, en silenceselect()et ses assistantsmutate()et les deux fonctions conditionnellesacross(): une règle, plusieurs colonnesarrange()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.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.filter()écarte les valeurs manquantes, en silence — En Rnrow(muac) #> [1] 4218 nrow(filter(muac, muac_mm < 125)) #> [1] 319 sum(is.na(muac$muac_mm)) #> [1] 72filter()é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 lignesNotes du présentateur
Une comparaison avecNAvautNA, etfilter()ne conserve que les lignesTRUE. 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.select()et ses assistants — En Rmuac |> select(child_id, commune, muac_mm) muac |> select(-oedema) muac |> select(starts_with("muac"), ends_with("_date")) muac |> select(where(is.numeric))select()et ses assistants — En Repi |> 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.mutate()et les deux fonctions conditionnelles — En Rmuac <- muac |> mutate( gam = muac_mm < 125, sam = muac_mm < 115, band = case_when( muac_mm < 115 ~ "severe", muac_mm < 125 ~ "moderate", .default = "normal" ) )mutate()et les deux fonctions conditionnelles — En Rcase_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 :< 115doit donc venir en premier. Son.defaultattrape tout le reste, y compris lesNA, ce qui n'est presque jamais souhaité :mutate()et les deux fonctions conditionnelles — En Rband = 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 :mutate()et les deux fonctions conditionnellesif_else()plutôt queifelse()
Notes du présentateur
Placer la brancheis.na()en premier est l'habitude à prendre. C'est le même piège que ledefaultdenp.selectdans le cours Python, et il vaut d'être reconnu aux deux endroits.mutate()et les deux fonctions conditionnelles — En Rifelse(c(TRUE, FALSE), 1L, "x") #> [1] "1" "x"mutate()et les deux fonctions conditionnelles — En Rif_else(c(TRUE, FALSE), 1L, "x") #> Error: Can't combine `true` <integer> and `false` <character>.Notes du présentateur
Leifelse()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. Leif_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 argumentmissing =, qui permet de dire ce qu'une conditionNAdoit produire plutôt que de la laisser se propager.across(): une règle, plusieurs colonnes — En Rmuac |> summarise(across(c(muac_mm, age_months), ~ mean(.x, na.rm = TRUE))) #> # A tibble: 1 × 2 #> muac_mm age_months #> 1 140. 30.9across(): une règle, plusieurs colonnes — En Rsurvey |> 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.across(): une règle, plusieurs colonnes — En Rsurvey |> 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~ .xabrège une fonction à un argument.\(x) ...est l'écriture plus récente de R de base et fonctionne à l'identique :arrange()— En Rmuac |> arrange(commune, desc(screening_date))Notes du présentateur
NAse 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.distinct()et le comptage — En Rn_distinct(muac$commune) #> [1] 12 muac |> distinct(child_id, .keep_all = TRUE)distinct()et le comptage — En Rmuac |> count(commune, sort = TRUE)Notes du présentateur
distinct()sans.keep_all = TRUEne 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.Les jointures, et le contrôle qui les accompagne — En R
daily <- attendance |> left_join(roster, by = "student_id")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 :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 duvalidate="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.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()etsummarise(), 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.