circaTime

A unified Bioconductor interface for inferring circadian phase (time of sample collection relative to the internal clock) from bulk and donor-level pseudobulk transcriptomes.

Status: submitted to Bioconductor. See Installation below for how to install in the meantime.

Why

Tens of thousands of public RNA-seq samples have no recorded collection time, but circadian phase can often be recovered computationally from expression data alone. Several published methods do this, but they live in separate repositories, in different languages, take incompatible inputs, produce incompatible outputs, and have never been benchmarked against each other on shared, labelled ground truth. circaTime does not introduce a new phase-inference algorithm — it wraps existing published methods behind one SummarizedExperiment-based interface, adds a shared cross-validation benchmarking harness, and adds a calibrated uncertainty layer: bootstrap and Bayesian confidence intervals validated for real coverage on labelled data, plus a quality flag (fixed core-clock amplitude, or a dynamic per-dataset coherence panel) for samples whose clock is too damped for a phase estimate to be trustworthy. A consensus mode combines every engine bundled for a sample instead of requiring you to pick one, and gene-ID/cross-species handling lets a reference model trained on one species and platform score data from another.

Installation

circaTime has been submitted to Bioconductor and has not yet been accepted. Until it is, install the development version directly from GitHub:

if (!requireNamespace("remotes", quietly = TRUE)) {
  install.packages("remotes")
}
remotes::install_github("dralperenuysal/circaTime")

Three of the wrapped engines (tauFisher, TimeSignatR, zeitzeiger) live only on GitHub, so they are Suggests, not hard dependencies — circaTime installs and works (Molecular Timetable engine included) without them. Install whichever ones you need separately:

remotes::install_github("micnngo/tauFisher")
remotes::install_github("braunr/TimeSignatR")
remotes::install_github("hugheylab/zeitzeiger")

Non-symbol gene identifiers (Ensembl/Entrez/RefSeq) and cross-species ortholog mapping (standardizeGeneIds(), mapOrthologs()) are also optional: install AnnotationDbi, org.Mm.eg.db/org.Hs.eg.db, and babelgene if you need them.

Once circaTime is accepted into Bioconductor, this section will be replaced with the standard:

if (!requireNamespace("BiocManager", quietly = TRUE)) {
  install.packages("BiocManager")
}
BiocManager::install("circaTime")

Usage

Estimate phase for new samples

library(circaTime)
library(SummarizedExperiment)

# See what reference models are currently bundled (species/tissue/method)
availableReferenceModels()

se <- estimatePhase(se, method = "timetable", tissue = "Liv")
colData(se)[, c("phase", "phase_lo", "phase_hi", "clock_amp", "phase_flag")]

estimatePhase() never hard-errors on a difficult individual sample — it sets phase = NA and an informative phase_flag ("low_amplitude", "gene_coverage", or "failed") instead. Only malformed input (an unsupported tissue/species/method combination, or missing gene identifiers) is a hard error.

Beyond the bootstrap phase_lo/phase_hi, every call also returns a Bayesian phase_credible_lo/phase_credible_hi (von Mises posterior, same residual pool). Set method = "consensus" to combine every engine bundled for a sample's (species, tissue, platform) via a weighted circular mean instead of committing to one, and qc_method = "dynamic" to flag low-quality samples with clockCoherence() (a per-dataset gene panel) instead of the fixed 16-gene amplitude flag — useful when the core clock genes themselves are not the ones affected (e.g. aging cohorts).

Benchmark a method against known ground truth

bm <- benchmarkPhase(se_truth,
  truth_col = "ZT",
  methods = c("timetable", "taufisher")
)
bm$summary   # one row per method: circular MAE, circular correlation, n
bm$residuals # one row per held-out sample

Check whether circadian time is confounding a design

You don't have to be a circadian researcher to care about this one. If your samples' collection times went unrecorded and they happen to be unbalanced across your comparison — cases collected in the morning, controls in the afternoon — every rhythmic gene will show an apparent condition effect that is really a clock effect.

phaseConfounding(se, ~ condition)
#>        term        type  n n_groups statistic effect_size p_value
#> 1 condition categorical 20        2  339.6259    8.132343   0.001

effect_size is in hours: here the two groups' mean collection times sit about eight hours apart. Phase that varies randomly within each group is not a confounder, and the test says so. All comparisons are circular (ZT23 and ZT1 are two hours apart, not twenty-two) and p-values come from permutation rather than parametric approximations.

What's wrapped so far

Method Reference Status
Molecular Timetable Ueda et al. 2004, PNAS Reimplemented from the method description (no maintained software release exists)
tauFisher Duan, Ngo et al. 2024, Nat Commun Wraps the authors' own R package (optional install, see above)
ZeitZeiger Hughey, Hastie & Butte 2016, Nucleic Acids Res Wraps the authors' own R package (optional install, see above)
TimeSignatR Braun et al. 2018, PNAS Wraps the authors' own R package (optional install, see above)

Reference models currently bundled for estimatePhase() are trained on GSE54650 (Zhang et al. 2014, PNAS; mouse microarray) and GSE54651 (same study, RNA-seq). The microarray set covers all 12 of GSE54650's tissues and two engines — Molecular Timetable and tauFisher, plus their consensus combinations (two weighting schemes, reachable via estimatePhase(method = "consensus", consensus_weights = ...)) — while the RNA-seq set bundles Molecular Timetable only (select with estimatePhase(platform = "rnaseq")). ZeitZeiger and TimeSignatR are wrapped and usable through their fit*()/predict*() functions and benchmarkPhase()/transferPhase(), but are deliberately not bundled as pre-trained estimatePhase() models (their fitted objects are much larger for no measured accuracy benefit). Call availableReferenceModels() for the exact current list. Additional labelled cohorts (human whole blood, GSE56931; mouse liver single-cell pseudobulk, GSE145197) used for benchmarking and transfer testing, but not yet bundled as trained reference models, will be distributed through a companion circaTimeData ExperimentHub package (in preparation, not yet public).

License

Artistic-2.0, matching Bioconductor's standard license requirements.

Citation

A manuscript describing circaTime's benchmarking results and uncertainty layer is in preparation. Citation details will be added here once available.