2. Audit-class case studies — LGALS3BP / LTBP1 / ALB / GAPDH

Goal

Walk through one representative gene per audit_class to show how the v0.3.0 audit framework handles different evidence patterns. None of these genes are presented as the paper’s flagship candidate; each is an illustrative case of how the framework’s three axes plus two gates resolve a specific evidence pattern into a transparent label.

Gene audit_class Why it ends here
LGALS3BP high_confidence Both gates clean; multi-layer + Early-onset evidence + serum bridge all strong; recovered as a secondary anchor
LTBP1 supported_uncertain Multi-layer concordant tissue evidence, but cohort I² = 75% triggers heterogeneity_gate < 1; Class B inverse-translation exemplar
ALB penalized Plasma high-abundance flag triggers leakage_gate = 0.5; tissue Early signal is real but not specific enough on its own
GAPDH excluded Housekeeping flag triggers leakage_gate = 0; hard-zeros the score regardless of trajectory shape
library(pdactrace)
library(data.table)
#> 
#> Attaching package: 'data.table'
#> The following object is masked from 'package:base':
#> 
#>     %notin%
data("pdactrace_reference", package = "pdactrace")

Side-by-side audit summary

case_genes <- c("LGALS3BP", "LTBP1", "ALB", "GAPDH")
audit <- compute_audit_score(case_genes)
audit
#>    gene_symbol evidence_strength biological_coherence translational_relevance
#>         <char>             <num>                <num>                   <num>
#> 1:    LGALS3BP               1.0            0.8602854                     1.0
#> 2:       LTBP1               1.0            0.5971764                     0.7
#> 3:         ALB               0.5            0.7335634                     0.0
#> 4:       GAPDH               0.5            0.0000000                     0.4
#>    leakage_gate heterogeneity_gate positive_score audit_score
#>           <num>              <num>          <num>       <num>
#> 1:          1.0                1.0      0.9510999   1.0000000
#> 2:          1.0                0.7      0.7840117   0.5770248
#> 3:          0.5                1.0      0.4567472   0.2401153
#> 4:          0.0                1.0      0.3000000   0.0000000
#>            audit_class
#>                 <char>
#> 1:     high_confidence
#> 2: supported_uncertain
#> 3:           penalized
#> 4:            excluded

Two key observations:

  1. audit_class is determined by gates first, then by score. GAPDH (housekeeping → leakage_gate = 0) is excluded regardless of how strong its trajectory is. ALB (plasma high-abundance → leakage_gate = 0.5) is penalized even though its tissue signal is real.
  2. LTBP1 is supported_uncertain, not high_confidence, because the heterogeneity gate (audit_heterogeneity_gate = 0.7) marks cohort divergence; we frame it as an exemplar of Class B inverse translation rather than a flagship candidate.

Detailed audit features per gene

detailed <- query_panel(case_genes)
extra <- pdactrace_reference[
  gene_symbol %in% case_genes,
  .(gene_symbol, audit_class, audit_score, max_I2_meta)]
detailed <- merge(detailed, extra, by = "gene_symbol", sort = FALSE)
detailed[, .(gene_symbol, rna_pattern, prot_pattern, translation_class,
              audit_class, audit_score, max_I2_meta,
              cell_origin_top)]
#>    gene_symbol    rna_pattern   prot_pattern translation_class
#>         <char>         <char>         <char>            <char>
#> 1:    LGALS3BP Early_Burst_Up Early_Burst_Up                 A
#> 2:       LTBP1 Early_Burst_Up Early_Burst_Up                 B
#> 3:         ALB   Early_Trough Early_Burst_Up              <NA>
#> 4:       GAPDH           <NA> Early_Burst_Up                 B
#>            audit_class audit_score max_I2_meta cell_origin_top
#>                 <char>       <num>       <num>          <char>
#> 1:     high_confidence   1.0000000    25.52226          Ductal
#> 2: supported_uncertain   0.5770248    75.16350           myCAF
#> 3:           penalized   0.2401153    56.84316            <NA>
#> 4:            excluded   0.0000000    51.79413            <NA>

Case 1 — LGALS3BP (high_confidence anchor)

