Skip to contents

This article collects the facts a risk assessor needs when qualifying ltsummary for a validated statistical computing environment: what the package depends on, how its statistics are tested, and where the evidence lives. It does not claim the package is “validated”. Validation attaches to a sponsor’s environment and process, not to a package, and the assessment is the sponsor’s to make.

The regulatory frame

FDA’s Statistical Software Clarifying Statement (May 2015) settles the threshold question: no specific software is required for statistical analyses, and none is certified. The statement creates one affirmative obligation, to document the software name, version, and build in the submission, and points to ICH E9 for the rest: the software should be reliable, and documentation of software testing procedures should be available.

What “reliable” means in practice comes from risk-based qualification of the computing environment. For R, the reference approach is the R Validation Hub’s white paper (A Risk-based Approach for Assessing R package Accuracy within a Validated Infrastructure, 2020). Base R and the recommended packages are treated as low risk on the strength of the R Foundation’s documented development process; qualify the installation rather than re-validate the code. Every contributed package intended for use needs a documented risk assessment covering its purpose, maintenance practice, community usage, and testing, and the whole dependency tree has to be pinned and reproducible.

That model is why the size of a dependency tree is a regulatory quantity. Each contributed package in the tree is one more assessment to write, one more upgrade to put through change control, and one more version to pin.

The dependency footprint

ltsummary imports lt, stats, and utils. The recursive hard-dependency closure beyond base R is two packages, and none of it is compiled:

db <- installed.packages()
tools::package_dependencies(c("ltsummary", "gtsummary"), db = db,
                            which = c("Depends", "Imports", "LinkingTo"),
                            recursive = TRUE)

Run against a library holding ltsummary 0.8.0 and gtsummary 2.6.1, the closure for ltsummary is lt and xfun, both pure R. The closure for gtsummary is 62 packages, including C and C++ toolchain dependencies (cpp11, Rcpp, stringi, V8, curl). Both trees produce the same tables; the difference is the surface a qualification has to cover and keep covering at every upgrade.

Every statistic ltsummary reports is computed in base R, almost entirely through stats. The two survival features are the exception: tbl_survfit() requires the survival package (a recommended package, inside the Hub’s trusted set), and nothing else reaches outside base R at run time.

How the statistics are tested

The testing policy is recorded in decisions/0016 and separates two kinds of claim.

Quantities (estimates, confidence limits, p-values, counts) are tested against independent oracles executed inside the test: a stats:: or survival:: call, or hand arithmetic written out in a comment. A regression table’s cells are compared to coef(), confint(), and vcov() on a refit of the same model; a p-value column is compared to the matching t.test() or fisher.test(); a Kaplan-Meier cell is compared to summary(survfit(...)). The review criterion is that every expected number can be checked without any other table package installed.

Formatting rules (how many decimals, when “<0.001” appears) are tested against the documented rounding rules, with the boundary cases hand-derived in decimal arithmetic and annotated in the test.

Two further layers back this up. Snapshot tests pin the full numeric body of each table type as a regression net, and a live parity suite compares ltsummary’s output cell for cell against gtsummary 2.6.1 across a configuration matrix whenever gtsummary is installed. The parity suite is compatibility evidence, not accuracy evidence: it shows the two packages agree on the displayed convention, while the oracle tests carry the claim that the numbers are right.

The suite runs on every push across macOS, Windows, and three Ubuntu R versions through GitHub Actions, and R CMD check must pass with no errors, warnings, or notes. Coverage is checked before each release against a 90% target, with 100% required for the formatting and summary-statistic engines.

For a package risk assessment

Inputs an assessor will want:

  • Purpose: statistical summary tables; the package computes statistics, so under the Hub model it warrants assessment by a statistician.
  • Testing: the oracle policy above; the test suite ships in the package (tests/testthat/) and runs at install time via R CMD check.
  • Maintenance: development happens in the open at github.com/tgerke/ltsummary, with design decisions recorded as numbered entries in decisions/ and behavior pinned to gtsummary 2.6.1 (decisions/0008 and decisions/0021).
  • Community usage: low. The package is new, and on this Hub criterion it scores below gtsummary, which is widely used. A sponsor weighing the two is trading assessment surface (2 packages against 62) against usage history, and should say so explicitly in the assessment.

For the submission record itself, document the package version with sessionInfo() output as usual, and pin the environment (renv or an equivalent lockfile) so the table code reruns against the same versions.

Scope limits

The statistical scope is the gtsummary interface for data frames: tbl_summary(), tbl_continuous(), tbl_cross(), the hierarchical tables, tbl_likert(), tbl_wide_summary(), tbl_custom_summary(), the regression tables for lm, glm, and coxph, tbl_survfit(), and their add-on functions. Survey designs, multiply-imputed models, and mixed models are out of scope, and the migration article lists every function that is deliberately not mirrored. Where ltsummary’s behavior departs from gtsummary on purpose (upstream bugs it fixes rather than reproduces), the departure is recorded in decisions/ and in the migration article.