pediatricsstatisticslmsgrowth-chartsdeveloper-guide

LMS Box-Cox Growth Charts Explained: The Math Behind Pediatric Anthropometry

· 6 min read · Martin Hejda

CDC and WHO growth charts — the grids that pediatricians use to track children’s height, weight, and head circumference from birth through adolescence — look simple: a few curved lines representing percentiles, with measurements plotted against them.

The mathematical machinery behind those lines is more sophisticated than the visualization suggests. It’s called the LMS method, and understanding it is essential for developers building applications that work with pediatric body measurements.


The problem LMS solves

For adult populations, many body measurements follow approximately normal distributions. The bell curve applies reasonably well. Height in adult males clusters around a mean, tapers symmetrically, and the normal distribution captures the population’s shape adequately.

Pediatric measurements don’t behave this way. Weight-for-age, BMI-for-age, and some other pediatric measures are strongly right-skewed: most children cluster at relatively low values, but a long tail extends toward very high values (due to obesity in the population). A normal distribution fit to this data systematically underestimates the tail and misclassifies children at the extremes.

The second problem is that pediatric measurements change continuously with age. You can’t define “normal weight” for a child without specifying their age — the normal distribution parameters shift monthly in infancy, and more slowly through adolescence. There isn’t one distribution to fit; there are hundreds, one for each age point.

The LMS method solves both problems simultaneously.


The three parameters

LMS stands for three age-specific, sex-specific parameters:

L (lambda): The Box-Cox transformation power. Controls how skewed the distribution is and which transformation normalizes it.

M (mu): The median — the 50th percentile value at this age and sex.

S (sigma): The coefficient of variation — a measure of the spread of values around the median.

These three parameters completely describe the distribution at any given age (in monthly increments, from birth through 20 years). They are published in table form by CDC and WHO, available as downloadable datasets.


The Box-Cox transformation

The Box-Cox family of transformations normalizes skewed data. For a given power λ (lambda):

y_transformed = (y^λ - 1) / λ    for λ ≠ 0
y_transformed = log(y)            for λ = 0

When λ = 1, no transformation occurs (the distribution is already symmetric). When λ < 1, the transformation compresses the right tail, pulling the distribution toward symmetry. When λ = 0, the transformation is logarithmic (the distribution is approximately log-normal).

For pediatric BMI-for-age, L is typically negative (ranging approximately from -2 to 0), indicating strong right-skew that requires a power transformation greater than log to normalize.

The key property: after applying the Box-Cox transformation with the correct λ for the age and sex, the transformed variable is approximately normally distributed. Standard normal statistics then apply.


Computing z-scores

Given a measurement value X for a child at a specific age and sex, the corresponding z-score (and therefore percentile) is computed using the LMS parameters for that age and sex:

z-score = ((X / M)^L - 1) / (L × S)    for L ≠ 0
z-score = log(X / M) / S                for L = 0

This formula looks complex, but it has a simple interpretation: it’s the Box-Cox transformation of the ratio X/M (measurement relative to median), normalized by the coefficient of variation S.

The resulting z-score is interpreted like any standard normal z-score:

  • z = 0: at the median (50th percentile)
  • z = 1.645: at the 95th percentile
  • z = -1.645: at the 5th percentile
  • z = 2.0: at the 97.7th percentile

And the percentile is simply Φ(z) where Φ is the standard normal CDF.


A working example in Python

Here’s how LMS calculations work for a real measurement:

from scipy import stats
import math

def lms_zscore(measurement_value: float, L: float, M: float, S: float) -> float:
    """
    Calculate z-score using LMS method.
    
    measurement_value: the observed measurement (height in mm, weight in kg, etc.)
    L: Box-Cox power (lambda)
    M: Median for this age/sex
    S: Coefficient of variation
    """
    if abs(L) < 1e-6:
        # L ≈ 0: use log transformation
        return math.log(measurement_value / M) / S
    else:
        return ((measurement_value / M) ** L - 1) / (L * S)

