Back to the lesson·Lesson 3 of 8·Who is a case
The case definitions, and the clause everyone drops
The same deck as the downloads, rendered as a page. Start the slideshow to present it full screen — arrow keys or a click advance one slide, Escape leaves.
What this lesson covers
- The definitions
- The implementation that is wrong
- The implementation that is right
- Check that the parts sum to the whole
- The MUAC definitions, on the register
- What a definition does not say
- What comes next
Speaker notes
Severe, moderate and global acute malnutrition, the cut-offs the sector agreed on, and the oedema clause that makes every "count below the threshold" implementation wrong.The definitions
Term Weight-for-height z MUAC Oedema SAM — severe acute malnutrition below -3 below 115 mm present MAM — moderate acute malnutrition -3 to below -2 115 to below 125 mm — GAM — global acute malnutrition below -2 below 125 mm present Speaker notes
Three terms, and they nest.The definitions
- GAM is SAM plus MAM — It is not a separate condition; it is the total burden of acute malnutrition, and it is the…
- Each row is a complete definition on its own — A child is SAM by weight-for-height, or by MUAC, or by oedema — the…
- The oedema column is an OR, not an AND — A child with bilateral pitting oedema is severe whatever their measurements say
Speaker notes
GAM is SAM plus MAM. It is not a separate condition; it is the total burden of acute malnutrition, and it is the figure the IPC phases and the 15% emergency threshold are defined against. Two structural points before any code. Each row is a complete definition on its own. A child is SAM by weight-for-height, or by MUAC, or by oedema — the criteria are alternatives, not requirements. Lesson 4 is entirely about how far the first two disagree. The oedema column is an OR, not an AND. A child with bilateral pitting oedema is severe whatever their measurements say. That clause is one term in a boolean expression and it is the term most often left out.The implementation that is wrong — In Python
# WRONG: the definition everybody writes first sam_wrong = (smart["whz"] < -3).sum()The implementation that is wrong — In R
# WRONG sum(smart$whz < -3, na.rm = TRUE)Speaker notes
It is wrong in two ways at once. It drops the oedematous children, andna.rmor a null-propagating comparison silently drops the children with no z-score — so both the numerator and the denominator are quietly different from what the label claims.The implementation that is right — In Python
import pandas as pd analysable = smart[smart["whz"].between(-5, 5)].copy() oedema = analysable["oedema"] == True analysable["sam"] = (analysable["whz"] < -3) | oedema analysable["mam"] = (analysable["whz"].between(-3, -2, inclusive="left")) & ~oedema analysable["gam"] = analysable["sam"] | analysable["mam"] n = len(analysable) for label in ["sam", "mam", "gam"]: print(f"{label.upper():4} {analysable[label].sum():>4} / {n} " f"{analysable[label].mean():.1%}")The implementation that is right — In R
analysable <- smart |> filter(between(whz, -5, 5)) analysable <- analysable |> mutate(sam = whz < -3 | oedema, mam = whz >= -3 & whz < -2 & !oedema, gam = sam | mam) analysable |> summarise(across(c(sam, mam, gam), list(n = sum, rate = mean)), n = n())The implementation that is right
- MAM excludes oedema explicitly — An oedematous child whose z-score falls in the moderate band is severe, not moderate,…
- The bounds are half-open —
-3 <= z < -2 - The denominator is stated — 852, not 930 — the difference is children with no computable z-score and children outside…
Speaker notes
852 analysable children: GAM 14.9%, SAM 3.8%. Three details in that code are load-bearing. MAM excludes oedema explicitly. An oedematous child whose z-score falls in the moderate band is severe, not moderate, and without the& ~oedemathey would be counted in both — so SAM plus MAM would exceed GAM. The bounds are half-open.-3 <= z < -2. A child at exactly -3.0 is severe. Getting this wrong moves a handful of children and is the kind of thing two analysts discover they disagree on at the worst moment. The denominator is stated. 852, not 930 — the difference is children with no computable z-score and children outside the flagging bounds, and lesson 2 required you to report those three groups separately.Check that the parts sum to the whole — In Python
assert (analysable["sam"] & analysable["mam"]).sum() == 0, "a child is both" assert analysable["gam"].sum() == analysable["sam"].sum() + analysable["mam"].sum()Check that the parts sum to the whole — In R
stopifnot(sum(analysable$sam & analysable$mam) == 0, sum(analysable$gam) == sum(analysable$sam) + sum(analysable$mam))Speaker notes
Two assertions, and they catch the oedema mistake, the bound mistake and any future edit that breaks the nesting. Run them every time.The MUAC definitions, on the register — In Python
muac = pd.read_csv("muac-screening-artibonite-2024.v1.csv") measured = muac[muac["muac_mm"].notna()].copy() oed = measured["oedema"] == True measured["sam"] = (measured["muac_mm"] < 115) | oed measured["gam"] = (measured["muac_mm"] < 125) | oed print(f"n = {len(measured):,} GAM {measured['gam'].mean():.1%} " f"SAM {measured['sam'].mean():.1%}")Speaker notes
The screening register has MUAC and oedema and no weight or height, so it supports the MUAC-based definitions only.The MUAC definitions, on the register — In R
muac |> filter(!is.na(muac_mm)) |> summarise(n = n(), gam = mean(muac_mm < 125 | oedema), sam = mean(muac_mm < 115 | oedema))The MUAC definitions, on the register
- Name the measure in the indicator —
gam_muac_percentandgam_whz_percentare different indicators that both get…
Speaker notes
Name the measure in the indicator.gam_muac_percentandgam_whz_percentare different indicators that both get called "GAM", and the indicator design course's rule about putting the measure in the name is not pedantry here — the next lesson shows the two disagreeing on three-quarters of severe cases.- Name the measure in the indicator —
What a definition does not say
- Age — The definitions apply to children 6 to 59 months
Speaker notes
Three things the case definition deliberately leaves out, each of which someone will assume. Age. The definitions apply to children 6 to 59 months. Below six months the standards and the case definitions are different; above 59 months they do not apply at all. Check the age range before classifying.What a definition does not say — In Python
out_of_scope = ~analysable["age_months"].between(6, 59) print(f"{out_of_scope.sum()} children outside 6-59 months")What a definition does not say — In R
sum(!between(analysable$age_months, 6, 59), na.rm = TRUE)What a definition does not say
- Admission — A case definition says who is malnourished
- Severity within severe — SAM with complications needs inpatient care; SAM without needs outpatient
Speaker notes
Admission. A case definition says who is malnourished. Whether they are admitted depends on the programme's protocol, which may use one criterion, both, or MUAC only for community screening and weight-for-height at the site. The register'sadmission_criterionrecords what the site entered, not everything the child met. Severity within severe. SAM with complications needs inpatient care; SAM without needs outpatient. The definition does not distinguish them, and the appetite test and clinical signs that do are not in an anthropometric dataset.What a definition does not say
A case definition is a classification rule, not a treatment decision. Confusing the two produces a caseload figure that does not match any programme's admissions and an argument nobody can resolve from the data.
What comes next
- You can classify a child by either measure.
Speaker notes
You can classify a child by either measure. The next lesson puts the two measures on the same children and finds that they disagree on three-quarters of severe cases — and that the disagreement has a cause you can name in one number.