LGALS3BP (Galectin-3 binding protein) sits at the top of the audit_score ranking and is one of the secondary anchors recovered by the framework. It illustrates the simplest case: every axis fires positively and neither gate triggers.

lg <- query_gene_detailed("LGALS3BP")
lg$summary
#> [1] "LGALS3BP: Early_Burst_Up (rho=0.98, padj=3.7e-04) | Tier1_gold | serum-detected (n=1 cohort, Class A)."
lg$per_stage
#>     stage    log2FC     lfcSE     ci_lo     ci_hi     lrt_padj    wald_padj
#>    <fctr>     <num>     <num>     <num>     <num>        <num>        <num>
#> 1: Normal 0.0000000 0.0000000 0.0000000 0.0000000           NA           NA
#> 2:  Early 0.4818728 0.1581037 0.1719896 0.7917561 0.0003660696 1.029833e-02
#> 3:    Mid 0.6011442 0.1284123 0.3494560 0.8528324 0.0003660696 1.565348e-05
#> 4:   Late 0.5433948 0.1347248 0.2793342 0.8074553 0.0003660696 2.160136e-04
#>    significant
#>         <lgcl>
#> 1:       FALSE
#> 2:        TRUE
#> 3:        TRUE
#> 4:        TRUE
plot_gene_hexagon("LGALS3BP",
                    comparison = "high_confidence_mean")

Case 2 — LTBP1 (supported_uncertain, Class B inverse exemplar)

LTBP1 has tissue-level concordant evidence (RNA + protein both Early_Burst_Up, ρ ≈ 1.0, myCAF cell origin) but cohort-level heterogeneity is high: TCGA shows an increasing trajectory, CPTAC a decreasing trajectory, and two cohorts are non-monotonic. The random-effects meta-analysis records max_I2_meta = 75 % and the heterogeneity gate downweights the score by 0.70.

ltbp1 <- query_gene_detailed("LTBP1")
ltbp1$per_cohort
#>       cohort         trend monotonic
#>       <char>        <char>    <lgcl>
#> 1:      TCGA    Increasing      TRUE
#> 2:     CPTAC    Decreasing      TRUE
#> 3: GSE224564 Non-monotonic     FALSE
#> 4:  GSE79668 Non-monotonic     FALSE

The framework correctly assigns LTBP1 to supported_uncertain rather than high_confidence, traceably attributing the uncertainty to cohort divergence:

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

LTBP1 also has translation_class = "B" (inverse): tissue Burst-Up evidence does not translate to a serum-up biomarker. Its panel value emerges only when paired with a Class A (concordant-translation) marker — see Class A × Class B panel diversity in the BiB Discussion (LTBP1 + SERPINA1 LOOCV AUC 0.97 vs LTBP1 alone 0.83 vs SERPINA1 alone 0.89).

Case 3 — ALB (penalized, plasma high-abundance gate)

Albumin (ALB) has a real Early-onset signal at the tissue level (Early_Trough — falling at PDAC onset) but is also one of the most abundant plasma proteins. The leakage gate downweights ALB’s score by 0.50 to reflect that abundance-driven false positives are a known failure mode in serum biomarker discovery.

alb <- compute_audit_score("ALB")
alb[, .(gene_symbol, evidence_strength, biological_coherence,
         translational_relevance, leakage_gate,
         heterogeneity_gate, audit_score, audit_class)]
#> Key: <gene_symbol>
#>    gene_symbol evidence_strength biological_coherence translational_relevance
#>         <char>             <num>                <num>                   <num>
#> 1:         ALB               0.5            0.7335634                       0
#>    leakage_gate heterogeneity_gate audit_score audit_class
#>           <num>              <num>       <num>      <char>
#> 1:          0.5                  1   0.2401153   penalized
plot_gene_hexagon("ALB", comparison = "penalized_mean")

ALB is admitted to the ranking (its real Early signal counts) but with a transparent abundance-driven discount, rather than being silently dropped. This is the difference between a “leakage gate” (graded 0 / 0.5 / 1) and a hard exclusion list.

Case 4 — GAPDH (excluded, housekeeping gate)

