Creating a Pediatrics ADVS ADaM

Introduction

This article describes creating a vital signs ADaM for pediatric clinical trials. This package creates SD scores (z-scores) and percentiles among children and adolescents for various body measures, such as Height, Weight, Body Mass Index (BMI), Weight-for-Length, and Head Circumference. Among adults, standard cut-points can be used (e.g., a BMI >= 30 for obesity), but because of the large changes that occur during growth and development, these measures are typically expressed relative to other children of the same sex and age. For example, the CDC classifies obesity as a BMI-for-age >= 95th percentile (a z-score of 1.645), while the WHO cut point is a weight-for-length >= 3 SDs among children under 2 years of age.

We advise you first consult the {admiral} Creating a BDS Finding ADaM vignette. The programming workflow around creating the general set-up of an ADVS using {admiral} functions is the same. We focus on only the pediatric-specific steps here to avoid repeating information and maintaining the same content in two places.

Note: All examples assume CDISC SDTM and/or ADaM format as input unless otherwise specified.

Programming Workflow

Metadata

Once the required packages have been loaded, the first step is preparing the metadata.

library(admiral)
library(admiralpeds)
library(dplyr, warn.conflicts = FALSE)
library(lubridate)
library(rlang)
library(stringr)

Here, we have made some default decisions regarding which metadata to use in the package. But you are free to replace these with any other source you prefer (as long as you keep the structure of the dataframe consistent - as expected by our downstream functions). For example, you might want to use a different age range selection of WHO metadata (such as including WHO reference data for between ages 5-19 years) or the International Obesity Task Force reference data.

The selection of growth reference files we included and used as metadata for this package are as follows:

Reference Files for Parameters by Age

As growth rates and patterns differ by age and sex, reference values differ by these two characteristics.

For the various measures, we use the WHO reference data for children <2 years of age (<730.5 days) and CDC growth charts for children >=2 years of age (>=730.5 days). This is in accord with the CDC recommendation. The reference data for both charts include the LMS parameters, in which L is the Box-Cox transformation for normality, M is the median, and S is the coefficient of variation.

So, the first step is combining the metadata for each measure and ensuring sex and age are shown consistently. We separate WHO and CDC metadata for weight-based derivations due to (1) the WHO adjustment (restricted application of the LMS method) and (2) the CDC extended method for high BMIs.

Additionally, as the CDC growth charts only offer monthly age intervals, on the advice of a retired CDC expert in this area, we added interpolation code in our template to have a record for each day of age. When converting the CDC months into days (30.4375 days in a month), we rounded to the nearest whole number of days. Relying on the monthly charts will result in less accurate calculations, particularly for weight and height values among children who are 2 years of age. If you would prefer to rely solely on the monthly chart here, then you could remove the rounding and the call to the derive_interp_records() function.

We do this pre-processing of the metadata using the following code for BMI as an example:

data(WHO_bmi_for_age_boys)
data(WHO_bmi_for_age_girls)
data(cdc_bmiage)

who_bmi_for_age <- who_bmi_for_age_boys %>%
  mutate(SEX = "M") %>%
  bind_rows(who_bmi_for_age_girls %>%
    mutate(SEX = "F")) %>%
  # Keep patients < 2 yrs old
  filter(Day < 730.5) %>%
  rename(AGE = Day) %>%
  # AGEU is added in metadata, required for derive_params_growth_age()
  mutate(AGEU = "DAYS") %>%
  arrange(AGE, SEX)

cdc_bmi_for_age <- cdc_bmiage %>%
  mutate(
    SEX = case_when(
      SEX == 1 ~ "M",
      SEX == 2 ~ "F",
      TRUE ~ NA_character_
    ),
    # Ensure first that Age unit is "DAYS"
    AGE = round(AGE * 30.4375),
    AGEU = "DAYS"
  ) %>%
  # Interpolate the AGE by SEX so that we get CDC metadata by day instead of
  # month in the same way as WHO metadata
  derive_interp_records(
    by_vars = exprs(SEX),
    parameter = "BMI"
  ) %>%
  # Keep patients >= 2 yrs till 20 yrs - Remove duplicates for 730 Days old which
  # must come from WHO metadata only
  filter(AGE >= 730.5 & AGE <= 7305) %>%
  arrange(AGE, SEX)

