Lesson 2 of 8
Unit · The ladder is not the source
The boolean that halves a coverage figure
62.0% of households have an improved sanitation facility and 43.2% have basic service. The difference is one column — whether the latrine is shared — and the hygiene ladder has an equivalent that costs it half again.
Sanitation has the same shape as water
An improved facility is necessary and not sufficient, and the field that makes the difference is a boolean nobody looks at twice.
| Rung | Definition |
|---|---|
| Safely managed | Improved, not shared, and excreta safely disposed of or treated |
| Basic | Improved, not shared with other households |
| Limited | Improved but shared |
| Unimproved | Pit latrine without a slab, hanging latrine, bucket |
| Open defecation | No facility |
IMPROVED_SANITATION = {
"flush-to-sewer", "flush-to-septic", "vip-latrine", "pit-latrine-with-slab",
}
improved = households["sanitation_facility"].isin(IMPROVED_SANITATION)
shared = households["shared_sanitation"]
print(f"improved facility: {improved.mean():.1%}")
print(f"basic (not shared): {(improved & ~shared).mean():.1%}")
print(f"limited (shared): {(improved & shared).mean():.1%}")
improved_sanitation <- c("flush-to-sewer", "flush-to-septic",
"vip-latrine", "pit-latrine-with-slab")
households |>
mutate(service = case_when(
sanitation_facility == "open-defecation" ~ "open defecation",
!sanitation_facility %in% improved_sanitation ~ "unimproved",
shared_sanitation ~ "limited",
TRUE ~ "basic"
)) |>
count(service) |>
mutate(share = n / sum(n))
| Rung | Households | Share |
|---|---|---|
| Basic | 1,038 | 43.2% |
| Limited (shared) | 452 | 18.8% |
| Unimproved | 594 | 24.7% |
| Open defecation | 319 | 13.3% |
62.0% have an improved facility and 43.2% have basic service. Eighteen points sit on a latrine that meets the construction standard and is shared with another household.
Why sharing costs a rung
Because the reason for the standard is use, not construction. A shared latrine is used less at night, less by women and girls, and less by children — which is where the health benefit is. The classification follows the behaviour, not the concrete.
This is the same argument as the thirty minutes. In both ladders the second condition exists because a facility that is technically adequate and practically avoided produces no health outcome, and an indicator that counted it would report progress that nobody experienced.
Open defecation is the number to lead with
od = households["sanitation_facility"].eq("open-defecation")
by_district = households.groupby("district")["sanitation_facility"].apply(
lambda s: s.eq("open-defecation").mean()
)
print(f"{od.sum()} households, {od.mean():.1%}")
print((by_district * 100).round(1))
households |>
summarise(open_defecation = mean(sanitation_facility == "open-defecation"),
n = n(), .by = district)
13.3%, and it is the one WASH figure that a non-specialist reads correctly without being told the definition. Lead with it, disaggregate it, and note that the district field in this file is written six ways — the cleaning course dealt with that, and grouping without normalising splits the worst district in four.
The hygiene ladder, and the observation that carries it
Basic hygiene service requires a handwashing facility with water and soap available. Three rungs, and the survey answers all three, because the enumerator observed rather than asked.
has_facility = households["handwashing_facility"].ne("no-facility")
soap = households["soap_observed"]
print(f"any facility: {has_facility.mean():.1%}")
print(f"basic (facility + soap): {(has_facility & soap).mean():.1%}")
print(f"limited (no soap): {(has_facility & ~soap).mean():.1%}")
print(f"no facility: {(~has_facility).mean():.1%}")
households |>
mutate(hygiene = case_when(
handwashing_facility == "no-facility" ~ "no facility",
soap_observed ~ "basic",
TRUE ~ "limited"
)) |>
count(hygiene) |>
mutate(share = n / sum(n))
| Rung | Households | Share |
|---|---|---|
| Basic — facility and soap | 819 | 34.1% |
| Limited — facility, no soap | 811 | 33.7% |
| No facility | 773 | 32.2% |
67.8% have somewhere to wash their hands and 34.1% have soap there. The hygiene ladder loses half its top rung to a single observed item, which is a larger drop than either of the other two ladders.
Observation is why this is trustworthy. Reported handwashing runs far above
observed handwashing everywhere it has been measured, because the question has an
obviously correct answer. The column here is what the enumerator saw, and the
column name should say so — soap_observed, not soap.
The three ladders cannot be averaged
summary = pd.DataFrame({
"ladder": ["Drinking water", "Sanitation", "Hygiene"],
"at_least_basic": [0.547, 0.432, 0.341],
"denominator": ["all households"] * 3,
})
print(summary)
tibble::tribble(
~ladder, ~at_least_basic,
"Drinking water", 0.547,
"Sanitation", 0.432,
"Hygiene", 0.341
)
Three numbers on the same denominator, which is unusual and worth noticing — they can legitimately be shown side by side. What they cannot be is combined. There is no “WASH coverage” indicator, and a mean of the three would describe no household: a household with basic water, shared sanitation and no soap is not “58% covered”, it is a household with one of three services.
Where a single composite is demanded, publish the three and the count of households that have all three, which is a real quantity and usually a sobering one.
What comes next
The ladders classify. They do not say how much water arrived, how long the queue was, or whether the water was safe to drink. The next two lessons are the measurements Sphere adds on top — and the first of them has two unit errors hiding in it.