cassionAnalyse de données

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

Là où naissent la plupart des erreurs d'indicateurs

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

    Ce que couvre cette leçon

    • Un indicateur, ce sont deux nombres
    • n() et les deux façons de compter
    • Le regroupement survit, et c'est l'erreur la plus fréquente
    • Une clé manquante devient son propre groupe
    • Niveaux de facteur inutilisés : count() écarte, table() conserve
    • mutate() groupé : une statistique de groupe sur chaque ligne
    • count() quand c'est tout ce que vous voulez
    • Deux clés, et le tableau qu'attend un rapport
    • Écrire le résultat
    • Les trois renversements, en un seul endroit
    • Ce qui vient ensuite
    Notes du présentateur
    group_by() et summarise() — le regroupement qui survit à l'appel, la clé manquante qui devient son propre groupe, et les trois comportements par défaut exactement inverses de ceux de pandas.
  2. Diapositive 2 / 26

    Un indicateur, ce sont deux nombres — En R

    library(dplyr)
    
    by_commune <- muac |>
      mutate(
        assessed = !is.na(muac_mm) | !is.na(oedema),
        gam = assessed & (muac_mm < 125 | oedema == "true")
      ) |>
      group_by(commune) |>
      summarise(
        screened  = n(),
        assessed  = sum(assessed, na.rm = TRUE),
        gam_cases = sum(gam, na.rm = TRUE),
        .groups = "drop"
      ) |>
      mutate(gam_rate = round(gam_cases / assessed, 3))
    Notes du présentateur
    Presque tout chiffre rapporté dans ce secteur est un numérateur sur un dénominateur, et presque toute dispute sur un chiffre est une dispute sur le dénominateur. La conséquence pratique : calculez les deux dans le même appel. Trois colonnes, et chacune porte quelque chose. screened est le nombre d'enfants venus ; assessed le nombre ayant produit une mesure exploitable, et c'est le dénominateur ; gam_cases est le numérateur. Deux calculs séparés divergent — l'un reçoit un filtre que l'autre n'a pas — et le rapport devient un chiffre que personne ne peut reconstituer.
  3. Diapositive 3 / 26

    n() et les deux façons de compter — En R

    summarise(muac, rows = n(), measured = sum(!is.na(muac_mm)))
  4. Diapositive 4 / 26

    n() et les deux façons de compter — En R

    sum(c(TRUE, NA))
    #> [1] NA
    
    sum(c(TRUE, NA), na.rm = TRUE)
    #> [1] 1
    Notes du présentateur
    n() compte les lignes du groupe, y compris celles à valeur manquante. Sommer un booléen compte les TRUE. Cette distinction fait la différence entre les deux dénominateurs ci-dessus, et mérite d'être énoncée à voix haute chaque fois que vous employez l'un des deux. sum() sur un booléen contenant NA renvoie NA — R, encore une fois, bruyant : Recourez à na.rm = TRUE délibérément, en sachant qu'il retire la ligne du décompte au lieu de la compter comme FALSE. Ce sont deux affirmations différentes.
  5. Diapositive 5 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — En R

    out <- muac |>
      mutate(month = format(screening_date, "%Y-%m")) |>
      group_by(commune, month) |>
      summarise(n = n())
    Notes du présentateur
    C'est le point à intérioriser. summarise() retire la dernière variable de regroupement et laisse les autres :
  6. Diapositive 6 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — Exemple

    `summarise()` has regrouped the output.
    ℹ Summaries were computed grouped by commune and month.
    ℹ Output is grouped by commune.
    ℹ Use `summarise(.groups = "drop_last")` to silence this message.
  7. Diapositive 7 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — En R

    class(out)[1]
    #> [1] "grouped_df"
    
    dplyr::group_vars(out)
    #> [1] "commune"
  8. Diapositive 8 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — En R

    out |> mutate(share = n / sum(n))     # part dans la commune, non dans le district
    out |> arrange(desc(n))               # trie a l'interieur de chaque commune
    out |> slice_max(n, n = 5)            # top 5 par commune, non au total
    Notes du présentateur
    Le résultat est encore groupé par commune. Chaque verbe qui suit opère silencieusement par commune :
  9. Diapositive 9 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente

    • Dites ce que vous voulez, à chaque fois
    Notes du présentateur
    Chacune de ces lignes produit un chiffre plausible qui répond à une autre question que la vôtre. Rien ne lève d'erreur. Dites ce que vous voulez, à chaque fois :
  10. Diapositive 10 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — En R

    summarise(..., .groups = "drop")        # resultat degroupe — la reponse habituelle
    summarise(..., .groups = "drop_last")   # garder tout sauf la derniere (le defaut)
    summarise(..., .groups = "keep")        # tout garder
  11. Diapositive 11 / 26

    Le regroupement survit, et c'est l'erreur la plus fréquente — En R

    muac |>
      summarise(n = n(), .by = c(commune, month))
    Notes du présentateur
    Ou évitez complètement cet état avec le regroupement par opération, apparu dans dplyr 1.1 : .by groupe pour ce seul appel et renvoie un résultat dégroupé. Préférez-le dans du code neuf : sans état résiduel, il n'y a pas d'erreur de cette forme.
  12. Diapositive 12 / 26

    Une clé manquante devient son propre groupe — En R

    d <- tibble::tibble(g = c("a", NA, "a"), v = c(1, 2, 3))
    
    d |> group_by(g) |> summarise(s = sum(v), .groups = "drop")
    Notes du présentateur
    Ici, R et pandas prennent des paris opposés.
  13. Diapositive 13 / 26

    Une clé manquante devient son propre groupe — Exemple

    # A tibble: 2 × 2
      g         s
      <chr> <dbl>
    1 a         4
    2 <NA>      2
  14. Diapositive 14 / 26

    Une clé manquante devient son propre groupe — En R

    d |> filter(!is.na(g)) |> group_by(g) |> summarise(s = sum(v), .groups = "drop")
    Notes du présentateur
    Le groupe NA est conservé. pandas l'écarte par défaut : la même opération dans les deux langages vous donne donc un ensemble de lignes différent et un total différent. Le comportement de R est le plus sûr, et dans ce secteur la clé manquante est généralement la plus intéressante : une formation sanitaire sans district saisi, un ménage sans code de site, un enfant sans commune. Mais cela signifie qu'un tableau peut arriver dans un rapport avec une ligne <NA> que personne n'avait prévu de publier.
  15. Diapositive 15 / 26

    Une clé manquante devient son propre groupe — En R

    stopifnot(!any(is.na(muac$commune)))
    Notes du présentateur
    L'exclure est légitime. L'exclure sans le remarquer, c'est ainsi que les parties cessent de faire le tout. Préférez une assertion là où la clé devrait être complète :
  16. Diapositive 16 / 26

    Niveaux de facteur inutilisés : count() écarte, table() conserve — En R

    d <- tibble::tibble(k = factor(c("a", "b", "a"), levels = c("a", "b", "c")))
    
    nrow(count(d, k))                #> 2   — le niveau vide a disparu
    nrow(count(d, k, .drop = FALSE)) #> 3   — conserve
    length(table(d$k))               #> 3   — conserve
    Notes du présentateur
    Le second renversement, et R n'est pas cohérent avec lui-même ici — raison pour laquelle la leçon sur les facteurs l'aborde et celle-ci le répète.
  17. Diapositive 17 / 26

    Niveaux de facteur inutilisés : count() écarte, table() conserve

    • Passez .drop explicitement — dans tout ce qui produit un tableau rapporté
    Notes du présentateur
    Les deux sont justes pour des questions différentes, et c'est la question même que le cours Python soulève à propos d'observed=. Rapporter sur les formations ayant transmis des données veut le niveau vide en moins ; rapporter la couverture face à une liste de formations qui auraient dû transmettre le veut présent, car une formation à zéro ligne est justement le constat. Passez .drop explicitement dans tout ce qui produit un tableau rapporté.
  18. Diapositive 18 / 26

    mutate() groupé : une statistique de groupe sur chaque ligne — En R

    muac |>
      group_by(commune) |>
      mutate(
        commune_mean = mean(muac_mm, na.rm = TRUE),
        vs_commune   = muac_mm - commune_mean
      ) |>
      ungroup()
    Notes du présentateur
    summarise() réduit ; mutate() sur un tableau groupé renvoie une valeur par ligne d'origine. C'est ainsi qu'on compare une ligne à son propre groupe sans jointure :
  19. Diapositive 19 / 26

    mutate() groupé : une statistique de groupe sur chaque ligne

    • Noter le ungroup() — Un tableau groupé qui s'échappe dans la suite d'un script est la même erreur que ci-dessus,…
    Notes du présentateur
    C'est le contrôle qu'emploie le projet Analyse d'enquête SMART pour repérer une équipe mesurant un demi-kilo trop léger. Noter le ungroup(). Un tableau groupé qui s'échappe dans la suite d'un script est la même erreur que ci-dessus, arrivant par une autre porte.
  20. Diapositive 20 / 26

    count() quand c'est tout ce que vous voulez — En R

    muac |> count(commune, outcome, sort = TRUE)
    muac |> count(commune, wt = muac_mm)          # sommer une colonne plutot que compter
    Notes du présentateur
    count() groupe, compte et dégroupe en un appel : il ne peut donc pas laisser d'état derrière lui. Là où un décompte suffit, c'est le meilleur verbe.
  21. Diapositive 21 / 26

    Deux clés, et le tableau qu'attend un rapport — En R

    by_commune_month <- muac |>
      mutate(month = format(screening_date, "%Y-%m")) |>
      summarise(cases = sum(gam, na.rm = TRUE), .by = c(commune, month))
    
    wide <- by_commune_month |>
      tidyr::pivot_wider(names_from = month, values_from = cases, values_fill = 0)
    Notes du présentateur
    values_fill = 0 est sans risque ici, car une commune-mois sans cas en avait réellement zéro. Ce serait faux pour un taux, où l'absence signifie « non calculé » et non « zéro pour cent » — distinction à vérifier chaque fois que vous y recourez.
  22. Diapositive 22 / 26

    Écrire le résultat — En R

    readr::write_csv(by_commune, here::here("outputs", "tables", "gam_by_commune.csv"))
  23. Diapositive 23 / 26

    Écrire le résultat — En R

    definitions <- tibble::tibble(
      indicateur   = "MAG",
      numerateur   = "PB < 125 mm ou oedemes bilateraux prenant le godet",
      denominateur = "enfants avec une mesure de PB ou une evaluation d'oedemes saisie"
    )
    
    readr::write_csv(definitions, here::here("outputs", "tables", "definitions.csv"))
    Notes du présentateur
    Pour un tableau destiné à être lu plutôt que calculé, écrivez la définition avec lui : Un nombre et sa définition voyagent ensemble, sinon la dispute mensuelle sur le dénominateur recommence.
  24. Diapositive 24 / 26

    Les trois renversements, en un seul endroit

    pandasR / dplyr
    Valeur manquante dans mean()ignorée en silencerenvoie NA jusqu'à na.rm
    Clé de groupe manquanteécartéeconservée comme groupe à part
    Catégorie inutiliséeécartée par observed=Trueconservée par table(), écartée par count()
    Notes du présentateur
    Aucun n'est meilleur. Chacun est un pari différent sur ce que le silence doit signifier, et savoir dans quel pari on se trouve constitue l'essentiel du métier.
  25. Diapositive 25 / 26

    Ce qui vient ensuite

    • Le tableau agrège correctement et les deux moitiés de chaque taux proviennent d'un seul appel.
    Notes du présentateur
    Le tableau agrège correctement et les deux moitiés de chaque taux proviennent d'un seul appel. L'unité suivante restructure : pivot_longer() sur le groupe répété aplati sous lequel arrive un export CommCare ou Kobo, puis la fonction qui fait tourner tout le nettoyage au trimestre suivant.
  26. Diapositive 26 / 26

    La suite

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