Here is how the first records of the WHO metadata for BMI now look:

AGE AGEU SEX L M S
0 DAYS F -0.0631 13.3363 0.09272
0 DAYS M -0.3053 13.4069 0.09560
1 DAYS F 0.0362 13.3185 0.09360
1 DAYS M -0.1867 13.3976 0.09597
2 DAYS F 0.1355 13.3006 0.09448
2 DAYS M -0.0681 13.3883 0.09634
3 DAYS F 0.2347 13.2828 0.09535
3 DAYS M 0.0505 13.3791 0.09672
4 DAYS F 0.3340 13.2649 0.09623
4 DAYS M 0.1690 13.3698 0.09709

Similarly, for the CDC metadata:

AGE AGEU SEX L M S P95 Sigma
731 DAYS F -0.9889765 16.42119 0.0854252 19.10324 1.572355
731 DAYS M -2.0093806 16.57332 0.0805634 19.33432 1.376857
732 DAYS F -0.9913446 16.41898 0.0853985 19.10024 1.573310
732 DAYS M -2.0075801 16.57162 0.0805343 19.33062 1.378115
733 DAYS F -0.9937126 16.41677 0.0853719 19.09724 1.574265
733 DAYS M -2.0057797 16.56992 0.0805053 19.32693 1.379372
734 DAYS F -0.9960806 16.41456 0.0853453 19.09424 1.575220
734 DAYS M -2.0039792 16.56821 0.0804762 19.32323 1.380629
735 DAYS F -0.9984486 16.41235 0.0853187 19.09124 1.576175
735 DAYS M -2.0021787 16.56651 0.0804471 19.31954 1.381887

For BMI in the CDC metadata, a dispersion parameter (Sigma) is used to calculate BMI percentiles and z-scores above the sex- and age-specific 95th percentile (P95).

The above example only shows the parameter BMI, but this follows other parameters like weight and height. We can combine WHO and CDC metadata for height as a WHO adjustment is unnecessary. Note that the head circumference parameter metadata comes only from the WHO reference data, as CDC provides no equivalent for children > 2 years of age.

Reference Files for Weight by Length/Height

WHO provides additional reference data for weight-for-length (recumbent length), instead of age. Again, we combined the metadata for sex to create a single reference file.

There are also weight-by-height files available, but upon advice from a retired CDC expert in this area, we chose only to include the weight-for-length files, as once children can stand, BMI by age is a more appropriate measure to use. However, in our template, we give the metadata values variable a generic name, HEIGHT_LENGTH, so that any user could equally choose to pass weight-for-height files if preferred.

We do this using the following code:

data(who_wt_for_lgth_boys)
data(who_wt_for_lgth_girls)

who_wt_for_lgth <- who_wt_for_lgth_boys %>%
  mutate(SEX = "M") %>%
  bind_rows(who_wt_for_lgth_girls %>%
    mutate(SEX = "F")) %>%
  mutate(HEIGHT_LENGTHU = "cm") %>%
  rename(HEIGHT_LENGTH = Length)

Here is how the metadata now looks for a body length of 65cm:

HEIGHT_LENGTH L M S SEX HEIGHT_LENGTHU
65 -0.3521 7.2666 0.08223 M cm
65 -0.3833 7.0812 0.09119 F cm

Initial ADVS Set-up

The following steps are to read in the source data, merge ADSL variables, and derive the usual ADVS analysis variables. The {admiral} Creating a BDS Finding ADaM vignette gives detailed guidance on all these steps.

The only difference here would be the parameter level values, which now include the pediatrics-specific parameters as needed - for example:

