Changelog
Source:NEWS.md
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 returnsNAfor anNAage or stage, instead of a finite but wrong value. The continuous bead-index interpolation above usedapprox()’s defaultna.rm = TRUE, which silently dropsNA(x, y) pairs from the interpolation table before interpolating – so anNAage (which makes every real bead percentileNA) 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 theNA.interpolate_trajectory()’s returnedstagecomponent is now the same length asx/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 asage, and thatstagemust be the same length asage(not recycled) – a length-1stage, or any other length mismatch, is unsupported and can silently truncate, warn, produceNAs, or error depending ontype.Added
vignettes/tanner.Rmd, covering a fullerplot_stadia()walkthrough across itstype/plotline/colors/overlayarguments, how rawtv(ml) readings are handled differently bycalculate_sds()(continuous interpolation) versusplot_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 classicknitr::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()andupload_tryCatch_pro()are now also grouped under a@family webapp-internalsroxygen tag, so they cross-reference each other in?tannerand on the pkgdown site.Added a pkgdown site (
_pkgdown.yml), with a reference index split into “Plotting & analysis”, “Reference data”, and “Webapp internals” sections, andURL/BugReportsfields inDESCRIPTION.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
- Added
testthatunit tests forcalculate_sds(),interpolate_trajectory(),find_stage_segments(), and smoke tests for theplot_stadia*drawing functions (which draw to a graphics device and return invisibleNULL, so are tested by asserting they run without error). Extracted thetvml-to-nearest-bead snapping logic out ofplot_stadia_data()into an internalsnap_tv_to_nearest_bead()helper so it can be unit tested directly.
Internal refactoring
-
calculate_sds()andsnap_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 internaltv_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 tomaster).windows-latestwas briefly excluded from the check matrix after CI revealed that thequarto::htmlvignette engine hits an unresolved upstream Windows-only bug duringR CMD build(the Quarto subprocess can’t see the temporary libraryR CMD buildjust installed the package into; r-lib/rcmdcheck#236); re-added once the vignette was converted to classicknitr::rmarkdown(see “Documentation” above), which doesn’t hit this bug since it renders in-process rather than via an external Quarto subprocess._pkgdown.ymlnow setstemplate: bootstrap: 5(Quarto-rendered articles require it; the default Bootstrap 3 made the pkgdown site build fail before it ever reached the deploy step) andllm-docs: false(pkgdown’sllms.txtgeneration, not needed here and fragile against the runner’s pandoc version).
Package loading
library(tanner)no longer creates aplotdumpdirdirectory (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 insideplotter()/plotterpro()/upload_tryCatch_pro();plotdumpdirremains exported and mutable.plotdumpdirnow defaults to a"plotfiles"subdirectory oftempdir()instead of the hardcoded/tmp/plotfiles, andupload_tryCatch_pro()’s uploaded-file and.RDatahandoff files move there too (previously hardcoded to/tmpindependently ofplotdumpdir). 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/tmpwas also a portability problem forR 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, matchingplot_stadia()’s existing naming style:-
calculateSDS()->calculate_sds() -
findXY()->interpolate_trajectory() -
findSegments()->find_stage_segments()
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 thenlreferencesandclopuspackages:-
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 ownpadidargument: the function always appended the patient id to the title regardless of what was passed, sopadid = FALSE(already used byplotter()) never actually worked. - Replaced
foreign::read.spss()withhaven::read_sav()inupload_tryCatch_pro();havenmoved fromDependstoImports.
Documentation
- Converted
README.RmdtoREADME.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 producegenital.pdf/boy.pdf, and noted the data source in the figure title.
tanner 1.5.0
Package renamed
- The package was renamed from
pubertyplottotanner, and the GitHub repository moved togrowthcharts/tanner. Updatelibrary(pubertyplot)calls tolibrary(tanner), anddevtools::install_github(...)calls to point atgrowthcharts/tanner.
Data corrections
Replaced the bundled testicular-volume reference data with a corrected R refit (
pub.ref.1997r.txt). The previously shippedpub.ref.1997.txtwas an older S-Plus fit that the original 2008 fitting script’s own validation had already flagged as containing errors in thetv(testicular volume) curves — sometvpercentiles 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.refandpub.ref.lineswere rebuilt from the corrected data viadata-raw/R/create_references.R.pub.ref$tv/pub.ref.lines$tvnow 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()andplot_stadia()previously requiredtvto be recoded from a raw ml reading into a bead-index (1-8, then later 1-12) by hand before use.tvis now passed as the raw ml value directly — no recoding step required — andcalculateSDS()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
tvrecoding tables fromplotter(),plotterpro()and the README examples, since they are no longer needed.upload_tryCatch_pro()’s upload validation fortvnow 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) withtvvalues 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
@examplesblock tofindXY(). - 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
.rdadata files.
Developer-facing changes
- Added
tools/run_mock_webapp.R, a local server (via httpuv) that serves thepubertyandpubertyprobrowser 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
plotdumpdirrather than the package-namespace variable thatplotter()/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/FwithTRUE/FALSEin default arguments and call sites. - Applied the AIR formatter across the R source.
- Replaced
foreign::read.spss()withhaven::read_sav()for reading uploaded.savfiles inupload_tryCatch_pro(), movinghavenfromDependstoImports.