Skip to contents

tanner 1.8.0

Bug fixes

  • calculate_sds(..., type = "tv") no longer has a discontinuity at Prader bead boundaries: a raw ml reading is now mapped onto a continuous bead-index scale before interpolating, so both the low and high percentiles in the mid-P formula move together and the SDS varies smoothly with ml instead of jumping right at each bead threshold (e.g. 11.99ml vs 12.00ml).

  • calculate_sds(..., type = "tv") now correctly returns NA for an NA age or stage, instead of a finite but wrong value. The continuous bead-index interpolation above used approx()’s default na.rm = TRUE, which silently drops NA (x, y) pairs from the interpolation table before interpolating – so an NA age (which makes every real bead percentile NA) left only the two artificial boundary values (P(TV>=0)=1, P(TV>=Inf)=0) in the table, and interpolated between just those two instead of propagating the NA.

  • interpolate_trajectory()’s returned stage component is now the same length as x/y/patient. It was previously built from the cumulative length of the output-so-far across all segments instead of the current segment’s own length, making it longer than the other three components whenever a patient had more than one stage segment.

Documentation

  • Documented that calculate_sds()’s return value is always the same length as age, and that stage must be the same length as age (not recycled) – a length-1 stage, or any other length mismatch, is unsupported and can silently truncate, warn, produce NAs, or error depending on type.

  • Added vignettes/tanner.Rmd, covering a fuller plot_stadia() walkthrough across its type/plotline/colors/overlay arguments, how raw tv (ml) readings are handled differently by calculate_sds() (continuous interpolation) versus plot_stadia() (snap to the nearest Prader bead curve), and the mid-P value SDS methodology of van Buuren & Ooms (2009) – including why it is a discrete/ordinal method distinct from LMS/BCCG continuous-measurement SDS (e.g. centile::y2z()). Originally written as a Quarto vignette (tanner.qmd, %\VignetteEngine{quarto::html}); converted to classic knitr::rmarkdown (VignetteBuilder: knitr) after CI revealed an unresolved upstream Windows-only bug in the Quarto vignette engine (see “Continuous integration” below) that would have blocked CRAN submission, since CRAN’s own build machines check Windows.

  • Added a “Package structure” section to the README distinguishing the supported general-purpose library API (plot_stadia*, interpolate_trajectory, find_stage_segments, calculate_sds, tnologo) from the rApache webapp request handlers (plotter, plotterpro, upload_tryCatch_pro) kept only for the existing deployment. plotter(), plotterpro() and upload_tryCatch_pro() are now also grouped under a @family webapp-internals roxygen tag, so they cross-reference each other in ?tanner and on the pkgdown site.

  • Added a pkgdown site (_pkgdown.yml), with a reference index split into “Plotting & analysis”, “Reference data”, and “Webapp internals” sections, and URL/BugReports fields in DESCRIPTION.

  • Added full citations for van Buuren S (2014), “Growth charts of human development,” Statistical Methods in Medical Research, 23(4), 346-368, in nl1997’s source documentation.

Testing

Internal refactoring

  • calculate_sds() and snap_tv_to_nearest_bead() each independently parsed the ml threshold from "T"-prefixed tv reference column names (e.g. "T12" -> 12); extracted the shared parsing step into an internal tv_ml_thresholds() helper. Pure refactor, no behavior change – the bracket-and-interpolate vs. snap-to-nearest logic that follows at each call site is untouched.

