cassionAnalyse de données

Leçon 6 sur 8

Unité · Le service est une année, non une journée

Tari n'est pas en panne

Les puits protégés tournent à 74,7 % en saison humide et à 36,4 % en saison sèche. Les forages à pompe manuelle ne bougent presque pas. Le champ d'état ne distingue pas ces deux défaillances, la séquence des visites le peut.

PythonR120 minStandards SphèreNorme humanitaire fondamentale (CHS)Objectifs de développement durable (ODD)

Un seul état, plusieurs défaillances différentes

functional_status dit qu’un point ne fonctionne pas. Il ne dit pas si la pompe est cassée, si la nappe a baissé, si le comité a cessé de percevoir les redevances, ou si la communauté est partie. Ces cas appellent quatre réponses différentes et l’une d’elles n’est pas une réparation.

La séquence des visites peut les séparer, parce qu’ils ont des formes différentes dans le temps.

Forme sur l’année Ce que c’est Que faire
À l’arrêt les mêmes mois chaque année, en service entre-temps Saisonnier — la source se tarit Approfondir, ou prévoir une alternative de saison sèche
À l’arrêt une fois, puis en service après un intervalle Panne — réparée Regarder la longueur de l’intervalle
À l’arrêt et le reste jusqu’à la fin Abandonné ou en attente de pièces Déterminer lequel ; ce n’est pas la même chose
En service et à l’arrêt à répétition Chronique — sous-entretenu ou surchargé Problème de gestion, non d’ouvrage

Le signal saisonnier, et là où il n’est pas

import pandas as pd

points = pd.read_csv("water-point-monitoring-2024.v1.csv", parse_dates=["visit_date"])
WORKING = {"functional", "partially-functional"}
DRY_MONTHS = {1, 2, 3, 11, 12}

points["dry_season"] = points["visit_date"].dt.month.isin(DRY_MONTHS)
points["ok"] = points["functional_status"].isin(WORKING)

print(points.groupby("dry_season")["ok"].agg(["mean", "size"]).round(3))
library(dplyr)

points |>
  mutate(dry = lubridate::month(visit_date) %in% c(1, 2, 3, 11, 12),
         ok = functional_status %in% c("functional", "partially-functional")) |>
  summarise(functionality = mean(ok), n = n(), .by = dry)

67,8 % en saison sèche contre 79,4 % en saison humide. Onze points d’écart, qu’un rapport comparant une évaluation de mars à une de juillet lirait comme un effondrement ou comme un redressement selon le sens de la soustraction.

Ventilez maintenant par type de source, où se trouve réellement le constat.

seasonal = (
    points.groupby(["source_type", "dry_season"])["ok"].mean().unstack()
)
seasonal.columns = ["wet", "dry"]
seasonal["swing"] = seasonal["wet"] - seasonal["dry"]
print((seasonal * 100).round(1).sort_values("swing", ascending=False))
points |>
  summarise(functionality = mean(ok), .by = c(source_type, dry)) |>
  tidyr::pivot_wider(names_from = dry, values_from = functionality)
Type de source Humide Sèche Écart
Puits protégé 74,7 % 36,4 % 38,3
Source protégée 84,8 % 41,0 % 43,8
Forage à pompe manuelle 75,0 % 76,1 % −1,1
Borne de réseau 95,9 % 98,8 % −2,9

Deux types de source portent tout l’effet saisonnier et deux ne bougent pas du tout. Les puits peu profonds et les sources puisent dans une nappe qui baisse ; un forage descend en dessous et un réseau dispose d’un réservoir. Les onze points de moyenne étaient un mélange d’un effet de quarante points et d’aucun effet.

C’est là le constat. Les forages de ce parc n’ont pas de problème saisonnier et les puits en ont un, si bien qu’un programme de saison sèche qui réhabilite des forages réhabilite le mauvais ouvrage.

Classer chaque point à partir de sa propre séquence

def classify(group):
    group = group.sort_values("visit_date")
    failures = group.loc[~group["ok"]]
    if failures.empty:
        return "never failed"
    if not group["ok"].tail(3).any() and len(group) >= 3:
        return "down at year end"
    if failures["dry_season"].all():
        return "seasonal"
    return "intermittent"

shape = points.groupby("water_point_id").apply(classify, include_groups=False)
print(shape.value_counts())
points |>
  arrange(visit_date) |>
  summarise(
    shape = case_when(
      all(ok) ~ "never failed",
      !any(tail(ok, 3)) & n() >= 3 ~ "down at year end",
      all(dry[!ok]) ~ "seasonal",
      TRUE ~ "intermittent"
    ), .by = water_point_id) |>
  count(shape)
