# Technical notes — Nutrition programme dashboard

Technical documentation for the project *Nutrition programme dashboard*. The
notebook applies these decisions; this document is where they are argued. If a
figure on the dashboard is challenged, the answer is here.

- **Source dataset:** `muac-screening-artibonite-2024.v1.csv` (4 218 screening
  records, 12 communes, Jan–Dec 2024)
- **Analysis:** `notebooks/nutrition-dashboard.python.en.ipynb`
- **Decision informed:** which of twelve CMAM sites to reallocate RUTF stock to
  before the next quarter opens
- **Standards applied:** WHO growth standards (MUAC cut-offs), RBM

Every dataset on this platform is synthetic. No real child is described, and
these figures must not be cited as a real nutrition situation.

---

## 1. Indicator definitions

The dashboard prints this table beside the figures. A number and its definition
travel together, or the monthly argument about the denominator starts again.

| Indicator | Numerator | Denominator | Value |
| --- | --- | --- | ---: |
| GAM | MUAC < 125 mm **or** bilateral pitting oedema | Children with a MUAC measurement or a recorded oedema assessment | 8.5% |
| SAM | MUAC < 115 mm **or** bilateral pitting oedema | Children with a MUAC measurement or a recorded oedema assessment | 2.3% |
| Assessment rate | Children with MUAC or oedema recorded | Children screened | 0.997 district-wide |

**Disaggregation.** Valid at commune and month. Not valid below commune: the
register carries no site identifier finer than the commune, and a
commune-level rate presented as a site rate is a different claim.

**Oedema overrides anthropometry.** A child with bilateral pitting oedema is a
SAM case whatever the MUAC reads. This is why the numerator is a disjunction and
why the denominator counts a child with an oedema assessment but no MUAC.

**The denominator is not "children screened".** 14 children have neither a usable
MUAC nor a recorded oedema assessment. Counting them in the denominator would
report a lower prevalence for a reason that has nothing to do with nutrition.
The assessment rate exists so that exclusion is visible rather than silent.

---

## 2. What none of these is

**Programme coverage.** This register holds the children who came to be
screened, not the children who exist. Nothing here estimates what share of cases
the programme reaches — that needs a population denominator and a coverage
survey (SQUEAC/SLEAC). Any report using these figures must say so.

**A treatment outcome.** Cure, defaulter and death rates come from the treatment
register, not the screening register. The project's business case is about
stock allocation, and screening is the leading indicator for that.

---

## 3. Cleaning decisions

Each rule below corresponds to a documented defect in the dataset's
`knownIssues`. Effects are measured on the committed v1 file.

| # | Rule | Rationale | Effect |
| --- | --- | --- | ---: |
| 1 | MUAC < 40 → multiply by 10 | Values near 12.5 are centimetres entered in a millimetre field. Left alone, every one becomes a SAM case | 7 corrected |
| 2 | MUAC outside 80–220 mm → missing | Outside physiological range for the screened age band; not recoverable by a rule | 0 in v1 |
| 3 | `-99` → missing at read time | The register's own missing-value sentinel; read as a number it is a severe SAM case | applied at load |
| 4 | Oedema string codes harmonised (`Y`/`y`/`yes`/`true`/`TRUE` → true, and the negatives) | One site recorded `Y`/`N`. A boolean cast turns those into missing silently | — |
| 5 | Exact duplicate rows dropped | Re-submitted forms; an exact duplicate is not a second screening | 12 removed |

**Rule 1 is the one to defend.** Multiplying by ten is an inference, not an
observation. It is applied only below 40 mm — a threshold no plausible MUAC
reaches, so no genuine measurement is rewritten — and the count is printed by
the notebook so a reviewer can see how much of the caseload rests on it. Seven
records out of 4 218 do.

**Rule 2 sets to missing rather than dropping the row.** The child was screened;
only the measurement is unusable. Dropping the row would shrink the screening
count and overstate the assessment rate.

44 records still have no oedema assessment after rule 4. They are kept, and
counted in the denominator when a MUAC exists — a child with a MUAC of 130 mm and
no oedema check is not a case, and excluding them would inflate every rate.

Rows analysed after cleaning: **4 206**. Children assessed: **4 204**.

---

## 4. Why the dashboard shows an interval

