| Title: | Standardized Metrics Based on the CDC and WHO Growth Charts |
| Version: | 0.4.0 |
| Date: | 2026-08-30 |
| Description: | Calculation of sex- and age-standardized growth metrics using the LMS method (lambda-mu-sigma). The package includes functions for the CDC Growth Charts (cdc_z) and the WHO Charts (who_z). Because CDC recommends using the WHO Charts for children under 24 months and the CDC Charts among older children, there can be large differences at age 2.0 years. For example, a girl weighing 9.9 kg would be at the WHO 10th percentile on the day before her second birthday, but at the CDC 2nd percentile the following day. The 'gradual_z' function reduces the differences among 2- to 5-year-olds by taking a weighted average of the CDC and WHO z-scores. |
| License: | GPL-3 |
| Encoding: | UTF-8 |
| LazyData: | true |
| ByteCompile: | true |
| NeedsCompilation: | no |
| Depends: | R (≥ 3.5.0) |
| Imports: | data.table (≥ 1.15.0), stats, utils |
| Suggests: | tibble |
| Packaged: | 2026-08-30 14:51:44 UTC; davidfreedman |
| Author: | David Freedman |
| Maintainer: | David Freedman <DavidSFreedman@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-08-31 15:20:03 UTC |
cdcanthro: Sex- and Age-Standardized Metrics from the CDC and WHO Growth Charts
Description
Calculation of sex- and age-standardized growth metrics using the LMS method (lambda-mu-sigma). The function cdc_z(), previously named cdcanthro(), uses the 2000 CDC growth charts for children and adolescents with ages of >= 24.0 to < 240 months, to generate sex- and age-standardized z-scores and percentiles for weight, height, and body mass index (BMI). The output includes extended BMI z-scores for values above the 95th percentile to more accurately characterize children with very high BMIs.
Because CDC recommends the WHO Growth Standards for children under 2 years of age, there is also a function, who_z(), which computes sex- and age- (or length-) standardized metrics for children under five years of age.
There is also a function, gradual_z(), which smooths the discontinuities at age 2.0 resulting, in large part, from the use of different reference data. For example, a girl weighing 9.9 kg would be at the 10th percentile the day before her second birthday and at the 2nd percentile the following day.
Author(s)
David S Freedman
Maintainer: David S Freedman <DavidSFreedman@gmail.com>
Other contributors:
Carrie Daymont cdaymont@pennstatehealth.psu.edu [contributor]
Sample Anthropometric Data from NHANES
Description
A sample dataset of 7393 NHANES participants. Variables include height, weight, sex, and age in months. Note that months is given as the completed number of months.
Usage
data(NHanes)
Format
A data frame or data table with observations on multiple anthropometric variables.
CDC Growth Chart Reference Data (2000)
Description
LMS parameters (L, M, S) for age- and sex-standardized growth metrics.
Usage
data(cdc_ref_data)
Format
A data.table with columns for sex, age, and LMS values. Parameter names represent the first letter of weight, height, or BMI; this is followed by 'l', 'm', or 's' for the LMS parameters. 'mref' and 'sref' are the M and S parameters at 240 months of age. For sexn, '1' are boys and '2' are girls.
Source
https://www.cdc.gov/growthcharts/percentile_data_files.htm
Generate Sex- and Age-Standardized Weight, Height, and BMI Metrics From the CDC Growth Charts
Description
Generate z-scores, percentiles, and other sex- and age-standardized metrics
for weight, height, and BMI based on the 2000 CDC growth charts. For children with obesity,
the extended BMI z-scores and percentiles are calculated. The calculations are restricted
to children with ages between 24.0 (inclusive) and < 240 months. Note that cdc_z
is the preferred name; the previous name, cdcanthro,
will work but will be deprecated
The BMI metrics include z-scores and percentiles, along with newer metrics such as extended BMIz and percent of the 50th and 95th percentiles. The extended BMI metrics are used for BMIs above the 95th percentile.
Usage
cdc_z(data, age, wt, ht, bmi, sex_code, all_vars = FALSE)
cdcanthro(data, age, wt, ht, bmi, sex_code, all_vars = FALSE)
Arguments
data |
a |
age |
Age in months, specified as accurately as possible. Required. |
wt |
Weight in kg. Optional. If omitted, WAZ-related metrics will be missing. |
ht |
Height in cm. Optional. If omitted, HAZ-related metrics will be missing. |
bmi |
BMI in |
sex_code |
Numeric coding for sex if other than 1 for boys and 2 for girls. Optional. |
all_vars |
logical. If |
Details
Argument values can be quoted (e.g., age='agemos') or unquoted (age = agemos).
The function expects the child's sex to be named sex; either upper- or
lower-case is fine. You cannot have both SEX and sex in your data.
Values can be coded as boys/girls, b/g, male/female,
m/f, or 1/2. Character values can be in upper- or lower-case;
only the first character is considered. If sex is numeric and not coded as 1/2, specify this
in the optional sex_code argument; the code for boys is the first number.
For example, 0/1 coding would be specified as sex_code = 0:1 or sex_code =
c(0,1).
Weight is in kg, height is in cm, and BMI is in kg/m^2.
At least one of wt, ht, or bmi must be present. Metrics
that cannot be calculated from the supplied variables will be missing from the output,
and warnings will indicate the missing variables. For example,
supplying only wt returns WAZ but omits HAZ and BMI metrics from the output.
Age in months should be given as accurately as possible because the function
linearly interpolates between completed months of age. If ages are given as the
completed number of months (as in NHANES), add 0.5 because values such as 36 represent the
interval [36, 37) months. If the function detects integer numbers for all ages, it
will add 0.5 to all age values.
If age is in days, divide by 30.4375 so that a child who is 3672 days old would have an age in months of 120.641. For additional information on age, see the ‘See Also’ section below.
If all_vars = TRUE, all variables in the Freedman et al. paper will be
output, along with the L, M, and S values for each child and the value of
sigma for the half-normal distribution.
For children without obesity, BMI z-scores are calculated as
Z = ((BMI / M)^L - 1) / (L \times S)
where BMI is the child's BMI, L is the Box-Cox transformation
for normality for the child's sex and age, M is the median, and
S is the coefficient of variation. Reference data are the merged
LMS files from the CDC (see ‘See Also’).
For children with obesity, BMI percentiles are calculated as
90 + 10 \times \Phi((\mathrm{BMI} - \mathrm{p95}) / \sigma)
where p95 is the sex- and age-specific 95th percentile, \Phi is the
CDF of a N(0, 1) distribution (pnorm()), and sigma is the scale parameter
of the half-normal distribution. Values of sigma for children with obesity
are based on formulas in Wei et al.
The class of the returned object matches the class of the input data:
a data.frame input returns a data.frame, a data.table
returns a data.table, and a tibble (tbl_df) returns a tibble
(provided the tibble package is installed; otherwise a
data.frame is returned with a warning).
Value
A data.table, data.frame, or tibble containing the original
data and various weight, height, and BMI metrics.
If your dataset has ages that are < 24 or >= 240 months, these rows will be present in the output
dataset. All calculated metrics will be NA. If this is not what you want, delete these rows before
running cdc_z().
Variables in output:
waz,haz,bmizSex- and age-standardized z-scores for weight, height, and BMI.
bmizis based on the 2000 CDC growth charts for non-obese children and extended BMIz for obese children.wap,hap,bmipSex- and age-standardized percentiles for weight, height, and BMI.
bmipis based on the extended BMI metrics for obese children.mod_waz,mod_haz,mod_bmizModified z-scores.
orig_bmip,orig_bmizOriginal BMI percentiles and z-scores based solely on the LMS method.
orig_bmizandbmizare identical for children with BMI at or below the 95th percentile.p50,p95Sex- and age-specific 50th and 95th percentiles of BMI.
bmip95BMI expressed as a percentage of the 95th percentile; 120 percent is the lower threshold for severe obesity.
perc_medianPercent difference between the child's BMI and the median (50th percentile) BMI for the child's sex and age.
If all_vars = TRUE, additional BMI metrics described in Freedman et al.
are also returned. These express BMI as distance or percent distance from
the median. To convert to percent of the median, add 100 to the values.
Note
library(cdcanthro) will also load the data.table package.
Note that argument names can be quoted or unquoted: cdc_z(data, age, wt, ht, bmi),
and cdc_z(data, 'age', 'wt', 'ht', 'bmi') produce the same result.
Author(s)
David Freedman DavidSFreedman@gmail.com
References
Kuczmarski RJ, Ogden CL, Guo SS, Grummer-Strawn LM, Flegal KM, Mei Z, et al. 2000 CDC Growth Charts for the United States: methods and development. Vital and Health Statistics Series 11, Data from the National Health Survey 2002;11:1–190.
Wei R, Ogden CL, Parsons VL, Freedman DS, Hales CM. A method for calculating BMI z-scores and percentiles above the 95th percentile of the CDC growth charts. Annals of Human Biology 2020;47:514–21.
Freedman DS, Woo JG, Ogden CL, Xu JH, Cole TJ. Distance and Percent Distance from Median BMI as Alternatives to BMI z-score. British Journal of Nutrition 2019;124:1–8.
See Also
CDC LMS percentile data files: https://www.cdc.gov/growthcharts/percentile_data_files.htm
CDC extended BMI data files: https://www.cdc.gov/growthcharts/extended-bmi-data-files.htm
CDC SAS programs and more information on agemos in Table 1 of
https://www.cdc.gov/growth-chart-training/hcp/computer-programs/sas.html
Examples
data <- data.table::CJ(sex = 1:2, agem = 120.5, wtk = c(30, 65), htc = c(135, 144))
data[, let(bmi = wtk / (htc / 100)^2)]
out <- cdc_z(data, age = agem, wt = wtk, ht = htc, bmi = bmi)
round(out, 2)[1:5]
out <- cdc_z(data, age = agem, wt = wtk, ht = htc, bmi = bmi, all_vars = TRUE)
round(out, 2)[1:5]
d <- data.frame(sex = c(1, 2, 1, 2, 2),
age = c(141, 54, 217, 155, 52),
wt = c(57, 25, 72, NA, 17.7),
ht = c(143, 102, 166, 169, 105))
d <- cdc_z(d, age, wt, ht)
round(d, 2)
d <- data.frame(sex = c(0, 1, 0),
age = c(120.5, 60.5, 180.5),
wt = c(40, 25, 65))
d <- tibble::as_tibble(d)
cdc_z(d, age, wt, sex_code = 0:1) # note use of sex_code
head(NHanes) # age is given as completed months
d <- data.table::copy(NHanes)
head(cdc_z(d, agemos, wt, ht, bmi))[,.(seqn, agemos, waz, haz, bmiz)]
d[, let(agemos = agemos + 0.5)]
head(cdc_z(d, agemos, wt, ht, bmi))[,.(seqn, agemos, waz, haz, bmiz)]
# same Z-scores
Gradual WHO-to-CDC Transition Z-Scores
Description
Computes a smooth, gradually transitioning z-score for weight, height/length, and body mass index (BMI) that blends World Health Organization (WHO) and Centers for Disease Control and Prevention (CDC) growth reference z-scores across early childhood. These gradual z-scores use WHO z-scores up to 730 days (2.0 y) of age, CDC z-scores are used from 1826 days (5.0 y) of age onward, and a weighted average of the CDC and WHO z-scores between ages 2 and 5 years.
Gradual z-scores avoid the abrupt jump produced by switching directly from WHO to CDC
references at age 2 years as recommended by CDC. For example, under an abrupt
switch, a girl weighing 9.9 kg would be at the 10th percentile (WHO) the day
before her second birthday and at the 2nd percentile (CDC) the following day;
gradual_z avoids this discontinuity.
Usage
gradual_z(data,
agedays = agedays,
whowtz = who_waz,
cdcwtz = waz,
whohtz = who_lhaz,
cdchtz = haz,
whobmiz = who_bmiz,
cdcbmiz = bmiz)
Arguments
data |
A |
agedays |
Column for age in days. Note that all arguments can be either unquoted or quoted. |
whowtz |
WHO weight-for-age z-score. |
cdcwtz |
CDC weight-for-age z-score. |
whohtz |
WHO length/height-for-age z-score. |
cdchtz |
CDC height-for-age z-score. |
whobmiz |
WHO BMI-for-age z-score. |
cdcbmiz |
CDC BMI-for-age z-score. |
Details
Naming the arguments rather than relying on position is recommended.
However, if the input was created using cdc_z and
who_z, the function can be called as gradual_z(data). Argument values
can be quoted or unquoted.
For each row, age in days determines which of three regions applies:
-
agedays
\le730 (through 2 years): the WHO z-score is used directly. -
730 < agedays < 1826 (2 to 5 years): a weighted average of the WHO and CDC z-scores is used, with weights that shift linearly with age so the blended value equals the WHO z-score at 730 days and the CDC z-score at 1826 days.
-
agedays
\ge1826 (5 years and beyond): the CDC z-score is used directly.
This produces a transition that is continuous at both boundaries, avoiding the discontinuity that would result from switching abruptly from WHO to CDC references at a single cutoff age.
For ages between 2 and 5 years, the gradual z-score is a weighted average of the WHO and CDC z-scores:
Z_{gradual} = \frac{Z_{WHO} \times (5 - \mathrm{ageyears}) + Z_{CDC} \times (\mathrm{ageyears} - 2)}{3}
It should be noted that gradual z-scores from 2 through 5 years of age are not directly comparable to those based on other reference data, and researchers may need sensitivity analyses to facilitate comparison with other studies.
Further, because of the change in recumbent length to standing height at age 2.0 years, a discontinuity at this age will be present for grad_haz and grad_bmiz.
Value
An object of the same class as data (data.frame,
data.table, or tibble), containing all columns of data, plus:
grad_waz |
Gradual (blended) weight-for-age z-score. Added only if both
|
grad_haz |
Gradual (blended) height/length-for-age z-score. Added only if
both |
grad_bmiz |
Gradual (blended) BMI-for-age z-score. Added only if both
|
Each of the three gradual measures is checked independently. If neither column of a
pair is found, that measure is skipped with an informational message().
If only one column of a pair is found, that measure is skipped with a warning() instead.
If no complete pair is found, a warning() is issued.
In the output, each measure's columns are grouped together in the order CDC,
WHO, gradual (e.g. who_waz, waz, grad_waz), for weight,
then height, then BMI; any other original columns (including agedays) come first.
Author(s)
David Freedman DavidSFreedman@gmail.com
References
Daymont C, Hwang W, Paul IM, Shur N, Freedman DS (2025). "Creation and Evaluation of New Growth Charts With a Gradual Transition From WHO to CDC Values." Pediatrics, 156(3), e2025070697. doi:10.1542/peds.2025-070697
See Also
The original growth charts, tables, and R/Stata code are available at https://github.com/carriedaymont/gradual-who-cdc.
Examples
library(data.table)
dt <- data.table(
agedays = c(730, 1096, 1461, 1826), # 2, 3, 4, and 5 years of age
who_waz = c(0.9, 0.7, 0.6, 0.4),
waz = c(0.7, 0.5, 0.3, 0.3),
who_lhaz = c(-0.2, 0.1, 0.2, 0.1),
haz = c(-0.3, 0.0, 0.1, 0.2),
who_bmiz = c(1.0, 0.9, 0.8, 0.7),
bmiz = c(0.1, 0.5, 0.2, 0.2)
)
result <- gradual_z(dt, agedays, who_waz, waz, who_lhaz, haz, who_bmiz, bmiz)
result[, ageyears:= round(agedays/365.25,2)]
result
gradual_z(dt) # because column names match defaults
WHO Reference Data
Description
LMS parameters (L, M, S) in the WHO charts for age- and sex-standardized growth metrics.
Usage
data(who_ref_data)
Format
A data.table with columns for sex (sexn coded as 1 (boys) / 2 (girls)), age in days, and LMS values. Variable names represent the weight (wei), length or height (len), BMI (bmi), weight-for-length (wfl), or head circumference (headc). This prefix is followed by '_' and then by 'l', 'm', or 's' representing the LMS parameters.
Sexn is coded as '1' (boys) and '2' (girls).
In contrast to the sex- and age-standardized metrics, weight-for-length is weight standardized for sex and length; it represents the distribution of weights for a specific length.
The 'denom' variable indicates if the row represents age-standardization or len-standardization.
Source
https://www.who.int/toolkits/child-growth-standards/standards
Sex- and Age- (or Length-) Standardized Weight, Length/Height, BMI, and Head Circumference Metrics from the WHO Growth Charts
Description
Calculates z-scores and biologically-implausible-value (BIV) flags for weight-for-age, length/height-for-age, BMI-for-age, head-circumference-for-age, and weight-for-length, based on the WHO Child Growth Standards. The original data are returned together with the calculated metrics.
This function is included in the cdcanthro package because CDC recommends using (1) the CDC Growth Charts for children with ages (months) >=24 and < 240, and (2) the WHO Standards for children < 24 months of age. The use of these different charts, along with the change from recumbent length to standing height at age 2.0 y, creates discontinuities at age 2.0 years (730 days). https://www.cdc.gov/growth-chart-training/hcp/overview/recommended.html
Usage
who_z(data,
agedays = agedays,
wt = wt,
lenhei = lenhei,
bmi = bmi,
headc = headc,
sex_code = 1:2)
Arguments
data |
a data.frame or data.table of measurements. Must contain a
variable named |
agedays |
age in completed days. Note that all arguments can be unquoted (e.g., agedays = days) or quoted(agedays = 'days') If age is given as completed number of months (e.g., NHANES), add 0.5 months, multiply by 30.4375 to convert to agedays, and round to the nearest day. |
wt |
weight in kg. |
lenhei |
recumbent length (ages <= 730 days) or standing height (ages > 730 days), in cm. Note that there is an ~0.7 difference in M (median) between 730 and 731 days in the reference data, when the measurement changes from recumbent length to standing height |
bmi |
body mass index, kg/m^2. If not supplied but both |
headc |
head circumference in cm. |
sex_code |
Numeric coding for sex if other than 1 for boys and 2 for girls. Optional. |
Details
Column names can be given with or without quotation marks. Naming the arguments rather than
relying on position is recommended, e.g. who_z(d, agedays = age, wt = weight);
otherwise a value can silently land in the wrong slot.
Any absent measurement is skipped with a warning, and its z-score is not returned.
sex may be coded as boys/girls, male/female, or
1/2. Character values may be upper or lower case; only the first
character is used, so 'b' / 'g' is fine. If sex is numeric and not coded as 1/2, specify this
in the optional sex_code argument; the code for boys is the first number.
For example, 0/1 coding would be specified as sex_code = 0:1 or
sex_code = c(0,1).
Age in the WHO reference data goes from 0 to 1856 days. However, weight-for-length Z is calculated only for children <= 730 days of age with a length between 45 and 110 cm.
Value
A data.table containing the original data plus the calculated metrics. The age-based z-scores are calculated for children with an agedays between 0 and 1826 (inclusive). Weight-for-length z is calculated for children between 0 and 730 days.
WHO Z-scores:
-
who_waz: weight-for-sex/age -
who_lhaz: length (or height) for sex/age -
who_wflz: weight-for-length for sex -
who_bmiz: BMI-for-sex/age -
who_headcz: head circumference for sex/age
BIV flags, following the anthro package. Each 'f' (flag) is 1 if the z-score is outside the WHO plausibility range, 0 if inside, and NA if the z-score is NA:
-
fwaz: 1 ifwho_waz< -6 or > 5 -
flhaz: 1 ifwho_lhaz< -6 or > 6 -
fwflz: 1 ifwho_wflz< -5 or > 5 -
fbmiz: 1 ifwho_bmiz< -5 or > 5 -
fheadcz: 1 ifwho_headcz< -5 or > 5
Note that there doesn't appear to be any publications that justify these cutpoints. While values outside these ranges are outliers, the measurement may be correct.
Author(s)
David Freedman DavidSFreedman@gmail.com
References
World Health Organization, Department of Nutrition for Health and Development. WHO Child Growth Standards: Length/height-for-age, weight-for-age, weight-for-length, weight-for-height, and body mass index-for-age. Methods and Development. Geneva; 2006. https://apps.who.int/iris/handle/10665/43413
The reference LMS values are in the 'Expanded Percentile' files at doi:10.1111/j.1651-2227.2006.tb02378.x
See Also
cdc_z for standardized values based on the CDC growth charts
Examples
NHanes
d <- NHanes
d <- data.table::copy(NHanes)
d[, let(lenhei=ifelse(agedays<=730,length,ht))]
# lenhei is a composite of length and ht:
# it is length for 0 to <=730 days, but height for >730 days
d <- d[agedays <= 1826]
out <- who_z(d, agedays, wt, lenhei, bmi, headc)
head(out)
# verify that the function works for head circumference (small N)
temp <- NHanes[!is.na(headc)]
temp[, let(lenhei=ifelse(agedays<=730,length,ht))]
out <- who_z(temp, agedays, wt, lenhei, bmi, headc)
head(out)