Forme Points
Jamais défailli 82
Intermittent 80
Saisonnier 46
À l’arrêt en fin d’année 34

L’ordre des tests compte et c’est un jugement, non un détail. Un point qui ne défaille qu’en mois secs et qui est encore à l’arrêt en décembre satisfait les deux règles. Tester la permanence d’abord l’appelle abandonné ; tester la saisonnalité d’abord l’appelle saisonnier. Ce code teste la permanence d’abord, parce qu’un point qui n’a pas tourné depuis trois visites est un problème opérationnel maintenant quelle qu’en soit la cause — et parce que la lecture de décembre est celle sur laquelle un plan de janvier s’écrit.

Notez l’ordre employé. Deux analystes avec le même fichier et des ordres différents produiront des effectifs différents et auront tous deux raison.

La valeur d’état qui ressemble à la réponse sans l’être

Le registre comporte un état abandoned, et il est tentant de l’employer directement.

ever_abandoned = points.loc[points["functional_status"].eq("abandoned"),
                            "water_point_id"].nunique()
print(f"points ever recorded as abandoned: {ever_abandoned}")
print(f"points down at the end of the year: 34")
points |> filter(functional_status == "abandoned") |>
  summarise(points = n_distinct(water_point_id))

Dix-sept points sont enregistrés comme abandonnés lors d’une visite ; trente-quatre sont encore à l’arrêt en fin d’année. L’état est le jugement d’un enquêteur porté à une visite, et la séquence est une preuve. La moitié des points qui ne reviennent jamais n’ont jamais été étiquetés abandonnés, parce que l’enquêteur qui les a vus en juillet n’avait aucun moyen de savoir qu’ils seraient encore à l’arrêt en décembre.

Dérivez l’état de l’historique là où vous le pouvez, et employez l’état enregistré comme recoupement. Un champ qu’un enquêteur remplit à partir d’une observation unique ne peut pas porter une information qui n’existe qu’à travers plusieurs observations.

La durée d’arrêt est une statistique de gestion

down = points.loc[~points["ok"] & points["days_since_breakdown"].notna()]
print(down.groupby("management")["days_since_breakdown"].agg(
    median="median", visits="size"
).sort_values("median"))
points |>
  filter(!ok, !is.na(days_since_breakdown)) |>
  summarise(median_days = median(days_since_breakdown), n = n(), .by = management)
Gestion Jours d’arrêt médians à la visite
Opérateur privé 29
Service public 32
Géré par une ONG 48
Comité communautaire 68

Un point en panne sous comité communautaire est à l’arrêt depuis plus de deux fois plus longtemps qu’un point sous opérateur privé. C’est le seul nombre du registre qui désigne une action plutôt qu’un ouvrage — la différence tient à une chaîne d’approvisionnement en pièces et à un fonds d’entretien, non à la pompe.

Méfiez-vous des trente-sept lignes où un point est enregistré comme fonctionnel et porte malgré tout un days_since_breakdown : c’est la réponse du mois précédent reportée. Filtrez sur l’état, non sur la présence du champ.

Rapportez les formes, non le seul taux

Water point failure, 2024, 242 points

  Never failed                82    33.9%
  Intermittent                80    33.1%
  Seasonal (dry months only)  46    19.0%
  Down at year end            34    14.0%

  Seasonality is concentrated in protected wells (74.7% wet, 36.4% dry) and
  protected springs (84.8% wet, 41.0% dry). Handpump boreholes and piped
  schemes show no seasonal effect.

  Points classified as down at year end if not working at any of the last
  three visits; seasonal if every recorded failure fell in a dry month.
  Permanence tested before seasonality.

Quatre effectifs et la règle qui les a produits. Un pourcentage de fonctionnalité unique enverrait un budget de réhabilitation vers les mauvais quarante-six points, car ils n’ont pas besoin d’être réhabilités — il leur faut une alternative de saison sèche, et leurs pompes vont bien.

La suite

Chaque nombre de cette leçon est calculé sur les visites qui ont été faites. Un district a manqué un tiers de ses tournées en saison des pluies, et les points manqués n’étaient pas un tiers au hasard — c’est la leçon suivante, et la raison pour laquelle sa fonctionnalité semble s’améliorer quand les routes se ferment.

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.