Skip to content

feat: Compute d2:zScore functions with WHO LMS method [DHIS2-22201] - #103

Open
enricocolasante wants to merge 1 commit into
mainfrom
DHIS2-22201-lms
Open

enricocolasante wants to merge 1 commit into
mainfrom
DHIS2-22201-lms

Conversation

@enricocolasante

@enricocolasante enricocolasante commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Compute d2:zScore* functions with the WHO LMS method DHIS2-22201

Problem

The z-score functions looked up 7 SD values per row (−3 SD … +3 SD, rounded to 1 decimal) and interpolated linearly between them. This had several issues:

  • Inaccurate results. Results differed from WHO Anthro by up to ~0.1, because the SD values are rounded and WHO's curve between SD points isn't a straight line.
  • Values beyond ±3 SD were always ±3.5. Anthro keeps counting past ±3.
  • d2:zScoreWFH used the wrong reference between 87 and 110 cm. It used weight-for-length values (meant for children measured lying down) up to 110 cm. When age is unknown, the usual convention is to switch to weight-for-height at 87 cm. Children 87–110 cm tall were compared against a reference 0.1–0.4 kg too light, so their z-scores came out too high.
  • Data errors:
    • the female WFH row at 112 cm was corrupted (an LMS parameter in place of −3 SD, with the other columns shifted)
    • boys' weight-for-age had no month 28, so d2:zScoreWFA(28, …, "m") threw an error
    • height-for-age at 24 months used the length table for girls but the height table for boys

Changes

Calculation (ZScore)

  • The formula: z-scores are computed from WHO's L, M and S parameters, the same way WHO Anthro does it: z = ((x / M)^L − 1) / (L · S).
  • Beyond ±3 SD: WHO's rule applies, so the z-score keeps counting in steps of the distance between the 2nd and 3rd SD instead of being capped at ±3.5.
  • Precision: calculations use Double (no float noise), results are rounded to 2 decimals, and 0.0 is returned instead of -0.0.

Tables (ZScoreTable): each row holds the WHO L, M, S parameters instead of 7 rounded SD values.

  • WFH: every 0.1 cm from 45.0 to 120.0 cm, with weight-for-length below 87 cm and weight-for-height from 87 cm.
    • Heights between two rows (e.g. 112.37 cm) blend L, M and S of the neighbouring rows, like Anthro.
    • Between 86.9 and 87.0 cm the length curve is extended from its last two rows, so the blend never mixes the two tables.
  • WFA: WHO monthly parameters for 0–60 months.
  • HFA: WHO monthly parameters for 0–60 months, with length-for-age below 24 months and height-for-age from 24 months for both sexes.
  • Cleanup: the old SD-table code is removed, including the public ZScoreTable.Entry (see the API dump).

Behaviour changes

  • Most results shift slightly, by up to ~0.1, and the corrected 87–110 cm WFH range shifts by up to ~0.4. Former whole numbers now usually have decimals (e.g. 0.02 instead of 0.0).
  • Values beyond ±3 SD are now real numbers (e.g. −4.71) instead of ±3.5. Program rules that check for exactly ±3.5 need updating.
  • WFH now accepts any height between 45 and 120 cm, not only values on a 0.1 cm step.
  • WFA/HFA still take whole months: fractional ages throw, as before.
  • API: ZScoreTable.Entry is removed and ZScoreTable.Lms is added.

Validation

  • Matches WHO Anthro to the hundredth for the reference cases. These include children over 2 measured standing, and infants (for the infants, Anthro was set to "measured standing", so the tests add its 0.7 cm adjustment).
  • Data sources: WHO growth standards LMS files (wflanthro, wfhanthro), as packaged with the WHO anthro R package, and WHO's monthly weight-for-age and length/height-for-age tables.
  • Tests cover:
    • the 87 cm and 24-month switches
    • heights between 0.1 cm rows
    • values beyond ±3 SD
    • the table limits
    • unknown parameters
    • the Anthro reference cases

@sonarqubecloud

sonarqubecloud Bot commented Oct 2, 2026

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant