CRC

This vignette demonstrates the SwitchClass workflow using a colorectal cancer (CRC) plasma proteomics dataset. It illustrates how to preprocess the data, perform label-switch classification to distinguish baseline-aligned and perturbation-aligned molecular responses, and visualize results using UMAP, quadrant plots, boxplots, and pathway enrichment.

Environment setup

library(SwitchClass)
library(ggplot2)
library(ggpubr)

library(limma)
library(PhosR)

1. Load and Prepare CRC Dataset

Load the preprocessed expression matrix and associated sample metadata from the CRC plasma proteome study. The dataset includes healthy control (HC), pre-treatment (Pre), and post-treatment (Post) groups.
Apply quality filtering to retain proteins consistently detected across samples, followed by imputation of missing values using PhosR utilities to obtain a complete expression matrix.

data("CRC_subset")

# log2 transform
expr.log <- log2(as.matrix(CRC_subset$expr) + 1)
rownames(expr.log) <- rownames(CRC_subset$expr)

# filtering
group <- CRC_subset$group
crc.filtered <- PhosR::selectGrps(expr.log, group, percent = 0.1, n = 3)
tmp <- apply(crc.filtered, 2, as.numeric)
rownames(tmp) <- rownames(crc.filtered)
dim(tmp)
## [1] 3077  135
# imputation
crc.imputed <- PhosR::tImpute(
  PhosR::scImpute(mat = tmp, percent = 0.5, grps = group),
  m = 1.8,
  s = 0.3
)

labels <- factor(colnames(crc.imputed), levels = colnames(crc.imputed)[order(group)])

p1 <- PhosR::plotQC(crc.filtered, panel = "pca", grps = group, labels = labels) +
  ggplot2::scale_color_manual(values = c(HC = "#5AAF59", Pre = "#D3542D", Post = "#E5CA4E")) +
  ggplot2::theme_classic() +
  ggplot2::theme(
    panel.background = ggplot2::element_blank(),
    plot.background = ggplot2::element_blank(),
    panel.border = ggplot2::element_blank(),
    panel.grid = ggplot2::element_blank()
  )

p2 <- PhosR::plotQC(crc.imputed, panel = "pca", grps = group, labels = labels) +
  ggplot2::scale_color_manual(values = c(HC = "#5AAF59", Pre = "#D3542D", Post = "#E5CA4E")) +
  ggplot2::theme_classic() +
  ggplot2::theme(
    panel.background = ggplot2::element_blank(),
    plot.background = ggplot2::element_blank(),
    panel.border = ggplot2::element_blank(),
    panel.grid = ggplot2::element_blank()
  )

ggpubr::ggarrange(p1, p2, ncol=2, nrow=1, common.legend = TRUE, legend="right")

2. Differential Expression Analysis

Perform differential expression analysis between healthy controls and pre-treatment CRC samples using limma. Identify features showing significant fold-change and adjusted p-value thresholds.

pp <- group[group %in% c("HC","Pre")]
design_DE <- model.matrix(~ 0 + pp)

cancer.pp <- crc.imputed[,group %in% c("HC","Pre")]
fit_DE  <- limma::lmFit(cancer.pp, design_DE)

cont_DE <- limma::makeContrasts(pre_vs_HC = ppPre - ppHC, levels = colnames(design_DE))
fit_DE2 <- limma::eBayes(limma::contrasts.fit(fit_DE, cont_DE))

DE.table <- limma::topTable(fit_DE2, number = Inf)
if (nrow(DE.table) == 0L) {
  stop("No differential expression results were returned by limma.")
}
DE.table$gene <- rownames(DE.table)
DE.table <- DE.table[order(DE.table$adj.P.Val, DE.table$P.Value), ]

DE.genes <- DE.table$gene[which((DE.table[, "adj.P.Val"] < 0.05) & (abs(DE.table[,"logFC"]) > 0.5849625))]
length(DE.genes)
## [1] 1058

3. Label-Switch Classification

Use the SwitchClass algorithm to train random forest classifiers under baseline-aligned and perturbation-aligned label schemes, then compute feature-level directional importance scores.

# Use DE genes to focus the classification
X_bc <- crc.imputed[DE.genes, , drop = FALSE]

# Label schemes: baseline-aligned vs perturbation-aligned
y_rev <- paste0("c", ifelse(group=="HC",1, ifelse(group=="Pre",2,1)))
y_per <- paste0("c", ifelse(group=="HC",1, ifelse(group=="Pre",2,2)))

# run SwitchClass
res <- SwitchClass::label_switch_classify(
    X_bc, y_reverse = y_rev, y_persist = y_per,
    ntimes = 3, ntree = 100, seed = 1
)
delta <- res$delta

4. UMAP Visualization of Feature Subsets

Visualize sample clustering based on UMAP embeddings computed using baseline-aligned, perturbation-aligned, or all features. Consistent color mapping highlights group separation.

res_trip <- umap_triptych_by_delta(
  X = X_bc,
  delta = delta,
  groups = group,
  top_n = 100,
  palette = c(HC = "#5AAF59", Pre = "#D3542D", Post = "#E5CA4E")
)

# View the combined plot
res_trip$plot

5. Quadrant Assignment and Delta–Fold-Change Scatter Plot

Combine directional importance scores with log fold-changes to assign each feature to one of four quadrants, reflecting distinct molecular response behaviors. Plot the scatter to reveal major feature trends.

Hm <- matrixStats::rowMeans2(as.matrix(X_bc[, group == "HC", drop = FALSE]))
Dm <- matrixStats::rowMeans2(as.matrix(X_bc[, group == "Pre", drop = FALSE]))
lfc <- Hm - Dm   

