Package 'pdactrace'

Title: Stage-Aware PDAC Atlas and Prospectively Frozen Tissue-to-Serum Biomarker Audit Framework
Description: pdactrace implements a transparent, deterministic claim-audit framework for stage-aware pancreatic ductal adenocarcinoma (PDAC) tissue-to-blood biomarker evidence. It integrates per-stage trajectory matching across bulk RNA-seq, tissue proteomics, single-cell origin, serum proteomics, and pancreatitis context, with explicit anchor-enrichment evaluation against a curated external reference set. The framework comprises a 12-template competitive trajectory catalog, a 3-axis + 2-gate reference score, an Evidence Math layer, TRACE-D tissue-to-serum directional evidence concordance, claim-tier classification, and weight-robustness and Pareto support audits. A prespecification layer supports freezing candidate scores, directions and a decision rule under a content hash before outcome labels are opened, with a distinct inconclusive terminal state when a frozen gate fails. Scope is deliberately PDAC-specific: the bundled atlas, the anchor reference set and every validated claim are PDAC, and no result is claimed for any other disease. Separately, no supervised classifier is trained or shipped, because validated non-circular early-detection ground truth is unavailable in PDAC.
Authors: Jibeom Ko [aut, cre] (ORCID: <https://orcid.org/0009-0002-8630-1973>)
Maintainer: Jibeom Ko <[email protected]>
License: MIT + file LICENSE
Version: 0.99.25
Built: 2026-09-28 15:49:32 UTC
Source: https://github.com/BiocStaging/pdactrace

Help Index


pdactrace . PDAC-TRACE: queryable stage-aware tissue-to-serum biomarker atlas

Description

Central message: PDAC tissue biomarker is not always a serum-up biomarker - tissue-derived candidates can preserve, invert, or decouple when projected into serum.

Details

pdactrace integrates public PDAC multi-omics data - bulk RNA-seq, tissue proteomics, scRNA cell origin, serum proteomics, and pancreatitis context - under a 12-template competitive trajectory framework (Normal -> Early -> Mid -> Late) where genes compete against 11 alternative shapes (Early × 4 + Mid × 4 + Late × 2 + Monotonic × 2) and only Early-onset best-matches are surfaced as resectable-stage candidates. The current release adds a **3-axis

  • 2-gate audit framework** (v0.3.0) and a user-data extension API + per-stage Wald padj (v0.4.0).

12-template framework with Early × 4 surface

Each gene's z-scored 4-point N/E/M/L trajectory is matched against 12 pre-declared templates by Pearson-rho argmax. A template call is retained only when the best-match rho passes rho_cutoff (0.85 by default in classify_trajectory()). Only the 4 Early-onset best-matches are surfaced in pdactrace_reference$rna_pattern:

  • Early_Burst_Up - Normal low -> Early up -> sustained

  • Early_Loss_Down - Normal high -> Early down -> sustained

  • Early_Peak - Early peak -> Mid/Late decline

  • Early_Trough - Early trough -> Mid/Late recovery

Non-Early best-matches (Mid × 4 / Late × 2 / Monotonic × 2) are excluded from the surface but flagged via excluded_mid_pattern, excluded_late_pattern, excluded_monotonic_pattern for transparent provenance. Including the 8 non-Early templates in the matching step is the v0.4.0 design discipline: a gene must beat 11 alternatives before being surfaced — this widens the negative-evidence pool and correctly demotes leaky candidates (e.g., GAPDH, CDH13 → Monotonic_Up).

Core lookup API

Trajectory scoring (user RNAseq input)

Filter audit + visualization

Class B inverse policy

translation_class == "B" is treated as a rare, manually curated failure mode of tissue-to-serum translation, not a predicted class. Even in v0.2.0+, Class B will be flagged via nearest-reference evidence rather than predicted.

Citation

Ko J. pdactrace: a queryable stage-aware PDAC tissue-to-serum biomarker reference atlas. (2026)

Author(s)

Maintainer: Jibeom Ko [email protected] (ORCID)

See Also

Useful links:


Align a single patient's omics profile against atlas stage trajectories

Description

Reports how a single patient's tumor-vs-matched-normal log2 fold-change profile aligns with the frozen pdactrace atlas's stage-trajectory axes (Early / Mid / Late), expressed as a Pearson correlation against each cohort-adjusted stage-effect vector and a complementary audit-weighted vote share. The result is an alignment readout, not a stage prediction. No supervised model has been fit — the atlas is frozen and the function is deterministic given (input, atlas).

Usage

align_patient_profile(
  rna_logfc,
  prot_logfc = NULL,
  weight_by = c("audit_score", "uniform"),
  min_audit_score = 0.3,
  top_n_genes = 500L,
  gene_selection = c("highest_audit", "highest_abs_beta"),
  min_genes = 50L,
  layer_combine = c("separate", "rna_only"),
  reference = NULL
)

align_patient(...)

Arguments

rna_logfc

Named numeric vector. Names are HGNC gene symbols; values are log2(patient_tumor / matched_normal). Missing values are dropped. Required.

prot_logfc

Optional named numeric vector with the same format for the protein layer. When supplied, returns a categorical concordance table in ⁠$prot⁠ (the atlas does not carry per-stage numeric protein effect sizes, so a numeric alignment is not possible).

weight_by

Per-gene weight in both rho and vote_share. One of "audit_score" (default — uses audit_score from the bundled atlas) or "uniform" (equal weights).

min_audit_score

Minimum atlas audit_score to include in the gene dictionary. Default 0.3 matches the low -> supported_uncertain boundary in the audit framework.

top_n_genes

Optional integer. Cap genes used at the top N by gene_selection. NULL = use all eligible genes.

gene_selection

If top_n_genes is set, rank by either "highest_audit" (default) or "highest_abs_beta" (largest max-stage effect size).

min_genes

Warn-only floor. If the number of overlapping genes is below this (default 50), a warning is issued. The function hard-errors below 10 (rho is meaningless on so few points).

layer_combine

Either "separate" (default — RNA and protein reported separately, never combined into one β-space metric) or "rna_only" (skip the protein arm even if prot_logfc is supplied).

reference

Optional atlas data.table override (advanced; for testability). Default NULL uses the bundled pdactrace_reference.

...

Forwarded to align_patient_profile() when invoked through the align_patient() short alias.

Details

Two complementary signals are reported, never collapsed into one decision:

Primary: cor_to_stage_axis

Audit-weighted Pearson rho between the patient's per-gene log2FC vector and each of the three stage axes rna_beta_E, rna_beta_M, rna_beta_L (with Fisher-z 95% CI). Invariant to per-patient global shifts (library-size / age / FFPE drift). Has a closed-form null distribution.

Secondary: vote_share

Audit-weighted fraction of genes whose closest atlas β is each stage's β. Less rigorous but interpretable for non-statistical readers; surfaces disagreement-with-rho as a diagnostic.

Misalignment with all three stage axes is a legitimate output and does not imply Normal — it indicates the patient does not resemble any of the atlas's PDAC stage profiles. Limitations: rho is unstable below ~50 overlapping genes; the protein layer reports only categorical concordance because the atlas does not carry per-stage numeric protein effect sizes (use layer_combine = "rna_only" to skip the protein arm).

Value

A list of class pdactrace_patient_alignment with elements ⁠$rna⁠ (4-row data.table with stage / rho / CI / vote_share / weighted distance / n_genes_used), ⁠$prot⁠ (data.table or NULL), ⁠$summary⁠ (one-line character), and ⁠$attrs⁠ (named list of parameters and provenance metadata).

Examples

# Synthetic Late-stage patient profile
  set.seed(7)
  ref <- pdactrace:::.get_reference()
  pool <- ref[!is.na(rna_beta_L) & audit_score >= 0.3][seq_len(300)]
  patient <- setNames(
    pool$rna_beta_L + rnorm(nrow(pool), sd = 0.3),
    pool$gene_symbol)
  aln <- align_patient_profile(patient, top_n_genes = 200)
  print(aln)
  aln$rna

Convert pdactrace_reference into a SummarizedExperiment

Description

Returns a SummarizedExperiment view of the bundled v0.4.0 atlas in which the four stage-effect coefficient columns (rna_beta_N, rna_beta_E, rna_beta_M, rna_beta_L) become a 4-column assay, the standard errors rna_lfcSE_E/M/L (with NA for the Normal reference column) become a parallel assay, and all of the per-gene metadata (audit components, RNA / protein trajectory pattern, translation class, scRNA cell origin, serum direction, meta-analysis I2, etc.) goes into rowData(). The colData() carries one row per stage with a reference_level flag for the Normal column.

Usage

as_summarized_experiment(reference = NULL)

Arguments

reference

Optional override of the bundled pdactrace_reference. Default NULL uses the bundled atlas. Useful for unit tests on a synthetic atlas-shaped table.

Details

Intended use: Bioconductor-native consumption of the atlas with assay(), rowData(), subsetByOverlaps() (when paired with a separate ranges object), and downstream tooling that expects the SE container — without forcing a one-time mass conversion of the bundled object (which remains a data.table for fast query-based use cases).

Value

A SummarizedExperiment with:

  • 2 assays — rna_beta (10,113 genes by 4 stages) and rna_lfcSE (same shape; NA in the Normal column).

  • colData — 4 rows: stage = c("Normal", "Early", "Mid", "Late"), reference_level = c(TRUE, FALSE, FALSE, FALSE).

  • rowData — every non-stage-axis column of the bundled atlas (~109 columns: identifiers, audit components, trajectory pattern calls, meta-analysis summaries, etc.).

  • metadata — list(atlas_version, build_date, n_cohorts, ...).

Examples

se <- as_summarized_experiment()
dim(se)
SummarizedExperiment::assayNames(se)


  if (requireNamespace("SummarizedExperiment", quietly = TRUE)) {
    se <- as_summarized_experiment()
    se
    SummarizedExperiment::assay(se, "rna_beta")[1:3, ]
    head(SummarizedExperiment::rowData(se))
  }

Assemble user multi-omics inputs into a per-gene evidence table

Description

Combines optional per-layer user inputs into a single per-gene evidence data.table whose column schema is the subset of the bundled pdactrace_reference atlas that compute_audit_score(evidence = ...) consumes. Layers the user does not supply are filled with NA; the downstream feature constructor in .audit_compute_features() is NA-tolerant and degrades gracefully (a layer absent → that layer contributes zero to evidence_strength; cross-layer concordance falls to the available pair; etc.).

Usage

assemble_user_evidence(
  rna_fit = NULL,
  prot_fit = NULL,
  scrna_summary = NULL,
  serum_summary = NULL,
  signal_peptide = NULL,
  cross_cohort_agreement = NULL,
  max_I2 = NULL
)

Arguments

rna_fit

Optional output of classify_trajectory() (i.e., a data.table with gene_symbol, rna_pattern, rna_pattern_rho, lrt_padj).

prot_fit

Optional output of classify_protein_trajectory() (a data.table with gene_symbol, prot_pattern, prot_pattern_rho).

scrna_summary

Optional data.table with gene_symbol and cell_origin_top columns. Cell-of-origin label per gene.

serum_summary

Optional data.table with gene_symbol, serum_detected (logical), and serum_log2fc_PDAC_vs_HC (numeric).

signal_peptide

Optional character vector of gene symbols that carry a signal peptide (UniProt SP-positive). Genes outside this set are flagged flt_signal_peptide = FALSE.

cross_cohort_agreement

Optional named numeric vector, names = gene symbols, values in ⁠[0, 1]⁠ representing fraction of cohorts agreeing on direction. When NULL, defaults to 1.0 for genes with rna_pattern set and NA otherwise.

max_I2

Optional named numeric vector, names = gene symbols, values are max meta-analysis I². When NULL, defaults to 0 (no heterogeneity penalty).

Value

A data.table with the column schema consumed by compute_audit_score(evidence = ...).

Examples

rna_fit <- data.table::data.table(
  gene_symbol = c("FAKE_UP", "FAKE_DOWN"),
  rna_pattern = c("Early_Burst_Up", "Early_Loss_Down"),
  rna_pattern_rho = c(0.95, 0.92),
  lrt_padj = c(1e-4, 1e-3))
ev <- assemble_user_evidence(rna_fit = rna_fit)
compute_audit_score(evidence = ev)

Atlas metadata

Description

Version, snapshot, source-script, and build metadata for the bundled pdactrace reference atlas.

Usage

atlas_metadata

Format

A named list.


Atlas provenance (version, build date, source repo, DOIs)

Description

Returns a named list summarising where the bundled atlas came from: package version, atlas snapshot date, the manuscript monorepo URL, both Zenodo concept DOIs (the package archive and the manuscript-monorepo archive), and the cohort count. Designed as a one-call provenance dossier for reviewers and citing authors.

Usage

atlas_provenance()

Value

Named list with fields package_version, atlas_version, build_date, n_cohorts, package_repo, manuscript_repo, package_doi, manuscript_doi, and data_layers. Missing metadata fields are returned as NA_character_.

Examples

atlas_provenance()
atlas_provenance()$package_doi

Build a transparent evidence graph for one gene

Description

build_evidence_graph() exposes the audit-score evidence as a small node/edge graph. It is intentionally descriptive: pdactrace v0.3.0 does not train a graph neural network, and the graph is used to make the hand-engineered evidence aggregation auditable.

Usage

build_evidence_graph(gene_symbol)

Arguments

gene_symbol

HGNC gene symbol.

Value

A list with nodes, edges, and score data.tables, or NULL invisibly if the gene is not in the atlas.

Examples

g <- build_evidence_graph("LTBP1")
head(g$nodes)
g$score

Summarize audit-score case-study genes

Description

Combines v0.2 tier, v0.3 score, uncertainty, anchor provenance, and artifact/rescue calls for manuscript case-study panels.

Usage

case_study(genes)

Arguments

genes

Character vector of HGNC gene symbols.

Value

A data.table with one row per matched gene.

Examples

case_study(c("THBS2", "LTBP1", "ALB", "GAPDH"))

Classify tissue-to-blood biomarker claim tiers

Description

classify_claim_tier() separates evidence strength from claim strength. A gene can have strong stage-aware tissue evidence while still receiving a conservative blood-biomarker claim such as translation_unknown or confounded.

Usage

classify_claim_tier(
  atlas = NULL,
  genes = NULL,
  tau_tissue = 0.5,
  tau_serum = 0.1
)

Arguments

atlas

Optional atlas-like data table. Defaults to the bundled pdactrace_reference.

genes

Optional character vector of genes to return. Ranking and thresholds are computed on the supplied atlas, then filtered.

tau_tissue

Minimum absolute tissue effect used by compute_trace_d().

tau_serum

Minimum absolute serum log2 fold-change used by compute_trace_d() and the serum-signal flag. Claim tiers use strict TRACE-D only: a gene is serum_concordant when its measured serum log2FC passes tau_serum in the tissue direction. The historical translation_class label, which records only the sign of the serum change, is returned as legacy_translation_class for reference and does not raise the tier.

Value

A data.table with one row per gene and claim audit columns.

Examples

classify_claim_tier(genes = c("LGALS3BP", "LTBP1", "ALB", "GAPDH"))

Classify each protein's trajectory against the 12-template catalog

Description

Protein-side wrapper of classify_trajectory(): takes the output of fit_stage_de_protein(), runs the same shared template-matching engine (12 templates: Early x 4 + Mid x 4 + Late x 2 + Monotonic x 2), and renames the output column rna_pattern to prot_pattern so assemble_user_evidence() can consume both layers without colliding on column names. As with the RNA side, the atlas surface restricts visible prot_pattern calls to Early x 4; non-Early best-matches are recorded internally but not surfaced.

Usage

classify_protein_trajectory(fit, rho_cutoff = 0.85, sig_only = TRUE)

classify_prot_trajectory(...)

Arguments

fit

A data.table returned by fit_stage_de_protein().

rho_cutoff

Numeric. Minimum Pearson rho for assignment. Default 0.85. The bundled protein layer (phase34) was built with a cutoff of 0.75; pass rho_cutoff = 0.75 to reproduce its labels.

sig_only

Logical. If TRUE (default), restrict to lrt_significant == TRUE rows.

...

Forwarded to classify_protein_trajectory() when invoked through the classify_prot_trajectory() short alias.

Value

A data.table with columns gene_symbol, prot_pattern, prot_pattern_rho, prot_pattern_rho_runner_up. The remaining columns of the input fit are preserved.

Examples

prot_fit <- data.table::data.table(
  gene_symbol = c("P1", "P2"),
  beta_N = 0, beta_E = c(2, -2), beta_M = c(0.5, -0.5),
  beta_L = 0, lrt_padj = c(0.01, 0.02),
  lrt_significant = TRUE)
classify_protein_trajectory(prot_fit, sig_only = FALSE)


if (requireNamespace("limma", quietly = TRUE)) {
  data(toy_protein)
  data(toy_coldata)
  prot_fit <- fit_stage_de_protein(toy_protein, toy_coldata$stage,
                                   toy_coldata$cohort)
  prot_pat <- classify_protein_trajectory(prot_fit)
}

Classify each gene's trajectory against the 12-template catalog

Description

Takes the output of fit_stage_de() and matches each LRT-significant gene's z-scored 4-point profile (beta_N, beta_E, beta_M, beta_L) against the canonical 12-template catalog (Early × 4 + Mid × 4 + Late × 2 + Monotonic × 2; v0.4.0). The best-match template is assigned if rho >= rho_cutoff. The atlas surface (rna_pattern in pdactrace_reference) restricts visible calls to Early × 4 only — Mid / Late / Monotonic best-matches are recorded internally but flagged via ⁠excluded_*_pattern⁠ columns rather than surfaced. Including the 8 non-Early templates in the matching step is a deliberate design choice for honest competition: a gene must beat 11 alternatives before it is surfaced as Early.

Usage

classify_trajectory(fit, rho_cutoff = 0.85, sig_only = TRUE)

Arguments

fit

A data.table returned by fit_stage_de().

rho_cutoff

Numeric. Minimum Pearson rho for assignment. Default 0.85. The bundled pdactrace_reference was built with a cutoff of 0.75; pass rho_cutoff = 0.75 to reproduce its labels. With four points the cutoff selects a shape, not a signal: for a gene with no stage effect the best of the 12 templates exceeds 0.85 about 58% of the time, so apply it only to LRT-significant genes (the default sig_only = TRUE).

sig_only

Logical. If TRUE (default), restrict to lrt_significant == TRUE rows.

Value

A data.table with the input plus columns:

  • rna_pattern - character: best 12-template match or NA. Note: this returns the raw best-match label (any of the 12 templates). The atlas-surfaced version in pdactrace_reference$rna_pattern is filtered to Early × 4 only; non-Early calls are flagged via excluded_mid_pattern / excluded_late_pattern / excluded_monotonic_pattern.

  • rna_pattern_rho - numeric: Pearson rho with best template

  • rna_pattern_rho_runner_up - second-best rho (gap detection)

Examples

fit <- data.table::data.table(
  gene_symbol = c("G1", "G2"),
  beta_N = 0, beta_E = c(2, -2), beta_M = c(0.5, -0.5),
  beta_L = 0, lrt_padj = c(0.01, 0.02),
  lrt_significant = TRUE)
classify_trajectory(fit, sig_only = FALSE)


if (requireNamespace("DESeq2", quietly = TRUE)) {
  data(toy_counts)
  data(toy_coldata)
  fit <- fit_stage_de(toy_counts, toy_coldata$stage, toy_coldata$cohort)
  pat <- classify_trajectory(fit, rho_cutoff = 0.85)
  table(pat$rna_pattern)
}

Compare multiple candidate genes side-by-side

Description

Returns a single data.table row-per-gene with the columns most useful for panel-design discussion: audit class + score, RNA and protein trajectory pattern, translation class, dominant scRNA cell origin, serum-detectability flags, and a redundancy hint based on shared cell origin and shared RNA pattern. The result is sorted by audit_score (descending) so the strongest candidates surface first.

Usage

compare_candidates(gene_symbols)

Arguments

gene_symbols

Character vector of HGNC gene symbols (length >= 2; length-1 input is allowed but the comparison is trivial).

Details

Input genes that are not in the bundled atlas are returned with audit_class = NA, so the call never fails on stray symbols — the returned table makes the gap explicit.

Value

A data.table (one row per gene) with columns:

  • gene_symbol, audit_class, audit_score

  • rna_pattern, prot_pattern, translation_class

  • cell_origin_top, serum_detected, serum_log2fc_PDAC_vs_HC

  • max_I2_meta, redundancy_with — the latter lists other genes in the input set that share rna_pattern and cell_origin_top (semicolon-separated, "" if none).

Examples

compare_candidates(c("LGALS3BP", "LTBP1", "SERPINA1", "ALB", "GAPDH"))

Side-by-side Evidence Math comparison across multiple genes

Description

Calls evidence_math() for each input gene and pivots the per-axis lists into a tidy data.table. Useful for assembling the "candidate vs. competitors" comparison tables that show up in the manuscript and in report_gene() output.

Usage

compare_genes(gene_symbols, axes = NULL, wide = FALSE, reference = NULL)

Arguments

gene_symbols

Character vector of HGNC gene symbols.

axes

Optional character vector to filter the axis set reported. Defaults to all axes from evidence_math(). Recognised values: "trajectory_fit", "effect_magnitude", "cohort_consistency", "rna_protein_coupling", "serum_bridge", "cell_specificity", "filter_survival", "clinical_role".

wide

Logical. If TRUE, returns one row per gene with axis.metric columns. Default FALSE (long form).

reference

Optional data.table to inject in place of the bundled atlas (used by tests).

Details

By default returns a long table with one row per (gene x axis x metric) tuple — friendly to data.table::dcast() if you need a custom wide layout. Set wide = TRUE for the convenience pivot to one row per gene with metric columns (axis.metric, e.g. trajectory_fit.delta_rho).

Note: this function reports the math evidence per axis. For the audit-score-driven candidate ranking with redundancy grouping, see compare_candidates() (different layer, same atlas).

Value

A data.table. Long form columns: gene, axis, metric, value (character; numerics formatted to 4 sig figs to keep the table renderable). Wide form: gene followed by axis.metric columns.

See Also

evidence_math(), explain_gene(), compare_candidates().

Examples

compare_genes(c("LGALS3BP", "LTBP1", "TIMP1"))
compare_genes(c("LGALS3BP", "LTBP1"),
              axes = c("trajectory_fit", "rna_protein_coupling"),
              wide = TRUE)

Compute the frozen pdactrace audit score (3-axis + 2-gate)

Description

Aggregates the seven implementation features into three transparent evidence axes (evidence_strength, biological_coherence, translational_relevance) and applies two pre-specified reliability gates (leakage_gate, heterogeneity_gate), then rescales by the largest gated score in the scored table: audit_score = positive_score * leakage_gate * heterogeneity_gate / max(...). The score is therefore relative to the best-scoring gene of the table it is computed on (the bundled atlas by default, or evidence). Each gene receives one of five audit_class labels. Gates take precedence over the score: excluded (leakage gate 0), penalized (leakage gate below 1), supported_uncertain (heterogeneity gate below 1), and otherwise high_confidence (score >= 0.5), supported_uncertain (>= 0.3) or low.

Usage

compute_audit_score(gene_symbol = NULL, evidence = NULL)

Arguments

gene_symbol

Optional character vector of HGNC gene symbols. When NULL (default) returns the full atlas.

evidence

Optional user-supplied evidence table (e.g., produced by assemble_user_evidence()) to score in place of the bundled atlas. When NULL (default), the frozen v0.3.0 reference atlas is used; the framework discipline (frozen weights, gates) applies identically to either input.

Details

This is not a supervised biomarker predictor; the formula is pre-specified and external anchors are used only post-freeze for enrichment evaluation. The seven implementation features are kept as supplementary detail; see extract_graph_features().

Value

A data.table with evidence_strength, biological_coherence, translational_relevance, leakage_gate, heterogeneity_gate, positive_score, audit_score (rescaled to the table maximum), and audit_class.

Examples

compute_audit_score(c("LGALS3BP", "LTBP1", "ALB", "GAPDH"))

Classify Pareto evidence support

Description

Convenience wrapper around compute_pareto_layers() that converts numeric layers into manuscript-facing evidence classes.

Usage

compute_pareto_class(atlas = NULL, genes = NULL, top_n = 2000L)

Arguments

atlas

Optional atlas-like data table.

genes

Optional genes to return.

top_n

Candidate pool size passed to compute_pareto_layers().

Value

A data.table with Pareto layer, rank, and class.

Examples

compute_pareto_class(genes = c("LGALS3BP", "LTBP1"), top_n = 200)

Compute weight-free Pareto layers on the three evidence axes

Description

compute_pareto_layers() implements layered non-dominated sorting on audit_evidence_strength, audit_biological_coherence, and audit_translational_relevance. The two reliability gates (audit_leakage_gate, audit_heterogeneity_gate) are applied as a hard pre-filter rather than as weights, mirroring the "axes graded, gates reliability filter" structure of the frozen 3+2 audit framework. Ranking is layer-first, with crowding distance (NSGA-II convention) as a within-layer tiebreaker and the frozen deterministic audit_score as a final deterministic tiebreaker.

Usage

compute_pareto_layers(
  atlas = NULL,
  axes = c("audit_evidence_strength", "audit_biological_coherence",
    "audit_translational_relevance"),
  gate_cols = c("audit_leakage_gate", "audit_heterogeneity_gate"),
  score_col = "audit_score",
  gate_filter = TRUE,
  top_n = 2000L
)

Arguments

atlas

Optional user-supplied atlas (defaults to the bundled pdactrace_reference). When non-NULL it must contain the columns referenced by axes, gate_cols, and score_col.

axes

Character(3) of the axis column names. Defaults to the three frozen axes.

gate_cols

Character(2) of the gate column names. Defaults to c("audit_leakage_gate", "audit_heterogeneity_gate").

score_col

Column used both to restrict the candidate pool to the top top_n rows and as a deterministic tiebreaker after crowding distance. Defaults to "audit_score".

gate_filter

Logical; when TRUE (default), genes with any gate value ⁠< 1⁠ are excluded from the Pareto candidate pool and recorded with pareto_excluded_by_gate = TRUE.

top_n

Integer; restrict the Pareto candidate pool to the top top_n rows by score_col. Defaults to 2000L which comfortably covers the BiB headline top-100 anchor enrichment.

Details

This function is complementary to compute_audit_score() and does not modify the frozen weighted formula. Use it when reviewers ask whether the audit ranking is driven by the scalar weights 0.40 / 0.35 / 0.25 — Pareto layers expose a weight-free alternative.

Value

A data.table with one row per gene in atlas and the columns pareto_layer (integer; NA for excluded genes), crowding_distance (numeric; Inf for boundary points, NA for excluded), pareto_rank (integer 1..N within the candidate pool, NA for excluded), and pareto_excluded_by_gate (logical).

Examples

head(compute_pareto_layers(top_n = 200L))

Algorithm 1 — TRACE-D: Tissue-to-serum Directional Evidence Concordance

Description

compute_trace_d() deterministically types each gene by the direction concordance between tissue (RNA + Protein) and serum evidence, with an explicit pancreatitis-specificity annotation that separates PDAC-specific signal from shared-inflammation signal. The four classes ("A" direction-preserved, "B" direction-inverted, "C" decoupled, NA insufficient tissue evidence) coexist with the legacy translation_class column rather than replacing it (back-compatible).

Usage

compute_trace_d(
  atlas = NULL,
  tau_tissue = 0.5,
  tau_serum = 0.1,
  weights = c(tissue = 0.5, serum = 0.3, specificity = 0.2),
  legacy_translation = c("strict", "fallback")
)

Arguments

atlas

Optional atlas (defaults to the bundled pdactrace_reference). Must contain gene_symbol, rna_pattern, prot_pattern, max_abs_beta_meta, serum_log2fc_PDAC_vs_HC, serum_log2fc_Pan_vs_HC, serum_detected, flt_signal_peptide.

tau_tissue

Numeric; minimum ⁠|max_abs_beta_meta|⁠ for a tissue signal to count. Default 0.5.

tau_serum

Numeric; minimum ⁠|serum_log2fc_PDAC_vs_HC|⁠ for a serum signal to count. Default 0.1.

weights

Named numeric vector of length 3 summing to 1 (tissue, serum, specificity components of tracd_confidence). Defaults to c(tissue = 0.50, serum = 0.30, specificity = 0.20).

legacy_translation

One of "strict" or "fallback". In "strict" mode, TRACE-D uses only measured serum log2FC and the thresholds above. In "fallback" mode, rows with no strict TRACE-D call can inherit the legacy atlas translation_class A/B/C as a compatibility annotation; confidence then receives tissue-only credit and the decision path records legacy_translation.

Details

This is one of two domain-specific audit algorithms introduced in pdactrace v0.99.19; see compute_pareto_layers() for Algorithm 2. Neither algorithm introduces new statistical machinery — the contribution is the domain-specific combination of direction consensus, magnitude thresholds, and pancreatitis-specificity annotation packaged as a deterministic, reproducible audit layer.

Pancreatitis-specificity is annotation only by design: a Class A candidate flagged shared_inflammation is not demoted — downstream consumers apply stricter filters via list_candidates() if required.

Value

A data.table with one row per gene in atlas and the columns tracd_tissue_dir, tracd_serum_dir, tracd_class, tracd_confidence, tracd_pancreatitis_overlap_score, tracd_pancreatitis_specificity, tracd_tissue_weight, tracd_decision_path.

Examples

head(compute_trace_d())
compute_trace_d()[gene_symbol %in% c("LGALS3BP", "LTBP1", "GAPDH")]

Default trajectory templates (12-template catalog, v0.4.0)

Description

Built-in 12 hypothesis-driven trajectory templates used by classify_trajectory() and score_trajectory(): Early × 4 (Burst_Up, Loss_Down, Peak, Trough) + Mid × 4 (Plateau_Up, Plateau_Down, Peak, Trough) + Late × 2 (Burst_Up, Loss_Down) + Monotonic × 2 (Up, Down). The atlas surface (pdactrace_reference$rna_pattern) restricts visible best-match calls to Early × 4 only — non-Early calls are flagged via ⁠excluded_*_pattern⁠ columns. See ?early_pattern_names, ?mid_pattern_names_excluded for membership lists.

Usage

default_templates

Format

A named list of length 12, each element a numeric vector of length 4 (z-scored expected values across stages Normal / Early / Mid / Late).


Download the two large upstream phase CSVs into a local cache

Description

The bundled inst/extdata carries the six small downstream phase tables (phase2c / phase29 / phase42 / phase60 / phase77 / phase80) but not the two large upstream fits — phase33_deseq2_coef_12template.csv (~2.6 MB RNA fit) and phase34_protein_pooled_12template.csv (~0.9 MB protein fit) — to keep the software tarball small. Both are gene-level summary statistics and live in ⁠data-raw/⁠ of the public source repository, github.com/jibeomko/pdactrace. This helper fetches them on demand via raw GitHub URLs and caches them locally with BiocFileCache::BiocFileCache().

Usage

download_phase_csvs(
  target = c("both", "phase33", "phase34"),
  ref = "main",
  cache = NULL,
  verbose = TRUE
)

Arguments

target

Optional character. Either "phase33", "phase34", or "both" (default). Selects which CSV(s) to fetch.

ref

Git ref (branch / tag / commit) on jibeomko/pdactrace to pull from. Default "main".

cache

A BiocFileCache::BiocFileCache object. Default uses the user's standard cache.

verbose

Logical. If TRUE (default), prints a progress message per file.

Details

A git checkout does not need this helper: data-raw/build_reference.R and data-raw/build_protein_betas.R read both files from ⁠data-raw/⁠ directly.

Value

Invisibly, a named character vector mapping the requested target value(s) to the absolute cached file path(s).

Examples

args(download_phase_csvs)


  # Pull both upstream CSVs once; subsequent calls hit the cache:
  paths <- download_phase_csvs("both")
  # ... and feed them to the build chain:
  p33 <- data.table::fread(paths[["phase33"]])

Early-onset pattern names (the 4 atlas-surfaced patterns)

Description

The 4 patterns surfaced by pdactrace_reference$rna_pattern. Under v0.4.0 the trajectory classifier matches against 12 templates total (Early × 4 + Mid × 4 + Late × 2 + Monotonic × 2) but only Early-onset best-matches are surfaced — non-Early best-matches are flagged via excluded_mid_pattern, excluded_late_pattern, excluded_monotonic_pattern.

Usage

early_pattern_names()

early_patterns()

Value

character(4)

Examples

early_pattern_names()
early_patterns()  # short alias

Evaluate external-anchor enrichment in top-ranked genes

Description

External anchors are used only for evaluation. They are not used by the frozen v0.3.0 scoring rule.

Usage

evaluate_anchor_enrichment(
  top_n = c(50, 100, 200, 500, 1000),
  tier = c("primary", "secondary", "exploratory", "all"),
  score_col = c("audit_score", "audit_score_median", "pareto_rank",
    "pareto_stability_top1", "tracd_confidence"),
  evidence = NULL,
  anchors = NULL
)

anchor_enrichment(...)

Arguments

top_n

Integer vector of top-N cutoffs.

tier

Anchor tier: primary, secondary, exploratory, or all.

score_col

Ranking column. Defaults to deterministic audit_score.

evidence

Optional user-supplied evidence table (see assemble_user_evidence()). When NULL (default), the bundled reference atlas is used.

anchors

Optional user-supplied anchor table with columns gene, include_primary_eval, and include_secondary_eval. When NULL (default), the bundled pdactrace_external_anchors table is used.

...

Forwarded to evaluate_anchor_enrichment() when invoked through the anchor_enrichment() short alias.

Value

A data.table with hypergeometric enrichment results.

Examples

evaluate_anchor_enrichment(top_n = 100, tier = "secondary")

Evaluate Monte-Carlo Pareto stability under perturbed evidence

Description

evaluate_pareto_stability() recomputes the Pareto layer assignment across n_draws Monte Carlo perturbations of the underlying evidence (pattern-correlation rho, cohort agreement, and between-cohort I^2), using the same perturbation kernel as propagate_uncertainty(). For each gene it returns the proportion of draws in which the gene appears in Pareto layer 1 of the gate-passing candidate pool. This quantifies axis-agnostic ranking robustness — a complement to the score-space CI returned by propagate_uncertainty().

Usage

evaluate_pareto_stability(
  atlas = NULL,
  n_draws = 1000L,
  axes = c("audit_evidence_strength", "audit_biological_coherence",
    "audit_translational_relevance"),
  gate_cols = c("audit_leakage_gate", "audit_heterogeneity_gate"),
  score_col = "audit_score",
  top_n = 2000L,
  seed = 20250506L
)

Arguments

atlas

Optional atlas (see compute_pareto_layers()).

n_draws

Integer number of Monte Carlo draws. Defaults to 1000L, matching the bundled MC summary.

axes

Character(3) axis columns; see compute_pareto_layers().

gate_cols

Character(2) gate columns.

score_col

Column used to restrict the candidate pool to the top top_n rows by deterministic ranking, per draw.

top_n

Integer pool size; see compute_pareto_layers().

seed

Random seed.

Details

This function is the MC analogue of compute_pareto_layers(); it does not modify the frozen audit score in any draw.

Value

A data.table with one row per gene in atlas, with columns pareto_layer_median, pareto_stability_top1, pareto_layer_lo95, pareto_layer_hi95, pareto_top10_pct_stability. Genes outside the gate-passing pool in every draw receive NA median/CI but a stability of 0.

Examples

head(evaluate_pareto_stability(n_draws = 10L, top_n = 50L))


  evaluate_pareto_stability(n_draws = 50L)

Per-axis mathematical evidence for one gene

Description

Returns the underlying numerical evidence values that fed the frozen v0.3.0 audit decisions, organised by axis. This is the canonical machine-readable form sitting between the plain-English phase-tag layer (format_provenance()) and the audit-score decomposition (explain_score()). The point is to expose what the math actually says – not just labels, not just a composite score.

Usage

evidence_math(gene_symbol, reference = NULL)

Arguments

gene_symbol

HGNC gene symbol (length-1 character).

reference

Optional data.table to inject in place of the bundled atlas (used by tests and downstream pipelines).

Details

Every value reported here is either read directly from pdactrace_reference (or pdactrace_protein_betas for the protein side) or derived by a single named arithmetic operation on those columns. No new fitting, no recomputation. The function name evidence_math reflects that it is the mathematical view of the same evidence captured by the verbose / provenance text layers.

Value

Invisibly, a list with one element per axis (named-list of named scalars). Suitable for str() / programmatic use. The full 12-rho vector for non-Early best-match genes can be recovered with score_trajectory() using the bundled fit.

Axes

trajectory_fit

Best 12-template Pearson rho, runner-up rho, and the specificity margin delta_rho = rho_best - rho_runner_up. A delta_rho above ~0.10 indicates clean template specificity (the gene's profile is closer to its matched template than to any other).

effect_magnitude

Euclidean norm and max-absolute of the per-stage beta vector, on both RNA and protein sides. The norm uses the 3-vector ⁠(beta_E, beta_M, beta_L)⁠ because beta_N = 0 is a reference-level constant by DESeq2 / limma convention, not a measured effect.

cohort_consistency

Stouffer Z, BH-adjusted p, fraction of contributing cohorts agreeing with the meta sign, and the maximum heterogeneity I-squared across the three contrasts N-vs-E, M-vs-E, L-vs-E (which feeds the heterogeneity gate).

rna_protein_coupling

Cosine similarity between the RNA and tissue-protein beta vectors over ⁠(E, M, L)⁠. +1 means same template + same direction; -1 means opposite. Reported alongside the categorical prot_tier and the boolean rnaprot_concordant flag.

serum_bridge

Translation class A / B / C (A = same-direction RNA<->serum, B = opposite, C = decoupled), serum log2 fold-changes against healthy and pancreatitis cohorts, number of detected cohorts, and the phase77_strict membership flag.

cell_specificity

Top scRNA cell of origin and the Yanai et al. tau index. tau approaches 1 when expression is concentrated in one cell type.

filter_survival

The 7-step phase60 audit as a count of passed steps plus a per-step boolean vector (signal_peptide, serum_measurable, serum_significant, pancreatitis_pdac, pancreatitis_hc, direction_match, final).

clinical_role

Whether the gene was screened in the phase29 resectable-marker shortlist and whether it is a member of the predeclared serum panel.

See Also

explain_gene() for the formatted text view, and compare_genes() for a multi-gene tidy table.

Examples

m <- evidence_math("LGALS3BP")
m$trajectory_fit
m$rna_protein_coupling$cosine
m$filter_survival$passed

Print a per-axis evidence summary for one gene

Description

User-facing text formatter that wraps the two evidence layers already in the package: format_provenance() (plain-English phase-tag rollup) and evidence_math() (per-axis mathematical values). Mirrors explain_score() in pattern: prints to console when verbose = TRUE, returns the underlying structured data invisibly either way.

Usage

explain_gene(
  gene_symbol,
  view = c("evidence", "math", "both"),
  verbose = TRUE,
  reference = NULL
)

Arguments

gene_symbol

HGNC gene symbol (length-1 character).

view

One of "evidence" (default), "math", or "both".

verbose

Logical. If TRUE (default), prints the formatted sections to the console. The structured list is returned invisibly either way.

reference

Optional data.table to inject in place of the bundled atlas (for tests / downstream pipelines).

Details

Three views are supported:

"evidence"

Plain-English provenance summary only – same content format_provenance() produces, plus the gene's audit_class headline.

"math"

Per-axis Evidence Math output – eight axes (trajectory_fit, effect_magnitude, cohort_consistency, rna_protein_coupling, serum_bridge, cell_specificity, filter_survival, clinical_role).

"both"

Both, separated by a horizontal rule.

Per-axis printing is the load-bearing design choice: it keeps the output interpretable rather than hiding the evidence behind a single composite score.

Value

Invisibly, a list with elements gene, view, provenance (length-1 character or NA), and math (list-of- axes from evidence_math() or NULL).

See Also

evidence_math(), format_provenance(), explain_score().

Examples

explain_gene("LGALS3BP", view = "math", verbose = FALSE)
explain_gene("LTBP1", view = "evidence", verbose = FALSE)
explain_gene("LTBP1", view = "both", verbose = FALSE)

Decompose a gene's audit score into its three axes and two gates

Description

Returns a transparent breakdown of how the frozen v0.3.0 audit rule arrived at one gene's audit_score and audit_class. The point is to make the score auditable in plain English: "LTBP1 lands in supported_uncertain because its heterogeneity_gate = 0.7 — the cohort-level meta-analysis I2 exceeds 70% on at least one contrast."

Usage

explain_score(gene_symbol, verbose = TRUE)

Arguments

gene_symbol

HGNC gene symbol (length-1 character).

verbose

Logical. If TRUE (default), prints a one-paragraph plain-language summary to the console. The structured breakdown is returned invisibly either way.

Details

The audit rule is a frozen weighted sum:

positive_score = 0.40 * evidence_strength
               + 0.35 * biological_coherence
               + 0.25 * translational_relevance
audit_score    = positive_score * leakage_gate * heterogeneity_gate
                 / max over the atlas of the same product

This function reports each component, the gate state, the audit label that the gate transitions trigger, and a short prose explanation derived from those numbers. It does NOT recompute any new evidence — it reads the bundled atlas and presents what the frozen rule already produced. Verbose dispatch is intended for interactive console use; the underlying data.table is also returned for programmatic use.

Value

Invisibly returns a list with elements:

  • gene — the resolved HGNC symbol.

  • audit_class, audit_score, positive_score — top-line scalars.

  • axes — data.table (3 rows): name, weight, value, contribution.

  • gates — data.table (2 rows): name, value, triggered_by.

  • explanation — character(1), one-paragraph rationale.

Examples

explain_score("LGALS3BP")
explain_score("LTBP1")
explain_score("GAPDH")

Explain a pdactrace_user_model via its top contributing features

Description

Prints (and invisibly returns) the top-N positive and top-N negative coefficients of the fitted elastic-net model, with feature names and signed coefficient values. This is the user-facing rationale for the supervised score: which evidence axes pushed the user's positive set up.

Usage

explain_user_evidence_model(model, top_n = 10L, verbose = TRUE)

Arguments

model

A pdactrace_user_model from fit_user_evidence_model().

top_n

Number of positive AND number of negative coefficients to surface. Default 10.

verbose

Logical. If TRUE (default), prints to console.

Value

Invisibly, a list with top_pos, top_neg, cv_auc_mean, cv_auc_sd, lambda_min, n_features, n_train, n_positives.

See Also

model_card() for the full reviewer-facing card.

Examples

if (requireNamespace("glmnet", quietly = TRUE)) {
  set.seed(1)
  feats <- data.frame(gene_symbol = paste0("G", seq_len(30)),
                      x1 = rnorm(30), x2 = rnorm(30))
  y <- rep(c(0L, 1L), each = 15)
  fit <- fit_user_evidence_model(feats, y, nfolds = 3L)
  explain_user_evidence_model(fit, top_n = 2, verbose = FALSE)
}


  feats <- make_evidence_features(scale = "z",
                                    drop_na_rows = TRUE)
  y <- as.integer(feats$gene_symbol %in%
                    c("LGALS3BP", "TIMP1", "LRG1"))
  fit <- fit_user_evidence_model(feats, y)
  explain_user_evidence_model(fit, top_n = 5)

Extract graph-aware audit features

Description

Returns the frozen v0.3.0 audit feature components used by compute_audit_score(). These are deterministic summaries of the evidence graph, not learned embeddings.

Usage

extract_graph_features(gene_symbol)

Arguments

gene_symbol

Character vector of HGNC gene symbols.

Value

A data.table with one row per matched gene.

Examples

extract_graph_features(c("LGALS3BP", "LTBP1", "GAPDH"))

Fit a stage-aware DESeq2 LRT model on user RNA-seq data

Description

Wraps the phase33 canonical pipeline: dataset + stage_group fixed-effect model on raw counts, LRT against ~ dataset reduced model, return per-gene coefficients (beta_E, beta_M, beta_L vs Normal) + BH-adjusted LRT padj.

Usage

fit_stage_de(object, ...)

## S4 method for signature 'ANY'
fit_stage_de(object, stage, cohort = NULL, min_count = 10, padj_cutoff = 0.05)

## S4 method for signature 'SummarizedExperiment'
fit_stage_de(
  object,
  stage_col,
  cohort_col = NULL,
  assay_name = "counts",
  min_count = 10,
  padj_cutoff = 0.05
)

Arguments

object

Either a count matrix/data.frame (genes by samples, rownames = gene symbols) or a SummarizedExperiment whose assay() is the count matrix and whose colData() carries the stage / cohort columns.

...

Method-specific arguments — see Details for the matrix and SummarizedExperiment interfaces.

stage

(matrix/data.frame interface) Character or factor vector of stage labels per sample. Required levels: at minimum "Normal", "Early", "Mid", "Late". Other levels are dropped.

cohort

(matrix/data.frame interface) Optional character / factor vector of dataset/cohort labels per sample. Used as a fixed effect to absorb cohort-level technical variance. If only one cohort, omit (default NULL).

min_count

Numeric. Pre-filter: genes with row sum below min_count are dropped before LRT. Default 10.

padj_cutoff

Numeric. BH-adjusted LRT padj cutoff for "stage-progressive" gene set. Default 0.05.

stage_col

(SummarizedExperiment interface) Name of the column in colData(object) that carries the stage labels.

cohort_col

(SummarizedExperiment interface) Optional name of the column in colData(object) that carries the cohort labels. Default NULL (single-cohort).

assay_name

(SummarizedExperiment interface) Name of the assay slot to pull as the count matrix. Default "counts".

Details

Two interfaces are supported via S4 method dispatch on object:

  • Matrix interface (the default): fit_stage_de(counts, stage, cohort). counts is a numeric matrix or data.frame with genes on rows and samples on columns; stage and cohort are vectors of length ncol(counts). This is the original v0.99.1 signature and is preserved unchanged for backwards compatibility.

  • SummarizedExperiment interface (new in v0.99.2): fit_stage_de(se, stage_col = "stage", cohort_col = "cohort"). The count matrix is pulled from assay(se, assay_name) and the stage / cohort vectors are pulled from colData(se). This is the Bioconductor-native entry point.

Value

A data.table with one row per gene and columns: ⁠gene_symbol, beta_N, beta_E, beta_M, beta_L, lrt_padj⁠, lrt_significant (logical, padj < cutoff), and lfcSE_*.

Examples

data(toy_counts)
data(toy_coldata)
if (requireNamespace("DESeq2", quietly = TRUE)) {
  fit <- fit_stage_de(toy_counts, toy_coldata$stage,
                      toy_coldata$cohort)
  head(fit)
}


# SummarizedExperiment interface (Bioconductor-native):
if (requireNamespace("DESeq2", quietly = TRUE)) {
  library(SummarizedExperiment)
  se <- SummarizedExperiment(
    assays = list(counts = toy_counts),
    colData = DataFrame(stage = toy_coldata$stage,
                        cohort = toy_coldata$cohort))
  fit <- fit_stage_de(se, stage_col = "stage", cohort_col = "cohort")
  pat <- classify_trajectory(fit)
}

Fit a stage-aware limma model on user tissue-protein intensity data

Description

Wraps the phase34 canonical pipeline: cohort + stage_group fixed-effect model on log-transformed protein intensities, returning per-gene contrasts (beta_E, beta_M, beta_L vs Normal) plus BH-adjusted F-test padj on the stage_group main effect.

Usage

fit_stage_de_protein(object, ...)

## S4 method for signature 'ANY'
fit_stage_de_protein(
  object,
  stage,
  cohort = NULL,
  min_nonNA = 10,
  padj_cutoff = 0.05
)

## S4 method for signature 'SummarizedExperiment'
fit_stage_de_protein(
  object,
  stage_col,
  cohort_col = NULL,
  assay_name = "intensity",
  min_nonNA = 10,
  padj_cutoff = 0.05
)

Arguments

object

Either a numeric intensity matrix/data.frame (genes by samples; rownames = gene symbols; values are log2 intensities) or a SummarizedExperiment whose assay() is the intensity matrix and whose colData() carries stage / cohort.

...

Method-specific arguments — see the matrix and SummarizedExperiment interfaces below.

stage

(matrix/data.frame interface) Character or factor vector of stage labels per sample. Required levels: at minimum "Normal", "Early", "Mid", "Late". Other levels are dropped.

cohort

(matrix/data.frame interface) Optional character / factor vector of dataset/cohort labels per sample. NULL for single-cohort.

min_nonNA

Numeric. Pre-filter: rows with fewer than this many non-NA samples are dropped. Default 10.

padj_cutoff

Numeric. BH-adjusted F-test padj cutoff for "stage-progressive" gene set. Default 0.05.

stage_col

(SummarizedExperiment interface) Name of the column in colData(object) that carries the stage labels.

cohort_col

(SummarizedExperiment interface) Optional name of the column in colData(object) that carries cohort labels. Default NULL.

assay_name

(SummarizedExperiment interface) Name of the assay slot to pull as the intensity matrix. Default "intensity".

Details

Designed to be the protein-side parallel of fit_stage_de(), so downstream classify_trajectory() / assemble_user_evidence() can consume both layers symmetrically. Two interfaces are supported via S4 dispatch: a numeric matrix / data.frame (the original v0.99.1 signature) or a SummarizedExperiment (new in v0.99.2; the Bioconductor-native entry point).

Value

A data.table with the same column schema as fit_stage_de(): ⁠gene_symbol, beta_N, beta_E, beta_M, beta_L, lfcSE_E, lfcSE_M, lfcSE_L, lrt_padj, lrt_significant⁠.

Examples

data(toy_protein)
data(toy_coldata)
if (requireNamespace("limma", quietly = TRUE)) {
  prot_fit <- fit_stage_de_protein(toy_protein,
                                   toy_coldata$stage,
                                   toy_coldata$cohort)
  head(prot_fit)
}


# SummarizedExperiment interface:
if (requireNamespace("limma", quietly = TRUE)) {
  library(SummarizedExperiment)
  se <- SummarizedExperiment(
    assays = list(intensity = toy_protein),
    colData = DataFrame(stage = toy_coldata$stage,
                        cohort = toy_coldata$cohort))
  prot_fit <- fit_stage_de_protein(
    se, stage_col = "stage", cohort_col = "cohort",
    assay_name = "intensity")
}

Fit a user-supplied supervised model on the evidence feature matrix

Description

Wraps glmnet::cv.glmnet(family = "binomial") to fit a sparse logistic model that ranks genes by feature-space evidence. The fit is owned entirely by the user: labels must be supplied explicitly. The package ships no pretrained model – this is a deliberate constraint for PDAC biomarker discovery, where validated non-circular ground truth is unavailable.

Usage

fit_user_evidence_model(
  features,
  labels,
  method = "elastic_net",
  alpha = 0.5,
  nfolds = 10L,
  weights = NULL,
  seed = 1L
)

Arguments

features

data.table from make_evidence_features() (or any frame with gene_symbol plus numeric feature columns). Z-scored input (scale = "z") is recommended for stable coefficients.

labels

Integer / logical vector of length nrow(features), one entry per row, with 1 / TRUE for positives and 0 / FALSE for negatives.

method

Currently only "elastic_net" is implemented. Argument exists so a future release can add "rf" / "gbm" without API churn.

alpha

Elastic-net mixing parameter in ⁠[0, 1]⁠. 0 is ridge, 1 is lasso. Default 0.5 (mid-point) keeps non-zero coefficients while damping correlated features.

nfolds

Number of CV folds. Default 10.

weights

Optional per-row observation weights passed through to glmnet::cv.glmnet(). Default NULL (uniform weights).

seed

Integer RNG seed for reproducibility. Default 1L.

Details

Use case: a user has their own positive set (e.g. their lab's validated panel) and wants a continuous prioritisation score across the rest of the atlas, with feature-level interpretability that survives Bioconductor review.

Value

An S3 object of class pdactrace_user_model (a list) with elements: cvfit (the underlying cv.glmnet object), coef_table (data.table: feature, coef_value), lambda_min, lambda_1se, cv_auc_mean, cv_auc_sd, n_train, n_features, n_positives, top_pos, top_neg, alpha, seed, method, feature_set_version, card.

Reproducibility

RNG is scoped via withr::local_seed() inside the fit. Two calls with the same ⁠(features, labels, alpha, nfolds, seed)⁠ produce byte-identical models.

Cross-validation

10-fold CV by glmnet::cv.glmnet() over the lambda path; AUC is recomputed per fold for the model card. Class imbalance is not reweighted by default (use the weights arg if needed).

See Also

predict_user_evidence_model(), explain_user_evidence_model(), model_card(), make_evidence_features().

Examples

if (requireNamespace("glmnet", quietly = TRUE)) {
  set.seed(1)
  feats <- data.frame(gene_symbol = paste0("G", seq_len(30)),
                      x1 = rnorm(30), x2 = rnorm(30))
  y <- rep(c(0L, 1L), each = 15)
  fit_user_evidence_model(feats, y, nfolds = 3L)
}


  if (requireNamespace("glmnet", quietly = TRUE)) {
    feats <- make_evidence_features(scale = "z",
                                      drop_na_rows = TRUE)
    y <- as.integer(feats$gene_symbol %in%
                      c("LGALS3BP", "TIMP1", "LRG1"))
    fit <- fit_user_evidence_model(feats, y)
    explain_user_evidence_model(fit, top_n = 5)
  }

Human-readable evidence labels for the bundled phase tags

Description

Maps the internal phase33, phase34, stouffer_consistency, ... provenance tags carried in pdactrace_reference$provenance onto plain-English evidence-source labels. The phase tags remain the canonical IDs for reproducibility (they correspond to the numbered analysis scripts in the companion manuscript-monorepo) but the user-facing print path now leads with the labels and folds the raw tag list into a ⁠Technical:⁠ footer.

Usage

format_provenance(provenance, style = c("compact", "verbose", "raw"))

Arguments

provenance

Character of length 1 or longer. Either a comma-separated phase string (e.g. "phase33,phase34,phase60") or a character vector of individual tags. Unknown tags fall through unchanged with a (unmapped) suffix in verbose mode.

style

One of:

  • "compact" (default) — returns a one-line plus-separated summary suitable for inline print, e.g. "RNA + Tissue protein + Multi-cohort RNA + ...".

  • "verbose" — returns a multi-line hyphen-prefixed list with "label: explanation" per evidence source.

  • "raw" — returns the input phase tags themselves (useful for the technical-provenance footer).

Details

Intended for internal use by query_gene(), query_gene_detailed(), and summarize_gene_evidence() — exported so end users who consume the raw provenance column can relabel it the same way.

Value

Character of length 1.

Examples

format_provenance("phase33,phase34,phase60", style = "compact")
format_provenance("phase33,phase34,phase60", style = "verbose")
format_provenance("phase33,phase34,phase60", style = "raw")

Inspect atlas metadata

Description

Returns the bundled atlas_metadata list with version, snapshot date, scope description, cohort tables, and source CSV provenance.

Usage

list_atlas_metadata()

Value

Named list. See ?atlas_metadata for structure.

Examples

meta <- list_atlas_metadata()
  meta$version
  meta$source_csvs

List candidate genes by biology-level filter parameters

Description

Multi-parameter filter on the pdactrace reference atlas. Pass NULL (default) for any parameter to leave it unconstrained. Returns a ranked data.table with key evidence columns.

Usage

list_candidates(
  onset = NULL,
  tissue_direction = NULL,
  pattern = NULL,
  translation_class = NULL,
  serum_detected = NULL,
  signal_peptide = NULL,
  panel_member = NULL,
  resectable_marker = NULL,
  phase77_strict = NULL,
  min_audit_class = c("ALL", "high_confidence", "supported_uncertain", "penalized",
    "low", "excluded"),
  top_n = NULL
)

Arguments

onset

Character. "Early" to restrict to the 4 Early-onset patterns (the surfaced atlas universe). NULL (default) = unconstrained, which equals onset = "Early" because the atlas surface only exposes Early × 4 calls (under the v0.4.0 12-template classifier, Mid / Late / Monotonic best-matches are flagged via ⁠excluded_*_pattern⁠ columns and not returned by this function).

tissue_direction

Character. "Up" keeps Early_Burst_Up + Early_Peak; "Down" keeps Early_Loss_Down + Early_Trough. NULL = both.

pattern

Character vector. Specific 4-Early pattern names (e.g., c("Early_Burst_Up", "Early_Peak")). Overrides tissue_direction if both are set.

translation_class

Character. One of "concordant" (Class A), "inverse" (Class B), "decoupled" (Class C). Aliases "A", "B", "C" are also accepted. NULL = any.

serum_detected

Logical. Restrict to genes detected (or not) in any serum cohort. NULL = any.

signal_peptide

Logical. Filter on flt_signal_peptide (phase60 SignalP pass). NULL = any.

panel_member

Logical. Member of LTBP1+SERPINA1 / hybrid panels. NULL = any.

resectable_marker

Logical. Member of phase29 25 markers (19 in atlas). NULL = any.

phase77_strict

Logical. Member of 22 strict tissue+serum convergent candidates. NULL = any.

min_audit_class

Minimum audit_class to retain. One of "high_confidence", "supported_uncertain", "penalized", "low", "excluded", or "ALL" (no filter). Defaults to "ALL". Hierarchy (high to low): high_confidence, supported_uncertain, penalized, low, excluded.

top_n

Optional integer. Return top N rows ranked by audit_score descending.

Value

data.table with gene_symbol, key evidence columns, and provenance.

Examples

list_candidates(onset = "Early", tissue_direction = "Up")
  list_candidates(translation_class = "inverse")
  list_candidates(translation_class = "concordant",
                  serum_detected = TRUE,
                  signal_peptide = TRUE,
                  top_n = 20)

List the public datasets that contributed to the pdactrace atlas

Description

Returns the bundled pdactrace_data_sources table — one row per public dataset (GEO / PRIDE / PDC / ICGC / GTEx / MassIVE / SRA) that was used to populate the v0.99.x reference atlas. Designed so a Bioconductor reviewer or end user can answer "where does this data come from?" programmatically rather than by chasing prose in the manuscript.

Usage

list_data_sources(layer = NULL)

Arguments

layer

Optional character vector to filter by data layer: one or more of "RNA", "Protein", "scRNA", "Serum", "Pancreatitis", "Validation". Default NULL returns the full table.

Value

A data.table with columns:

  • accession — public identifier (GEO ID, PXD, etc.)

  • layer — RNA / Protein / scRNA / Serum / ...

  • source_type — repository name (GEO / PRIDE / PDC / ...)

  • used_for — short role description

  • citation — primary publication citation when available

  • url — direct repository URL

Examples

head(list_data_sources())
list_data_sources(layer = "Serum")
nrow(list_data_sources())

Build a flat numeric feature matrix for downstream modelling

Description

Returns a wide numeric data.table (genes by features) suitable for off-the-shelf supervised or descriptive modelling. Computes the same arithmetic that evidence_math() exposes per-axis, but as a single tabular shape so callers can hand it directly to glmnet::cv.glmnet(), ranger::ranger(), scikit-learn, etc.

Usage

make_evidence_features(
  genes = NULL,
  reference = NULL,
  protein_betas = NULL,
  scale = c("none", "z", "robust"),
  impute = c("none", "mean", "zero"),
  drop_na_rows = FALSE
)

Arguments

genes

Optional character vector of gene symbols. NULL (default) returns features for every gene in the reference.

reference

Optional data.table to inject in place of the bundled atlas (used by tests and downstream pipelines).

protein_betas

Optional data.table to inject in place of the bundled protein-betas table (used by tests).

scale

One of "none" (default), "z", or "robust".

impute

One of "none" (default), "mean", or "zero". Most genes are missing from pdactrace_protein_betas and / or from the bundled serum cohorts; the resulting NAs leave most genes incomplete-case. Use "mean" to fill NA with the column mean (over complete cases) – standard pre-fit hygiene for glmnet. Use "zero" to fill NA with 0 – conservative "no evidence" assumption, useful when the absence of an evidence layer is itself meaningful.

drop_na_rows

Logical. If TRUE, drop rows containing any NA in feature columns. Default FALSE. Applied after impute, so impute = "mean" + drop_na_rows = FALSE is typically what you want for ML fitting.

Details

Feature columns are deterministic functions of the bundled pdactrace_reference and pdactrace_protein_betas objects; no new fitting is performed. The full column list is grouped below by evidence axis.

Value

A data.table keyed on gene_symbol with one row per gene and one column per feature.

Feature columns

Trajectory fit

trajectory_rho_best, trajectory_delta_rho (= rho_best - rho_runner_up).

Effect magnitude

rna_beta_norm (= sqrt(sum(rna_beta_E,M,L^2))), rna_beta_max_abs, prot_beta_norm, prot_beta_max_abs.

Cohort robustness

cohort_agreement, stouffer_abs_z, stouffer_neglog10_fdr, max_meta_I2.

RNA-protein convergence

rna_protein_cosine (cosine of (E,M,L) beta vectors), rna_protein_concordant (0/1), prot_pattern_rho.

Serum bridge

serum_detected (0/1), serum_abs_log2fc, serum_n_cohorts_detected, phase77_strict (0/1).

Specificity

cell_specificity_tau, filter_pass_fraction (passed / 7).

Leakage flags

leakage_housekeeping (0/1), leakage_plasma_high_abundance (0/1).

Plus one identifier column (gene_symbol). Logical inputs are coerced to integer 0/1 so the resulting frame is uniformly numeric except for the key.

Scaling

scale = "z" standardizes each feature column to mean 0, sd 1 over the complete-case subset (NAs are preserved as NA and do not contribute to the moment estimates). scale = "robust" uses median / MAD instead. scale = "none" (default) returns raw values.

NA policy

Genes missing from pdactrace_protein_betas get NA for the protein-side columns (⁠prot_beta_*⁠, rna_protein_cosine, prot_pattern_rho). Genes with NA rna_pattern (no Early best-match on the atlas surface) get NA for trajectory_rho_best and trajectory_delta_rho. Set drop_na_rows = TRUE to keep only complete-case rows; default FALSE preserves the full atlas row count.

See Also

evidence_math() for the per-axis nested-list view, score_anchor_similarity() for descriptive feature-space scoring against bundled anchors, fit_user_evidence_model() for the user-supplied supervised wrapper.

Examples

feats <- make_evidence_features()
dim(feats)
head(feats)
z <- make_evidence_features(scale = "z", drop_na_rows = TRUE)
summary(z[, -1L])

Random-effects meta-analysis table

Description

Cohort-level and random-effects meta-analysis summaries used to populate v0.2.0 meta-analysis and tier columns in pdactrace_reference.

Usage

meta_analysis

Format

A data.frame with one row per gene and meta-analysis summary columns.


Mid-onset pattern names (excluded from atlas surface)

Description

Returned for transparency. These patterns are not surfaced in pdactrace_reference$rna_pattern; genes whose canonical phase33 4-Early-pattern call falls in this set will have rna_pattern = NA and excluded_mid_pattern = TRUE.

Usage

mid_pattern_names_excluded()

mid_patterns()

Value

character(4)

Examples

mid_pattern_names_excluded()
mid_patterns()  # short alias

Model card metadata for the optional ML layer

Description

Single point of documentation reviewers can audit when a descriptive (score_anchor_similarity()) or supervised (fit_user_evidence_model()) score is in use. Returns a structured list and prints a multi-line summary to the console.

Usage

model_card(
  source = c("anchor_similarity", "user_model"),
  model = NULL,
  verbose = TRUE
)

Arguments

source

One of "anchor_similarity" or "user_model".

model

Optional pdactrace_user_model from fit_user_evidence_model() (required when source = "user_model").

verbose

Logical. If TRUE (default), prints the card to the console. The structured list is returned invisibly either way.

Details

Two sources are supported:

"anchor_similarity"

The descriptive layer. No labels are used to train a predictor; the bundled pdactrace_external_anchors table is used only to define a reference centroid in the z-scored feature space. Always available, no model argument needed.

"user_model"

The user-supplied supervised fit produced by fit_user_evidence_model(). Pass the fit object via model = ....

The package ships no pretrained predictor. This deliberate constraint is the central defense against the "what was the training label?" reviewer question for PDAC biomarker discovery, where validated non-circular ground truth is not available.

Value

Invisibly, a list with at least: source, feature_set_version, intended_use, not_intended_for, labels_used_for_training, limitations. For "user_model", also includes n_features, n_train, n_positives, cv_auc_mean, cv_auc_sd, top_pos, top_neg, seed, alpha, lambda_min.

See Also

score_anchor_similarity(), fit_user_evidence_model().

Examples

model_card("anchor_similarity")

  if (requireNamespace("glmnet", quietly = TRUE)) {
    feats <- make_evidence_features(scale = "z",
                                      drop_na_rows = TRUE)
    y <- as.integer(feats$gene_symbol %in%
                      c("LGALS3BP", "TIMP1", "LRG1"))
    fit <- fit_user_evidence_model(feats, y)
    model_card(source = "user_model", model = fit)
  }

Public-data accession table backing list_data_sources()

Description

One row per public dataset that contributed to the v0.99.x pdactrace reference atlas, covering bulk RNA-seq, tissue proteomics, single-cell RNA, serum proteomics, pancreatitis context, and validation cohorts. Backs the list_data_sources() helper and is the source-of-truth for the manuscript's data-availability section.

Usage

pdactrace_data_sources

Format

data.table with one row per accession and columns accession, layer, source_type, used_for, citation, url. Generated by data-raw/build_data_sources.R.


External PDAC biomarker anchor provenance table

Description

Literature and database anchors used only for enrichment evaluation of the frozen pdactrace audit ranking. These anchors are not used to fit or tune the audit score.

Usage

pdactrace_external_anchors

Format

A data.frame with provenance columns:

gene

HGNC gene symbol.

source

Short source description.

source_type

Primary study, database, review, or related source.

evidence_tier

T1 validated, T2 literature/database, or exploratory.

assay_context

Serum, plasma, tissue, cyst fluid, or mixed context.

early_detection

Whether the source supports early-detection use.

gene_level_mapping

Direct, indirect, or excluded mapping status.

include_primary_eval

Primary-enrichment inclusion flag.

include_secondary_eval

Secondary-enrichment inclusion flag.

pmid_or_url

PMID or URL provenance.

note

Curation note.


Per-stage tissue-protein effect-size table for trajectory plotting

Description

Bundled per-gene tissue-protein stage coefficients and 12-template best-match labels, used by plot_template_atlas() and plot_gene_template() for the protein layer (the main pdactrace_reference only carries categorical prot_pattern / prot_tier, not the per-stage betas needed for trajectory visualisation). Generated by data-raw/build_protein_betas.R from phase34_protein_pooled_12template.csv in the companion manuscript-monorepo.

Usage

pdactrace_protein_betas

Format

data.table, 5,917 rows x 8 cols, keyed on gene_symbol:

  • gene_symbol character HGNC symbol

  • prot_beta_N numeric, always 0 (Normal is the reference)

  • prot_beta_E numeric, log2 fold change vs Normal at Early

  • prot_beta_M numeric, ditto at Mid

  • prot_beta_L numeric, ditto at Late

  • prot_pattern_12 character, full 12-template best-match (Early × 4 + Mid × 4 + Late × 2 + Monotonic × 2 + "Unclassified" for non-significant rows)

  • prot_pattern_rho numeric, Pearson rho with the matched template

  • prot_lrt_padj numeric, BH-adjusted F-test p-value


Save a ggplot to PDF using cairo_pdf

Description

Convenience wrapper used in vignettes; logs filename + size.

Usage

pdactrace_save(p, dir, name, w, h)

Arguments

p

A ggplot object.

dir

Output directory.

name

File stem (no extension).

w, h

Width/height in inches.

Value

Invisibly returns the saved file path.

Examples

library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point()
tmp <- tempdir()
fp <- pdactrace_save(p, tmp, "demo", w = 3, h = 2.5)
file.exists(fp)
unlink(fp)

Palettes for pdactrace figures

Description

Named character vectors for ggplot2 ⁠scale_*_manual()⁠.

  • pdactrace_pal_class - Class A / B / Other

  • pdactrace_pal_group - HC / Pancreatitis / PDAC

  • pdactrace_pal_dir - UP / DOWN / NS

  • pdactrace_pal_pattern - Early × 4 pattern colors (atlas-surfaced subset of the v0.4.0 12-template catalog)

Usage

pdactrace_pal_class

pdactrace_pal_group

pdactrace_pal_dir

pdactrace_pal_pattern

Format

An object of class character of length 4.

An object of class character of length 4.

An object of class character of length 3.

An object of class character of length 4.

Examples

pdactrace_pal_class
pdactrace_pal_pattern

pdactrace NCS-grade ggplot2 theme

Description

Bundles the publication-grade ggplot2 theme used across the BiB manuscript figures. Source-of-truth: BiB_framework/scripts/bib_ncs_theme.R.

Usage

NCS_W_SINGLE

NCS_W_15COL

NCS_W_DOUBLE

NCS_W_TRIPLE

pdactrace_panel_theme()

pdactrace_axes_theme()

Format

An object of class numeric of length 1.

An object of class numeric of length 1.

An object of class numeric of length 1.

An object of class numeric of length 1.

Details

Two flavors:

Width constants (NCS spec, inches):

  • NCS_W_SINGLE = 3.46 (88 mm, single column)

  • NCS_W_15COL = 4.72 (120 mm, 1.5-column)

  • NCS_W_DOUBLE = 7.08 (180 mm, double column)

  • NCS_W_TRIPLE = 10.50 (full-width composite)

Examples

library(ggplot2)
p <- ggplot(mtcars, aes(wt, mpg)) + geom_point() +
  pdactrace_axes_theme()
identical(NCS_W_DOUBLE, 7.08)

Visualise the candidate landscape across tissue and serum

Description

Scatter plot of atlas-surfaced Early × 4 genes (12-template classifier surface) with measurable tissue + serum evidence. X-axis: tissue protein effect (max |beta| across stages, signed by direction). Y-axis: serum log2FC PDAC vs HC. Color: translation_class (A / B / C / unclassified). Selected genes (default LTBP1, SERPINA1, CDH13, SPARC) get text labels.

Usage

plot_candidate_landscape(
  highlight = c("LTBP1", "SERPINA1", "CDH13", "SPARC"),
  min_serum_n = 1L
)

Arguments

highlight

Character vector of gene symbols to label prominently.

min_serum_n

Integer. Drop genes detected in fewer cohorts.

Value

A ggplot2 object.

Examples

plot_candidate_landscape()
  plot_candidate_landscape(highlight = c("LTBP1", "SERPINA1",
                                          "CDH13", "SPARC"))

Full 11-celltype expression bar

Description

Replaces the v0.1.0 plot_gene_evidence() top-3 cell-type panel with the full 11-celltype distribution from the scRNA atlas. Useful when the dominant cell type is borderline or when the user wants to see the long tail (e.g., mast cells, plasma cells).

Usage

plot_celltype_full(
  gene_symbol,
  highlight = c("myCAF", "iCAF", "Acinar", "Ductal")
)

Arguments

gene_symbol

HGNC symbol.

highlight

Optional character vector of cell types to draw in red (default c("myCAF", "iCAF", "Acinar", "Ductal") - the four PDAC-relevant origins).

Value

A ggplot2 object.

Examples

plot_celltype_full("LTBP1")

Filter diagnostic step bar with underlying metric annotation

Description

For each of the 7 phase60 filter steps, shows pass/fail status as a horizontal bar and annotates each row with the underlying metric (e.g., ⁠pool padj = 0.32⁠) so the user can see why a step failed.

Usage

plot_filter_diagnostics(gene_symbol)

Arguments

gene_symbol

HGNC symbol.

Details

Distinct from plot_filter_trace() which compares multiple genes across PASS/FAIL only. plot_filter_diagnostics() focuses on a single gene with full numeric audit.

Value

A ggplot2 object.

Examples

plot_filter_diagnostics("LTBP1")

Visualise tissue-to-serum filter passage with serum direction context

Description

For each gene, draws the 7-step phase60 filter trail (filled = pass, open = fail, grey = not evaluated) with two extra layers that the original filter-trace view lacked:

Usage

plot_filter_trace(gene, show_routes = TRUE, show_serum = TRUE)

Arguments

gene

Character vector of HGNC symbols.

show_routes

Logical. If TRUE (default), append the right-side panel showing phase77_strict / panel_member / resectable_marker route flags.

show_serum

Logical. If TRUE (default), append the serum-direction strip above the filter grid.

Details

  • A serum direction strip above the filter grid showing the per-gene serum log2FC vs healthy control and vs pancreatitis (the values that actually drive the direction-match filter step), coloured by translation class (Class A blue / Class B red / Class C grey / NA light-grey).

  • A per-gene title prefix naming the translation class so the reader can read the trail in context.

Multi-gene calls show one row per gene, ordered by phase60 pass count descending; this lets you compare phase60_final exemplars (e.g. SERPINA1) against phase77 Class B exemplars (e.g. LTBP1, CDH13, SPARC) at a glance.

Value

A ggplot2 object (or patchwork composite if show_routes = TRUE or show_serum = TRUE).

Examples

plot_filter_trace(c("SERPINA1", "LTBP1", "SPARC", "CDH13"))
  plot_filter_trace("LTBP1", show_routes = FALSE)

Multi-panel ggplot composite of gene evidence

Description

Builds a publication-ready composite (patchwork) showing:

  • 4-point trajectory (RNA + Protein overlay)

  • Cell origin distribution bar

  • Serum log2FC (PDAC, Pancreatitis vs HC)

  • Provenance + summary footer

Usage

plot_gene_evidence(
  gene_symbol,
  layers = c("trajectory", "cell_origin", "serum", "summary")
)

Arguments

gene_symbol

HGNC standard symbol.

layers

Subset of panels to draw. Default all four (c("trajectory", "cell_origin", "serum", "summary")).

Details

Width/height: NCS spec single column (NCS_W_SINGLE = 3.46 in) by ~3 in.

Value

A patchwork object. Returns NULL invisibly with message if gene is not in atlas.

Examples

p <- plot_gene_evidence("LTBP1")
  print(p)

Hexagonal evidence radar for one or more genes

Description

Draws a compact 6-axis hexagonal radar chart summarising the pdactrace audit-feature components for one or more genes. Inspired by the multi-polygon radar style used in graph-of-graphs analyses (e.g., GATE; Liu and Cheng 2025), but reuses the six interpretable feature axes from extract_graph_features() rather than learned graph embeddings.

Usage

plot_gene_hexagon(
  gene_symbol,
  comparison = NULL,
  show_score = TRUE,
  palette = NULL
)

Arguments

gene_symbol

Character vector of one or more HGNC gene symbols. Multiple genes are overlaid as separate polygons.

comparison

Optional. Either NULL (default), a single gene symbol, or one of "high_confidence_mean", "supported_uncertain_mean", "penalized_mean", "excluded_mean". Adds a translucent reference polygon for visual context.

show_score

Logical. If TRUE (default), annotate the centre with each gene's audit_score and audit_class.

palette

Optional named character vector of polygon colors keyed by gene symbol. Defaults to the pdactrace audit-class palette where possible, otherwise to a Set1-style palette.

Details

The six axes (clockwise from the top vertex) are:

  1. Multi-layer — score_layer (fraction of RNA/Protein/scRNA/Serum layers with a measurable signal).

  2. Direction — score_direction (cross-cohort + cross-layer direction concordance).

  3. Stage-onset — score_early (Pearson rho against the four Early templates, weighted by LRT significance).

  4. Serum bridge — score_serum (serum detected + signal peptide + tissue-to-serum direction concordance).

  5. Leakage safety — leakage_mult (1 if clean, 0.5 if plasma high-abundance, 0 if housekeeping).

  6. Cohort consistency — het_mult (1 if I² < 70, 0.7 if 70-90, 0.3 if >= 90).

Each axis is plotted on a fixed 0–1 scale; concentric hexagons mark the 0.25 / 0.50 / 0.75 / 1.00 contours.

Value

A ggplot2 object (NCS-grade compact, cairo-pdf safe).

Examples

plot_gene_hexagon("LTBP1")
plot_gene_hexagon(c("LGALS3BP", "LTBP1"))
plot_gene_hexagon("LTBP1", comparison = "high_confidence_mean")

Per-gene template-overlay plot

Description

For one gene/protein, draws the trajectory-template panel that the gene was matched to (12-template Pearson-rho argmax) with the gene's own z-scored Normal/Early/Mid/Late trajectory overlaid as a highlight line. Companion to the atlas-wide plot_template_atlas(): that one walks all 12 templates without a gene; this one zeroes in on a single gene's matched panel.

Usage

plot_gene_template(
  gene_symbol,
  layer = c("rna", "protein"),
  highlight_color = "#C62828",
  output_file = NULL,
  width = 1.55,
  height = 1.4,
  reference = NULL
)

Arguments

gene_symbol

HGNC gene symbol (length-1 character).

layer

Either "rna" (default) or "protein".

highlight_color

Colour of the gene-highlight line. Default "#C62828" (NCS-grade muted red).

output_file

Optional output PDF path. If NULL (default), the function returns a ggplot object; otherwise it writes one cairo PDF via pdactrace_save() and returns the path invisibly.

width, height

PDF size in inches when output_file is set. Defaults to the compact 1.55 x 1.40 fig2C style.

reference

Optional override of the bundled pdactrace_reference (RNA layer) for unit-test injection.

Details

The matched template is recovered via the same Pearson-rho argmax across the 12 bundled templates that backs classify_trajectory() and score_trajectory(). The matched rho is annotated as a small caption.

Value

Either a ggplot object (when output_file is NULL) or the absolute path of the written PDF (invisibly).

Examples

p <- plot_gene_template("LGALS3BP", "rna")
class(p)
p$labels$title

Random-effects meta-analysis forest plot per gene

Description

Visualises the per-cohort log2FoldChange estimates with 95% confidence intervals, plus the random-effects meta-summary diamond for each of three contrasts: Mid_vs_Early, Late_vs_Early, Normal_vs_Early. Heterogeneity (I2) is annotated.

Usage

plot_meta_forest(
  gene_symbol,
  contrast = c("Mid_vs_Early", "Late_vs_Early", "Normal_vs_Early"),
  per_cohort_path = NULL
)

Arguments

gene_symbol

HGNC standard symbol.

contrast

Character, one of "Mid_vs_Early", "Late_vs_Early", "Normal_vs_Early". Default "Mid_vs_Early".

per_cohort_path

Optional path to the per-cohort betas CSV (long-format with gene_symbol, contrast, cohort, beta, se, n_ref, n_target). Defaults to ⁠${PDAC_BASE_DIR}/analysis/transcriptomics/results/figure1/per_cohort_betas_long.csv⁠, where PDAC_BASE_DIR is read from the environment. Returns invisible(NULL) with a message if the file is unavailable.

Details

Reveals cohort divergence that the headline pattern label can hide. E.g., for LTBP1 the forest plot shows TCGA mostly null, CPTAC clearly elevated, and the meta-summary diamond crossing zero.

Value

A ggplot2 object.

Examples

plot_meta_forest("LTBP1")
  plot_meta_forest("SERPINA1", contrast = "Normal_vs_Early")

Compare a gene panel across evidence layers as a heatmap

Description

Builds a multi-row heatmap (genes x evidence columns) showing key RNA / protein / serum / class / route evidence side-by-side. Useful for paper Figure 7 (Class A vs Class B comparison).

Usage

plot_panel_heatmap(genes, layers = c("rna", "protein", "serum", "class"))

Arguments

genes

Character vector of HGNC symbols.

layers

Character vector of evidence groups to include. Default: c("rna", "protein", "serum", "class").

Value

A ggplot2 object.

Examples

plot_panel_heatmap(c("LTBP1", "SERPINA1", "CDH13", "SPARC", "CP", "FGB"))

Per-cohort sign vote bar with Stouffer summary

Description

For each of the 4 RNA cohorts (TCGA, CPTAC, GSE224564, GSE79668), shows the gene's trend direction (Increasing / Decreasing / Non-monotonic) and whether the trend is monotonic. Headline annotation displays Stouffer aggregate p-value and cohort agreement %.

Usage

plot_per_cohort(gene_symbol)

Arguments

gene_symbol

HGNC symbol.

Details

This plot reveals cohort divergence that the headline pattern label may hide.

Value

A ggplot2 object.

Examples

plot_per_cohort("LTBP1")  # reveals TCGAup but CPTACdown divergence

Per-gene serum direction context, coloured by translation class

Description

Bar plot of per-gene serum log2FC against healthy control (HC) and against pancreatitis (Pan), faceted by contrast and coloured by translation class (Class A / B / C). These two values together drive the direction-match step of the phase60 tissue-to-serum filter audit; the panel is the visual analog of the serum_log2fc_PDAC_vs_HC and serum_log2fc_Pan_vs_HC columns from pdactrace_reference, with the categorical translation_class mapped to the bar fill.

Usage

plot_serum_direction(gene)

Arguments

gene

Character vector of HGNC gene symbols.

Details

Used as a standalone diagnostic and as one of the six viz_gene() panels.

Value

A ggplot2 object, or NULL invisibly if none of the requested genes are in the bundled atlas.

See Also

plot_filter_trace() which embeds the same panel above its filter grid by default; viz_gene() for the 6-panel composite.

Examples

plot_serum_direction("LTBP1")
  plot_serum_direction(c("SERPINA1", "LTBP1", "LGALS3BP"))

Forest plot of per-stage effect size

Description

For RNA, draws log2FC +/- 1.96 x lfcSE per stage with significance asterisks at per-stage Wald padj < 0.05. For tissue protein, draws log2FC point estimates per stage; the bundled per-stage SE is unavailable so no CI is drawn (the overall F-test padj is reported in the subtitle instead).

Usage

plot_stage_effect(gene_symbol, layer = c("rna", "protein"))

Arguments

gene_symbol

HGNC symbol.

layer

One of "rna" (default) or "protein". The protein layer reads from pdactrace_protein_betas.

Value

A ggplot2 object.

Examples

plot_stage_effect("LTBP1")
  plot_stage_effect("LTBP1", layer = "protein")

Per-template trajectory atlas plots

Description

Renders one panel per trajectory template in the bundled 12-template catalogue. Each panel shows the cohort of genes / proteins whose z-scored Normal / Early / Mid / Late trajectory matches that template best (Pearson rho argmax across the 12 templates; deterministic given the bundled atlas), as thin translucent lines plus a mean line and a +/-1 SD ribbon. Reproduces the figures in analysis/manuscript/PDAC_졸업v1/fig2/individual/fig2C_RNA_*.pdf inside the package and extends them to the four templates that did not exist in the v0.3.0 era (Late_Burst_Up, Late_Loss_Down, Monotonic_Up, Monotonic_Down).

Usage

plot_template_atlas(
  layer = c("rna", "protein"),
  templates = NULL,
  output_dir = NULL,
  filename_pattern = "template_{layer}_{template}.pdf",
  width = 1.55,
  height = 1.4,
  reference = NULL
)

Arguments

layer

Either "rna" (default) or "protein". The protein layer reads the bundled pdactrace_protein_betas (5,917 measurable proteins).

templates

Optional character vector of template names to restrict the output to. Default NULL returns all 12.

output_dir

Optional directory. If non-NULL, one cairo PDF per template is written via pdactrace_save() and the file paths are reported in attr(out, "files"). Default NULL returns ggplot objects only.

filename_pattern

File-name template containing {layer} and {template} placeholders. Default "template_{layer}_{template}.pdf".

width, height

Output PDF size in inches. Defaults match the compact fig2C style (1.55 x 1.40 in; ~ 4 cm square).

reference

Optional override of the bundled pdactrace_reference (RNA layer) for unit-test injection. The protein layer always reads pdactrace_protein_betas.

Details

Cohort assignment is recomputed inside the package via the same z-scored Pearson-rho argmax that backs classify_trajectory() and score_trajectory(); the bundled atlas's rna_pattern column surfaces only the four Early templates, so the eight non-Early panels rely on this argmax recovery rather than on a directly bundled label.

Edge case: very small cohorts degrade gracefully — singletons drop the ribbon, cohorts of 2-4 drop the ribbon but keep individual lines, cohorts of >=5 show the full thin-lines + mean + ribbon.

Value

A named list of ggplot objects, one per template, in c(early_pattern_names(), mid_pattern_names_excluded(), "Late_Burst_Up", "Late_Loss_Down", "Monotonic_Up", "Monotonic_Down") order. When output_dir is non-NULL, the list also carries the file paths in attr(out, "files").

Examples

panels <- plot_template_atlas("rna",
                                templates = "Early_Burst_Up")
length(panels)
names(panels)

Predict from a pdactrace_user_model

Description

Predict from a pdactrace_user_model

Usage

predict_user_evidence_model(model, new_features, s = "lambda.min")

Arguments

model

A pdactrace_user_model from fit_user_evidence_model().

new_features

data.table from make_evidence_features() (or a frame with gene_symbol and the same numeric feature columns the model was trained on).

s

lambda value at which to evaluate. Default "lambda.min". Passed through to predict.cv.glmnet().

Value

A data.table with gene_symbol and predicted_prob (P(label=1) under the fitted model). Rows with any NA feature are returned with predicted_prob = NA.

Examples

if (requireNamespace("glmnet", quietly = TRUE)) {
  set.seed(1)
  feats <- data.frame(gene_symbol = paste0("G", seq_len(30)),
                      x1 = rnorm(30), x2 = rnorm(30))
  y <- rep(c(0L, 1L), each = 15)
  fit <- fit_user_evidence_model(feats, y, nfolds = 3L)
  head(predict_user_evidence_model(fit, feats))
}


  feats <- make_evidence_features(scale = "z",
                                    drop_na_rows = TRUE)
  y <- as.integer(feats$gene_symbol %in%
                    c("LGALS3BP", "TIMP1", "LRG1"))
  fit <- fit_user_evidence_model(feats, y)
  head(predict_user_evidence_model(fit, feats))

End-to-end user-cohort projection through the audit framework

Description

One-call wrapper that takes a user RNA-seq cohort (and optionally a tissue-protein cohort) and walks it end-to-end through the four v0.99 framework steps: stage-aware DE fit, 12-template trajectory matching, evidence-table assembly, and frozen audit scoring. Returns a structured list with each intermediate object plus the final audit table, so the user can inspect at any step.

Usage

project_user_cohort(
  rna,
  coldata = NULL,
  stage_col,
  cohort_col = NULL,
  protein = NULL,
  protein_assay_name = "intensity",
  signal_peptide = NULL,
  sig_only = FALSE
)

Arguments

rna

Either an integer count matrix / data.frame (genes by samples) or a SummarizedExperiment. Required.

coldata

Optional data.frame / DataFrame of sample-level metadata. Required when rna is a matrix; ignored when rna is a SummarizedExperiment (its colData() is used instead).

stage_col

Name of the column in coldata (or colData(rna)) that carries the stage labels.

cohort_col

Optional name of the cohort column. Default NULL (single-cohort).

protein

Optional second SummarizedExperiment or numeric intensity matrix carrying log2 protein intensities for the same stage / cohort design. When supplied, the protein side runs through fit_stage_de_protein() + classify_protein_trajectory() and is layered into assemble_user_evidence(rna_fit, prot_fit). Default NULL.

protein_assay_name

Assay slot to use when protein is a SummarizedExperiment. Default "intensity".

signal_peptide

Optional character vector of gene symbols carrying a signal peptide; passed straight to assemble_user_evidence().

sig_only

Passed to classify_protein_trajectory() (default FALSE here so small / noisy cohorts still surface best-template calls).

Details

This is a convenience around the chain fit_stage_de() -> classify_trajectory() -> assemble_user_evidence() -> compute_audit_score(). Calling those four explicitly continues to work; this wrapper reduces a typical user-cohort workflow to a single line and provides a consistent return shape for reporting and review.

Bioconductor-native input is supported via the same S4 method dispatch as fit_stage_de(): the rna argument can be either a count matrix / data.frame plus coldata, or a SummarizedExperiment whose assay() is the count matrix and whose colData() carries the stage / cohort columns.

Value

A list of class pdactrace_user_projection with elements:

  • rna_fit – output of fit_stage_de().

  • rna_pattern – output of classify_trajectory().

  • prot_fit – output of fit_stage_de_protein() (or NULL).

  • prot_pattern– output of classify_protein_trajectory() (or NULL).

  • evidence – output of assemble_user_evidence().

  • audit – output of compute_audit_score(evidence = ...).

  • summary – one-row data.table with input shape + audit-class counts.

Examples

data(toy_counts)
data(toy_coldata)
if (requireNamespace("DESeq2", quietly = TRUE)) {
  res <- project_user_cohort(
    rna = toy_counts, coldata = toy_coldata,
    stage_col = "stage", cohort_col = "cohort")
  res$summary
}


  if (requireNamespace("DESeq2", quietly = TRUE)) {
    res <- project_user_cohort(
      rna        = toy_counts,
      coldata    = toy_coldata,
      stage_col  = "stage",
      cohort_col = "cohort",
      protein    = toy_protein)
    res$summary
    head(res$audit[order(-audit_score)])
  }

Project a user-supplied serum proteomics cohort (binary contrast)

Description

The serum side of the framework only requires binary group contrasts – PDAC vs healthy control (HC) and optionally Pancreatitis vs HC – not stage-aware data. This makes user projection asymmetric to the tissue side: where project_user_cohort() needs Normal / Early / Mid / Late stage labels for its 12-template trajectory matching, this helper accepts the kind of serum DIA dataset most public PDAC serum studies actually publish (PDAC + HC +/- pancreatitis, no clinical stage labels needed).

Usage

project_user_serum_cohort(
  intensity,
  coldata,
  group_col = "group",
  pdac_label = "PDAC",
  hc_label = "HC",
  pan_label = NULL,
  test = c("limma_eBayes", "wilcox", "t_test"),
  padj_cutoff = 0.05,
  link_to_atlas = TRUE,
  reference = NULL
)

Arguments

intensity

Log2 protein abundance matrix (genes x samples).

coldata

data.frame of sample metadata.

group_col

Name of the column in coldata carrying group labels. Default "group".

pdac_label

Group label for PDAC samples. Default "PDAC".

hc_label

Group label for healthy-control samples. Default "HC".

pan_label

Group label for pancreatitis samples (optional). Default NULL (PDAC vs HC only; no Class A / B assignment without pancreatitis context, but Class C and serum_detected are still set).

test

One of "limma_eBayes" (default; requires the limma package), "wilcox", or "t_test".

padj_cutoff

Significance threshold for serum_detected. Default 0.05. Genes with padj_PDAC_vs_HC <= padj_cutoff are flagged as serum_detected = TRUE.

link_to_atlas

Logical. If TRUE (default), join the bundled pdactrace_reference's rna_pattern direction to assign translation_class. If FALSE, leave translation_class as NA (defer to downstream code).

reference

Optional data.table to inject in place of the bundled atlas (used by tests).

Value

A data.table with one row per gene in intensity and columns:

  • gene_symbol

  • serum_log2fc_PDAC_vs_HC, serum_padj_PDAC_vs_HC

  • serum_log2fc_Pan_vs_HC, serum_padj_Pan_vs_HC (NA if pan_label = NULL)

  • translation_class ("A" / "B" / "C" / NA)

  • serum_detected (logical)

Suitable for direct use as assemble_user_evidence( serum_summary = ...).

What this function does

  1. Splits samples into PDAC / HC / (optional) Pancreatitis groups by coldata[[group_col]].

  2. Computes serum_log2fc_PDAC_vs_HC = mean(PDAC) - mean(HC) and (if pan_label is set) serum_log2fc_Pan_vs_HC = mean(Pan) - mean(HC) per gene.

  3. Runs a per-gene two-group test (default limma_eBayes; fallback to wilcox or t_test) to attach BH-adjusted p-values for each contrast.

  4. Optionally joins the bundled pdactrace_reference's rna_pattern direction to derive a per-gene translation_class (A / B / C):

    • Class A – tissue and serum agree on direction ('same dir.', concordant translation).

    • Class B – tissue and serum oppose ('opposite', inverse translation; the rare, pancreatitis-discriminating case).

    • Class C – decoupled (tissue direction unclear, or serum log2FC very small).

    Set link_to_atlas = FALSE to skip the atlas join and emit translation_class = NA (deferring class assignment to downstream code).

Output is shaped to plug straight into assemble_user_evidence() via the serum_summary argument.

Input format

intensity

A numeric matrix or data.frame of log2 protein abundance, with genes (HGNC symbols) in rows and samples in columns. Pre-log2 transformation is required; raw MS intensities will produce nonsensical log2FC values.

coldata

A data.frame with at least one column (group_col) carrying group labels matching pdac_label, hc_label, and (optionally) pan_label. nrow(coldata) must equal ncol(intensity). Sample order must match column order of intensity.

Minimum 3 samples per group is recommended (limma and wilcox need at least that for a stable variance estimate / rank distribution). Fewer samples emit a warning.

See Also

project_user_cohort() for the tissue side (stage-aware), assemble_user_evidence() for combining tissue + serum into the audit-rule input, plot_serum_direction() for visualisation.

Examples

set.seed(1)
intensity <- matrix(rnorm(20 * 12, mean = 5), nrow = 20,
                    dimnames = list(paste0("G", seq_len(20)),
                                    paste0("S", seq_len(12))))
coldata <- data.frame(group = rep(c("PDAC", "HC"), each = 6))
serum <- project_user_serum_cohort(
  intensity, coldata, test = "t_test", link_to_atlas = FALSE)
head(serum)


  # Synthetic example: 50 genes x 24 samples (8 PDAC, 8 HC,
  # 8 Pancreatitis) with PDAC-up + Pan-flat signal.
  set.seed(1)
  g <- 50; n <- 24
  intensity <- matrix(rnorm(g * n, mean = 5),
                      nrow = g, ncol = n,
                      dimnames = list(paste0("GENE", seq_len(g)),
                                       paste0("S", seq_len(n))))
  intensity[1:10, 1:8] <- intensity[1:10, 1:8] + 2  # PDAC-up
  coldata <- data.frame(
    sample = colnames(intensity),
    group  = rep(c("PDAC", "HC", "Pancreatitis"), each = 8))

  serum_summary <- project_user_serum_cohort(
    intensity, coldata,
    group_col  = "group",
    pdac_label = "PDAC",
    hc_label   = "HC",
    pan_label  = "Pancreatitis")
  head(serum_summary)

  # Feed into the downstream evidence assembler:
  ev <- assemble_user_evidence(serum_summary = serum_summary)

Propagate audit-score uncertainty

Description

By default this returns the stored Monte Carlo uncertainty summary from the atlas. If n_mc is supplied, the uncertainty is recomputed over the full atlas using the frozen scoring rule and perturbing evidence values only.

Usage

propagate_uncertainty(gene_symbol, n_mc = NULL, seed = 42, evidence = NULL)

Arguments

gene_symbol

Character vector of HGNC gene symbols.

n_mc

Optional integer number of Monte Carlo iterations.

seed

Random seed used when n_mc is supplied.

evidence

Optional user-supplied evidence table (see assemble_user_evidence()). When NULL (default), the bundled reference atlas is used. When supplied without n_mc, the stored MC summary is unavailable and n_mc must be set.

Value

A data.table with score CI, rank CI, uncertainty width, and class.

Examples

propagate_uncertainty(c("LTBP1", "THBS2", "GAPDH"))

Query the pdactrace reference atlas for a single gene

Description

Returns a layered list with all evidence available for gene_symbol across the requested layers. If the gene is not in the LRT-significant atlas universe, returns NULL with an informative message.

Usage

query_gene(
  gene_symbol,
  layers = c("rna", "protein", "scrna", "serum", "clinical", "filter_status",
    "annotation")
)

Arguments

gene_symbol

HGNC standard symbol (case-sensitive).

layers

Character vector subset of c("rna", "protein", "scrna", "serum", "clinical", "filter_status", "annotation"). Default: all seven.

Value

Named list with one slot per requested layer plus ⁠$summary⁠ (1-line text) and ⁠$provenance⁠ (comma-separated source phase scripts). Returns NULL invisibly if gene_symbol is not in the atlas.

Examples

q <- query_gene("LTBP1")
  q$rna$pattern        # "Early_Burst_Up"
  q$serum$translation_class  # "B"

Detailed gene-level evidence - 5 granularity tables

Description

Returns a named list of data.tables exposing the hidden per-stage, per-cohort, per-celltype, per-filter-step, and per-serum-cohort detail that query_gene() summarizes only at the headline level.

Usage

query_gene_detailed(gene_symbol)

Arguments

gene_symbol

HGNC standard symbol (case-sensitive).

Details

Use this for reviewer-grade audit (e.g., "is the LTBP1 upregulation consistent across all 4 RNA cohorts?") or for downstream computation.

Value

Named list with 5 slots (each a data.table) plus ⁠$summary⁠ (1-line text) and ⁠$provenance⁠. Returns NULL invisibly with a message if gene_symbol is not in the atlas.

Slots

  • per_stage - 4 rows (Normal, Early, Mid, Late) with log2FC, lfcSE, ci_lo, ci_hi, lrt_padj, wald_padj, significant. lrt_padj is the omnibus DESeq2 LRT padj (identical across stages by construction); wald_padj is the per-contrast Wald padj from a nbinomWaldTest() refit of the same model (v0.4.0). The significant column uses Wald padj.

  • per_cohort - 4 rows (TCGA, CPTAC, GSE224564, GSE79668) with trend (-1/0/+1), monotonic (lgl), plus Stouffer summary as table attribute.

  • per_celltype - 11 rows with mean_expression, pct_of_total, ranked descending.

  • filter_diag - 7 rows (phase60 steps) with pass (lgl), underlying_metric, cutoff, note.

  • serum_per_cohort - 3 rows (PDAC, Pancreatitis, HC) with mean, n_unknown (cohort sample n not always available), plus PDAC-vs-Pan t-test p-value as attribute.

Examples

d <- query_gene_detailed("LTBP1")
  d$per_stage      # log2FC + 95% CI per stage
  d$per_cohort     # cohort x trend x monotonic
  d$filter_diag    # 7-step audit with underlying numbers

Query the pdactrace reference atlas for multiple genes

Description

Convenience join of query_gene() outputs across multiple genes.

Usage

query_panel(
  genes,
  joined = TRUE,
  layers = c("rna", "protein", "scrna", "serum", "clinical", "filter_status",
    "annotation")
)

Arguments

genes

Character vector of HGNC symbols.

joined

Logical. If TRUE (default), returns a wide data.table with one row per gene and key columns from each layer. If FALSE, returns a long data.table with ⁠gene_symbol x layer x variable x value⁠ rows.

layers

Character vector subset of available layers (passed through to query_gene()).

Value

data.table (wide or long).

Examples

query_panel(c("LTBP1", "SERPINA1", "CDH13"))

Render a pdactrace evidence report for one gene or a panel

Description

Renders a self-contained HTML report summarising the bundled atlas's evidence for a single gene OR for a multi-gene panel. The single-gene layout (length-1 input) carries the audit class + score, the audit-component table, the six-axis evidence radar, the cohort-adjusted stage trajectory, the per-cohort breakdown, and the tissue-to-serum filter trace. The panel layout (length >= 2) starts with a side-by-side comparison table from compare_candidates() (audit class, RNA pattern, translation class, cell origin, serum detectability, redundancy hint) and follows with one condensed section per gene: explain_score() rationale plus the evidence radar.

Usage

report_gene(
  gene_symbol,
  output_file = NULL,
  output_dir = tempdir(),
  quiet = TRUE
)

Arguments

gene_symbol

HGNC gene symbol — character vector of length 1 or more. Length 1 produces the single-gene template; length 2+ produces the panel template.

output_file

Optional output filename (without directory). Defaults to ⁠paste0(<stem>, "_pdactrace_report.html")⁠ where ⁠<stem>⁠ is the gene name for length-1 input or paste0("panel_", length(gene_symbol), "gene") otherwise.

output_dir

Output directory. Defaults to tempdir().

quiet

Logical. If TRUE (default), suppress rmarkdown's chatty progress output.

Details

The render is deterministic given (gene_symbol, atlas) because the audit rule is frozen.

Value

Invisibly returns the absolute path to the rendered HTML file.

Examples

args(report_gene)


  if (requireNamespace("rmarkdown", quietly = TRUE) &&
      requireNamespace("knitr",     quietly = TRUE)) {
    # Single-gene report:
    fp1 <- report_gene("LTBP1", output_dir = tempdir())

    # Panel report (3 genes):
    fp2 <- report_gene(c("LGALS3BP", "LTBP1", "GAPDH"),
                         output_dir = tempdir())
    file.exists(c(fp1, fp2))
  }

Evaluate ranking robustness across plausible audit weights

Description

Samples weight vectors over the three audit axes and reports how often each gene remains inside top-N sets. This is a sensitivity analysis, not a new trained model.

Usage

run_weight_robustness(
  atlas = NULL,
  genes = NULL,
  n_draws = 1000L,
  top_n = c(50L, 100L, 500L),
  weight_floor = 0.05,
  seed = 20260527L
)

Arguments

atlas

Optional atlas-like data table.

genes

Optional genes to return after atlas-wide ranking.

n_draws

Number of sampled weight vectors.

top_n

Integer vector of top-N cutoffs.

weight_floor

Minimum weight assigned to each axis. The residual mass is sampled uniformly on the simplex.

seed

Random seed.

Value

A data.table with rank intervals and top-N stability columns.

Examples

run_weight_robustness(genes = "LGALS3BP", n_draws = 10)


run_weight_robustness(genes = c("LGALS3BP", "LTBP1"), n_draws = 50)

Average precision

Description

Average precision

Usage

sb_average_precision(truth, score)

Arguments

truth

Logical or binary vector.

score

Numeric score; larger values predict positives.

Value

A single numeric value in ⁠[0, 1]⁠, or NA_real_ when no positive remains after dropping missing and non-finite entries. Tied scores are collapsed into one threshold.

Examples

sb_average_precision(c(TRUE, FALSE, TRUE, FALSE), c(4, 3, 2, 1))

Binary area under the ROC curve

Description

Binary area under the ROC curve

Usage

sb_binary_auc(truth, score)

Arguments

truth

Logical or binary vector.

score

Numeric score; larger values predict positives.

Value

A single numeric value in ⁠[0, 1]⁠, or NA_real_ when either class is absent after dropping missing and non-finite entries.

Examples

sb_binary_auc(c(TRUE, TRUE, FALSE, FALSE), c(4, 3, 2, 1))

Default frozen thresholds for sb_one_shot()

Description

These are the thresholds as first frozen in prospective/locks/PXD055218_crc_v1/DECISION_RULE.json, which lives in the archived cross-cancer working tree rather than in this package (see audit/PROVENANCE.md). They are reused unchanged as the PDAC default so that the numbers are traceable to a rule that predates any PDAC outcome label, and tests/testthat/test-sb-one-shot.R keeps a golden-equivalence test against that original audit. Supply your own rule to sb_one_shot() for a new lock.

Usage

sb_default_one_shot_rule()

Value

Named list of the frozen coverage, endpoint and resampling thresholds consumed by sb_one_shot().

Examples

str(sb_default_one_shot_rule())

Leave-one-cohort-out rank stability

Description

Leave-one-cohort-out rank stability

Usage

sb_loco_stability(effects, detection = NULL, source_score = NULL, top_k = 50L)

Arguments

effects

StageBridge effect table.

detection

Optional detection table.

source_score

Optional source score.

top_k

Top-ranked set used for selection frequency.

Value

Stability summary by feature.

Examples

sim <- sb_simulate(n_features = 40L, seed = 1L)
head(sb_loco_stability(sim$effects, sim$detection, top_k = 10L), 3)

Score candidates with StageBridge and benchmark comparators

Description

Score candidates with StageBridge and benchmark comparators

Usage

sb_method_scores(effects, detection = NULL, source_score = NULL)

Arguments

effects

Stage-resolved effect table.

detection

Optional blood detection table.

source_score

Optional source score used by StageBridge only.

Value

Wide feature-by-method score table.

Examples

sim <- sb_simulate(n_features = 20L, seed = 1L)
head(sb_method_scores(sim$effects, sim$detection), 3)

Score shared features with matched-sample multiblock PLS-DA

Description

Fits the dense multiblock PLS-DA model implemented by mixOmics and combines block-specific loading magnitudes into a shared-feature priority score. This comparator requires the same observations, in the same order, in every block; it is therefore not applicable to unpaired cohort-level effect tables.

Usage

sb_multiblock_plsda_scores(blocks, outcome, ncomp = 2L, design = 0.1)

Arguments

blocks

Named list of sample-by-feature numeric matrices.

outcome

Factor or vector of class labels, one per sample.

ncomp

Number of latent components.

design

Off-diagonal connection strength between omics blocks.

Value

A feature-by-score data frame. The fitted model is attached as the model attribute.

Examples

if (requireNamespace("mixOmics", quietly = TRUE)) {
  outcome <- factor(rep(c("Normal", "Tumor"), each = 12))
  dn <- list(paste0("S", seq_len(24)), paste0("F", seq_len(8)))
  rna <- matrix(withr::with_seed(7, stats::rnorm(24 * 8)), 24, dimnames = dn)
  protein <- rna + withr::with_seed(8, stats::rnorm(24 * 8, sd = 0.5))
  head(sb_multiblock_plsda_scores(list(RNA = rna, Protein = protein), outcome), 3)
}

Calculate depth-adjusted blood observability

Description

A detection in a shallow cohort contributes more information than the same detection in a cohort where nearly every candidate is observed.

Usage

sb_observability(detection)

Arguments

detection

Data frame with feature, cohort, and detected columns.

Value

Data frame with one observability score per feature.

Examples

sim <- sb_simulate(n_features = 20L, seed = 1L)
head(sb_observability(sim$detection), 3)

Run one prospectively locked destination-cohort audit to a terminal decision

Description

Applies the frozen gate stack (ontology/group-count, broad coverage, top-10 and top-20 panel coverage, complete panel) and, only if every gate passes, the locked statistical endpoint (rank-weighted panel AUROC with a permutation test and a feature-replication count). Returns a terminal decision whose status is exactly one of PASS, FAIL or INCONCLUSIVE. INCONCLUSIVE ("the test never ran") is returned whenever a gate halts execution and is operationally distinct from a biological FAIL (the endpoint ran and did not meet the criteria).

Usage

sb_one_shot(
  abundance,
  frozen,
  panel,
  groups,
  expected_counts,
  rule = sb_default_one_shot_rule()
)

Arguments

abundance

Long data frame: gene, sample, group, log2_abundance.

frozen

Broad frozen score table with feature and frozen_direction.

panel

Ordered top-k panel with feature, frozen_direction, rank_weight.

groups

List with case, primary_reference, secondary_reference group labels.

expected_counts

Named integer vector of expected samples per group.

rule

Named list of frozen thresholds (see sb_default_one_shot_rule()).

Value

A terminal decision list with status, gates, statistical_runner_entered and, when the runner ran, the endpoint statistics.

Examples

frozen <- data.frame(
  feature = sprintf("G%03d", 1:320),
  frozen_direction = rep(c(1L, -1L), length.out = 320L)
)
panel <- data.frame(
  feature = frozen$feature[1:20],
  frozen_direction = frozen$frozen_direction[1:20],
  rank_weight = 1 / log2(2:21)
)
samples <- c(sprintf("CP_%02d", 1:20), sprintf("CN_%02d", 1:20),
             sprintf("HC_%02d", 1:20))
abundance <- expand.grid(
  gene = frozen$feature[1:50], sample = samples,
  KEEP.OUT.ATTRS = FALSE, stringsAsFactors = FALSE
)
abundance$group <- sub("_.*$", "", abundance$sample)
abundance$log2_abundance <- withr::with_seed(
  1, stats::rnorm(nrow(abundance), 8, 1)
)

# Only 50 of the 320 frozen candidates are quantifiable, so the frozen
# coverage gate halts before the endpoint runs: a terminal INCONCLUSIVE,
# which is operationally distinct from a biological FAIL.
decision <- sb_one_shot(
  abundance, frozen, panel,
  groups = list(case = "CP", primary_reference = "CN",
                secondary_reference = "HC"),
  expected_counts = c(CP = 20L, CN = 20L, HC = 20L)
)
decision$status
decision$failed_gates

Assign exact non-dominated Pareto fronts

Description

Assign exact non-dominated Pareto fronts

Usage

sb_pareto_fronts(components)

Arguments

components

Numeric matrix; larger values are preferred.

Value

Integer front labels, where 1 is non-dominated.

Examples

sb_pareto_fronts(cbind(a = c(1, 0.5, 0.2), b = c(0.2, 0.6, 0.1)))

Simulate a multi-cohort, multi-omic biomarker benchmark

Description

Simulate a multi-cohort, multi-omic biomarker benchmark

Usage

sb_simulate(
  n_features = 1200L,
  positive_fraction = 0.06,
  scenario = c("translation", "generic_signal", "heterogeneity", "dropout",
    "neutral_observability", "mixed_relation", "partial_destination",
    "anti_observability"),
  seed = 1L
)

Arguments

n_features

Number of simulated features.

positive_fraction

Fraction of true translatable markers.

scenario

One of generic_signal, translation, heterogeneity, dropout, neutral_observability, mixed_relation, partial_destination, or anti_observability. Translation scenarios include coherent tissue-only and inflammation-associated blood decoys; the final three deliberately violate a default StageBridge assumption.

seed

Random seed.

Value

List with effects, detection, and feature-level truth.

Examples

sim <- sb_simulate(n_features = 40L, seed = 1L)
vapply(sim, nrow, integer(1))

Recall among the top-ranked candidates

Description

Recall among the top-ranked candidates

Usage

sb_topk_recall(truth, score, k = 50L)

Arguments

truth

Logical or binary vector.

score

Numeric score.

k

Number of top candidates.

Value

A single numeric value in ⁠[0, 1]⁠ giving the fraction of positives recovered in the top k scores, or NA_real_ when no positive remains after dropping missing and non-finite entries.

Examples

sb_topk_recall(c(TRUE, TRUE, FALSE, FALSE), c(4, 3, 2, 1), k = 2)

Validate a StageBridge effect table

Description

Validate a StageBridge effect table

Usage

sb_validate_effects(effects)

Arguments

effects

Data frame with one row per feature, cohort, modality, and contrast.

Value

A normalized data frame with numeric estimates and standard errors.

Examples

sim <- sb_simulate(n_features = 20L, seed = 1L)
str(sb_validate_effects(sim$effects))

Schema column specification

Description

Returns the canonical column list with type and T2.5/T3 status. Used by data-raw/build_reference.R to allocate columns and by tests/testthat/test-schema.R to verify completeness.

Usage

schema_spec(include_derived = FALSE)

Arguments

include_derived

Logical. If TRUE, also include derived algorithm-output columns that can be attached by data-raw scripts but are not part of the bundled reference atlas by default.

Value

data.table with columns: name, type, layer, t25_status.

Examples

head(schema_spec())

Descriptive feature-space similarity to bundled external anchors

Description

Computes how close each gene's evidence-feature vector is to the centroid of a curated positive-anchor reference set (cosine or inverse-Euclidean similarity in the z-scored feature space). This is the descriptive layer that gives a continuous, ML-flavoured prioritisation signal without training a supervised classifier – the bundled anchor labels are used only to define the centroid, never as training labels for a shipped predictor.

Usage

score_anchor_similarity(
  genes = NULL,
  anchors = NULL,
  method = c("cosine", "euclidean"),
  tier = c("primary", "secondary", "all"),
  reference = NULL
)

Arguments

genes

Optional character vector of gene symbols. NULL (default) scores every gene in the reference.

anchors

Optional data.table with columns gene and evidence_tier (matching the bundled pdactrace_external_anchors schema). NULL uses the bundled anchor table.

method

One of "cosine" (default) or "euclidean".

tier

Anchor tier filter: "primary" (T1_validated; the smallest, highest-evidence set; default), "secondary" (T1 + T2_literature_db), or "all" (also includes the single exploratory anchor). Matches the partition used by evaluate_anchor_enrichment().

reference

Optional data.table to inject in place of the bundled atlas (for tests).

Details

This complements evaluate_anchor_enrichment() (post-hoc top-N hypergeometric evaluation) and the deterministic audit_score. The three are independent: audit_score ranks by frozen rule, anchor_similarity ranks by feature-space proximity to known biomarkers, and evaluate_anchor_enrichment() is the evaluation harness that consumes either ranking via its score_col argument.

Value

A data.table with one row per requested gene and columns gene_symbol, anchor_similarity, anchor_n (number of anchor rows the centroid was built from), and anchor_tier. Rows are sorted by anchor_similarity descending with NA last.

Method

  1. Build the per-gene feature matrix via make_evidence_features() with scale = "z" (mean 0, sd 1 per feature over complete cases).

  2. Resolve the anchor set via the same internal helper evaluate_anchor_enrichment() uses, filtered by tier.

  3. Anchor centroid ⁠c_k = mean over selected anchors of feature_k⁠ (NAs skipped per feature).

  4. For each atlas gene g, compute ⁠sim(g) = <z_g, c> / (||z_g|| * ||c||)⁠ (cosine, default) or sim(g) = 1 / (1 + sqrt(sum_k (z_g_k - c_k)^2)) (Euclidean).

  5. Genes with too few non-NA features (⁠< 3⁠) get anchor_similarity = NA.

See Also

make_evidence_features(), evaluate_anchor_enrichment(), model_card().

Examples

sim <- score_anchor_similarity(tier = "primary")
head(sim, 10)

Score one or more genes against all 12 templates

Description

Returns full rho vector (12 values) per gene. Useful for inspecting how clearly a gene maps to a single pattern vs ambiguous/borderline, and for diagnosing whether a non-Early best-match excluded the gene from the Early × 4 atlas surface.

Usage

score_trajectory(pat, gene)

Arguments

pat

A data.table from classify_trajectory() or a fit from fit_stage_de() (auto-classified if no rna_pattern col).

gene

Character vector. One or more gene symbols.

Value

data.table with gene_symbol and 12 rho columns (one per template): rho_Early_Burst_Up, rho_Early_Loss_Down, rho_Early_Peak, rho_Early_Trough, rho_Mid_Plateau_Up, rho_Mid_Plateau_Down, rho_Mid_Peak, rho_Mid_Trough, rho_Late_Burst_Up, rho_Late_Loss_Down, rho_Monotonic_Up, rho_Monotonic_Down.

Examples

fit <- data.table::data.table(
  gene_symbol = "G1", beta_N = 0, beta_E = 2,
  beta_M = 0.5, beta_L = 0, lrt_padj = 0.01,
  lrt_significant = TRUE)
pat <- classify_trajectory(fit, sig_only = FALSE)
score_trajectory(pat, "G1")

Rank stage-resolved multi-omic biomarker candidates

Description

Rank stage-resolved multi-omic biomarker candidates

Usage

stagebridge(
  effects,
  detection = NULL,
  source_score = NULL,
  target_modality = NULL,
  target_relation = c("concordant", "inverse", "agnostic", "exclude"),
  weights = c(target = 0.55, anchor = 0.1, evidence = 0.05, replication = 0.08,
    concordance = 0.07, observability = 0.05, coverage = 0.05, source = 0.05),
  compute_pareto = TRUE
)

Arguments

effects

Stage-resolved effect table accepted by sb_validate_effects().

detection

Optional blood detection table accepted by sb_observability().

source_score

Optional named numeric vector or data frame with feature and source_score.

target_modality

Optional destination modality. If omitted, plasma, serum, or blood is detected automatically.

target_relation

Predeclared relationship between tissue and destination effects: concordant, inverse, agnostic, or exclude.

weights

Named component weights.

compute_pareto

Whether to calculate exact non-dominated fronts.

Value

Ranked data frame and auditable component scores. pareto_tier is populated only when compute_pareto = TRUE; score_percentile_tier is an explicitly separate score-only top-5%/top-20% tier.

Examples

sim <- sb_simulate(n_features = 40L, seed = 1L)
ranked <- stagebridge(sim$effects, detection = sim$detection)
head(ranked[, c("feature", "stagebridge_score", "pareto_front")], 3)

Human-readable text summary of gene evidence

Description

Returns a multi-line, glue-formatted text summary of all evidence available for gene_symbol in the pdactrace reference atlas.

Usage

summarize_gene_evidence(gene_symbol, detail = FALSE)

Arguments

gene_symbol

HGNC standard symbol.

detail

Logical. If TRUE, append per-stage, per-cohort, pancreatitis raw-mean, and filter-metric detail blocks.

Value

A character scalar (single string with ⁠\\n⁠ separators). Returns NULL invisibly with a message if gene is not in atlas.

Examples

cat(summarize_gene_evidence("LTBP1"))

Summarize the tissue-to-blood translation gap

Description

Summarize the tissue-to-blood translation gap

Usage

summarize_translation_gap(atlas = NULL, tau_tissue = 0.5, tau_serum = 0.1)

Arguments

atlas

Optional atlas-like data table. Defaults to the bundled pdactrace_reference.

tau_tissue

Minimum absolute tissue effect used by compute_trace_d().

tau_serum

Minimum absolute serum log2 fold-change used by compute_trace_d() and the serum-signal flag. Claim tiers use strict TRACE-D only: a gene is serum_concordant when its measured serum log2FC passes tau_serum in the tissue direction. The historical translation_class label, which records only the sign of the serum change, is returned as legacy_translation_class for reference and does not raise the tier.

Value

A data.table with claim-tier counts and percentages.

Examples

summarize_translation_gap()

Synthetic stage / cohort metadata for runnable examples

Description

Sample-level metadata that pairs with toy_counts and toy_protein. 24 samples balanced across the four canonical stages and two synthetic cohorts.

Usage

toy_coldata

Format

data.frame with 24 rows and 3 columns:

  • sample (character): S01..S24.

  • stage (character): "Normal", "Early", "Mid", "Late", 6 samples per stage.

  • cohort (character): "CohortA", "CohortB", 12 samples per cohort.


Synthetic RNA count matrix for runnable examples

Description

Tiny synthetic RNA-seq count matrix bundled so the user-cohort workflow (fit_stage_de -> classify_trajectory -> assemble_user_evidence -> compute_audit_score) can be exercised end-to-end inside examples and the user-cohort vignette without an external download. The first 10 rows (TGENE01-TGENE10) carry an injected Early_Burst_Up signal (count lift of ~60-90 reads from Stage I onward); the remaining 40 rows are negative-binomial noise. Generated by data-raw/build_toy_data.R with set.seed(2026).

Usage

toy_counts

Format

Integer matrix, 50 genes x 24 samples. Row names TGENE01..TGENE50; column names S01..S24 matching toy_coldata$sample.

Examples

data("toy_counts", package = "pdactrace")
data("toy_coldata", package = "pdactrace")
if (requireNamespace("DESeq2", quietly = TRUE)) {
  fit <- fit_stage_de(toy_counts,
                      stage  = toy_coldata$stage,
                      cohort = toy_coldata$cohort)
  head(fit)
}

Synthetic tissue-protein log2 intensity matrix for runnable examples

Description

Tiny synthetic protein intensity matrix paired with toy_counts / toy_coldata. The first 8 rows (TPROT01-TPROT08) carry an injected Early_Burst_Up shape (log2 lift of ~0.6-1.0 from Stage I); the remaining 42 rows are Gaussian noise around 12. Generated by data-raw/build_toy_data.R with set.seed(2026).

Usage

toy_protein

Format

Numeric matrix (log2 intensity), 50 proteins x 24 samples. Row names TPROT01..TPROT50; column names S01..S24.


Trace which selection filters a gene passed or failed

Description

Returns the gene's pass/fail status for each step of pdactrace's two parallel narrowing funnels:

Usage

trace_filters(gene)

Arguments

gene

Character vector of HGNC symbols.

Details

  • phase60 7-step pipeline - strict cumulative funnel (signal peptide -> serum measurable -> pool significance -> vs pancreatitis -> HC-in-middle -> direction match -> final).

  • phase77 strict candidate set - independent narrow funnel producing the 22 strict tissue+serum convergent candidates that anchor manual Class B curation.

Most genes (including LTBP1) pass one of the two funnels but not both - this is the audit-trail message of pdactrace.

Value

A data.table with one row per requested gene and one column per filter step (logical). Includes n_phase60_pass (0-7) and class_route summary column.

Examples

trace_filters("LTBP1")
  trace_filters(c("LTBP1", "SPARC", "CDH13", "SERPINA1"))

Single-call visual evidence + scoring canvas for one gene

Description

Returns a 2x3 patchwork composite that puts one panel per evidence layer plus the final audit-axis radar for a gene on a single page (v0.99.8 expansion from the previous 4-panel layout). The figure shows the entire prioritisation flow on one page: four input layers (RNA, tissue protein, scRNA cell origin, serum direction), the 7-step tissue-to-serum filter trail that gates them, and the 6-axis hexagon that compresses all of the above into the final audit-score components.

Usage

viz_gene(
  gene_symbol,
  title = NULL,
  ncol = 3L,
  layout = c("split", "compact"),
  output_dir = NULL,
  width = 6,
  height = 4.5
)

Arguments

gene_symbol

HGNC gene symbol (length-1 character).

title

Optional title override. NULL (default) builds the title from the bundled atlas headline.

ncol

Layout: 3 (default; 2x3), 2 (3x2), or 1 (6x1 vertical strip suitable for narrow embedding).

layout

One of "split" (default; returns a pdactrace_viz_panels list of six full-size ggplots whose print() method renders each panel as its own page in the active plot device — in RStudio you flip through the six panels with the plot-pane arrows) or "compact" (the single-page 2x3 patchwork composite preserved for backward compatibility and embedding contexts).

output_dir

Optional directory path. Only used when layout = "split". If supplied, each of the six panels is written to a separate cairo-PDF file via pdactrace_save() and the named list is returned invisibly with file paths attached as attr(., "files").

width, height

Per-panel width and height in inches when layout = "split" and output_dir is set. Defaults ⁠6 x 4.5⁠ (single-column, NCS-grade).

Details

  1. Top-left – bulk RNA-seq: per-stage log2FC forest (mean +/- 95% CI vs Normal).

  2. Top-middle – tissue proteomics: per-stage protein log2FC trajectory (point estimates; per-stage SE not available from the bundled limma F-test fit).

  3. Top-right – scRNA cell origin: 11-celltype expression distribution with the dominant lineage highlighted.

  4. Bottom-left – serum direction: per-gene PDAC vs HC and Pancreatitis vs HC log2FC, coloured by translation class (Class A blue, Class B red, Class C grey).

  5. Bottom-middle – 7-step filter trace: the phase60 tissue-to-serum audit trail with per-gene pass-count badge.

  6. Bottom-right – 6-axis audit hexagon: the final score's six component axes (multi-layer, direction, stage-onset, serum bridge, leakage safety, cohort consistency), the same axes explain_score() decomposes.

A title strip across the top names the gene, its matched template, audit class, and translation class. This single figure is the visual analog of summarize_gene_evidence() + explain_score() combined and is the recommended first call for a gene a clinician or biologist hands you – one plot, the whole evidence-to-score chain, no per-axis function names required.

Internally composes plot_stage_effect() (RNA + protein), plot_celltype_full(), plot_serum_direction(), plot_filter_trace(), and plot_gene_hexagon() via patchwork::wrap_plots(). Every sub-panel remains available standalone; the previous RNA per-cohort sign-vote view is at plot_per_cohort().

Value

A patchwork object printable to any active graphics device or saveable via ggsave() / pdactrace_save().

See Also

summarize_gene_evidence() for the text counterpart; report_gene() for an HTML report (requires pandoc).

Examples

viz_gene("LGALS3BP")

  # Alternative layouts (skipped in R CMD check examples for time;
  # all run normally in interactive use).
  viz_gene("LTBP1", ncol = 2)            # 3x2 grid
  viz_gene("LTBP1", ncol = 1)            # vertical strip

  # Split mode: get each panel as its own full-size figure
  panels <- viz_gene("LTBP1", layout = "split")
  panels$rna       # bulk RNA per-stage forest
  panels$protein   # tissue protein per-stage trajectory
  panels$cell      # scRNA cell-of-origin distribution
  panels$serum     # serum direction strip
  panels$filter    # 7-step filter trail
  panels$hexagon   # 6-axis audit hexagon


  # Or write each panel to a separate PDF in one call:
  viz_gene("LTBP1", layout = "split", output_dir = tempdir())