Leçon 1 sur 8
Unité · Le dénominateur, c'est l'épidémiologie
Taux, rapport, proportion — et personnes-temps
Trois formes que l'on appelle toutes « taux » et une seule l'est, plus le dénominateur qui donne son sens à l'incidence quand les gens entrent et sortent d'une population à des moments différents.
Trois formes, un seul mot
Le cours sur la conception d’indicateurs a établi quatre familles de mesure. L’épidémiologie en emploie trois constamment et les appelle toutes « taux », et c’est de là que part l’essentiel des contresens.
| Forme | Structure | Plage | Exemple |
|---|---|---|---|
| Proportion | Partie sur tout, mêmes unités | 0 à 1 | Létalité — décès parmi les cas |
| Rapport | Deux quantités non emboîtées | quelconque | Sex-ratio des cas ; rapport de cotes |
| Taux | Événements sur population-temps | positif | Incidence pour 1 000 personnes-années |
Seule la troisième est un taux, et la différence est le temps au dénominateur. Une proportion demande « quelle part » ; un taux demande « à quelle vitesse ».
Prévalence et incidence
Le couple autour duquel tourne tout ce cours.
- La prévalence — combien de personnes ont l’affection maintenant. Une proportion, issue d’une enquête, un instantané.
- L’incidence — combien de nouveaux cas surviennent par unité de population-temps. Un taux, issu de la surveillance, un flux.
Ils répondent à des questions différentes et bougent pour des raisons différentes. La prévalence monte si l’incidence monte ou si les gens survivent plus longtemps avec l’affection, ce qui explique qu’un programme de traitement réussi puisse faire monter la prévalence du VIH tout en faisant baisser son incidence — le couple de nombres le plus mal lu de ce secteur.
import pandas as pd
cases = pd.read_csv("cholera-line-list-2024.v1.csv", parse_dates=["onset_date"])
population = pd.read_csv("district-population-2024.v1.csv")
total_population = population["population"].sum()
print(f"{len(cases)} cases in a population of {total_population:,}")
print(f"attack rate over the outbreak: {1000 * len(cases) / total_population:.2f} per 1,000")
library(dplyr)
cases <- readr::read_csv("cholera-line-list-2024.v1.csv")
population <- readr::read_csv("district-population-2024.v1.csv")
c(cases = nrow(cases),
population = sum(population$population),
per_1000 = 1000 * nrow(cases) / sum(population$population))
975 cas dans 145 000 personnes — 6,7 pour 1 000 sur l’épidémie. À strictement parler c’est un taux d’attaque, qui est une proportion malgré son nom : les cas sur la population à risque, sur une période d’épidémie définie. La leçon 4 le prend au sérieux.
Ce qu’apportent les personnes-temps
Un dénominateur en effectifs suppose que tout le monde était présent et à risque pendant toute la période. Dans ce secteur, ce n’est régulièrement pas le cas — les gens arrivent, partent, naissent, meurent, ou entrent dans un programme en cours d’année.
Les personnes-temps comptent chaque personne aussi longtemps qu’elle était réellement à risque.
# One person followed for 6 months contributes 0.5 person-years
follow_up = pd.DataFrame({
"person_id": ["A", "B", "C"],
"entered": pd.to_datetime(["2024-01-01", "2024-01-01", "2024-07-01"]),
"exited": pd.to_datetime(["2024-12-31", "2024-06-30", "2024-12-31"]),
})
follow_up["person_years"] = (
(follow_up["exited"] - follow_up["entered"]).dt.days / 365.25
)
events = 1
print(f"{events / follow_up['person_years'].sum():.3f} events per person-year")
print(f"naive headcount rate: {events / len(follow_up):.3f} per person")
follow_up <- tibble::tibble(
person_id = c("A", "B", "C"),
entered = as.Date(c("2024-01-01", "2024-01-01", "2024-07-01")),
exited = as.Date(c("2024-12-31", "2024-06-30", "2024-12-31"))
) |>
mutate(person_years = as.numeric(exited - entered) / 365.25)
c(per_person_year = 1 / sum(follow_up$person_years),
naive = 1 / nrow(follow_up))
Trois personnes, dont deux présentes la moitié de l’année. Le dénominateur en effectifs vaut 3 ; le dénominateur en personnes-temps vaut 2,0 personnes-années. Les deux réponses diffèrent d’un tiers, et la seconde est celle qui se compare entre périodes et lieux à durées de suivi différentes.
Employez les personnes-temps dès que :
- le suivi varie — une cohorte de traitement où l’on s’inscrit tout au long de l’année ;
- la population change — déplacements, arrivées en camp, bassin qui grandit ;
- vous comparez des périodes de longueurs différentes — une réponse de neuf mois face à une de douze.
Employez un dénominateur en effectifs quand tout le monde était présent tout du long et que la période est fixée, ce qui rend légitime un taux d’attaque d’épidémie.
Mettez le dénominateur et le multiplicateur dans le nom
indicators = {
"cholera_attack_rate_per_1000_outbreak": 1000 * len(cases) / total_population,
"case_fatality_percent_of_cases_with_outcome": None,
"incidence_per_1000_person_years": None,
}
# The same discipline: the name carries the denominator and the multiplier
Deux choses ont leur place dans le nom et en sont d’ordinaire absentes.
Le multiplicateur. Pour 100, pour 1 000, pour 100 000. Les taux d’attaque du choléra se donnent conventionnellement pour 1 000 ; la mortalité maternelle pour 100 000 ; la couverture vaccinale pour 100. Se tromper de convention représente un facteur cent, et cela arrive.
La population du dénominateur. « Pour 1 000 habitants » et « pour 1 000 enfants de moins de cinq ans » sont deux indicateurs différents. Le cours sur les indicateurs le disait en général ; ici les conventions sont publiées et s’en écarter en silence est pire que d’en inventer une.
La population à risque n’est pas toujours toute la population
Le dénominateur d’un taux est la population à risque de l’événement.
- Le ratio de mortalité maternelle — décès pour 100 000 naissances vivantes, non pour la population, car seules les grossesses sont à risque.
- La mortalité néonatale — décès dans les 28 premiers jours pour 1 000 naissances vivantes.
- Le taux d’attaque du choléra — cas pour 1 000 habitants, car tout le monde est à risque.
- Le taux d’attaque de la rougeole — sans doute pour 1 000 personnes réceptives, et la population réceptive est précisément ce qu’un programme de vaccination modifie.
under_five = population.loc[population["age_band"] == "0-4", "population"].sum()
u5_cases = (cases["age_band"] == "0-4").sum()
print(f"under-five attack rate: {1000 * u5_cases / under_five:.2f} per 1,000")
print(f"all-age attack rate: {1000 * len(cases) / total_population:.2f} per 1,000")
under5 <- population |> filter(age_band == "0-4") |> summarise(sum(population)) |> pull()
u5_cases <- sum(cases$age_band == "0-4")
c(under_five = 1000 * u5_cases / under5,
all_age = 1000 * nrow(cases) / sum(population$population))
Les moins de cinq ans ont ici environ le double du taux d’attaque tous âges. Cet écart est toute la raison d’être de la leçon 6 — deux districts aux structures par âge différentes différeront sur le taux tous âges même si chaque taux par âge est identique.
Le contrôle à faire sur tout taux
Quatre questions, et un taux incapable d’y répondre n’est pas utilisable.
- Que compte le numérateur — des événements, des personnes, des épisodes ?
- Qui est au dénominateur, et tous étaient-ils à risque ?
- Sur quelle période, et tous y étaient-ils présents ?
- Quel est le multiplicateur, et est-ce la convention de cet indicateur ?
def describe_rate(numerator, denominator, period, multiplier, at_risk_note):
return {
"value": multiplier * numerator / denominator,
"numerator": numerator, "denominator": denominator,
"period": period, "multiplier": multiplier, "at_risk": at_risk_note,
}
describe_rate <- function(numerator, denominator, period, multiplier, at_risk) {
list(value = multiplier * numerator / denominator, numerator = numerator,
denominator = denominator, period = period, at_risk = at_risk)
}
C’est la fiche de référence d’indicateur du module 3, réduite aux cinq champs dont une mesure épidémiologique ne peut pas se passer.
La suite
Un dénominateur est simple. La leçon suivante en enchaîne plusieurs — les cascades de soins du VIH et de la tuberculose, où le dénominateur de chaque étape est le numérateur de la précédente, et où le nombre intéressant est le maillon qui perd le plus.