GAPDH is a canonical housekeeping gene. Its audit_is_housekeeping flag triggers leakage_gate = 0, which hard-zeros the audit score regardless of how strong its per-stage trajectory looks. This is the “non-nominatable” failure mode of the framework: even a perfect trajectory shape cannot rescue a housekeeping artifact.

gapdh <- compute_audit_score("GAPDH")
hk <- pdactrace_reference[gene_symbol == "GAPDH",
                            .(gene_symbol, audit_is_housekeeping)]
gapdh <- merge(gapdh, hk, by = "gene_symbol", sort = FALSE)
gapdh[, .(gene_symbol, audit_is_housekeeping, leakage_gate,
           audit_score, audit_class)]
#>    gene_symbol audit_is_housekeeping leakage_gate audit_score audit_class
#>         <char>                <lgcl>        <num>       <num>      <char>
#> 1:       GAPDH                  TRUE            0           0    excluded
plot_gene_hexagon("GAPDH", comparison = "excluded_mean")

GAPDH is also reclassified under v0.4.0’s 12-template catalog as Monotonic_Up (linear pan-stage progression) rather than Early_Burst_Up, which removes it from the Early × 4 atlas surface entirely — the leakage gate and the 12-template catalog discipline defend against housekeeping contamination through two independent mechanisms.

Take-away

The four cases illustrate the explicit decomposition that the audit framework provides: each gene’s audit_class is traceable to specific axes and gates, and the same framework discipline applies uniformly across genes regardless of biological role. When you query your own candidate, the same decomposition is available via compute_audit_score(c("YOUR_GENE")) and query_gene_detailed(...) — see vignette user_cohort_extension for applying this discipline to your own multi-omics cohort.

Session information

sessionInfo()
#> R version 4.6.1 (2026-06-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 26.04.1 LTS
#> 
#> Matrix products: default
#> BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
#> LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.32.so;  LAPACK version 3.12.0
#> 
#> locale:
#>  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
#>  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
#>  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
#>  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
#>  [9] LC_ADDRESS=C               LC_TELEPHONE=C            
#> [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       
#> 
#> time zone: Etc/UTC
#> tzcode source: system (glibc)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] data.table_1.18.6.1 pdactrace_0.99.25   rmarkdown_2.32     
#> 
#> loaded via a namespace (and not attached):
#>  [1] sass_0.4.10                 generics_0.1.4             
#>  [3] SparseArray_1.13.3          lattice_0.23-1             
#>  [5] digest_0.6.39               magrittr_2.0.5             
#>  [7] evaluate_1.0.5              grid_4.6.1                 
#>  [9] RColorBrewer_1.1-3          fastmap_1.2.0              
#> [11] jsonlite_2.0.0              Matrix_1.7-6               
#> [13] scales_1.4.0                jquerylib_0.1.4            
#> [15] abind_1.4-8                 cli_3.6.6                  
#> [17] rlang_1.3.0                 XVector_0.53.0             
#> [19] Biobase_2.73.2              withr_3.0.3                
#> [21] cachem_1.1.0                DelayedArray_0.39.7        
#> [23] yaml_2.3.12                 otel_0.2.0                 
#> [25] S4Arrays_1.13.1             tools_4.6.1                
#> [27] dplyr_1.2.1                 ggplot2_4.0.3              
#> [29] SummarizedExperiment_1.43.0 BiocGenerics_0.59.12       
#> [31] buildtools_1.0.0            vctrs_0.7.3                
#> [33] R6_2.6.1                    matrixStats_1.5.0          
#> [35] stats4_4.6.1                lifecycle_1.0.5            
#> [37] Seqinfo_1.3.2               S4Vectors_0.51.10          
#> [39] IRanges_2.47.5              pkgconfig_2.0.3            
#> [41] pillar_1.11.1               bslib_0.12.0               
#> [43] gtable_0.3.6                glue_1.8.1                 
#> [45] tidyselect_1.2.1            tibble_3.3.1               
#> [47] xfun_0.61                   GenomicRanges_1.65.4       
#> [49] sys_3.4.3                   MatrixGenerics_1.25.0      
#> [51] knitr_1.52                  farver_2.1.2               
#> [53] htmltools_0.5.9             labeling_0.4.3             
#> [55] maketools_1.3.2             compiler_4.6.1             
#> [57] S7_0.2.2