cassionData Analysis

Back to the lessonLesson 4 of 8Who is a case

Where the two criteria disagree

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.

Slides · PDFSlides · PowerPoint

  1. Slide 1 / 22

    What this lesson covers

    • The question the register can answer
    • Compute both on the same children
    • The cross-tabulation
    • The disagreement is age
    • Which one is right?
    • What it means for your numbers
    • Report the pair, always
    • What comes next
    Speaker notes
    468 children severe by weight-for-height alone, 24 by MUAC alone, 142 by both. A concordance of 22.4%, and the whole of the disagreement is age — 31.4 months against 14.3.
  2. Slide 2 / 22

    The question the register can answer

    • Both admission criteria are in the case definitions and both are used in the field.
    Speaker notes
    Both admission criteria are in the case definitions and both are used in the field. Whether they find the same children is an empirical question, and it needs a register with both measurements on the same child. The CMAM admission register has exactly that: MUAC and weight-for-height at admission, for 1,100 episodes. This lesson is what it says.
  3. Slide 3 / 22

    Compute both on the same children — In Python

    import pandas as pd
    
    cmam = pd.read_csv("cmam-admissions-2024.v1.csv")
    cmam["whz"] = cmam.apply(whz, axis=1)          # from lesson 2
    
    both = cmam[cmam["whz"].notna()].copy()
    print(f"{len(both)} of {len(cmam)} admissions have both measurements")
    
    both["sam_whz"] = both["whz"] < -3
    both["sam_muac"] = both["muac_admission_mm"] < 115
  4. Slide 4 / 22

    Compute both on the same children — In R

    both <- cmam |> filter(!is.na(whz))
    
    both <- both |>
      mutate(sam_whz = whz < -3,
             sam_muac = muac_admission_mm < 115)
    Speaker notes
    1,028 of 1,100. The 72 without a height are the missing-height defect from the dataset's known issues, and they are the first finding: the two criteria do not have the same denominator even on the same register, because one needs two measurements and the other needs one.
  5. Slide 5 / 22

    The cross-tabulation — In Python

    table = pd.crosstab(both["sam_muac"], both["sam_whz"],
                        rownames=["SAM by MUAC"], colnames=["SAM by WHZ"])
    print(table)
  6. Slide 6 / 22

    The cross-tabulation — In R

    both |> count(sam_muac, sam_whz)
  7. Slide 7 / 22

    The cross-tabulation

    Not severe by WHZSevere by WHZ
    Not severe by MUAC394468
    Severe by MUAC24142
  8. Slide 8 / 22

    The cross-tabulation — In Python

    overlap = table.loc[True, True]
    union = table.values.sum() - table.loc[False, False]
    print(f"concordance {overlap / union:.1%} of {union} severe by either measure")
    Speaker notes
    Read the two bold cells. 468 children are severe by weight-for-height and not by MUAC. 24 are severe by MUAC and not by weight-for-height. 142 are severe by both.
  9. Slide 9 / 22

    The cross-tabulation — In R

    both |>
      summarise(overlap = sum(sam_muac & sam_whz),
                union = sum(sam_muac | sam_whz)) |>
      mutate(concordance = overlap / union)
  10. Slide 10 / 22

    The cross-tabulation

    • 22.4% — Of the children this register would call severe by one measure or the other, fewer than a quarter are called…
    Speaker notes
    22.4%. Of the children this register would call severe by one measure or the other, fewer than a quarter are called severe by both. That number is not an artefact of this dataset. Published comparisons find overlaps in the 10% to 40% range depending on the population, and a programme that has not measured its own is assuming one.
  11. Slide 11 / 22

    The disagreement is age — In Python

    mean_age = pd.Series({
        "MUAC only": both.loc[both["sam_muac"] & ~both["sam_whz"], "age_months"].mean(),
        "WHZ only": both.loc[both["sam_whz"] & ~both["sam_muac"], "age_months"].mean(),
        "Both": both.loc[both["sam_muac"] & both["sam_whz"], "age_months"].mean(),
    })
    print(mean_age.round(1))
  12. Slide 12 / 22

    The disagreement is age — In R

    both |>
      mutate(group = case_when(sam_muac & sam_whz ~ "both",
                               sam_muac ~ "muac only",
                               sam_whz ~ "whz only",
                               TRUE ~ "neither")) |>
      summarise(mean_age = mean(age_months), n = n(), .by = group)
  13. Slide 13 / 22

    The disagreement is age

    GroupMean age
    Severe by MUAC only14.3 months
    Severe by both15.6 months
    Severe by weight-for-height only31.4 months
  14. Slide 14 / 22

    The disagreement is age — In Python

    by_age = both.assign(
        band=pd.cut(both["age_months"], [5, 12, 24, 36, 60],
                    labels=["6-11", "12-23", "24-35", "36-59"])
    ).groupby("band")[["sam_muac", "sam_whz"]].mean()
    print((by_age * 100).round(1))
    Speaker notes
    There it is. The children MUAC finds and weight-for-height misses are half the age of the children weight-for-height finds and MUAC misses. The mechanism is simple and it is not a defect of either measure. MUAC grows with age: a healthy arm is about 135 mm at six months and about 155 mm at five years. A fixed 115 mm cut-off is therefore a much deeper deficit for a five-year-old than for an infant, so MUAC becomes progressively harder to fail as a child grows. Weight-for-height has no age term at all and applies equally across the range.
  15. Slide 15 / 22

    The disagreement is age — In R

    both |>
      mutate(band = cut(age_months, c(5, 12, 24, 36, 60),
                        labels = c("6-11", "12-23", "24-35", "36-59"))) |>
      summarise(across(c(sam_muac, sam_whz), mean), .by = band)
    Speaker notes
    Run that and the two lines cross. MUAC identifies more of the youngest children; weight-for-height identifies more of the oldest. A programme admitting on MUAC alone is running a younger caseload than one admitting on weight-for-height alone, and that is a clinical fact about who gets treated, not a measurement detail.
  16. Slide 16 / 22

    Which one is right?

    • MUAC predicts mortality at least as well, and better in several studies. If the purpose of admission is to treat…
    • Weight-for-height is the survey standard, and the IPC thresholds and international comparisons are built on it. If…
    • MUAC is operationally feasible at community level. Weight-for-height is not.
    Speaker notes
    Neither, and the question is the wrong one. What each is for differs. The sector's answer is to use both, admit on either, and discharge on the one the child was admitted on. Which is sensible clinically and creates the analytical problem this lesson exists for.
  17. Slide 17 / 22

    What it means for your numbers

    • Caseload depends on the criterion — Admitting on either criterion gives a caseload of 634 severe children here; on MUAC…
    • Prevalence is not comparable across measures — A GAM by MUAC and a GAM by weight-for-height are different indicators
    • Discharge must use the admission criterion — A child admitted on MUAC and discharged on weight-for-height may be…
    • A programme changing criterion breaks its own series — A caseload that rises 40% when the protocol changes is a…
    Speaker notes
    Four consequences, and each is a thing you will be asked about. Caseload depends on the criterion. Admitting on either criterion gives a caseload of 634 severe children here; on MUAC alone, 166; on weight-for-height alone, 610. Those are not estimates of the same quantity. Prevalence is not comparable across measures. A GAM by MUAC and a GAM by weight-for-height are different indicators. Lesson 6 shows what that does when a figure is read against the 15% threshold. Discharge must use the admission criterion. A child admitted on MUAC and discharged on weight-for-height may be discharged before recovering, or held long after. The CMAM protocol is explicit and registers routinely are not. A programme changing criterion breaks its own series. A caseload that rises 40% when the protocol changes is a definitional break, and the indicator design course's rule about versioning a definition applies exactly.
  18. Slide 18 / 22

    What it means for your numbers — In Python

    for name, mask in [("either", both["sam_muac"] | both["sam_whz"]),
                       ("muac only", both["sam_muac"]),
                       ("whz only", both["sam_whz"])]:
        print(f"{name:10} caseload {mask.sum():>4}")
  19. Slide 19 / 22

    What it means for your numbers — In R

    both |> summarise(either = sum(sam_muac | sam_whz),
                      muac = sum(sam_muac), whz = sum(sam_whz))
  20. Slide 20 / 22

    Report the pair, always — Example

    Severe acute malnutrition, admissions 2024, n = 1,028 with both measurements
    
      By MUAC (<115 mm)                166
      By weight-for-height (z < -3)    610
      By either                        634
      By both                          142    (22.4% of those severe by either)
    
      Children severe by MUAC alone average 14.3 months; by weight-for-height alone,
      31.4 months. The two criteria identify overlapping but substantially different
      caseloads, and the difference is age.
    Speaker notes
    Six lines. They pre-empt the question, they name the mechanism, and they stop somebody comparing a MUAC-based caseload to a weight-for-height-based one as though the gap were a change in nutrition.
  21. Slide 21 / 22

    What comes next

    • You can classify children and you know what the classification depends on.
    Speaker notes
    You can classify children and you know what the classification depends on. The next unit turns to whether the survey that produced the measurements can be believed at all — the SMART plausibility report, which is where the standard deviation of 1.22 from lesson 2 finally gets read.
  22. Slide 22 / 22

    Where this goes next

    Read the full lesson, with runnable code Back to the lesson