Fenton 2025 electronic growth chart QA testing

Quality assurance testing of Fenton 2025 LMS parameter calculations to generate an electronic growth chart
Published

July 20, 2025

The 2025 Fenton third-generation growth charts for preterm infants was released recently and I’ve received a few requests to update tools, including an electronic growth chart that plots points on old school paper chart images. In practice, I use the charts built into the electronic health record, but since there will probably be some delay before they are released, it seemed worth the effort to update the tool.

While I’m confident in the numeric calculations (i.e., for Z-scores), when plotting points onto an image, it’s a good idea to do a bit of quality assurance testing to make sure it’s working properly. This post just has some code snippets that I used to create test points for QI.

Load packages

library(tidyverse)
library(knitr)
library(peditools)
library(ggh4x) # facet_grid2(), for multiple free scales using `independent`

Retrieve LMS parameters

First a bit of setup, including retrieving the table of LMS parameters. The peditools R package includes many chart LMS parameters, but exclude the Fenton 2013 or 2025 chart parameters, which are available here.

The LMS data looks like this (using WHO infant chart parameters as an eample):

lmsdata <- get_lmsdata()
lmsdata |> filter(chart == "who_2006_infant") |> head() |> knitr::kable()
chart age age_units gender measure measure_units L M S
who_2006_infant 0 months f head_circ cm 1 33.8787 0.03496
who_2006_infant 1 months f head_circ cm 1 36.5463 0.03210
who_2006_infant 2 months f head_circ cm 1 38.2521 0.03168
who_2006_infant 3 months f head_circ cm 1 39.5328 0.03140
who_2006_infant 4 months f head_circ cm 1 40.5817 0.03119
who_2006_infant 5 months f head_circ cm 1 41.4590 0.03102

Calculate anthropometric measurements

The goal is to calculate expected measurement values from a set of charts, anthropometric measures, ages, genders, and percentiles.

percentiles <- c(3, 10, 50, 90, 97)
measures <- c("weight", "head_circ", "length")
charts <- "fenton_2025" # not included in `peditools` package

df_list <- list()
for (c in charts) {
  for (s in c("m", "f")) {
    for (m in measures) {
      lms <- lmsdata |>
        filter(chart == c, gender == s, measure == m) |>
        unique() |> # in case of duplicate rows; shouldn't be present
        arrange(age)
      # cartesian product, expanding each age of lms to have percentiles
      d <- crossing(lms, data.frame(percentile = percentiles)) |>
        mutate (x = z_lms_to_x(qnorm(percentile / 100.0), L, M, S)) |> 
        # min, max, and even integer ages
        filter((age == min(lms$age)) | (age == round(age)) | age == max(lms$age))
      df_list[[paste0(c, "_", s, "_", m)]] <- d
    }
  }
}

df <- bind_rows(df_list) |> 
  transmute(
    chart, gender, measure, age,
    percentile = as.character(percentile),
    x = round(x, 1)
  )

Plot the generated measures

Test to see whether measure calculations look sensible.

df |> ggplot(aes(age, x, color = percentile)) +
  geom_point(size = 0.2) +
  geom_line() +
  facet_grid2(gender ~ measure, scales = "free_y", independent = "y") +
  theme_bw()

Plot points on “paper” chart

The electronic growth chart calculator I created for the Fenton 2013 chart uses growth data for each patient copied from an Excel spreadsheet and pasted into a web page input form field in a particular format. The following code recreates that format.

I updated the old Fenton 2013 PHP scripts to use new Fenton 2025 data and plotted the measures generated above onto the “paper” chart.

df_wide <- df |> 
  mutate(x = as.character(round(x, 1))) |> 
  pivot_wider(names_from = "measure", values_from = "x", values_fill = "") |> 
  select(chart, gender, age, weight, head_circ, length, percentile) |>
  arrange(chart, gender, percentile, age)

write.csv(df_wide, file = "~/Desktop/universal_test.csv", row.names = FALSE)

Excel spreadsheet to copy from

Paste into PHP web tool

Output generated by updated Electronic Growth Chart PHP script

Looking good: the points generated by calculation appear to overly the curves perfectly. Should be ready to deploy soon.