PARAMCD PARAM PARAMN PARCAT1 PARCAT1N
BMISDS BMI-for-age z-score 9 Subject Characteristic 1
BMIPCTL BMI-for-age percentile 10 Subject Characteristic 1

Derive Additional Variables for Anthropometric indicators

To compare against the reference files, we need to know each child’s age and length/height at the time of each vital signs assessment. So, these need to be first derived as variables in your ADVS.

Remember to ensure that the unit of these matches that of the reference files metadata you are comparing against, i.e. “days” for age and “cm” for length/height in our examples here.

Derived Variables for Parameters by Age

A variable for current analysis age could be achieved using the following code (you might first need to derive BRTHDT if not already available in ADSL, including any partial date imputation rules if those are possible in your study data collection rules):

# Calculate Current Analysis Age AAGECUR and unit AAGECURU
advs <- advs %>%
  derive_vars_duration(
    new_var = AAGECUR,
    new_var_unit = AAGECURU,
    start_date = BRTHDT,
    end_date = ADT
  )

Here is how these age variables look:

USUBJID BRTHDT ADT AAGECUR AAGECURU
01-701-1023 2010-08-05 2012-07-22 718 DAYS
01-701-1023 2010-08-05 2012-08-05 732 DAYS
01-701-1023 2010-08-05 2012-08-27 754 DAYS
01-701-1023 2010-08-05 2012-09-02 760 DAYS
01-701-1023 2010-08-05 2012-07-22 718 DAYS
01-701-1023 2010-08-05 2012-08-05 732 DAYS
01-701-1023 2010-08-05 2012-08-27 754 DAYS
01-701-1023 2010-08-05 2012-09-02 760 DAYS
01-701-1023 2010-08-05 2012-07-22 718 DAYS
01-701-1023 2010-08-05 2012-08-05 732 DAYS

Derived Variables for Weight by Length/Height

Similar to the above, a variable for current analysis length/height could be achieved using the following code:

# Derive Current HEIGHT/LENGTH at each time point Temporary variable
advs <- advs %>%
  derive_vars_merged(
    dataset_add = advs,
    by_vars = c(get_admiral_option("subject_keys"), exprs(AVISIT)),
    filter_add = PARAMCD == "HEIGHT" & toupper(VSSTRESU) == "CM",
    new_vars = exprs(HGTTMP = AVAL, HGTTMPU = VSSTRESU)
  )

Derive Additional Parameters for Anthropometric indicators

Now, we get to the most important section, which shows how to create new records for each derived pediatrics parameter. Only specific examples are included here, but you’ll find more parameters in the example template script referenced at the bottom of this vignette.

Derived Parameters by Age

Parameters for BMI-for-age z-scores and percentiles could be achieved using the derive_params_growth_age() function as follows:

## Derive Anthropometric indicators (Z-Scores/Percentiles-for-Age) based on Standard Growth Charts
## For BMI by Age
advs <- advs %>%
  slice_derivation(
    derivation = derive_params_growth_age,
    args = params(
      sex = SEX,
      age = AAGECUR,
      age_unit = AAGECURU,
      parameter = VSTESTCD == "BMI",
      analysis_var = AVAL,
      set_values_to_sds = exprs(
        PARAMCD = "BMISDS",
        PARAM = "BMI-for-age z-score"
      ),
      set_values_to_pctl = exprs(
        PARAMCD = "BMIPCTL",
        PARAM = "BMI-for-age percentile"
      )
    ),
    derivation_slice(
      filter = AAGECUR < 730.5,
      args = params(
        who_correction = TRUE,
        meta_criteria = who_bmi_for_age
      )
    ),
    derivation_slice(
      filter = AAGECUR >= 730.5,
      args = params(
        bmi_cdc_correction = TRUE,
        meta_criteria = cdc_bmi_for_age
      )
    )
  )

If only a z-score or percentile were needed, you leave out one of the set_values_to_ arguments.