def zscore_to_percentile(z: float) -> float:
    """Convert z-score to percentile (0–100)."""
    return stats.norm.cdf(z) * 100

# Example: 7-year-old girl, height = 1200mm (120cm)
# CDC LMS parameters for female stature at age 84 months (7 years):
# These are approximate values — actual LMS tables provide exact parameters
L = 1.0      # height is approximately normally distributed
M = 1215.0   # median height at 84 months for girls (mm), approximate
S = 0.0415   # coefficient of variation, approximate

height_mm = 1200.0  # measured height

z = lms_zscore(height_mm, L, M, S)
percentile = zscore_to_percentile(z)
print(f"Height z-score: {z:.2f}")
print(f"Height percentile: {percentile:.1f}th")

# Output approximately:
# Height z-score: -0.30
# Height percentile: 38.4th

This tells you: a 7-year-old girl who is 120cm tall is at approximately the 38th percentile for height — below average, but within the normal range.


Going in the other direction: predicted value from percentile

You can also compute the expected measurement at a given percentile for a given age and sex:

def percentile_to_measurement(percentile: float, L: float, M: float, S: float) -> float:
    """
    Given a percentile, compute the expected measurement value using LMS parameters.
    """
    z = stats.norm.ppf(percentile / 100)  # convert percentile to z-score
    
    if abs(L) < 1e-6:
        return M * math.exp(S * z)
    else:
        return M * (1 + L * S * z) ** (1 / L)

# Expected height at the 95th percentile for a 7-year-old girl:
p95_height = percentile_to_measurement(95, L, M, S)
print(f"95th percentile height for 7-year-old girl: {p95_height:.0f}mm ({p95_height/10:.1f}cm)")

This is how LMS-based prediction APIs generate expected measurements: given age, sex, and the relevant LMS table, compute the expected median measurement and the 95% prediction interval bounds. No anchors are needed because the LMS model is itself a complete generative model of the population distribution.


CDC vs. WHO: different LMS parameters

CDC and WHO publish separate LMS tables, calibrated to different reference populations and different growth philosophies (descriptive vs. prescriptive). In practice:

  • WHO tables (0–5 years): LMS parameters derived from the Multicentre Growth Reference Study (MGRS) — healthy children in optimal conditions across 6 countries.
  • CDC tables (2–19 years): LMS parameters derived from NHANES survey data — US children across the actual population distribution.

When integrating a pediatric body measurement API, the underlying LMS tables determine which reference standard is being used. For international applications, WHO tables are generally preferred; for US clinical contexts, the convention is WHO for under-2, CDC for 2–19.


Why this matters for developers

If you’re building a pediatric health application that displays growth chart data or body measurement predictions:

  1. Display percentiles, not raw z-scores. Parents and clinicians understand “75th percentile” intuitively; “z-score 0.67” is opaque.

  2. Specify which reference. “In the 75th percentile” is incomplete; “75th percentile on the WHO growth standard” is a meaningful clinical statement.

  3. Use age in months for infants. Growth changes so rapidly in the first two years that age in years isn’t precise enough. LMS tables are indexed in months.

  4. Don’t display predictions as measurements. A LMS-based predicted height for a 7-year-old is the expected median, not a measured value. Label it “expected” or “typical for this age” — not “your child’s height.”

  5. Handle the clinical range. Percentiles below 3 or above 97 are clinically significant thresholds (approximately ±2 standard deviations). An application displaying growth data should flag these ranges, not just display them.


The LMS method is elegant mathematics in service of a practical clinical goal: converting a measurement of one child at one age into a meaningful statement about where that child stands relative to their peers. Understanding the math doesn’t change how you’d use the output — but it helps you communicate the output correctly and build the logic that handles edge cases appropriately.

Try DimensionsPot

Free tier — 100 requests/month, no credit card required.

Get API on RapidAPI