This package is PDAC-specific in scope. The bundled atlas, the anchor reference set, and every validated claim in the other vignettes are PDAC. Nothing here extends that scope.
What this vignette does establish is narrower and purely technical:
the R API does not hard-code PDAC. The 12-template
trajectory catalog, the 3-axis + 2-gate audit score, and the Evidence
Math layer operate on a per-stage expression model (Normal -> Early
-> Mid -> Late), so project_user_cohort() accepts a
cohort from another disease without modification. Whether the
results mean anything there is an open question this package
does not answer, and the “Limitations” section below says so
explicitly.
Read this as documentation of an interface boundary – useful if you want to reuse the machinery on your own data – and not as a claim that pdactrace generalises.
We use a synthetic CRC-flavored cohort (50 genes x
24 samples, balanced across stages and cohorts) so the vignette renders
without external dataset downloads. The exact same call signature works
on a real CRC cohort – e.g. TCGA-COAD, CPTAC-COAD, or GSE39582 – as long
as you supply a stage column with values in
c("Normal", "Early", "Mid", "Late") (Appendix A in
vignette("user_cohort_extension") maps AJCC and TNM to
these four levels).
# 50 genes x 24 samples; 6 per stage; 2 synthetic cohorts.
n_genes <- 50
n_per <- 6
stages <- factor(rep(c("Normal", "Early", "Mid", "Late"),
each = n_per),
levels = c("Normal", "Early", "Mid", "Late"))
cohorts <- rep(c("CohortA", "CohortB"), length.out = length(stages))
# Plant a CRC-specific Early-onset signal in the first 8 genes:
# log-fold change increases monotonically from Normal to Early
# with a small additional bump at Mid. This is the kind of
# signal pdactrace's `Early_Burst_Up` template should pick up.
counts <- matrix(rnbinom(n_genes * length(stages),
size = 5, mu = 50),
nrow = n_genes, ncol = length(stages),
dimnames = list(paste0("CRC_GENE_", seq_len(n_genes)),
paste0("S", seq_along(stages))))
# Inject Early-onset signal into 8 genes:
multiplier <- ifelse(stages == "Normal", 1,
ifelse(stages == "Early", 4,
ifelse(stages == "Mid", 4.5, 4.5)))
for (g in 1:8) counts[g, ] <- as.integer(counts[g, ] * multiplier)
coldata <- data.frame(
sample = colnames(counts),
stage = as.character(stages),
cohort = cohorts,
stringsAsFactors = FALSE)
table(coldata$stage)
#>
#> Early Late Mid Normal
#> 6 6 6 6project_user_cohort() does not care that the cohort is
CRC and not PDAC. The function takes the stage-aware counts, fits DESeq2
LRT, classifies the per-gene profile against the 12-template catalog,
assembles user evidence, and runs the audit score.
res_crc <- project_user_cohort(
rna = counts,
coldata = coldata,
stage_col = "stage",
cohort_col = "cohort")
res_crc
#> <pdactrace_user_projection>
#> Input: 50 genes; protein layer: no
#> Audit: 50 genes scored
#> Classes: high=2 supp_unc=4 penalized=0 excluded=0 low=44
# The 8 planted Early-onset genes should dominate the high-rank list:
top_crc <- res_crc$audit[order(-audit_score)][1:10]
top_crc[, .(gene_symbol, audit_class, rna_pattern, audit_score)]The exact same call works on a real CRC cohort (e.g. TCGA-COAD counts
+ clinical stage table mapped via the AJCC harmonisation in Appendix A
of vignette("user_cohort_extension")).
| Layer | What is cancer-specific | What stays the same |
|---|---|---|
| Trajectory catalog (12 templates) | nothing – shapes are biology-agnostic | applies unchanged |
| 3-axis + 2-gate audit rule | nothing – operates on user evidence | applies unchanged |
| External anchor set | PDAC-specific (30 anchors curated for PDAC serum biomarkers) | not portable; users projecting their own cancer should curate their own anchor set |
| Translation class A / B / C | uses the bundled atlas’s RNA direction as the tissue reference | re-derive from the user’s own RNA pattern when projecting non-PDAC |
Bundled atlas (pdactrace_reference) |
PDAC-only (10,113 genes from PDAC cohorts) | replace with user’s projection for non-PDAC analysis |
The framework’s three “always-portable” components are:
classify_trajectory()).compute_audit_score() over user
evidence).evidence_math() /
compare_genes()).The two “swap-out” components are:
anchors = in
evaluate_anchor_enrichment()).For PDAC, project_user_serum_cohort()’s
link_to_atlas = TRUE joins your serum data against
pdactrace_reference’s RNA direction to assign Class A / B /
C. For a non-PDAC cohort, the correct reference is your own RNA
projection, not pdactrace’s PDAC atlas. Pass
link_to_atlas = FALSE and assemble the class yourself by
joining on the user RNA pattern:
# Fictional CRC serum cohort (PDAC -> COL: 'CRC' label, 'HC')
serum_summary <- project_user_serum_cohort(
intensity = my_crc_serum_intensity,
coldata = my_crc_serum_coldata,
pdac_label = "CRC", # use your cancer's group label
hc_label = "HC",
link_to_atlas = FALSE) # do NOT join pdactrace's PDAC atlas
# Then assemble class yourself:
# Class A if user_rna_dir == sign(serum_log2fc_PDAC_vs_HC), B if
# opposite, C otherwise.This vignette uses a synthetic cohort to demonstrate the call signature. For a real cross-cancer publication study you would:
The demonstration here establishes that the framework’s R API accepts non-PDAC input, not that the bundled PDAC atlas’s results generalize to other cancers (they should not – that would be a different study, with its own cohorts and its own anchor set).
sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#>
#> Matrix products: default
#> BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so; LAPACK version 3.12.0
#>
#> locale:
#> [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
#> [3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
#> [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
#> [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
#> [9] LC_ADDRESS=C LC_TELEPHONE=C
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
#>
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] data.table_1.18.6.1 pdactrace_0.99.25 rmarkdown_2.32
#>
#> loaded via a namespace (and not attached):
#> [1] sass_0.4.10 generics_0.1.4
#> [3] SparseArray_1.13.3 lattice_0.23-1
#> [5] digest_0.6.39 magrittr_2.0.5
#> [7] evaluate_1.0.5 grid_4.6.1
#> [9] RColorBrewer_1.1-3 fastmap_1.2.0
#> [11] jsonlite_2.0.0 Matrix_1.7-6
#> [13] scales_1.4.0 jquerylib_0.1.4
#> [15] abind_1.4-8 cli_3.6.6
#> [17] rlang_1.3.0 XVector_0.53.0
#> [19] Biobase_2.73.2 withr_3.0.3
#> [21] cachem_1.1.0 DelayedArray_0.39.7
#> [23] yaml_2.3.12 otel_0.2.0
#> [25] S4Arrays_1.13.1 tools_4.6.1
#> [27] dplyr_1.2.1 ggplot2_4.0.3
#> [29] SummarizedExperiment_1.43.0 BiocGenerics_0.59.12
#> [31] buildtools_1.0.0 vctrs_0.7.3
#> [33] R6_2.6.1 matrixStats_1.5.0
#> [35] stats4_4.6.1 lifecycle_1.0.5
#> [37] Seqinfo_1.3.2 S4Vectors_0.51.10
#> [39] IRanges_2.47.5 pkgconfig_2.0.3
#> [41] pillar_1.11.1 bslib_0.12.0
#> [43] gtable_0.3.6 glue_1.8.1
#> [45] tidyselect_1.2.1 tibble_3.3.1
#> [47] xfun_0.61 GenomicRanges_1.65.4
#> [49] sys_3.4.3 MatrixGenerics_1.25.0
#> [51] knitr_1.52 farver_2.1.2
#> [53] htmltools_0.5.9 labeling_0.4.3
#> [55] maketools_1.3.2 compiler_4.6.1
#> [57] S7_0.2.2