cassionAnalyse de données

Retour à la leçonLeçon 3 sur 8Une épidémie, un cas à la fois

La courbe n'est pas l'épidémie

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

    Ce que couvre cette leçon

    • À quoi sert la courbe
    • Tracez-la
    • Ce que font les dates manquantes
    • Le district dont la courbe n'est pas une courbe
    • Lire la forme
    • Tracez des cas, non des taux
    • La suite
    Notes du présentateur
    975 cas, 47 sans date de début, et un district dont le registre est rempli après coup depuis le cahier d'admission — si bien que son délai médian d'admission affiche zéro alors que sa létalité est la plus élevée des trois.
  2. Diapositive 2 / 21

    À quoi sert la courbe

    • Une courbe épidémique, ce sont les cas par date de début des symptômes.
    Notes du présentateur
    Une courbe épidémique, ce sont les cas par date de début des symptômes. C'est la première chose que l'on trace dans toute épidémie et elle répond à trois questions qu'aucun tableau ne règle aussi vite — cela croît-il, où est le pic, et la forme est-elle compatible avec une source ponctuelle ou avec une transmission interhumaine ? C'est aussi l'analyse la plus facilement cassée par un champ manquant, ce qui est le véritable sujet de cette leçon.
  3. Diapositive 3 / 21

    Tracez-la — En Python

    import pandas as pd
    
    cases = pd.read_csv("cholera-line-list-2024.v1.csv", parse_dates=["onset_date"])
    
    with_onset = cases[cases["onset_date"].notna()]
    print(f"{len(with_onset)} of {len(cases)} cases have an onset date")
    
    curve = (
        with_onset.assign(week=with_onset["onset_date"].dt.to_period("W").dt.start_time)
        .groupby("week").size()
    )
    print(curve)
    print(f"peak week {curve.idxmax().date()} with {curve.max()} cases")
  4. Diapositive 4 / 21

    Tracez-la — En R

    library(dplyr)
    
    cases <- readr::read_csv("cholera-line-list-2024.v1.csv")
    
    curve <- cases |>
      filter(!is.na(onset_date)) |>
      mutate(week = lubridate::floor_date(onset_date, "week")) |>
      count(week)
    Notes du présentateur
    928 des 975 cas portent une date de début, et la courbe culmine à la septième semaine — celle qui s'achève le 23 juin — avec 111 cas. Par date de début, non d'admission, non de saisie — tout l'objet de la courbe est le moment où les gens sont tombés malades, et chacune des deux autres dates est décalée d'un montant différent et inconnu. Employez des pas hebdomadaires pour une épidémie de cette longueur. Le journalier est trop bruité pour être lu ; le mensuel masque entièrement le pic.
  5. Diapositive 5 / 21

    Ce que font les dates manquantes — En Python

    missing = cases[cases["onset_date"].isna()]
    print(missing.groupby("district").size())
    print(f"{len(missing) / len(cases):.1%} of all cases")
    Notes du présentateur
    Quarante-sept cas n'ont pas de date de début, et ils ne sont pas dispersés au hasard.
  6. Diapositive 6 / 21

    Ce que font les dates manquantes — En R

    cases |> filter(is.na(onset_date)) |> count(district)
  7. Diapositive 7 / 21

    Ce que font les dates manquantes

    • Les exclure — Le défaut, et généralement le bon
    • Les imputer d'après la date d'admission — Tentant, et cela décale toute la courbe vers la droite du délai médian, ce…
    • Les tracer en bande séparée — Honnête et rarement fait — une barre empilée avec « début inconnu » dans une teinte…
    Notes du présentateur
    Trois traitements possibles, et ils donnent trois courbes différentes. Les exclure. Le défaut, et généralement le bon. La courbe porte alors sur 928 cas et le libellé doit le dire, car 975 apparaîtra ailleurs dans le même rapport. Les imputer d'après la date d'admission. Tentant, et cela décale toute la courbe vers la droite du délai médian, ce qui aplatit la montée et déplace le pic apparent. Les tracer en bande séparée. Honnête et rarement fait — une barre empilée avec « début inconnu » dans une teinte distincte, placée à la semaine d'admission, pour que le lecteur voie à la fois la courbe et son incertitude.
  8. Diapositive 8 / 21

    Ce que font les dates manquantes — En Python

    imputed = cases.copy()
    fallback = pd.to_datetime(imputed["admission_date"], errors="coerce")
    imputed["onset_or_admission"] = imputed["onset_date"].fillna(fallback)
    
    both = pd.DataFrame({
        "excluded": curve,
        "imputed": (imputed.assign(
            week=imputed["onset_or_admission"].dt.to_period("W").dt.start_time)
            .groupby("week").size()),
    }).fillna(0).astype(int)
    print(both)
  9. Diapositive 9 / 21

    Ce que font les dates manquantes — En R

    cases |>
      mutate(onset_or_admission = coalesce(onset_date, admission_date),
             week = lubridate::floor_date(onset_or_admission, "week")) |>
      count(week)
  10. Diapositive 10 / 21

    Ce que font les dates manquantes

    • Quel que soit votre choix, énoncez le dénominateur sur le graphique — « n = 928 des 975 cas avec une date de début…
    Notes du présentateur
    Quel que soit votre choix, énoncez le dénominateur sur le graphique. « n = 928 des 975 cas avec une date de début enregistrée » fait une ligne et c'est la différence entre une courbe et une affirmation.
  11. Diapositive 11 / 21

    Le district dont la courbe n'est pas une courbe — En Python

    delay = (
        pd.to_datetime(cases["admission_date"], errors="coerce") - cases["onset_date"]
    ).dt.days
    
    by_district = cases.assign(delay=delay).dropna(subset=["delay"]).groupby("district")
    print(by_district["delay"].median())
    print((by_district["delay"].apply(lambda s: (s == 0).mean()) * 100).round(0))
    Notes du présentateur
    Vient maintenant le constat pour lequel ce jeu de données a été bâti.
  12. Diapositive 12 / 21

    Le district dont la courbe n'est pas une courbe — En R

    cases |>
      filter(!is.na(onset_date), !is.na(admission_date)) |>
      mutate(delay = as.integer(admission_date - onset_date)) |>
      summarise(median_delay = median(delay), zero_day = mean(delay == 0), .by = district)
  13. Diapositive 13 / 21

    Le district dont la courbe n'est pas une courbe

    DistrictDélai médianMême jour
    Nord0 jour80 %
    Centre2 jours13 %
    Sud2 jours20 %
  14. Diapositive 14 / 21

    Le district dont la courbe n'est pas une courbe

    • Les cas de Nord semblent atteindre le traitement le jour même où ils tombent malades, quatre fois plus souvent que…
    • La courbe épidémique de Nord est une courbe d'admissions — Elle est décalée vers la droite du temps que ses cas ont…
    • La statistique de délai de Nord est inutilisable — et c'est celle que la leçon suivante emploie pour expliquer la…
    Notes du présentateur
    Les cas de Nord semblent atteindre le traitement le jour même où ils tombent malades, quatre fois plus souvent que partout ailleurs. Ce n'est pas un système de santé performant. C'est un registre rempli après coup depuis le cahier d'admission — quelqu'un saisit la date d'admission dans les deux champs parce que la date de début n'a jamais été demandée. Deux conséquences, graves toutes deux. La courbe épidémique de Nord est une courbe d'admissions. Elle est décalée vers la droite du temps que ses cas ont réellement mis à arriver, si bien que comparer le moment du pic entre districts compare deux quantités différentes. La statistique de délai de Nord est inutilisable, et c'est celle que la leçon suivante emploie pour expliquer la létalité — où Nord est le pire à 6,30 % contre 2,00 % au Centre. Le district qui paraît avoir l'accès le plus rapide a la mortalité la plus élevée, et la résolution est que l'apparence est un artefact.
  15. Diapositive 15 / 21

    Le district dont la courbe n'est pas une courbe — En Python

    print("Nord: median delay 0 days, case fatality 6.30%")
    print("Centre: median delay 2 days, case fatality 2.00%")
  16. Diapositive 16 / 21

    Le district dont la courbe n'est pas une courbe — En R

    # The contradiction is the finding: the fastest-looking district dies most.
  17. Diapositive 17 / 21

    Le district dont la courbe n'est pas une courbe

    • Une contradiction entre deux indicateurs d'un même registre est une question de données avant d'être une question…
    Notes du présentateur
    Une contradiction entre deux indicateurs d'un même registre est une question de données avant d'être une question d'épidémiologie. La règle du cours de nettoyage sur les colonnes qui doivent concorder, arrivant dans un décor où s'y tromper oriente mal une réponse d'épidémie.
  18. Diapositive 18 / 21

    Lire la forme

    • Un pic unique et net suggère une source ponctuelle — un point d'eau contaminé, un événement — avec des cas…
    • Une série de pics espacés d'environ une période d'incubation suggère une propagation interhumaine.
    • Un long plateau suggère une exposition à source commune persistante, ce qui pour le choléra signifie en général que…
    Notes du présentateur
    Une fois la courbe digne de confiance, trois lectures sont classiques. Cette épidémie monte sur six semaines et décroît sur dix, ce qui est une forme propagée plutôt qu'une source ponctuelle. Dites quelle lecture vous faites et pourquoi, et soyez prudent — la forme est aussi affectée par le moment où la réponse a démarré, et une intervention efficace tronque la courbe d'une manière qui ressemble à une décroissance naturelle.
  19. Diapositive 19 / 21

    Tracez des cas, non des taux

    • Un dernier point pratique.
    Notes du présentateur
    Un dernier point pratique. La courbe épidémique est un décompte dans le temps, non un taux. Diviser par la population rend les districts comparables et détruit ce à quoi sert la courbe — la charge absolue arrivant chaque semaine dans les centres de traitement. Tracez des effectifs. Mettez les taux dans le tableau à côté, ce qui est la leçon suivante.
  20. Diapositive 20 / 21

    La suite

    • La courbe dit quand.
    Notes du présentateur
    La courbe dit quand. La leçon suivante dit combien et à quel point c'est grave — taux d'attaque avec le dénominateur de population que ce jeu de données livre, et létalité au regard du 1 % sur lequel la réponse est jugée.
  21. Diapositive 21 / 21

    La suite

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