
scSidekick - Pathway Analysis: RunGSEA_pseudobulk
09_pathway_pseudobulk.RmdOverview
RunGSEA_pseudobulk() performs pseudobulk gene set
enrichment analysis using sample-level limma-voom contrasts - a
statistically rigorous alternative to the cell-level Wilcoxon approach
in RunGSEA().
The key idea: cells from the same biological sample are aggregated into a pseudobulk profile before DE testing, so each biological replicate contributes one observation rather than thousands. This avoids pseudoreplication and produces calibrated p-values.
When to use each approach:
RunGSEA |
RunGSEA_pseudobulk |
|
|---|---|---|
| Input | Any Seurat object | Seurat object with sample metadata |
| DE method | Wilcoxon (cell-level) | limma-voom (sample-level) |
| Replicates needed | No | Yes (>=3 per group) |
| Controls confounders | No | Yes (covariates) |
| Best for | Exploratory; no replicates | Multi-sample studies |
In addition to GSEA, RunGSEA_pseudobulk() optionally
runs per-patient ssGSEA (set run.ssgsea = TRUE, the
default) to produce individual-level pathway heatmaps showing
patient-by-patient variability.
Standard setup
library(scSidekick)
library(Seurat)
library(dplyr)
# Assumes a multi-sample Seurat object with:
# celltype - annotated cell types
# sample - unique biological sample ID (one per donor/timepoint/condition)
# condition - the comparison variable (e.g. "Control" vs "Disease")1. Minimal call
The three required metadata columns are group.by (cell
type), sample.by (sample/donor ID), and
contrast.by (the condition to compare).
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
output_dir = "./Figures/Pathways_PB"
)This runs GSEA for each cell type in group.by, comparing
all levels of condition against each other with default
limma-voom contrasts.
2. Specifying contrasts
By default, RunGSEA_pseudobulk tests all pairwise
comparisons within contrast.by. Supply
contrasts explicitly to control which comparisons are made,
in which direction.
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
contrasts = list(
Disease_vs_Control = "Disease - Control",
Treatment_vs_Control = "Treatment - Control"
),
output_dir = "./Figures/Pathways_PB"
)Each name becomes the label for a set of output figures; each value is a standard limma contrast expression.
3. Adding covariates
Correct for batch effects or other sample-level metadata by passing
them to covariates. These are included as additional terms
in the limma-voom model.
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
covariates = c("batch", "sex"),
output_dir = "./Figures/Pathways_PB"
)4. Splitting by broad cell type
Use split.by to run the full pipeline independently
within each major lineage. This is the pseudobulk equivalent of the
split.by parameter in RunGSEA.
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype_fine",
sample.by = "sample",
contrast.by = "condition",
split.by = "celltype_broad",
output_dir = "./Figures/Pathways_PB"
)5. Custom pathway sets
Same syntax as RunGSEA. Default: Hallmark, KEGG,
Reactome, WikiPathways.
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
pathway_sets = list(
Hallmark = list(category = "H"),
ImmuneSigDB = list(category = "C7", subcategory = "IMMUNESIGDB")
),
output_dir = "./Figures/Pathways_PB"
)6. Using a precomputed pseudobulk
If you have already aggregated pseudobulk counts (e.g. from
ComputePseudobulk), pass them directly via
pseudobulk to skip re-aggregation.
pb <- ComputePseudobulk(seu,
group.by = "celltype",
sample.by = "sample")
res <- RunGSEA_pseudobulk(
pseudobulk = pb,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
output_dir = "./Figures/Pathways_PB"
)7. Per-patient ssGSEA heatmaps
run.ssgsea = TRUE (default) computes per-patient pathway
activity via ssGSEA after the main GSEA, producing heatmaps where
columns are individual samples. This makes patient-level variability
visible alongside the group-level result.
Use ssgsea.use.padj = TRUE (default) to filter ssGSEA
rows by BH-adjusted p-value, or ssgsea.use.padj = FALSE to
use raw p-values.
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
run.ssgsea = TRUE,
ssgsea.use.padj = TRUE,
top_n_ssgsea = 20L,
output_dir = "./Figures/Pathways_PB"
)8. Customizing heatmap colors
The same heatmap_colors parameter works here as in
RunGSEA. Pass any circlize::colorRamp2 object
to override the default 5-stop blue-white-orange-red palette.
col_custom <- circlize::colorRamp2(
c(-3, -1.5, 0, 1.5, 3),
c("#762a83", "#c2a5cf", "white", "#7fbf7b", "#1b7837")
)
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
heatmap_colors = col_custom,
output_dir = "./Figures/Pathways_PB"
)9. Resuming an interrupted run
save.rds = TRUE (default) saves intermediate RDS files
after each cell type. If the run is interrupted, use
redo_plots = TRUE to reload the saved RDS files and
regenerate all PDFs without re-running the DE analysis.
# Regenerate all PDFs from saved RDS (no re-computation)
res <- RunGSEA_pseudobulk(
seurat_object = seu,
group.by = "celltype",
sample.by = "sample",
contrast.by = "condition",
redo_plots = TRUE,
output_dir = "./Figures/Pathways_PB"
)10. Return value
RunGSEA_pseudobulk returns a nested list, one element
per cell type. Each element contains:
# GSEA results table for one cell type
res$`CD4 T`$gsea_results
# NES heatmap ggplot object
res$`CD4 T`$nes_heatmap
# ssGSEA score matrix (pathways x samples)
res$`CD4 T`$ssgsea_scoresParameter reference
| Parameter | Type | Default | Description |
|---|---|---|---|
seurat_object |
Seurat or NULL
|
NULL |
Seurat object (use with pseudobulk to skip
re-aggregation) |
group.by |
character | NULL |
Cell-type column; one GSEA run per level |
sample.by |
character | NULL |
Sample/donor ID column for pseudobulk aggregation |
contrast.by |
character | NULL |
Condition column for limma contrasts |
split.by |
character or NULL
|
NULL |
Run pipeline independently per level |
subset.by |
character or NULL
|
NULL |
Run pipeline on each subset separately |
covariates |
character vector | NULL |
Sample-level covariates added to limma model |
contrasts |
named list | NULL |
Named limma contrast expressions; NULL = all
pairwise |
pathway_sets |
named list | Hallmark + KEGG + Reactome + WP | MSigDB collections to test |
species |
character | "human" |
Species for msigdbr
|
assay |
character or NULL
|
NULL |
Seurat assay containing raw counts |
min.cells |
integer | 10 |
Minimum cells a sample must contribute per cell type |
min.samples.per.group |
integer | 3 |
Minimum samples per condition level |
run.ssgsea |
logical | TRUE |
Also run per-patient ssGSEA |
ssgsea.use.padj |
logical | TRUE |
Use BH-adjusted p-value for ssGSEA row selection |
nes.cutoff |
numeric | 1.0 |
Minimum absolute NES for heatmap inclusion |
top_n_heatmap |
integer | 30 |
Max pathways shown in NES heatmaps |
top_n_ssgsea |
integer | 30 |
Max pathways shown in ssGSEA heatmaps |
heatmap_colors |
colorRamp2 or NULL
|
NULL |
Custom heatmap color function |
heatmap_params |
named list | list(...) |
Extra args forwarded to ComplexHeatmap::Heatmap()
|
save.rds |
logical | TRUE |
Save intermediate RDS after each cell type |
redo_plots |
logical | FALSE |
Reload saved RDS and regenerate PDFs only |
caffeinate |
logical | FALSE |
Prevent system sleep during the run |
width |
numeric or NULL
|
NULL |
Override the auto-calculated heatmap PDF width in inches |
height |
numeric or NULL
|
NULL |
Override the auto-calculated heatmap PDF height in inches |
timestamp |
logical | TRUE |
Write each run into its own timestamped subfolder under
output_dir
|
See also
- Vignette 08:
RunGSEA- cell-level Wilcoxon-based GSEA (no replicates needed) - Vignette 10:
RunSCssGSEA- per-cell pathway scoring;PlotPathwayButterfly- 4-pathway state hierarchy - Vignette 11:
RunCellChat- pathway-level cell-cell communication