Retour à la leçon·Leçon 5 sur 8·Repérer les sites qui méritent une visite
Comparer une formation sanitaire à son propre passé
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
- La cohérence est une comparaison, et vous en choisissez le terme
- Rapport à la médiane
- Le problème des petits effectifs
- Adjoignez un plancher absolu au rapport
- Le sens n'est pas symétrique
- Des séries trop stables
- Classez les formations, ne signalez pas des lignes
- La suite
Notes du présentateur
Rapport à la médiane glissante, le problème des petits effectifs qui rend les plus grandes aberrations insignifiantes, et la série si stable qu'elle mérite un second regard.La cohérence est une comparaison, et vous en choisissez le terme
- À ses pairs — La formation A a rapporté 40 doses ; la médiane du district est de 25
- À son propre passé — La formation A a rapporté 40 doses ce mois-ci, contre sa propre médiane annuelle de 25
Notes du présentateur
La dimension de cohérence demande si les nombres se tiennent. En pratique cela revient à comparer chaque valeur rapportée à quelque chose, et il y a deux candidats. À ses pairs. La formation A a rapporté 40 doses ; la médiane du district est de 25. Tentant et généralement faux — les formations diffèrent énormément par la taille de leur bassin, et une grande formation sera signalée chaque mois pour cause de grandeur. À son propre passé. La formation A a rapporté 40 doses ce mois-ci, contre sa propre médiane annuelle de 25. Voilà une comparaison qui vaut la peine, car une formation est un témoin raisonnable d'elle-même. Employez la seconde. Tous les contrôles de cette leçon confrontent une formation à son propre historique.Rapport à la médiane — En Python
import pandas as pd reported = vax[vax["reported"]].copy() reported["own_median"] = reported.groupby(["facility_id", "antigen"])[ "doses_administered" ].transform("median") reported["ratio"] = reported["doses_administered"] / reported["own_median"] print(reported.nlargest(5, "ratio")[ ["facility_id", "antigen", "period", "doses_administered", "own_median", "ratio"] ])Notes du présentateur
La statistique utile la plus simple, et elle résiste à la brièveté de ces séries.Rapport à la médiane — En R
library(dplyr) reported <- vax |> filter(report_submitted) |> mutate(own_median = median(doses_administered), ratio = doses_administered / own_median, .by = c(facility_id, antigen)) reported |> slice_max(ratio, n = 5)Rapport à la médiane
- La médiane, pas la moyenne — La moyenne est tirée par l'aberration même que vous cherchez, si bien qu'une valeur peut…
Notes du présentateur
La médiane, pas la moyenne. La moyenne est tirée par l'aberration même que vous cherchez, si bien qu'une valeur peut gonfler sa propre base de comparaison et se dissimuler. C'est la raison pour laquelle les contrôles de plausibilité SMART emploient des statistiques robustes. Là où la série est assez longue, préférez une médiane glissante, pour qu'un véritable changement de niveau saisonnier ne signale pas tous les mois suivants.Rapport à la médiane — En Python
reported = reported.sort_values(["facility_id", "antigen", "period"]) reported["rolling_median"] = ( reported.groupby(["facility_id", "antigen"])["doses_administered"] .transform(lambda s: s.rolling(6, min_periods=3, center=True).median()) )Rapport à la médiane — En R
reported <- reported |> arrange(facility_id, antigen, period) |> mutate(rolling_median = zoo::rollapply(doses_administered, 6, median, partial = TRUE, align = "center"), .by = c(facility_id, antigen))Le problème des petits effectifs
Formation Antigène Mois Valeur Médiane propre Rapport FAC013 mcv2 décembre 5 3 1,67 FAC037 opv3 septembre 13 8 1,62 FAC030 mcv2 octobre 8 5 1,60 Notes du présentateur
Exécutez le contrôle de rapport sur cette extraction et voici le haut de la liste. La plus grande aberration de tout le district est une formation qui a administré cinq secondes doses de vaccin antirougeoleux au lieu de trois. Deux doses. Personne n'ira enquêter là-dessus, et si votre rapport d'évaluation ouvre là-dessus, personne ne lira la suite. C'est la défaillance caractéristique du signalement par rapport, et elle est garantie plutôt que malchanceuse — un rapport a un petit dénominateur en bas de la plage, donc les rapports les plus extrêmes proviennent toujours des plus petits effectifs. Le contrôle fait exactement ce qu'on lui a demandé et c'est la question qui était mauvaise.Adjoignez un plancher absolu au rapport — En Python
RATIO_HIGH, RATIO_LOW, MIN_ABSOLUTE = 2.0, 0.5, 10 reported["difference"] = reported["doses_administered"] - reported["own_median"] reported["flag"] = ( ((reported["ratio"] > RATIO_HIGH) | (reported["ratio"] < RATIO_LOW)) & (reported["difference"].abs() >= MIN_ABSOLUTE) ) print(f"{reported['flag'].sum()} flags from {len(reported)} facility-antigen-months")Adjoignez un plancher absolu au rapport — En R
reported <- reported |> mutate( difference = doses_administered - own_median, flag = (ratio > 2 | ratio < 0.5) & abs(difference) >= 10 )Adjoignez un plancher absolu au rapport
- Voilà le constat, et il tient en une ligne — Un contrôle qui produit un seul élément à investiguer sur 2 094…
Notes du présentateur
Appliqué ici, rien du tout ne dépasse le double de sa propre médiane, et onze couples formation-antigène-mois tombent sous la moitié de la leur. Le mouvement réel le plus important est le penta3 de FAC005 en octobre — 13 doses contre une médiane de 43, une baisse réelle de trente. Voilà le constat, et il tient en une ligne. Un contrôle qui produit un seul élément à investiguer sur 2 094 observations fonctionne ; un contrôle qui en produit deux cents est un contrôle que personne n'exécutera deux fois.Le sens n'est pas symétrique
- Une baisse est généralement réelle ou liée au rapportage. Rupture de stock, absence de personnel, grève, formation…
- Un pic est généralement une campagne, une sortie de proximité ou un rattrapage après rupture — toutes légitimes —…
Notes du présentateur
Une baisse et un pic ne veulent pas dire la même chose, et une évaluation qui les traite pareillement manque l'essentiel de ce qui se passe.Le sens n'est pas symétrique — En Python
reported["previous"] = reported.groupby(["facility_id", "antigen"])[ "doses_administered"].shift(1) reported["next"] = reported.groupby(["facility_id", "antigen"])[ "doses_administered"].shift(-1) spike_and_dip = reported[ (reported["ratio"] > 1.5) & (reported["previous"] < reported["own_median"] * 0.7) ]Le sens n'est pas symétrique — En R
reported <- reported |> mutate(previous = lag(doses_administered), next_value = lead(doses_administered), .by = c(facility_id, antigen))Notes du présentateur
Un mois élevé précédé d'un mois faible est très souvent le travail d'un mois consigné dans la remontée du suivant. C'est un constat d'actualité déguisé en constat de cohérence, et le remède est une échéance, pas un recomptage.Des séries trop stables — En Python
stability = ( reported.groupby(["facility_id", "antigen"])["doses_administered"] .agg(["mean", "std", "size"]) ) stability["cv"] = stability["std"] / stability["mean"] print(stability[stability["size"] >= 8].nsmallest(5, "cv"))Notes du présentateur
Le contrôle inverse, celui qu'on oublie. La prestation de service réelle est bruitée. Une formation rapportant un chiffre presque identique chaque mois mérite un coup d'œil.Des séries trop stables — En R
reported |> summarise(mean = mean(doses_administered), cv = sd(doses_administered) / mean(doses_administered), n = n(), .by = c(facility_id, antigen)) |> filter(n >= 8) |> slice_min(cv, n = 5)Notes du présentateur
La série la plus stable de cette extraction a un coefficient de variation d'environ 0,06 — six pour cent d'un mois à l'autre. C'est faible, et ce n'est la preuve de rien. Une grande formation à bassin stable et à jour de consultation régulier peut aisément produire cela. Énoncez la limite franchement, car ce contrôle est le plus facilement détourné : un faible coefficient de variation met une formation sur la liste des visites. Il ne figure pas au rapport comme un constat, et surtout pas dans une phrase contenant le mot « fabriqué ».Classez les formations, ne signalez pas des lignes — En Python
risk = ( reported.groupby("facility_id") .agg(flags=("flag", "sum"), months=("flag", "size")) .assign(flag_rate=lambda d: d["flags"] / d["months"]) .sort_values("flag_rate", ascending=False) ) print(risk.head(6))Notes du présentateur
Le produit de cette leçon n'est pas une liste de mois signalés. C'est un classement d'unités de rapportage, ce dont la leçon d'échantillonnage avait besoin.Classez les formations, ne signalez pas des lignes — En R
risk <- reported |> summarise(flags = sum(flag), months = n(), .by = facility_id) |> mutate(flag_rate = flags / months) |> arrange(desc(flag_rate))Notes du présentateur
Une formation avec quatre signalements sur douze mois est candidate à une visite. Un mois signalé isolé dans une formation par ailleurs régulière est une question pour un appel téléphonique. L'unité d'action est la formation, parce que c'est là que va le véhicule.La suite
- Les contrôles de tendance comparent un nombre à son propre passé.
Notes du présentateur
Les contrôles de tendance comparent un nombre à son propre passé. La leçon suivante regarde à l'intérieur du nombre lui-même — le dernier chiffre, l'attraction des multiples de cinq, et les limites honnêtes de ce que cela peut dire de la façon dont une mesure a été produite.