The site ranking carries a 95% confidence interval, computed with the
Clopper–Pearson (exact binomial) method on the site's GAM cases and assessed
children.

Clopper–Pearson rather than the normal approximation because several sites have
fewer than 20 cases, where the normal interval is too narrow and can extend
below zero.

| Commune | Assessed | GAM | 95% CI |
| --- | ---: | ---: | --- |
| Anse-Rouge | 228 | 15.4% | 10.9 – 20.7% |
| Gros-Morne | 361 | 13.6% | 10.2 – 17.5% |
| L-Estère | 189 | 12.7% | 8.3 – 18.3% |
| Ennery | 261 | 8.8% | 5.7 – 12.9% |
| Terre-Neuve | 241 | 8.7% | 5.5 – 13.0% |
| Marmelade | 255 | 8.2% | 5.2 – 12.3% |
| Gonaïves | 767 | 7.3% | 5.6 – 9.4% |
| Saint-Michel | 332 | 7.2% | 4.7 – 10.6% |
| Desdunes | 196 | 7.1% | 4.0 – 11.7% |
| Saint-Marc | 630 | 7.1% | 5.3 – 9.4% |
| Verrettes | 297 | 6.7% | 4.2 – 10.2% |
| Dessalines | 447 | 5.4% | 3.5 – 7.9% |

**The ranking is not a queue.** Intervals from Ennery down to Dessalines overlap
one another substantially; the order among them is not supported by the
screening. Only three sites — Anse-Rouge, Gros-Morne, L-Estère — have a lower
bound above the district median rate, and those three are also the only ones
whose interval reaches the 15% emergency threshold.

**That set of three is the dashboard's answer**, not the top of the sorted list.
A manager allocating stock on rank order alone would treat the gap between
Ennery (8.8%) and Terre-Neuve (8.7%) as information. It is not.

---

## 5. Reporting-period handling

Screening runs 15 January to 13 December 2024. The first and last months are
therefore partial: December carries 156 screenings against a monthly average
near 375.

The dashboard **labels** partial months rather than dropping them or letting a
reader take the drop for a decline. A prevalence computed on a half-reported
month is precisely the failure this project exists to prevent, and hiding the
month would leave a manager wondering where the data went.

---

## 6. Data-quality panel

The panel that makes the dashboard trustworthy rather than impressive. It ranks
sites by confidence-interval width, so the manager sees which rates are too
imprecise to act on.

One commune stands clear on missing age: **Gros-Morne, 17.7%**, against 3–6%
everywhere else — a single campaign week with a misconfigured form. It does not
affect the MUAC indicator, which needs no age, and the dashboard says so rather
than dropping the site. Age matters only if the register is later used for a
weight-for-height analysis, where the length/height rule switches at 24 months.

---

## 7. Refresh and operating assumptions

- **Cadence.** The dashboard is rebuilt from the current export; the six-week
  reporting lag this project exists to remove is a process constraint, not a
  technical one.
- **Idempotence.** The whole pipeline is deterministic and re-runnable — rules 1
  to 5 applied to the same export always yield the same figures.
- **Failure mode to watch.** A site that stops submitting appears as fewer
  screenings, not as a flagged gap. Watch the monthly screening count per site
  alongside the rates.

---

## 8. Limitations

1. Screening is not a survey. Children brought to a screening are not a random
   sample of children, and self-selection plausibly runs toward the unwell.
   Prevalence here is not community prevalence.
2. MUAC and weight-for-height identify overlapping but different children. A GAM
   rate by MUAC is not comparable to a SMART survey GAM by WHZ; see the
   *SMART survey analysis* project.
3. No child-level linkage. A child screened twice in different months counts
   twice; the register has no follow-up identifier.
4. `-99` is the only sentinel handled. If a future export introduces another,
   rule 3 must be extended — it is an allow-list, not a heuristic.

---

## 9. Reproducing this analysis

```bash
pnpm examples:build
```

Then run `notebooks/nutrition-dashboard.python.en.ipynb`. It needs pandas, numpy
and scipy (for the exact binomial interval) and reads the CSV over HTTPS.

The dataset is versioned by filename and immutable; a correction ships as
`.v2.csv` with this document revised beside it.

---

## 10. Change log

| Date | Change |
| --- | --- |
| 2026-07-27 | First issue, against dataset v1. |
