cassionAnalyse de données

Leçon 6 sur 8

Unité · Comparer des lieux

La moitié de l'écart tenait à qui habite là

Le taux d'attaque brut de Nord vaut 7,94 pour 1 000 et celui de Sud 5,51. Standardisés sur une population commune ils valent 7,25 et 5,95, si bien que l'écart tombe de 2,43 à 1,30 — et la moitié disparue était la structure par âge.

PythonR135 minDéfinitions d'indicateurs de l'UNICEFObjectifs de développement durable (ODD)

Deux districts ne sont pas comparables tels quels

La leçon 4 a posé trois taux d’attaque bruts côte à côte et la comparaison paraissait simple. Elle ne l’est pas, et la raison est dans le fichier de population.

import pandas as pd

population = pd.read_csv("district-population-2024.v1.csv")

structure = (
    population.pivot(index="district", columns="age_band", values="population")
)
shares = structure.div(structure.sum(axis=1), axis=0)
print((shares * 100).round(1))
library(dplyr)

population |>
  mutate(share = population / sum(population), .by = district) |>
  tidyr::pivot_wider(id_cols = district, names_from = age_band, values_from = share)
District 0-4 5-14 15-44 45+
Nord 22 % 30 % 35 % 13 %
Centre 15 % 25 % 42 % 18 %
Sud 11 % 20 % 44 % 25 %

Nord compte deux fois plus de moins de cinq ans que Sud, et la leçon 4 a établi que les moins de cinq ans ont trois à quatre fois le taux d’attaque des adultes en âge de travailler.

Nord aurait donc un taux brut plus élevé que Sud même si chaque taux par âge des deux districts était identique. La comparaison brute mélange deux choses — à quel point chaque district est risqué, et qui y habite.

La standardisation directe

La méthode tient en une ligne d’arithmétique appliquée systématiquement — calculez les taux par âge de chaque district, puis appliquez-les à une seule population commune.

cases = pd.read_csv("cholera-line-list-2024.v1.csv")

observed = (
    cases.groupby(["district", "age_band"]).size().rename("cases").to_frame()
    .join(population.set_index(["district", "age_band"])["population"])
)
observed["rate"] = observed["cases"] / observed["population"]

standard = population.groupby("age_band")["population"].sum()
standard_share = standard / standard.sum()

standardised = (
    observed["rate"].unstack()          # district x age_band
    .mul(standard_share, axis=1).sum(axis=1)
)
crude = (
    cases.groupby("district").size()
    / population.groupby("district")["population"].sum()
)

comparison = pd.DataFrame({
    "crude_per_1000": 1000 * crude,
    "standardised_per_1000": 1000 * standardised,
})
comparison["difference"] = (
    comparison["standardised_per_1000"] - comparison["crude_per_1000"]
)
print(comparison.round(2))
observed <- cases |> count(district, age_band, name = "cases") |>
  left_join(population, by = c("district", "age_band")) |>
  mutate(rate = cases / population)

standard <- population |> summarise(pop = sum(population), .by = age_band) |>
  mutate(share = pop / sum(pop))

observed |>
  left_join(standard, by = "age_band") |>
  summarise(standardised = sum(rate * share), .by = district)
District Brut Standardisé Écart
Nord 7,94 7,25 −0,69
Centre 6,47 6,60 +0,13
Sud 5,51 5,95 +0,44

Nord baisse, Sud monte, et l’écart entre eux se resserre de 2,43 à 1,30 pour 1 000. Environ la moitié de la différence apparente entre le pire et le meilleur district était une structure par âge et non un risque.

Les 1,30 restants sont réels. Nord est réellement plus touché — ses taux par âge sont plus élevés dans trois bandes sur quatre — et c’est la standardisation qui permet de le dire plutôt que de l’affirmer.

Ce qu’est la population standard, et pourquoi elle compte

