Retour à la leçon·Leçon 7 sur 8·Juger un programme
Le dénominateur qui déplace le taux de guérison de neuf points
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
- Quatre issues et un standard
- Les trois dénominateurs candidats
- Celui que Sphere désigne
- Décomposez par site
- La cause est en général la distance, non l'observance
- Le gain de poids, et les saisies qui ne peuvent pas être justes
- Le tableau de performance à publier
- La suite
Notes du présentateur
Guéris, abandons, décès, non-réponse — au regard des standards minimums Sphere, sur le dénominateur que Sphere spécifie réellement. 82,3 % ou 73,6 %, selon un choix que personne ne documente, et un site hors standard que le chiffre du district masque.Quatre issues et un standard
Issue Minimum Sphere, soins thérapeutiques ambulatoires Guéris plus de 75 % Décès moins de 10 % Abandons moins de 15 % Non-réponse pas de seuil fixe ; investiguée si élevée Notes du présentateur
Un programme PCIMA est jugé sur ce qu'il est advenu des enfants qu'il a admis, et les standards minimums Sphere donnent les seuils.Quatre issues et un standard
- Chacun est une proportion, et la discussion porte toujours sur le dénominateur
Notes du présentateur
L'alimentation supplémentaire a son propre jeu — guéris au-dessus de 75 %, décès sous 3 %, abandons sous 15 %. Les nombres diffèrent ; la structure non. Chacun est une proportion, et la discussion porte toujours sur le dénominateur.Les trois dénominateurs candidats — En Python
import pandas as pd cmam = pd.read_csv("cmam-admissions-2024.v1.csv") print(f"admissions: {len(cmam):>5}") print(f"with an outcome: {cmam['outcome'].notna().sum():>5}") print(f"excluding transfers: " f"{(cmam['outcome'].notna() & (cmam['outcome'] != 'transferred')).sum():>5}")Les trois dénominateurs candidats — En R
cmam |> summarise( admissions = n(), with_outcome = sum(!is.na(outcome)), excluding_transfers = sum(!is.na(outcome) & outcome != "transferred") )Les trois dénominateurs candidats
Dénominateur n Ce qu'il fait des exclus Toutes les admissions 1 100 Les enfants encore en traitement comptés comme non guéris Ayant atteint une issue 1 029 Les transferts comptés comme une issue Ayant atteint une issue, transferts exclus 984 — Les trois dénominateurs candidats — En Python
cured = (cmam["outcome"] == "cured").sum() for label, n in [("all admissions", 1100), ("with an outcome", 1029), ("excluding transfers", 984)]: print(f"cure rate on {label:22} {cured / n:.1%}")Les trois dénominateurs candidats — En R
cured <- sum(cmam$outcome == "cured", na.rm = TRUE) c(all = cured / 1100, outcome = cured / 1029, sphere = cured / 984)Les trois dénominateurs candidats
- 73,6 %, 78,7 %, 82,3 % — Un numérateur, trois dénominateurs, neuf points
Notes du présentateur
73,6 %, 78,7 %, 82,3 %. Un numérateur, trois dénominateurs, neuf points. Et l'un d'eux franchit un seuil : sur toutes les admissions le programme est sous le minimum Sphere de 75 % et sur le dénominateur Sphere il est confortablement au-dessus. La différence n'est pas la performance. C'est un dénominateur que personne n'a écrit.Celui que Sphere désigne
- Les enfants ayant atteint une issue thérapeutique, transferts exclus — 984 ici
- Les enfants encore en traitement ne sont pas une issue — Soixante et onze enfants ont été admis assez tard pour ne pas…
- Les transferts sont l'issue de quelqu'un d'autre — Quarante-cinq enfants sont passés d'un programme à l'autre —…
Notes du présentateur
Les enfants ayant atteint une issue thérapeutique, transferts exclus. 984 ici. Les deux exclusions ont des motifs différents et les deux sont justes. Les enfants encore en traitement ne sont pas une issue. Soixante et onze enfants ont été admis assez tard pour ne pas être sortis à la date d'arrêt. Ils ne sont ni guéris ni abandons ; ils sont en traitement. Les compter dans une catégorie quelconque est une affirmation sur un avenir qui n'a pas eu lieu. Les transferts sont l'issue de quelqu'un d'autre. Quarante-cinq enfants sont passés d'un programme à l'autre — ambulatoire vers hospitalier, ou l'inverse — et leur issue thérapeutique sera enregistrée là où ils aboutissent. Les compter ici reviendrait à les compter deux fois entre les deux programmes, ou à créditer ce programme d'un résultat qu'il n'a pas produit.Celui que Sphere désigne — En Python
performance = cmam[cmam["outcome"].notna() & (cmam["outcome"] != "transferred")] rates = performance["outcome"].value_counts(normalize=True) print((rates * 100).round(1)) print(f"n = {len(performance)}")Celui que Sphere désigne — En R
performance <- cmam |> filter(!is.na(outcome), outcome != "transferred") performance |> count(outcome) |> mutate(rate = n / sum(n))Celui que Sphere désigne
Issue Taux Sphere Guéris 82,3 % plus de 75 % passe Abandons 13,7 % moins de 15 % passe Décès 1,0 % moins de 10 % passe Non-réponse 2,9 % — Notes du présentateur
Quatre lignes, toutes dans le standard. C'est là que s'arrêtent la plupart des rapports PCIMA, et c'est là que le constat se cache.Décomposez par site — En Python
by_site = ( performance.assign(**{o: performance["outcome"] == o for o in ["cured", "defaulted", "died", "non-response"]}) .groupby("site_id") .agg(n=("outcome", "size"), cured=("cured", "mean"), defaulted=("defaulted", "mean"), died=("died", "mean")) ) print((by_site[["cured", "defaulted", "died"]] * 100).round(1))Décomposez par site — En R
performance |> summarise(n = n(), cured = mean(outcome == "cured"), defaulted = mean(outcome == "defaulted"), died = mean(outcome == "died"), .by = site_id) |> arrange(desc(defaulted))Décomposez par site
Site n Abandons SITE-03 156 20,5 % SITE-02 168 15,5 % SITE-06 125 13,6 % SITE-05 142 11,3 % SITE-01 188 11,2 % SITE-04 205 11,2 % Décomposez par site
- Le programme abandonne à 13,7 % et un site à 20,5 % — Le chiffre du district est sous le maximum Sphere et ce site ne…
Notes du présentateur
Le programme abandonne à 13,7 % et un site à 20,5 %. Le chiffre du district est sous le maximum Sphere et ce site ne l'est pas, de cinq points. C'est toute la raison pour laquelle le cours d'évaluation insistait sur les distributions plutôt que sur les moyennes, et cela arrive ici avec une conséquence clinique — un enfant qui abandonne est un enfant qui a cessé le traitement avant guérison.Décomposez par site — En Python
flagged = by_site[(by_site["defaulted"] > 0.15) | (by_site["cured"] < 0.75)] print(f"{len(flagged)} of {len(by_site)} sites outside a Sphere standard")Décomposez par site — En R
performance |> summarise(cured = mean(outcome == "cured"), defaulted = mean(outcome == "defaulted"), .by = site_id) |> filter(defaulted > 0.15 | cured < 0.75)Notes du présentateur
Deux sites, et le second est limite à 15,5 %. Signalez par rapport au standard, non par rapport à la moyenne du district — c'est au standard que le programme est tenu.La cause est en général la distance, non l'observance — En Python
length_of_stay = ( pd.to_datetime(performance["discharge_date"]) - pd.to_datetime(performance["admission_date"]) ).dt.days print(performance.assign(stay=length_of_stay) .groupby("outcome")["stay"].median().round(0))Notes du présentateur
L'abandon a une littérature et elle est constante : le meilleur prédicteur est le temps de trajet jusqu'au site. Un accompagnant qui doit perdre une journée de travail chaque semaine pour marcher deux heures cessera de venir quand l'enfant aura meilleure mine, ce qui est rationnel et n'est pas de l'inobservance.La cause est en général la distance, non l'observance — En R
performance |> mutate(stay = as.integer(as.Date(discharge_date) - as.Date(admission_date))) |> summarise(median_stay = median(stay), .by = outcome)Notes du présentateur
Les abandons partent bien plus tôt que les enfants guéris — le séjour médian est d'environ trois semaines contre environ huit. Un abandon n'est pas un échec thérapeutique, c'est un enfant parti avant la fin du traitement, et l'action corrective est la décentralisation ou un appui au transport, non du counseling. C'est la discipline de cause racine du cours d'évaluation, appliquée à un indicateur clinique.Le gain de poids, et les saisies qui ne peuvent pas être justes — En Python
gain = ( (performance["weight_discharge_kg"] - performance["weight_admission_kg"]) / performance["weight_admission_kg"] / length_of_stay * 1000 ) print(gain.describe().round(1)) print(f"{(gain < 0).sum()} discharges with weight loss")Le gain de poids, et les saisies qui ne peuvent pas être justes — En R
performance |> mutate(gain = (weight_discharge_kg - weight_admission_kg) / weight_admission_kg / as.integer(as.Date(discharge_date) - as.Date(admission_date)) * 1000) |> summarise(median = median(gain, na.rm = TRUE), negative = sum(gain < 0, na.rm = TRUE))Notes du présentateur
Le gain de poids en grammes par kilogramme et par jour est la mesure classique de la réponse au traitement, et neuf sorties présentent une perte de poids. C'est possible chez un enfant décédé ou ayant abandonné tôt, et c'est aussi exactement l'allure d'une saisie inversée. Le registre ne permet pas de les distinguer, ce qui est un constat pour la section qualité des données plutôt qu'un nombre à nettoyer.Le tableau de performance à publier — Exemple
CMAM performance, 2024 Admissions 1,100 Still in treatment at cut-off 71 excluded: not an outcome Transferred 45 excluded: outcome recorded elsewhere Performance denominator 984 Cured 82.3% Sphere >75% pass Defaulted 13.7% Sphere <15% pass Died 1.0% Sphere <10% pass Non-response 2.9% One site (SITE-03, n=156) defaults at 20.5%, outside the Sphere maximum. Median stay for defaulters is 24 days against 58 for cured children, consistent with distance rather than non-response to treatment.Notes du présentateur
Les lignes exclues sont montrées, non supprimées. Un lecteur peut reconstituer n'importe lequel des trois dénominateurs à partir de ce bloc, ce qui fait la différence entre un tableau de performance et une affirmation.La suite
- Le programme guérit les enfants qu'il admet.
Notes du présentateur
Le programme guérit les enfants qu'il admet. La dernière leçon pose la question plus difficile — quelle part des enfants ayant besoin d'un traitement il a jamais vue — et pourquoi le chiffre que la plupart des programmes rapportent comme couverture n'en est pas une.