Effective visualization is crucial for understanding and communicating scGate results. This guide covers various visualization techniques for exploring gating results, signature scores, and model structures.
# Load example data
data(query.seurat)
# Create a multi-level model
model <- gating_model(level = 1, name = "Immune", signature = c("PTPRC"))
model <- gating_model(model = model, level = 2, name = "Tcell",
signature = c("CD3D", "CD3E"))
# Apply scGate with level saving
query.seurat <- scGate(
data = query.seurat,
model = model,
reduction = "pca",
save.levels = TRUE
)The most common visualization shows Pure vs Impure cells:
# Side-by-side comparison
p1 <- DimPlot(query.seurat, group.by = "cell_type", label = TRUE, repel = TRUE) +
ggtitle("Original Cell Types") +
NoLegend()
p2 <- DimPlot(query.seurat, group.by = "is.pure",
cols = c("Pure" = "#00ae60", "Impure" = "#e0e0e0")) +
ggtitle("scGate Classification") +
theme(legend.position = "bottom")
p1 + p2Visualize UCell scores as a continuous gradient:
# Find UCell score columns
ucell_cols <- grep("_UCell$", colnames(query.seurat@meta.data), value = TRUE)
print(paste("Available UCell scores:", paste(ucell_cols, collapse = ", ")))
# Plot signature scores
if (length(ucell_cols) > 0) {
p1 <- FeaturePlot(query.seurat, features = ucell_cols[1],
cols = c("gray95", "navy")) +
ggtitle(paste(ucell_cols[1], "Score"))
p2 <- DimPlot(query.seurat, group.by = "is.pure",
cols = c("Pure" = "#00ae60", "Impure" = "gray80"))
print(p1 + p2)
}scGate provides a built-in function to visualize results at each gating level:
# Find level columns
level_cols <- grep("^is.pure\\.level", colnames(query.seurat@meta.data), value = TRUE)
if (length(level_cols) >= 1) {
plots <- list()
for (i in seq_along(level_cols)) {
col <- level_cols[i]
level_name <- gsub("is.pure\\.", "", col)
plots[[i]] <- DimPlot(query.seurat, group.by = col,
cols = c("Pure" = "#00ae60", "Impure" = "#e0e0e0")) +
ggtitle(paste("Level:", level_name)) +
theme(legend.position = "bottom")
}
# Add final result
plots[[length(plots) + 1]] <- DimPlot(query.seurat, group.by = "is.pure",
cols = c("Pure" = "#00ae60", "Impure" = "#e0e0e0")) +
ggtitle("Final Result") +
theme(legend.position = "bottom")
wrap_plots(plots, ncol = min(3, length(plots)))
}if (length(ucell_cols) > 0) {
# Prepare data
plot_data <- data.frame(
score = query.seurat@meta.data[[ucell_cols[1]]],
classification = query.seurat$is.pure,
cell_type = query.seurat$cell_type
)
# Violin plot by classification
ggplot(plot_data, aes(x = classification, y = score, fill = classification)) +
geom_violin(alpha = 0.7, scale = "width") +
geom_boxplot(width = 0.15, fill = "white", alpha = 0.9) +
scale_fill_manual(values = c("Pure" = "#00ae60", "Impure" = "#808080")) +
labs(title = paste("Distribution of", ucell_cols[1]),
x = "Classification",
y = "UCell Score") +
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(hjust = 0.5, face = "bold"))
}if (length(ucell_cols) > 0) {
ggplot(plot_data, aes(x = score, fill = cell_type)) +
geom_density(alpha = 0.5) +
geom_vline(xintercept = 0.2, linetype = "dashed", color = "red", linewidth = 1) +
labs(title = paste("Score Distribution by Cell Type"),
subtitle = "Red dashed line = default threshold (0.2)",
x = "UCell Score",
y = "Density") +
theme_minimal() +
theme(legend.position = "right")
}# Compare scGate results with original annotations
confusion_data <- table(
Original = query.seurat$cell_type,
scGate = query.seurat$is.pure
)
# Convert to data frame for plotting
conf_df <- as.data.frame(confusion_data)
names(conf_df) <- c("Original", "scGate", "Count")
# Calculate percentages within each original cell type
conf_df <- conf_df %>%
dplyr::group_by(Original) %>%
dplyr::mutate(Percentage = Count / sum(Count) * 100) %>%
dplyr::ungroup()
# Create heatmap
ggplot(conf_df, aes(x = scGate, y = Original, fill = Percentage)) +
geom_tile(color = "white", linewidth = 0.5) +
geom_text(aes(label = sprintf("%.0f%%\n(n=%d)", Percentage, Count)),
color = "black", size = 3) +
scale_fill_gradient2(low = "white", mid = "#a8d5ba", high = "#00ae60",
midpoint = 50, limits = c(0, 100)) +
labs(title = "scGate Classification by Cell Type",
x = "scGate Result",
y = "Original Annotation",
fill = "Percentage") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 0),
plot.title = element_text(hjust = 0.5, face = "bold"),
panel.grid = element_blank())# Create a comprehensive figure
if (length(ucell_cols) > 0) {
# Panel A: UMAP with cell types
pA <- DimPlot(query.seurat, group.by = "cell_type", label = TRUE,
repel = TRUE, label.size = 3) +
ggtitle("A. Original Annotation") +
NoLegend() +
theme(plot.title = element_text(face = "bold"))
# Panel B: UMAP with scGate result
pB <- DimPlot(query.seurat, group.by = "is.pure",
cols = c("Pure" = "#00ae60", "Impure" = "#e0e0e0")) +
ggtitle("B. scGate Classification") +
theme(plot.title = element_text(face = "bold"),
legend.position = "bottom")
# Panel C: UCell scores
pC <- FeaturePlot(query.seurat, features = ucell_cols[1],
cols = c("gray95", "navy")) +
ggtitle("C. Signature Score") +
theme(plot.title = element_text(face = "bold"))
# Panel D: Score distribution
pD <- ggplot(plot_data, aes(x = classification, y = score, fill = classification)) +
geom_violin(alpha = 0.7) +
geom_boxplot(width = 0.15, fill = "white") +
scale_fill_manual(values = c("Pure" = "#00ae60", "Impure" = "#808080")) +
labs(title = "D. Score Distribution", x = "", y = "UCell Score") +
theme_minimal() +
theme(legend.position = "none",
plot.title = element_text(face = "bold"))
# Combine
(pA | pB) / (pC | pD)
}# Define color palettes
palettes <- list(
"Default" = c("Pure" = "#00ae60", "Impure" = "#e0e0e0"),
"Vibrant" = c("Pure" = "#2ecc71", "Impure" = "#95a5a6"),
"Professional" = c("Pure" = "#27ae60", "Impure" = "#bdc3c7"),
"High Contrast" = c("Pure" = "#00ff00", "Impure" = "#808080")
)
# Display palettes
par(mfrow = c(1, 4), mar = c(2, 1, 2, 1))
for (name in names(palettes)) {
barplot(c(1, 1), col = palettes[[name]], main = name,
names.arg = c("Pure", "Impure"), border = NA)
}# Save as PDF (vector format, best for publications)
ggsave("scgate_results.pdf", width = 10, height = 8, dpi = 300)
# Save as PNG (raster format, good for presentations)
ggsave("scgate_results.png", width = 10, height = 8, dpi = 300)
# Save as TIFF (required by some journals)
ggsave("scgate_results.tiff", width = 10, height = 8, dpi = 300, compression = "lzw")sessionInfo()
#> R version 4.6.0 (2026-04-24)
#> Platform: x86_64-pc-linux-gnu
#> Running under: Ubuntu 24.04.4 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.26.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] patchwork_1.3.2 ggplot2_4.0.3 Seurat_5.5.0 SeuratObject_5.4.0
#> [5] sp_2.2-1 scGate_1.7.2 rmarkdown_2.31
#>
#> loaded via a namespace (and not attached):
#> [1] deldir_2.0-4 pbapply_1.7-4 gridExtra_2.3
#> [4] rlang_1.2.0 magrittr_2.0.5 RcppAnnoy_0.0.23
#> [7] otel_0.2.0 spatstat.geom_3.8-1 matrixStats_1.5.0
#> [10] ggridges_0.5.7 compiler_4.6.0 png_0.1-9
#> [13] vctrs_0.7.3 reshape2_1.4.5 stringr_1.6.0
#> [16] pkgconfig_2.0.3 fastmap_1.2.0 promises_1.5.0
#> [19] purrr_1.2.2 xfun_0.57 cachem_1.1.0
#> [22] jsonlite_2.0.0 goftest_1.2-3 later_1.4.8
#> [25] BiocParallel_1.47.0 spatstat.utils_3.2-3 irlba_2.3.7
#> [28] parallel_4.6.0 cluster_2.1.8.2 R6_2.6.1
#> [31] ica_1.0-3 spatstat.data_3.1-9 bslib_0.11.0
#> [34] stringi_1.8.7 RColorBrewer_1.1-3 reticulate_1.46.0
#> [37] spatstat.univar_3.2-0 parallelly_1.47.0 lmtest_0.9-40
#> [40] jquerylib_0.1.4 scattermore_1.2 Rcpp_1.1.1-1.1
#> [43] knitr_1.51 tensor_1.5.1 future.apply_1.20.2
#> [46] zoo_1.8-15 sctransform_0.4.3 httpuv_1.6.17
#> [49] Matrix_1.7-5 splines_4.6.0 igraph_2.3.1
#> [52] tidyselect_1.2.1 abind_1.4-8 yaml_2.3.12
#> [55] spatstat.random_3.5-0 codetools_0.2-20 miniUI_0.1.2
#> [58] spatstat.explore_3.8-1 listenv_0.10.1 lattice_0.22-9
#> [61] tibble_3.3.1 plyr_1.8.9 withr_3.0.2
#> [64] shiny_1.13.0 S7_0.2.2 ROCR_1.0-12
#> [67] evaluate_1.0.5 Rtsne_0.17 future_1.70.0
#> [70] fastDummies_1.7.6 survival_3.8-6 polyclip_1.10-7
#> [73] fitdistrplus_1.2-6 pillar_1.11.1 KernSmooth_2.23-26
#> [76] plotly_4.12.0 generics_0.1.4 RcppHNSW_0.6.0
#> [79] scales_1.4.0 globals_0.19.1 xtable_1.8-8
#> [82] glue_1.8.1 lazyeval_0.2.3 maketools_1.3.2
#> [85] tools_4.6.0 BiocNeighbors_2.7.2 sys_3.4.3
#> [88] data.table_1.18.4 RSpectra_0.16-2 RANN_2.6.2
#> [91] buildtools_1.0.0 dotCall64_1.2 cowplot_1.2.0
#> [94] grid_4.6.0 tidyr_1.3.2 colorspace_2.1-2
#> [97] nlme_3.1-169 cli_3.6.6 spatstat.sparse_3.2-0
#> [100] spam_2.11-3 viridisLite_0.4.3 dplyr_1.2.1
#> [103] uwot_0.2.4 gtable_0.3.6 sass_0.4.10
#> [106] digest_0.6.39 progressr_0.19.0 ggrepel_0.9.8
#> [109] htmlwidgets_1.6.4 farver_2.1.2 htmltools_0.5.9
#> [112] lifecycle_1.0.5 httr_1.4.8 mime_0.13
#> [115] MASS_7.3-65