Once again, it is essential to remember the importance of the metadata being supplied to the above function via the meta_criteria argument. Our default case from this package uses WHO reference data for children < 2 years of age and CDC reference data for children >= 2 years of age. As the user, you can use other (e.g., International Obesity Task Force) reference data files created with the LMS method.

For BMI only, you’ll notice from the code above that we have set bmi_cdc_correction = TRUE. This is because we used the CDC extended percentiles (> 95th percentile) to monitor high BMI values here. If you left this argument as default FALSE value, only the 2000 CDC Growth Chart for BMI would be used from the CDC metadata. In 2022, the CDC released extended percentiles (and z-scores) for BMIs > 95th percentile because the original CDC method did not accurately characterize very high BMIs.

For all weight-based parameters (including BMI), you’ll also see in the code above that we have set who_correction = TRUE. This comes from the WHO Child Growth Standards Guidelines (pages 301-304). This correction is made because the WHO used the LMS method only for weight-based measures for which the calculated z-scores were between -3 and +3; this correction allows for the calculation of more extreme z-scores. If you are not using the WHO metadata (or WHO metadata for length or head circumference), you could leave who_correction = FALSE, and only the usual LMS method would be applied. WHO recommends using this correction for all parameters that involve weight (including BMI).

Here is how these newly derived parameters look:

USUBJID AAGECUR AAGECURU PARAMCD PARAM AVAL
01-701-1023 718 DAYS BMISDS BMI-for-age z-score 0.3796861
01-701-1023 718 DAYS BMIPCTL BMI-for-age percentile 64.7910777
01-701-1023 732 DAYS BMISDS BMI-for-age z-score -0.0155017
01-701-1023 754 DAYS BMISDS BMI-for-age z-score 0.1112091
01-701-1023 760 DAYS BMISDS BMI-for-age z-score 0.2718150
01-701-1023 732 DAYS BMIPCTL BMI-for-age percentile 49.3815944
01-701-1023 754 DAYS BMIPCTL BMI-for-age percentile 54.4274727
01-701-1023 760 DAYS BMIPCTL BMI-for-age percentile 60.7117857

For height parameters by age, it should be noted that the WHO growth chart we use for children <2 years of age refers to body length, whereas the CDC growth chart for children >=2 years of age refers to height. This is explained thoroughly in the next section.

Derived Weight by Length/Height

The following code is for Weight by Length/Height z-score and percentile for children < 2 years of age, which assumes the “HEIGHT” parameter is always collected as recumbent length.

You’ll notice the derive_params_growth_height() function used is very similar to that used above, but now passing in height or length instead of age.

advs <- advs %>%
  restrict_derivation(
    derivation = derive_params_growth_height,
    args = params(
      sex = SEX,
      height = HGTTMP,
      height_unit = HGTTMPU,
      meta_criteria = who_wt_for_lgth,
      parameter = VSTESTCD == "WEIGHT",
      analysis_var = AVAL,
      who_correction = TRUE,
      set_values_to_sds = exprs(
        PARAMCD = "WGTHSDS",
        PARAM = "Weight-for-length/height Z-Score"
      ),
      set_values_to_pctl = exprs(
        PARAMCD = "WGTHPCTL",
        PARAM = "Weight-for-length/height Percentile"
      )
    ),
    filter = AAGECUR < 730.5
  )

We consulted with a pediatrician with extensive experience working with length and height data, who advised that analysts rarely know whether standing height or recumbent length was measured. It depends mainly on the age at which the child can stand (on average, two years) and the preference of the measurer.

If you have strict CRF (case report form) guidelines that standing height (rather than body length) was measured, you could add 0.7 cm to these values to approximate the child’s body length.

Remaining ADVS Set-up

The {admiral} Creating a BDS Finding ADaM vignette covers all remaining steps, such as merging the parameter-level values, variables, and analysis flags.

Example Scripts

ADaM Sourcing Command
ADVS admiral::use_ad_template("ADVS", package = "admiralpeds")