Le standard est la population à laquelle vous appliquez les taux de chaque district. Trois choix courants.

  • La population combinée de l’étude, comme ci-dessus. Simple, défendable, et interne — les taux standardisés sont comparables entre eux et à rien d’autre.
  • Une population nationale. Permet la comparaison avec d’autres districts standardisés de la même façon.
  • Un standard mondial publié — la population type de l’OMS ou de Segi. Permet la comparaison internationale, et produit des nombres qui ne ressemblent en rien aux taux bruts.

Énoncez le standard. Un taux standardisé ne s’interprète que face à d’autres standardisés sur la même population, et deux rapports employant des standards différents produisent des nombres incomparables qui ont tous deux l’air officiel.

print("Standard: combined population of the three districts, 145,000")
# Say it in the table caption, every time.

La standardisation indirecte, et quand elle est nécessaire

La standardisation directe exige des taux par âge pour chaque district, ce qui exige assez de cas dans chaque cellule. Là où un district compte quatre cas dans une bande, son taux par âge est instable et la méthode directe propage cette instabilité.

La méthode indirecte inverse le problème — appliquer un jeu de taux standard à la population propre de chaque district, et comparer les cas observés aux attendus.

standard_rates = observed.groupby("age_band").apply(
    lambda g: g["cases"].sum() / g["population"].sum()
)

expected = (
    population.assign(rate=population["age_band"].map(standard_rates))
    .assign(expected=lambda d: d["population"] * d["rate"])
    .groupby("district")["expected"].sum()
)
smr = cases.groupby("district").size() / expected
print(smr.round(3))
standard_rates <- observed |>
  summarise(rate = sum(cases) / sum(population), .by = age_band)

population |>
  left_join(standard_rates, by = "age_band") |>
  summarise(expected = sum(population * rate), .by = district) |>
  left_join(count(cases, district, name = "observed"), by = "district") |>
  mutate(smr = observed / expected)

Le résultat est un rapport standardisé de morbidité — observés sur attendus, où 1,0 signifie que le district a exactement les cas que sa structure par âge prédit. Au-dessus de 1 c’est pire qu’attendu, en dessous c’est mieux.

Employez la standardisation indirecte quand les cellules sont petites, et dites quelle méthode vous avez employée — les deux répondent à des questions légèrement différentes et leurs nombres ne sont pas interchangeables.

L’âge n’est pas le seul facteur de confusion

La standardisation retire la variable sur laquelle vous standardisez, et rien d’autre. Les taux d’attaque du choléra varient aussi avec la source d’eau, la densité de population, la distance à un centre de traitement et le statut de déplacement, et aucun de ces facteurs n’est dans le fichier de population.

Standardiser sur l’âge puis affirmer que la différence restante est la performance du programme est l’erreur que cette leçon rend possible, et la leçon suivante lui est entièrement consacrée.

Rapportez la paire

Cholera attack rate by district, weeks 1-16

  District   Crude    Age-standardised   Population
  Nord       7.94     7.25               48,000
  Centre     6.47     6.60               62,000
  Sud        5.51     5.95               35,000

  Standardised directly to the combined population of the three districts.
  About half the crude Nord-Sud difference is age structure: Nord has 22% of
  its population under five against Sud's 11%, and under-fives have three to
  four times the attack rate of adults aged 15-44.

Les deux colonnes, le standard nommé, et une phrase disant de combien cela a bougé et pourquoi. Un tableau qui n’a que la colonne standardisée cache qu’un ajustement a eu lieu ; un tableau qui n’a que la colonne brute invite à une comparaison à moitié démographique.

La suite

La standardisation a retiré une explication alternative. La leçon suivante liste les autres — et demande à quoi une différence entre deux districts a le droit d’être attribuée quand aucune d’elles ne peut être retirée.

Animer cette leçon

La leçon en diaporama, la prose étant reléguée dans les notes du présentateur plutôt que projetée. Produit à partir de cette page, dont il ne peut donc pas s'écarter.

Lancer le diaporamaLire les diapositives

Le PDF ne requiert aucun logiciel et se projette depuis n'importe quel poste. Le fichier PowerPoint est fait pour être modifié : appliquez la charte de votre organisation, retirez une section pour une séance plus courte, ou fusionnez deux leçons en atelier.