out <- plot_delta_vs_fc(delta, lfc, delta_thresh = 0.15, label_top = 8)

df_quad <- out$data  # tidy table with quadrants
out$plot  # the scatter plot

6. Expression Patterns of Representative Features

Generate boxplots for selected representative features from each quadrant to illustrate characteristic expression changes across groups.

genes_order <- c("RASA4","RASA4B","SMYD5","MRPS25","BLM","CPSF6","HBB","MSL1")
palette_crc <- c(HC = "#5AAF59", Pre = "#D3542D", Post = "#E5CA4E")
group <- factor(group, levels = c("HC","Pre", "Post"))

p <- plot_feature_boxplots(
  X = X_bc,
  group = group,
  features = genes_order,
  feature_order = genes_order,
  palette = palette_crc,
  ncol = 4,
  title = "Top example genes across groups"
)
p

## 7. Heatmap of Quadrant-Defined Features Create a heatmap showing standardized expression of genes across samples, grouped by both quadrants and sample type (HC, Pre, Post).

plot_heatmap_quadrants(
  X = crc.imputed,
  group = group,
  df_quad = df_quad,
  cutoff = 0.15,
  palette_group = c(HC = "#5AAF59", Pre = "#D3542D", Post = "#E5CA4E")
)

8. Pathway Enrichment Analysis (Reactome)

Perform over-representation analysis using Reactome gene sets from msigdbr to identify biological pathways enriched within each quadrant. Visualize the top pathways per quadrant as -log10(p-value) barplots.

out <- enrich_reactome_quadrants(
  df = df_quad,
  universe = rownames(CRC_subset$expr),
  top_terms = 5
)

out$plot      # the combined 4-panel barplot
# out$results$Q1  # raw over-representation table for Q1
# out$barplot_df  # tidy plotted values

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] PhosR_1.23.0       limma_3.99.0       ggpubr_1.0.0       ggplot2_4.0.3     
## [5] SwitchClass_0.99.1 BiocStyle_2.41.0  
## 
## loaded via a namespace (and not attached):
##   [1] gridExtra_2.3.1             rlang_1.3.0                
##   [3] magrittr_2.0.5              otel_0.2.0                 
##   [5] matrixStats_1.5.0           e1071_1.7-17               
##   [7] compiler_4.6.1              vctrs_0.7.3                
##   [9] reshape2_1.4.5              stringr_1.6.0              
##  [11] pkgconfig_2.0.3             shape_1.4.6.1              
##  [13] fastmap_1.2.0               backports_1.5.1            
##  [15] XVector_0.53.0              labeling_0.4.3             
##  [17] rmarkdown_2.32              preprocessCore_1.75.1      
##  [19] network_1.20.0              purrr_1.2.2                
##  [21] xfun_0.61                   randomForest_4.7-1.2       
##  [23] cachem_1.1.0                jsonlite_2.0.0             
##  [25] DelayedArray_0.39.6         irlba_2.3.7                
##  [27] parallel_4.6.1              broom_1.0.13               
##  [29] R6_2.6.1                    bslib_0.12.0               
##  [31] stringi_1.8.9               RColorBrewer_1.1-3         
##  [33] GGally_2.4.0                car_3.1-5                  
##  [35] GenomicRanges_1.65.4        jquerylib_0.1.4            
##  [37] Rcpp_1.1.2                  Seqinfo_1.3.2              
##  [39] SummarizedExperiment_1.43.0 knitr_1.52                 
##  [41] FNN_1.1.4.1                 IRanges_2.47.5             
##  [43] Matrix_1.7-6                igraph_2.3.3               
##  [45] tidyselect_1.2.1            abind_1.4-8                
##  [47] yaml_2.3.12                 viridis_0.6.5              
##  [49] lattice_0.23-1              tibble_3.3.1               
##  [51] plyr_1.8.9                  Biobase_2.73.2             
##  [53] withr_3.0.3                 S7_0.2.2                   
##  [55] coda_0.19-4.1               evaluate_1.0.5             
##  [57] ggstats_0.14.0              proxy_0.4-29               
##  [59] circlize_0.4.18             pillar_1.11.1              
##  [61] BiocManager_1.30.27         MatrixGenerics_1.25.0      
##  [63] carData_3.0-6               stats4_4.6.1               
##  [65] generics_0.1.4              S4Vectors_0.51.10          
##  [67] scales_1.4.0                class_7.3-24               
##  [69] glue_1.8.1                  pheatmap_1.0.13            
##  [71] maketools_1.3.2             tools_4.6.1                
##  [73] dendextend_1.19.1           sys_3.4.3                  
##  [75] ggsignif_0.6.4              buildtools_1.0.0           
##  [77] cowplot_1.2.0               grid_4.6.1                 
##  [79] tidyr_1.3.2                 colorspace_2.1-3           
##  [81] Formula_1.2-6               cli_3.6.6                  
##  [83] ruv_0.9.7.2                 S4Arrays_1.13.0            
##  [85] viridisLite_0.4.3           ggdendro_0.2.0             
##  [87] dplyr_1.2.1                 uwot_0.2.5                 
##  [89] pcaMethods_2.5.0            gtable_0.3.6               
##  [91] rstatix_1.1.0               sass_0.4.10                
##  [93] digest_0.6.39               BiocGenerics_0.59.12       
##  [95] ggrepel_0.9.8               SparseArray_1.13.2         
##  [97] farver_2.1.2                htmltools_0.5.9            
##  [99] lifecycle_1.0.5             GlobalOptions_0.1.4        
## [101] statnet.common_4.13.0       statmod_1.5.2              
## [103] MASS_7.3-66