Retour à la leçon·Leçon 4 sur 8·Ce que Sphère demande et qu'une échelle ne dit pas
Testé sur un tiers, censuré sur un cinquième
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
- Le dénominateur change, et le rapport généralement non
- Les classes de risque du JMP
- Amélioré n'est pas sûr, et c'est ici que cela se démontre
- La valeur censurée, et pourquoi elle n'est pas manquante
- Que faire d'une valeur censurée
- L'incohérence entre champs qu'il faut nommer
- Rapportez-le comme un bloc à part
- La suite
Notes du présentateur
E. coli a été testé sur 33,4 % des ménages, le chlore sur 40,0 %, et les deux sur 13,3 %. Dans le registre des points d'eau, 271 lectures de chlore sont la chaîne "<0.1" — et les supprimer relève la conformité apparente de seize points.Le dénominateur change, et le rapport généralement non — En Python
import pandas as pd households = pd.read_csv("wash-household-survey-2024.v1.csv") ecoli = households["ecoli_cfu_100ml"].notna() chlorine = households["free_residual_chlorine_mgl"].notna() print(f"E. coli tested: {ecoli.sum():>5} = {ecoli.mean():.1%}") print(f"chlorine tested: {chlorine.sum():>5} = {chlorine.mean():.1%}") print(f"both: {(ecoli & chlorine).sum():>5} = {(ecoli & chlorine).mean():.1%}")Notes du présentateur
La qualité de l'eau coûte cher à mesurer. Une enquête qui interroge 2 403 ménages en teste une fraction, et tout indicateur de qualité tourne donc sur un dénominateur différent de tout indicateur d'accès.Le dénominateur change, et le rapport généralement non — En R
library(dplyr) households |> summarise( ecoli = mean(!is.na(ecoli_cfu_100ml)), chlorine = mean(!is.na(free_residual_chlorine_mgl)), both = mean(!is.na(ecoli_cfu_100ml) & !is.na(free_residual_chlorine_mgl)) )Le dénominateur change, et le rapport généralement non
- 802 ménages pour E. coli, 961 pour le chlore, et 320 pour les deux — Le dernier nombre est celui qui compte : toute…
- Imprimez le n analysable pour chaque chiffre de qualité — dans le tableau et non en note de bas de page
Notes du présentateur
802 ménages pour E. coli, 961 pour le chlore, et 320 pour les deux. Le dernier nombre est celui qui compte : toute question liant les deux porte sur 13,3 % de l'enquête, et son intervalle sera large. Imprimez le n analysable pour chaque chiffre de qualité, dans le tableau et non en note de bas de page. Un pourcentage de qualité voisin d'un pourcentage d'accès sur la même ligne se lit comme le même dénominateur, et ici ce n'est jamais le cas.Les classes de risque du JMP — En Python
tested = households[ecoli] classes = pd.cut( tested["ecoli_cfu_100ml"], [-1, 0, 10, 100, 10**9], labels=["safe <1", "low 1-10", "moderate 11-100", "high >100"], ) print(classes.value_counts(normalize=True).round(3))Les classes de risque du JMP — En R
households |> filter(!is.na(ecoli_cfu_100ml)) |> mutate(risk = cut(ecoli_cfu_100ml, c(-1, 0, 10, 100, Inf), labels = c("safe", "low", "moderate", "high"))) |> count(risk) |> mutate(share = n / sum(n))Les classes de risque du JMP
Classe de risque Ménages Part des testés Sûre (<1 UFC/100 mL) 430 53,6 % Faible (1–10) 173 21,6 % Modérée (11–100) 139 17,3 % Élevée (>100) 60 7,5 % Les classes de risque du JMP
- Rapportez les classes, non une moyenne — Les dénombrements d'E
Notes du présentateur
Rapportez les classes, non une moyenne. Les dénombrements d'E. coli sont fortement asymétriques et une moyenne de 14 UFC/100 mL ne décrit rien — les classes sont ce dans quoi la directive est écrite et ce sur quoi une décision se prend.Amélioré n'est pas sûr, et c'est ici que cela se démontre — En Python
IMPROVED = {"piped-into-dwelling", "piped-into-yard", "public-tap", "borehole", "protected-well", "protected-spring"} tested = tested.assign(improved=tested["water_source"].isin(IMPROVED)) print(tested.groupby("improved")["ecoli_cfu_100ml"].agg( n="size", safe=lambda s: (s < 1).mean(), high=lambda s: (s > 100).mean() ).round(3))Amélioré n'est pas sûr, et c'est ici que cela se démontre — En R
households |> filter(!is.na(ecoli_cfu_100ml)) |> mutate(improved = water_source %in% improved) |> summarise(n = n(), safe = mean(ecoli_cfu_100ml < 1), high = mean(ecoli_cfu_100ml > 100), .by = improved)Amélioré n'est pas sûr, et c'est ici que cela se démontre
Source Testés Sûre Risque élevé Améliorée 625 61,1 % 3,8 % Non améliorée ou de surface 177 27,1 % 20,3 % Notes du présentateur
Le type de source prédit fortement la qualité, et 38,9 % des ménages sur une source améliorée ont malgré tout de l'E. coli détectable. La leçon 1 disait que l'échelon supérieur exige l'absence de contamination en plus d'une source améliorée ; voici le nombre qui montre pourquoi c'est une condition distincte et non une formalité.La valeur censurée, et pourquoi elle n'est pas manquante — En Python
points = pd.read_csv("water-point-monitoring-2024.v1.csv") readings = points["free_residual_chlorine_mgl"].dropna() censored = readings.astype(str).str.startswith("<") print(f"tested {len(readings)}, censored {censored.sum()}") print(readings[censored].unique())Notes du présentateur
Le registre des points d'eau mesure le chlore sur le terrain, et le kit a une limite de détection.La valeur censurée, et pourquoi elle n'est pas manquante — En R
points |> filter(!is.na(free_residual_chlorine_mgl)) |> count(censored = startsWith(free_residual_chlorine_mgl, "<"))La valeur censurée, et pourquoi elle n'est pas manquante — En Python
numeric = pd.to_numeric(readings, errors="coerce") # <-- the silent one print(f"after coercion: {numeric.isna().sum()} became NaN") in_range = readings[~censored].astype(float).between(0.2, 0.5) print(f"in target range, over all tested: {in_range.sum() / len(readings):.1%}") print(f"in target range, dropping censored: {in_range.mean():.1%}")Notes du présentateur
811 visites portent une lecture et 271 d'entre elles sont la chaîne<0.1. C'est une mesure : la vraie valeur se situe entre zéro et la limite de détection. Ce n'est pas une valeur manquante, et les deux choses en lesquelles on la transforme habituellement sont fausses toutes les deux.La valeur censurée, et pourquoi elle n'est pas manquante — En R
points |> filter(!is.na(free_residual_chlorine_mgl), !startsWith(free_residual_chlorine_mgl, "<")) |> summarise(in_range = mean(between(as.numeric(free_residual_chlorine_mgl), 0.2, 0.5)))La valeur censurée, et pourquoi elle n'est pas manquante
- 30,9 % contre 46,5 % — Supprimer les lectures censurées relève la conformité apparente à la cible de chloration de près…
Notes du présentateur
30,9 % contre 46,5 %. Supprimer les lectures censurées relève la conformité apparente à la cible de chloration de près de seize points, et le fait dans une seule direction — toute valeur censurée est un échec, si bien que les retirer ne retire que des échecs.pd.to_numeric(..., errors="coerce")le fait en silence et ressemble à une correction de type.Que faire d'une valeur censurée
- Classer plutôt que moyenner — La cible est une plage, si bien qu'une lecture censurée répond parfaitement à la question…
- Substituer la limite, ou sa moitié — s'il vous faut vraiment une moyenne
- Rapporter la part sous la limite comme un nombre à part entière — 33,4 % de ces lectures sont sous le seuil de…
Notes du présentateur
Trois options, par ordre de préférence pour cet indicateur. Classer plutôt que moyenner. La cible est une plage, si bien qu'une lecture censurée répond parfaitement à la question :<0.1est en dessous de 0,2 et donc non conforme. La conformité se calcule sur les 811 lectures sans aucune substitution. Substituer la limite, ou sa moitié, s'il vous faut vraiment une moyenne. Dites laquelle vous avez employée —LD/2est la convention courante — et rapportez à combien de valeurs elle s'est appliquée. Rapporter la part sous la limite comme un nombre à part entière. 33,4 % de ces lectures sont sous le seuil de détection, ce qui est un constat sur la pratique de chloration et non une gêne dans les données.Que faire d'une valeur censurée — En Python
compliant = pd.to_numeric(readings.where(~censored), errors="coerce").between(0.2, 0.5) result = pd.DataFrame({ "readings": [len(readings)], "below detection": [censored.sum()], "in 0.2-0.5 range": [compliant.sum()], "compliance": [f"{compliant.sum() / len(readings):.1%}"], }) print(result)Que faire d'une valeur censurée — En R
# The denominator is every reading, including the ones below the limit.Que faire d'une valeur censurée
- Ne laissez jamais une limite de détection rétrécir un dénominateur — C'est la façon la plus courante dont un rapport de…
Notes du présentateur
Ne laissez jamais une limite de détection rétrécir un dénominateur. C'est la façon la plus courante dont un rapport de qualité de l'eau devient optimiste, et elle survit à la relecture parce que l'arithmétique sur les valeurs restantes est juste.L'incohérence entre champs qu'il faut nommer — En Python
treats = households["water_treated_at_home"] print(f"treat at home: {treats.sum()}") print(f" of which no chlorine reading: {(treats & ~chlorine).sum()}")L'incohérence entre champs qu'il faut nommer — En R
households |> filter(water_treated_at_home) |> count(no_reading = is.na(free_residual_chlorine_mgl))Notes du présentateur
533 ménages déclarent traiter leur eau et n'ont aucune mesure de chlore. Le cours sur le nettoyage a établi de quoi il s'agit — une absence corrélée à la chose mesurée — et la conséquence ici est précise : supprimer ces lignes retire des ménages qui traitent leur eau plus souvent que la moyenne, si bien que le sous-échantillon testé n'est pas représentatif de l'enquête dont il vient.Rapportez-le comme un bloc à part — Exemple (suite)
Water quality E. coli, point of collection 802 households tested (33.4% of survey) Safe <1 CFU/100 mL 53.6% Low 1-10 21.6% Moderate 11-100 17.3% High >100 7.5% Improved sources: 61.1% safe (n=625) Unimproved and surface: 27.1% safe (n=177) Free residual chlorine, water points 811 visits tested (30.8% of visits) Below detection limit (<0.1 mg/L) 33.4% counted as non-compliant Within 0.2-0.5 mg/L target 30.9% Quality was tested on a subsample and does not share the denominator of the access indicators above. 533 households reporting home treatment have noRapportez-le comme un bloc à part — Exemple (suite)
chlorine reading, so the tested subsample over-represents untreated water.Notes du présentateur
Son propre bloc, ses propres dénominateurs, sa propre limite. Un chiffre de qualité placé dans un tableau de chiffres d'accès sera lu contre leur dénominateur, et rien dans la mise en page ne l'en empêchera.La suite
- Tout ce qui précède est une coupe transversale : ce qui était vrai le jour où quelqu'un est passé.
Notes du présentateur
Tout ce qui précède est une coupe transversale : ce qui était vrai le jour où quelqu'un est passé. L'unité suivante porte sur le registre qui visite les mêmes points d'eau douze fois, et sa première conséquence est de transformer un taux de fonctionnalité en trois.