| Title: | Clinical Research Statistics Toolkit |
|---|---|
| Description: | Statistical utilities that recur in clinical and translational research workflows: receiver operating characteristic (ROC) summaries, odds ratios, univariable and multivariable Cox and logistic regression tables, time-dependent survival cut points, resampling-based Cox screening, TCGA clinical extraction, and expression-matrix cleaning. Includes curated colorectal cancer clinical datasets assembled from public repositories, a Quarto report template, and tidy data-frame output that is convenient for both interactive and programmatic use. |
| Authors: | Feng Gao [aut, cre], Lina Zhu [ctb], Wei Wang [ctb] |
| Maintainer: | Feng Gao <[email protected]> |
| License: | Apache License (>= 2) |
| Version: | 0.1.2 |
| Built: | 2026-10-16 06:12:21 UTC |
| Source: | https://github.com/gflab/clinstats |
Prepares a gene expression matrix for downstream analysis: drops unnamed
and empty columns, expands columns with " /// " multi-mappings, maps
gene identifiers when requested, and keeps the column with the largest
median absolute deviation when several columns map to the same gene.
clean_expression(expression, id_from = NULL, id_to = "SYMBOL")clean_expression(expression, id_from = NULL, id_to = "SYMBOL")
expression |
Numeric matrix or data frame with samples in rows and gene identifiers in columns. |
id_from |
Optional identifier type of the column names, for example
|
id_to |
Target identifier type used when |
A numeric matrix with samples in rows and unique gene identifiers in columns.
expr <- matrix( c(1, 2, 3, 4, 5, 6, NA, 8), nrow = 2, dimnames = list(NULL, c("A", "A", "B", "")) ) clean_expression(expr)expr <- matrix( c(1, 2, 3, 4, 5, 6, NA, 8), nrow = 2, dimnames = list(NULL, c("A", "A", "B", "")) ) clean_expression(expr)
Clinical information for colorectal cancer samples assembled from public
repositories: the GEO datasets GSE14333, GSE17538, GSE18088, GSE26906,
GSE31595, GSE33113, GSE37892, GSE39084, and GSE39582 (the PETACC-3
cohort), and TCGA colon and rectal cancer clinical exports. The data
contain research-level variables only and no patient identifiers. These
objects are carried over from the archived curatedClinicalData
repository without changing the stored values.
clin_crc clin_crc_gf clin_crc_cellclin_crc clin_crc_gf clin_crc_cell
A data frame with 25 columns:
Sample or patient identifier.
Sex ("M"/"F").
Age at diagnosis in years.
TNM stage as an ordered number.
TNM T, N, and M categories.
Lymph node involvement.
Tumour location ("L"/"R").
Tumour grade.
Adjuvant chemotherapy indicator.
Relapse-free survival event and time (years).
Overall survival event and time (years).
Disease-free survival event and time (years).
Molecular subtype indicators.
Mutation status.
Source dataset.
Consensus molecular subtype label.
A data frame with 2,868 rows and 25 columns, in the same layout as clin_crc. This version adds the TCGA update of 2018.
A data frame with 2,865 rows and 25 columns, in the same layout as clin_crc. This version carries the 2019 TCGA cell-line update.
GEO series GSE14333, GSE17538, GSE18088, GSE26906, GSE31595, GSE33113, GSE37892, GSE39084, and GSE39582; TCGA clinical exports.
dim(clin_crc) table(clin_crc$dataset)dim(clin_crc) table(clin_crc$dataset)
Copies analysis-report, a Quarto document with lab-style defaults for
HTML and PDF output, into a directory of the current project.
copy_report_template(path = ".", overwrite = FALSE)copy_report_template(path = ".", overwrite = FALSE)
path |
Directory into which the template files are copied. |
overwrite |
Replace existing files. |
The paths of the copied files, invisibly.
cox_table() and logistic_table() fit each variable on its own, fit one
multivariable model, and return a single tidy table with the univariable
and multivariable estimates side by side. Numeric variables contribute one
row with a per-unit estimate; factors contribute one row per level against
the reference level.
cox_table( data, time, event, factors = NULL, max_time = NULL, multivariable = c("all", "significant", "none"), significance = 0.05, ci_level = 0.95 ) logistic_table( data, outcome, factors = NULL, positive = NULL, multivariable = c("all", "significant", "none"), significance = 0.05, ci_level = 0.95 )cox_table( data, time, event, factors = NULL, max_time = NULL, multivariable = c("all", "significant", "none"), significance = 0.05, ci_level = 0.95 ) logistic_table( data, outcome, factors = NULL, positive = NULL, multivariable = c("all", "significant", "none"), significance = 0.05, ci_level = 0.95 )
data |
Data frame holding the variables. |
time, event
|
Column names of the follow-up time and event indicator
used by |
factors |
Character vector of variable names to analyse. Defaults to
every column of |
max_time |
Optional follow-up limit for |
multivariable |
Which variables enter the multivariable model:
|
significance |
Significance level used by
|
ci_level |
Confidence level of the reported intervals. |
outcome |
Column name of the binary outcome used by
|
positive |
Value of |
The multivariable model includes all requested variables by default. With
multivariable = "significant" only variables whose univariable
likelihood-ratio test is below significance are included, which
reproduces the screening rule of the original helper functions.
A tibble with one row per term and the columns term, variable,
level, n, n_event, the univariable estimate (hr_univariable or
or_univariable) with interval and p-value, and the corresponding
multivariable columns.
set.seed(42) dat <- data.frame( time = rexp(60, 0.1), event = rbinom(60, 1, 0.7), age = rnorm(60, 60, 8), stage = factor(sample(c("II", "III"), 60, replace = TRUE)) ) cox_table(dat, time = "time", event = "event") set.seed(42) dat <- data.frame( response = rbinom(60, 1, 0.4), age = rnorm(60, 60, 8), sex = factor(sample(c("F", "M"), 60, replace = TRUE)) ) logistic_table(dat, outcome = "response")set.seed(42) dat <- data.frame( time = rexp(60, 0.1), event = rbinom(60, 1, 0.7), age = rnorm(60, 60, 8), stage = factor(sample(c("II", "III"), 60, replace = TRUE)) ) cox_table(dat, time = "time", event = "event") set.seed(42) dat <- data.frame( response = rbinom(60, 1, 0.4), age = rnorm(60, 60, 8), sex = factor(sample(c("F", "M"), 60, replace = TRUE)) ) logistic_table(dat, outcome = "response")
Fits a logistic regression model on train, evaluates discrimination on
both train and test, and compares the two areas under the curve with
DeLong's test for unpaired samples.
evaluate_model(train, test, formula, positive = NULL, ci_level = 0.95)evaluate_model(train, test, formula, positive = NULL, ci_level = 0.95)
train, test
|
Data frames with the model variables. |
formula |
Model formula, for example |
positive |
Value of the outcome that represents the event. |
ci_level |
Confidence level of the reported AUC intervals. |
A tibble with one row: sample sizes and event counts of both data sets, the training and test AUC with confidence intervals, and the DeLong p-value comparing them.
set.seed(1) train <- data.frame(y = rbinom(80, 1, 0.5), x = rnorm(80)) test <- data.frame(y = rbinom(40, 1, 0.5), x = rnorm(40)) evaluate_model(train, test, y ~ x)set.seed(1) train <- data.frame(y = rbinom(80, 1, 0.5), x = rnorm(80)) test <- data.frame(y = rbinom(40, 1, 0.5), x = rnorm(40)) evaluate_model(train, test, y ~ x)
Binarises a marker and reports the odds ratio for the event in the high
group relative to the low group. Continuous markers are split at the
Youden-optimal ROC threshold unless cutoff is supplied; two-level
markers and factors are used as they are.
odds_ratio(x, outcome, positive = NULL, cutoff = NULL, ci_level = 0.95)odds_ratio(x, outcome, positive = NULL, cutoff = NULL, ci_level = 0.95)
x |
Marker or factor to binarise. Continuous numeric values are split at the optimal threshold; numeric, logical, character, and factor inputs with two distinct values are used directly. |
outcome |
Binary outcome. Logical vectors are coded |
positive |
Value of |
cutoff |
Optional threshold supplied by the user. Values greater than
|
ci_level |
Confidence level for the AUC interval. |
A tibble with one row: the threshold that was used, sample size, number of events, group sizes, the odds ratio with a Wald confidence interval, and the Wald p-value.
set.seed(1) marker <- c(rnorm(40, 0), rnorm(40, 1)) outcome <- rep(c(0, 1), each = 40) odds_ratio(marker, outcome)set.seed(1) marker <- c(rnorm(40, 0), rnorm(40, 1)) outcome <- rep(c(0, 1), each = 40) odds_ratio(marker, outcome)
Computes the Oncotype DX colon cancer recurrence score from the published
gene panel. The formula and the panel, including the reference genes, are
unchanged from the original calc_oncotypedx_crc() helper.
oncotype_crc(expression, gene_axis = c("columns", "rows"))oncotype_crc(expression, gene_axis = c("columns", "rows"))
expression |
Numeric matrix of gene expression values. |
gene_axis |
Axis that holds the gene symbols. |
A tibble with one row per sample: the stromal, cell-cycle, and
individual gene scores, the reference score, the corrected scores, the
recurrence score, and the recurrence score category ("Low",
"Intermediate", or "High").
panel <- c( "BGN", "FAP", "INHBA", "MKI67", "MYC", "MYBL2", "GADD45B", "ATP5E", "GPX1", "PGK1", "VDAC2", "UBB" ) samples_by_genes <- matrix( rnorm(3 * 12, 10), nrow = 3, dimnames = list(paste0("s", 1:3), panel) ) oncotype_crc(samples_by_genes) # Genes in rows also work when declared explicitly. oncotype_crc(t(samples_by_genes), gene_axis = "rows")panel <- c( "BGN", "FAP", "INHBA", "MKI67", "MYC", "MYBL2", "GADD45B", "ATP5E", "GPX1", "PGK1", "VDAC2", "UBB" ) samples_by_genes <- matrix( rnorm(3 * 12, 10), nrow = 3, dimnames = list(paste0("s", 1:3), panel) ) oncotype_crc(samples_by_genes) # Genes in rows also work when declared explicitly. oncotype_crc(t(samples_by_genes), gene_axis = "rows")
Repeatedly draws subsamples of the samples, fits a univariable Cox model
for every candidate column, and reports how often each candidate reached
p_threshold. This is the resampling screening previously provided by
calc_resamp_cox(), reported as per-marker selection frequencies instead
of a raw p-value matrix.
resample_cox( expression, time, event, ratio = 0.8, times = 1000, p_threshold = 0.05, seed = NULL )resample_cox( expression, time, event, ratio = 0.8, times = 1000, p_threshold = 0.05, seed = NULL )
expression |
Numeric matrix or data frame with samples in rows and candidate markers in columns. |
time, event
|
Follow-up time and event indicator, one value per sample. |
ratio |
Fraction of samples drawn in each resample. |
times |
Number of resamples. |
p_threshold |
Significance threshold that counts as a selection. |
seed |
Optional integer seed for reproducible resampling. |
The function uses the future framework. Set a parallel plan before
calling it to use several workers, for example
future::plan(future::multisession).
A tibble with one row per candidate marker: the number of valid resamples, the number of resamples in which the marker was significant, the selection fraction, and the median p-value.
set.seed(1) expr <- matrix(rnorm(60 * 5), nrow = 60, dimnames = list(NULL, paste0("g", 1:5))) time <- rexp(60) event <- rbinom(60, 1, 0.7) resample_cox(expr, time, event, times = 20, seed = 1)set.seed(1) expr <- matrix(rnorm(60 * 5), nrow = 60, dimnames = list(NULL, paste0("g", 1:5))) time <- rexp(60) event <- rbinom(60, 1, 0.7) resample_cox(expr, time, event, times = 20, seed = 1)
Computes the area under the receiver operating characteristic curve (AUC) with a confidence interval and the optimal operating point of a numeric marker against a binary outcome. A tibble with one row is returned, so the result can be combined across markers or subgroups.
roc_summary( marker, outcome, positive = NULL, direction = "auto", best_method = c("youden", "closest.topleft"), ci_level = 0.95, power = FALSE, alternative = c("one.sided", "two.sided") )roc_summary( marker, outcome, positive = NULL, direction = "auto", best_method = c("youden", "closest.topleft"), ci_level = 0.95, power = FALSE, alternative = c("one.sided", "two.sided") )
marker |
Numeric vector of marker values. |
outcome |
Binary outcome. Logical vectors are coded |
positive |
Value of |
direction |
Direction of the comparison passed on to
|
best_method |
Method used to choose the optimal operating point,
|
ci_level |
Confidence level for the AUC interval. |
power |
If |
alternative |
Alternative hypothesis used for the power estimate. |
A tibble with one row: sample size, number of events, AUC and its confidence interval, the optimal threshold, and the sensitivity, specificity, positive and negative predictive values, and accuracy at that threshold.
odds_ratio() for the odds ratio at the optimal threshold.
set.seed(1) marker <- c(rnorm(40, 0), rnorm(40, 1)) outcome <- rep(c(0, 1), each = 40) roc_summary(marker, outcome)set.seed(1) marker <- c(rnorm(40, 0), rnorm(40, 1)) outcome <- rep(c(0, 1), each = 40) roc_summary(marker, outcome)
Estimates Kaplan-Meier survival above and below each candidate cut point
of a marker at predict_time and returns the cut point that maximises the
Youden index (sensitivity + specificity - 1), the same criterion that the
long-unmaintained survivalROC package uses, computed here directly from
survival::survfit(). Candidates are drawn from a quantile grid to keep
the computation bounded.
survival_cutoff(time, event, marker, predict_time, grid_size = 200)survival_cutoff(time, event, marker, predict_time, grid_size = 200)
time |
Numeric follow-up times. |
event |
Event indicator (0/1, logical, or a two-level value). |
marker |
Numeric marker to split. |
predict_time |
Time point at which sensitivity and specificity are evaluated. |
grid_size |
Number of candidate cut points evaluated between the 1st and 99th percentile of the marker. |
A tibble with one row: the optimal cutoff, the Youden index
(youden), the time-dependent true positive rate (tp) and false
positive rate (fp) at that cut point, and the number of observations.
set.seed(1) time <- rexp(100) event <- rbinom(100, 1, 0.6) marker <- rnorm(100) survival_cutoff(time, event, marker, predict_time = 1)set.seed(1) time <- rexp(100) event <- rbinom(100, 1, 0.6) marker <- rnorm(100) survival_cutoff(time, event, marker, predict_time = 1)
Build a survival response from a clinical data frame
survival_response(data, type = c("rfs", "os", "dfs"))survival_response(data, type = c("rfs", "os", "dfs"))
data |
Data frame such as the included clin_crc dataset. |
type |
Endpoint prefix: |
A survival::Surv() object.
survival_response(clin_crc, type = "rfs")survival_response(clin_crc, type = "rfs")
Converts the wide clinical export distributed with legacy TCGA data
releases (column names of the form
patient.age_at_initial_pathologic_diagnosis) into an analysis-ready
tibble with follow-up times in days. Three column layouts are supported:
the generic layout, the oesophageal carcinoma layout ("esca"), and the
stomach adenocarcinoma layout ("stad").
tcga_clinical(clin, cancer = c("default", "esca", "stad"))tcga_clinical(clin, cancer = c("default", "esca", "stad"))
clin |
Data frame with one row per patient in the legacy TCGA layout. |
cancer |
Column layout: |
A tibble with the patient barcode, age, sex, stage, node status,
T stage, disease-free survival, relapse and death status with their
times, and, where available, chemotherapy and response. The "esca"
layout additionally returns the histological subtype and tumour
location.