Clinical research statistics, as tidy data. clinstats covers the analyses that recur in clinical and translational papers — discrimination, odds ratios, univariable and multivariable regression tables, survival cut points, and expression-matrix preparation — and returns a tibble from every function so results can be printed, filtered, joined, and exported.
The package also ships curated colorectal cancer clinical datasets assembled from public repositories, and a Quarto analysis report template.
library(clinstats)
cox_table(
clin_crc,
time = "rfs.delay",
event = "rfs.event",
factors = c("sex", "age", "tnm.stage")
)
#> # A tibble: 3 × 13
#> term variable level n n_event hr_univariable univ_ci_lower
#> <chr> <chr> <chr> <int> <int> <dbl> <dbl>
#> 1 sexM sex M 1641 561 1.14 0.967
#> 2 age age <NA> 1640 561 0.999 0.993
#> 3 tnm.stage tnm.stage <NA> 1632 558 1.82 1.62
#> # ℹ 6 more variables: univ_ci_upper <dbl>, univ_p <dbl>,
#> # hr_multivariable <dbl>, multi_ci_lower <dbl>, multi_ci_upper <dbl>,
#> # multi_p <dbl>Why clinstats exists
Clinical statistics code tends to be written once per paper and copied forward. The same ROC summary, the same two regression tables, the same expression cleanup, reimplemented slightly differently each time — which makes results hard to reproduce and easy to get subtly wrong.
clinstats collects those analyses into tested functions. Three properties do the work:
- A tibble from every function. Results can be filtered, joined, and written out with the rest of a tidy pipeline, and printed in a report without reshaping. Regression tables put univariable and multivariable estimates side by side in one table.
- Validated input. Every function checks its arguments and reports what is wrong and how to fix it, instead of failing deep inside a model fit.
- One expression-matrix orientation. Samples in rows and genes in columns throughout, so the functions chain without manual transposes.
The statistical routines were rewritten from the retired gaofenglib package, and the datasets carried over unchanged from curatedClinicalData. See Compatibility for the migration table.
Installation
Prebuilt binaries for Linux, macOS, and Windows come from r-universe, so no compiler is needed:
install.packages(
"clinstats",
repos = c(gflab = "https://gflab.r-universe.dev", CRAN = "https://cloud.r-project.org")
)Installing from source through GitHub also works:
# install.packages("remotes")
remotes::install_github("gflab/clinstats")R 4.1 or later is required.
What is inside
| Area | Functions |
|---|---|
| Discrimination |
roc_summary(), odds_ratio()
|
| Regression tables |
cox_table(), logistic_table(), evaluate_model()
|
| Survival analysis |
survival_cutoff(), survival_response(), resample_cox()
|
| Expression data |
clean_expression(), oncotype_crc()
|
| TCGA clinical data | tcga_clinical() |
| Reporting | copy_report_template() |
Quick start
Discrimination and odds ratios
marker <- scale(clin_crc$age)
roc_summary(marker, clin_crc$rfs.event)
#> # A tibble: 1 × 12
#> n n_event auc auc_ci_lower auc_ci_upper direction threshold
#> <int> <int> <dbl> <dbl> <dbl> <chr> <dbl>
#> 1 1698 574 0.514 0.485 0.542 > 0.143
odds_ratio(marker, clin_crc$rfs.event)roc_summary() returns the area under the curve, its confidence interval, the optimal operating point, and the sensitivity and specificity at that point. direction = "auto" lets pROC choose the orientation from the data, which is what you want when the sign of a marker is not known in advance.
Regression tables
cox_table() and logistic_table() fit every variable on its own, fit one multivariable model, and return both sets of estimates in a single table. Numeric variables contribute one row; factors contribute one row per level against the reference level.
cox_table(
clin_crc,
time = "rfs.delay",
event = "rfs.event",
factors = c("sex", "age", "tnm.stage", "cms")
)The multivariable model includes every requested variable by default. Use multivariable = "significant" to keep only variables with a univariable likelihood-ratio p-value below 0.05, or multivariable = "none" for univariable estimates alone.
Model evaluation
evaluate_model(train, test, outcome ~ marker1 + marker2)Returns the training and test AUC with confidence intervals and a DeLong comparison between them.
Expression data
cleaned <- clean_expression(expression) # samples in rows, genes in columns
oncotype_crc(cleaned) # composes directlyclean_expression() drops unusable columns, expands " /// " multi-mappings, optionally maps identifiers with org.Hs.eg.db, and collapses duplicated gene columns to the probe with the largest median absolute deviation.
Data
clin_crc, clin_crc_gf, and clin_crc_cell hold clinical annotations for 2,816 to 2,868 colorectal cancer samples across 25 columns: demographics, TNM stage and T/N/M categories, tumour location and grade, adjuvant chemotherapy, relapse-free, overall, and disease-free survival with their times, MMR/CIMP/CIN status, TP53/KRAS/BRAF mutation status, source dataset, and consensus molecular subtype.
Sources are the GEO series GSE14333, GSE17538, GSE18088, GSE26906, GSE31595, GSE33113, GSE37892, GSE39084, and GSE39582 (the PETACC-3 cohort), plus TCGA clinical exports. Every source is public and the data contain no patient identifiers.
?clin_crc documents the column layout; data-raw/README.md records the source of each cohort and the SHA-256 of each released object. Biological and clinical conclusions drawn from these datasets remain the responsibility of the analyst.
Analysis report template
copy_report_template() writes a Quarto document into the current project, with settings for HTML and PDF output and example chunks that use the bundled datasets.
Compatibility
clinstats is the maintained successor of the archived gaofenglib and curatedClinicalData repositories. Those packages still install, and clinstats consolidates them so the lab maintains one package rather than several overlapping ones.
| Old | New |
|---|---|
calc_logit() |
roc_summary() |
calc_or() |
odds_ratio() |
factor_analysis_cox() |
cox_table() |
factor_analysis_logit() |
logistic_table() |
eval_logit() |
evaluate_model() |
calc_cutoff_survivalroc() |
survival_cutoff() |
calc_resamp_cox() |
resample_cox() |
clean_dat() |
clean_expression() |
calc_oncotypedx_crc() |
oncotype_crc() |
extract_tcga_clinical() |
tcga_clinical() |
get_survival() |
survival_response() |
clin_crc, clin_crc_gf, clin_crc_cell
|
same names |
Changes to expect when migrating:
-
Results are tibbles, not base data frames. Positional indexing and
rownames()no longer apply. -
Regression tables include every variable by default. Use
multivariable = "significant"to reproduce the previous univariable-screening rule. -
survival_cutoff()computes the Kaplan-Meier Youden index with thesurvivalpackage, so the long-unmaintainedsurvivalROCis no longer a dependency. -
resample_cox()reports per-marker selection frequencies instead of a raw p-value matrix, and follows thefutureparallel plan. -
init_R()andupdate_all()were removed. A library should not reinstall the user’s environment.
Related packages
gfplot produces the figures that go with these analyses — Kaplan-Meier curves with hazard ratios, ROC curves, risk score distributions — in the lab house style. The two packages share the same datasets, so clinstats results feed directly into gfplot figures.
Citation
citation("clinstats")See CITATION.cff for machine-readable metadata. Release v0.1.2 is available as a tag, so a specific state can be pinned.
Getting help
Bug reports and feature requests are welcome through GitHub Issues. Reference documentation for every function is on the package site.
License
Apache License 2.0. See LICENSE.md. Copyright 2026 FengGao Lab contributors.