The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
Tools to plot Tanner pubertal stage measurements against the Dutch 1997 references and express them as standard deviation scores (SDS).
This package implements the stage line diagram method described in:
van Buuren, S. and Ooms, J.C.L. (2009). Stage line diagram: An age-conditional reference diagram for tracking development. Statistics in Medicine, 28(11), 1569–1579. https://doi.org/10.1002/sim.3567
@article{vanbuuren-2009-1,
title = {Stage line diagram: An age-conditional reference diagram for tracking development},
author = {{van Buuren}, S. and Ooms, J.C.L.},
year = {2009},
journal = {Statistics in Medicine},
volume = {28},
number = {11},
pages = {1569--1579},
doi = {10.1002/sim.3567}
}tanner has two identities. plot_stadia()
(and its helpers plot_stadia_general(),
plot_stadia_lines(), plot_stadia_data()),
interpolate_trajectory(),
find_stage_segments(), calculate_sds() and
tnologo() are the supported general-purpose library API:
pure functions (or plotting functions taking explicit arguments) that
anyone can library(tanner) and call interactively, covered
by unit tests and this README’s walkthrough.
plotter(), plotterpro() and
upload_tryCatch_pro() are rApache request handlers for the
existing webapp deployment (see Production deployment
(rApache)): they read
GET/POST/FILES globals injected
by rApache at request time and aren’t meant to be called interactively
from R. They’re held to a lower bar — no unit tests, not part of this
walkthrough — and kept exported only so the existing deployment keeps
working.
This README covers three things:
For the original production deployment (Apache + rApache), see Production deployment (rApache) at the end.
# from GitHub
install.packages("devtools")
devtools::install_github("growthcharts/tanner")Or from a local clone:
install.packages("devtools")
devtools::install(".")
# or load without installing, from the repo root
devtools::load_all(".")plot_stadia() draws a patient’s pubertal stage
trajectory against the Dutch 1997 reference percentiles. Here’s a boy’s
genital stage followed from age 8 to 16.5 (patient 7 from the original
pubertal demo data.txt demo dataset used to produce
genital.pdf/boy.pdf in this package’s source
history):
library(tanner)
mydata <- data.frame(
id = 1,
age = c(
8.10,
8.37,
8.58,
8.87,
9.12,
9.34,
9.58,
9.86,
10.11,
10.36,
10.59,
10.86,
11.15,
11.34,
11.59,
11.88,
12.13,
12.36,
12.61,
13.13,
13.63,
14.16,
14.66,
16.13,
16.53
),
sex = "M",
gen = c(
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
2,
2,
3,
3,
3,
3,
4,
4
),
phb = NA,
tv = NA,
bre = NA,
phg = NA,
men = NA
)
plot_stadia(
data = mydata,
persons = 1,
type = c(TRUE, FALSE, FALSE),
plotline = c(TRUE, FALSE, FALSE),
title = "Puberty Plot (patient 7, pubertal demo data.txt)",
padid = FALSE
)
The shaded bands mark the 1%, 5% and 10% earliest/latest percentiles for the genital reference curve; the thick line is this patient’s own trajectory.
calculate_sds(age, stage, type) converts an age +
observed pubertal stage into a standard deviation score, by
interpolating the Dutch 1997 reference percentiles
(nl1997). type is one of "gen",
"phb", "tv", "bre",
"phg" or "men".
library(tanner)
# a single measurement: a 12-year-old boy at genital stage 3
calculate_sds(age = 12, stage = 3, type = "gen")[1] 1.11
bre and phg stages are coded 1–5,
men is coded 1 (not yet) or 2 (menarche reached) — use them
as-is:
girls <- read.csv("inst/webapps/pubertypro/demo.csv")
names(girls) <- tolower(names(girls))
girls$age <- as.numeric(gsub(",", ".", girls$age))
girls <- girls[girls$sex == "F", ]
girls$SDS_bre <- round(calculate_sds(girls$age, girls$bre, "bre"), 2)
girls$SDS_phg <- round(calculate_sds(girls$age, girls$phg, "phg"), 2)
girls$SDS_men <- round(calculate_sds(girls$age, girls$men, "men"), 2)gen and phb are coded 1–5 and can be used
as-is. tv (testicular volume) is recorded in
ml, typically read off a 12-point Prader orchidometer
(1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20 or 25), but
calculate_sds() and plot_stadia() accept
any ml value directly — including readings between
Prader beads (e.g. 11 or 16 ml) — by interpolating between the
surrounding reference percentiles. No recoding needed:
boys <- read.csv("inst/webapps/pubertypro/demo.csv")
names(boys) <- tolower(names(boys))
boys$age <- as.numeric(gsub(",", ".", boys$age))
boys <- boys[boys$sex == "M", ]
boys$SDS_gen <- round(calculate_sds(boys$age, boys$gen, "gen"), 2)
boys$SDS_phb <- round(calculate_sds(boys$age, boys$phb, "phb"), 2)
boys$SDS_tv <- round(calculate_sds(boys$age, boys$tv, "tv"), 2)Note: an age/stage combination can fall outside the plottable range (the SDS is effectively ±Inf for an extreme outlier, e.g. genital stage 1 at age 19).
calculate_sds()still returns a value, butplot_stadia()will silently skip drawing a point for it, since it falls off the chart.
calculate_sds() is vectorized, so a whole data.frame
with both sexes can be processed in one go. Calling it on a stage column
that doesn’t apply to a row (e.g. gen for a girl) just
returns NA for that row, so there’s no need to split the
data by sex first:
mydata <- read.csv("inst/webapps/pubertypro/demo.csv")
names(mydata) <- tolower(names(mydata))
mydata$age <- as.numeric(gsub(",", ".", mydata$age))
mydata$SDS_gen <- round(calculate_sds(mydata$age, mydata$gen, "gen"), 2)
mydata$SDS_phb <- round(calculate_sds(mydata$age, mydata$phb, "phb"), 2)
mydata$SDS_tv <- round(calculate_sds(mydata$age, mydata$tv, "tv"), 2)
mydata$SDS_bre <- round(calculate_sds(mydata$age, mydata$bre, "bre"), 2)
mydata$SDS_phg <- round(calculate_sds(mydata$age, mydata$phg, "phg"), 2)
mydata$SDS_men <- round(calculate_sds(mydata$age, mydata$men, "men"), 2)This is the same calculation Puberty Pro runs for every uploaded dataset.
inst/webapps/ ships two browser frontends
(puberty for a single patient, pubertypro for
batch upload). They were originally built to run behind Apache +
rApache, which is now hard to set up on modern systems. tools/run_mock_webapp.R serves both
frontends locally with httpuv, without
needing Apache or rApache at all.
install.packages("httpuv")
install.packages("devtools") # only needed if you run from a source checkoutFrom the repo root, in a terminal:
Rscript tools/run_mock_webapp.Ror, from an R/RStudio/Positron console, with the repo as the working directory:
source("tools/run_mock_webapp.R")Either way it prints the URLs and opens the single-patient app in your default browser automatically:
Mock webapps running at:
http://127.0.0.1:7654/puberty/
http://127.0.0.1:7654/pubertypro/
Press Ctrl+C to stop.
Open http://127.0.0.1:7654/pubertypro/ yourself for the
batch upload app — only the first URL is opened automatically.
Rscript): press
Ctrl+C, or close the terminal.source()): press
Ctrl+C in the console (or the stop/interrupt button in
RStudio/Positron) to break the server loop, or restart the R
session.If you try to start a second instance while one is still running,
you’ll get Failed to create server /
address already in use. Find and stop the process holding
port 7654:
# macOS / Linux
lsof -nP -iTCP:7654 -sTCP:LISTEN
kill <PID># Windows (PowerShell)
Get-NetTCPConnection -LocalPort 7654 | Select-Object OwningProcess
Stop-Process -Id <PID>The pubertypro app processes an entire dataset (many
patients) in one go: it draws one puberty plot per patient into a single
multi-page PDF, computes SDS values for every measurement, and offers
the result as a downloadable CSV/TXT alongside the PDF.
http://127.0.0.1:7654/pubertypro/.id, age, sex
("M"/"F"), plus gen,
phb, tv for boys and/or bre,
phg, men for girls (NA-able per row).plot_stadia() under the hood).SDS_gen, SDS_phb, SDS_tv,
SDS_bre, SDS_phg, SDS_men
columns.Doing the same thing from plain R, without the web app, looks like:
library(tanner)
mydata <- read.csv("inst/webapps/pubertypro/demo.csv")
names(mydata) <- tolower(names(mydata))
mydata$age <- as.numeric(gsub(",", ".", mydata$age))
pdf("all_patients.pdf", paper = "a4r", width = 11.67, height = 8.27)
plot_stadia(
data = mydata,
persons = unique(mydata$id),
type = c(TRUE, TRUE, TRUE),
plotline = c(FALSE, FALSE, FALSE),
padid = TRUE
)
dev.off()The original deployment runs the webapps behind Apache with rApache:
# install R and the tanner package
R CMD INSTALL tanner_1.5.0.tar.gz
# install rapache, e.g. using the package
sudo apt-get install libapache2-mod-r-base
# copy the site file from the package and activate
sudo cp -Rf /usr/local/lib/R/site-library/tanner/sites-available/puberty /etc/apache2/sites-available
sudo a2ensite puberty
# restart apache
sudo service apache2 restart
# if anything isn't working, check the error log:
tail /var/log/apache2/error.logThese binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.