Continuous integration

  • Added .github/workflows/R-CMD-check.yaml (a standard R CMD check matrix across macOS/Windows/Ubuntu) and .github/workflows/pkgdown.yaml (builds and deploys the pkgdown site to GitHub Pages on push to master). windows-latest was briefly excluded from the check matrix after CI revealed that the quarto::html vignette engine hits an unresolved upstream Windows-only bug during R CMD build (the Quarto subprocess can’t see the temporary library R CMD build just installed the package into; r-lib/rcmdcheck#236); re-added once the vignette was converted to classic knitr::rmarkdown (see “Documentation” above), which doesn’t hit this bug since it renders in-process rather than via an external Quarto subprocess.

  • _pkgdown.yml now sets template: bootstrap: 5 (Quarto-rendered articles require it; the default Bootstrap 3 made the pkgdown site build fail before it ever reached the deploy step) and llm-docs: false (pkgdown’s llms.txt generation, not needed here and fragile against the runner’s pandoc version).

Package loading

  • library(tanner) no longer creates a plotdumpdir directory (or writes to disk at all) as a side effect of loading the package. The directory is now created lazily, on the first actual write inside plotter()/plotterpro()/upload_tryCatch_pro(); plotdumpdir remains exported and mutable.

  • plotdumpdir now defaults to a "plotfiles" subdirectory of tempdir() instead of the hardcoded /tmp/plotfiles, and upload_tryCatch_pro()’s uploaded-file and .RData handoff files move there too (previously hardcoded to /tmp independently of plotdumpdir). This is a behavior change for the rApache deployment: tempdir() must resolve to a writable location that stays stable across the upload and plot requests for a given user session (true for a single long-running R process, which is how rApache normally hosts an R interpreter per worker). Unix-only /tmp was also a portability problem for R CMD check/CI on Windows and macOS runners, even though the webapp handlers themselves only ever ran on Linux.

Breaking changes

  • Renamed three exported functions to consistent snake_case, matching plot_stadia()’s existing naming style:

    There are no backwards-compatible aliases for the old names; update any code calling them directly.

  • Renamed the two bundled reference datasets, following the <population><year> naming convention used by the nlreferences and clopus packages:

    • pub.ref -> nl1997
    • pub.ref.lines -> nl1997_lines

tanner 1.6.0

Visual style matched to original TNO reference diagrams

  • Percentile-band colors, default plot colors, symbol/line/font sizes, and the single-patient webapp’s PNG resolution now match the package’s own historical reference diagrams (genital.pdf, boy.pdf, and the breast stage-line-diagram template), sourced by sampling exact pixel colors and by recovering the original plotting script (figures.r) that generated them.
  • Fixed plot_stadia() silently ignoring its own padid argument: the function always appended the patient id to the title regardless of what was passed, so padid = FALSE (already used by plotter()) never actually worked.
  • Replaced foreign::read.spss() with haven::read_sav() in upload_tryCatch_pro(); haven moved from Depends to Imports.

Documentation

  • Converted README.Rmd to README.qmd (Quarto), for better editor support and to avoid a pandoc version mismatch on this system. Replaced the synthetic example data in the first README figure with a real patient’s genital-stage trajectory, recovered from the original demo dataset (pubertal demo data.txt) used to produce genital.pdf/ boy.pdf, and noted the data source in the figure title.

tanner 1.5.0

Package renamed

  • The package was renamed from pubertyplot to tanner, and the GitHub repository moved to growthcharts/tanner. Update library(pubertyplot) calls to library(tanner), and devtools::install_github(...) calls to point at growthcharts/tanner.

Data corrections

  • Replaced the bundled testicular-volume reference data with a corrected R refit (pub.ref.1997r.txt). The previously shipped pub.ref.1997.txt was an older S-Plus fit that the original 2008 fitting script’s own validation had already flagged as containing errors in the tv (testicular volume) curves — some tv percentiles were missing entirely at younger ages. The refit’s genital-stage percentiles match Table 5 of van Buuren S (2014), “Growth charts of human development,” Statistical Methods in Medical Research, 23(4), 346-368, https://doi.org/10.1177/0962280212473300, exactly. pub.ref and pub.ref.lines were rebuilt from the corrected data via data-raw/R/create_references.R.

  • pub.ref$tv / pub.ref.lines$tv now carry the full 12-point testicular volume scale (1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25 ml), up from 7 columns previously.

Testicular volume is now recorded directly in ml

  • calculateSDS() and plot_stadia() previously required tv to be recoded from a raw ml reading into a bead-index (1-8, then later 1-12) by hand before use. tv is now passed as the raw ml value directly — no recoding step required — and calculateSDS() correctly interpolates readings that fall between Prader orchidometer beads (e.g. 11 ml or 16 ml), rather than requiring one of the 12 standard bead volumes. plot_stadia() traces the trajectory along the nearest reference curve for the same off-bead readings.

  • Removed the manual tv recoding tables from plotter(), plotterpro() and the README examples, since they are no longer needed.

  • upload_tryCatch_pro()’s upload validation for tv now accepts any plausible ml reading (0-30) instead of rejecting anything outside the old fixed bead list.

  • The single-patient web app’s testicular volume dropdown (inst/webapps/puberty/puberty.js) now offers the correct 12 Prader bead volumes (1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 20, 25), replacing the previous incomplete/incorrect 8-value list.

  • Regenerated the bundled demo data (inst/webapps/pubertypro/demo.csv, demo.txt, demo.sav) with tv values spanning the corrected 12-point scale, including off-bead readings, since the original demo values were from a different orchidometer scale and no longer matched the corrected reference data.

  • Fixed the testis reference lines and labels in plot_stadia() / plot_stadia_lines(), which still only drew 8 of the 12 reference curves and labeled them with the old bead volumes.

Documentation

  • Added a runnable @examples block to findXY().
  • Rewrote the README with a plain-R SDS calculation walkthrough, a local mock web server (see below) for trying out the browser frontends without Apache/rApache, a bulk-plotting walkthrough for Puberty Pro, a citation for the underlying method (van Buuren & Ooms, 2009, Statistics in Medicine), GitHub install instructions, and an embedded example chart.
  • Documented all exported functions and data objects with roxygen2 (R/data.R, man pages for every exported function).
  • Bumped the minimum R version to 3.5.0, required by the version-3 serialization format used by the rebuilt .rda data files.

Developer-facing changes

  • Added tools/run_mock_webapp.R, a local server (via httpuv) that serves the puberty and pubertypro browser frontends and answers their AJAX endpoints without needing a real Apache + rApache deployment.
  • Fixed the mock server writing generated plots to the wrong directory (it overrode a global plotdumpdir rather than the package-namespace variable that plotter()/plotterpro() actually read from).
  • Renamed dotted function names (e.g. plot.stadia) to underscore form (plot_stadia) to avoid the misleading appearance of S3 methods.
  • Replaced T/F with TRUE/FALSE in default arguments and call sites.
  • Applied the AIR formatter across the R source.
  • Replaced foreign::read.spss() with haven::read_sav() for reading uploaded .sav files in upload_tryCatch_pro(), moving haven from Depends to Imports.