GSEA, GSVA and ssGSEA
TL;DR: Three complementary gene set enrichment methods: GSEA compares two conditions, GSVA transforms expression to pathway scores across samples, ssGSEA scores pathways per individual sample. Choose based on your experimental design and question.
What is Gene Set Enrichment Analysis?
Instead of analyzing individual genes, gene set methods test whether groups of related genes (pathways, GO terms) show coordinated changes. This:
- Increases statistical power (biological signal > individual gene noise)
- Provides mechanistic insights (pathways, not just gene lists)
- Enables cross-study comparisons at the pathway level

Method Comparison
| Method | Input Requirement | Output | Use Case | Supervised? |
|---|---|---|---|---|
| GSEA | Ranked gene list + 2 groups | NES, p-value, FDR | Compare conditions | Yes |
| GSVA | Expression matrix (no groups) | Per-sample pathway scores | Unsupervised clustering | No |
| ssGSEA | Expression matrix (no groups) | Per-sample enrichment scores | Sample-level profiling | No |
Quick Decision Guide
Do you have two groups to compare?
├─ YES → Use GSEA
└─ NO → Do you want pathway-based clustering?
├─ YES → Use GSVA
└─ NO → Need per-sample scores? → Use ssGSEA
Method 1: GSEA (Gene Set Enrichment Analysis)
Purpose
Test whether a pre-defined gene set is enriched at the top or bottom of a ranked gene list comparing two conditions.
When to Use
- ✅ Differential expression between two groups (disease vs. control, treated vs. untreated)
- ✅ You have enough replicates for statistical testing
- ✅ Want to identify pathways driving the difference
Input Requirements
- Normalized expression matrix
- Phenotype labels (group A vs. group B)
- Gene sets (e.g., MSigDB, GO, KEGG)
Running GSEA in R
library(fgsea)
library(msigdbr)
# 1. Prepare ranked gene list (from DESeq2, edgeR, etc.)
res <- results(dds, contrast = c("condition", "treated", "control"))
gene_ranks <- res$log2FoldChange
names(gene_ranks) <- rownames(res)
gene_ranks <- sort(gene_ranks, decreasing = TRUE)
# 2. Load gene sets (Hallmark pathways from MSigDB)
hallmark <- msigdbr(species = "Homo sapiens", category = "H")
pathways <- split(hallmark$gene_symbol, hallmark$gs_name)
# 3. Run GSEA
fgsea_res <- fgsea(
pathways = pathways,
stats = gene_ranks,
minSize = 15,
maxSize = 500,
nperm = 10000
)
# 4. View significant pathways
fgsea_res %>%
filter(padj < 0.05) %>%
arrange(NES) %>%
select(pathway, NES, padj, size)
Interpretation
| Metric | Meaning |
|---|---|
| NES > 0 | Gene set enriched in group A (upregulated) |
| NES < 0 | Gene set enriched in group B (downregulated) |
| ** | NES |
| padj < 0.05 | Statistically significant |
Example:NES = 2.3, padj = 0.001 for "HALLMARK_INFLAMMATORY_RESPONSE" → Strong upregulation of inflammatory genes in treated samples.
Method 2: GSVA (Gene Set Variation Analysis)
Purpose
Transform gene expression into pathway-level scores for each sample, enabling unsupervised analysis and sample clustering.
When to Use
- ✅ No predefined groups (exploratory analysis)
- ✅ Want to cluster samples by pathway activity
- ✅ Correlate pathways with continuous clinical variables
Running GSVA in R
library(GSVA)
library(limma)
# 1. Prepare expression matrix (log2 TPM/CPM)
expr_matrix <- assay(vst(dds)) # Variance-stabilized counts
# 2. Load gene sets
pathways <- getGmt("c2.cp.kegg.v7.5.symbols.gmt") # KEGG pathways
# 3. Run GSVA
gsva_scores <- gsva(
expr_matrix,
pathways,
method = "gsva",
kcdf = "Gaussian", # Use "Poisson" for count data
min.sz = 10,
max.sz = 500,
verbose = TRUE
)
# 4. Differential pathway analysis
design <- model.matrix(~ condition, data = colData(dds))
fit <- lmFit(gsva_scores, design)
fit <- eBayes(fit)
topTable(fit, coef = "conditiontreated", number = 10)
# 5. Heatmap of pathway scores
pheatmap(gsva_scores,
annotation_col = data.frame(condition = colData(dds)$condition),
scale = "row")
Output Interpretation
- Positive score: Pathway activity higher than average across samples
- Negative score: Pathway activity lower than average
- Use for: Clustering samples, correlating with metadata
Method 3: ssGSEA (Single-Sample GSEA)
Purpose
Calculate enrichment scores for each gene set within individual samples, useful for single-sample predictions or heterogeneous datasets.
When to Use
- ✅ Single sample or very small N
- ✅ Sample-specific pathway profiling (e.g., tumor subtyping)
- ✅ Building pathway-based classifiers
Running ssGSEA in R
library(GSVA)
# 1. Prepare expression matrix
expr_matrix <- assay(vst(dds))
# 2. Run ssGSEA
ssgsea_scores <- gsva(
expr_matrix,
pathways,
method = "ssgsea", # Key difference from GSVA
ssgsea.norm = TRUE, # Normalize scores
verbose = TRUE
)
# 3. Example: Score a new sample
new_sample <- read.csv("new_sample.csv")
new_scores <- gsva(
as.matrix(new_sample),
pathways,
method = "ssgsea"
)
# 4. Compare to reference cohort
reference_mean <- rowMeans(ssgsea_scores)
deviation <- new_scores - reference_mean
ssGSEA vs GSVA
| Feature | GSVA | ssGSEA |
|---|---|---|
| Algorithm | Kernel smoothing | Sample-wise ranking |
| Normalization | Across samples | Within sample |
| Best for | Group comparisons | Individual profiling |
Other Methods
PLAGE (Pathway Level Analysis)
Uses singular value decomposition (SVD) to summarize gene set expression:
plage_scores <- gsva(
expr_matrix,
pathways,
method = "plage"
)
Best for: Reducing dimensionality while preserving pathway structure.
Z-score Method
Simple standardized mean:
zscore_scores <- gsva(
expr_matrix,
pathways,
method = "zscore"
)
Best for: Quick exploratory analysis.
Practical Workflow
Step 1: Choose Gene Sets
| Source | Description | Size |
|---|---|---|
| MSigDB Hallmark | 50 well-defined processes | Curated, minimal overlap |
| KEGG | ~300 pathways | Metabolic + signaling |
| GO Biological Process | ~7,000 terms | Comprehensive but redundant |
| Reactome | ~2,000 pathways | Detailed, hierarchical |
# Download via msigdbr
library(msigdbr)
# Hallmark (recommended starting point)
hallmark <- msigdbr(species = "Homo sapiens", category = "H")
# KEGG
kegg <- msigdbr(species = "Homo sapiens", category = "C2", subcategory = "CP:KEGG")
# GO Biological Process
go_bp <- msigdbr(species = "Homo sapiens", category = "C5", subcategory = "GO:BP")
Step 2: Preprocessing
# Filter low-expressed genes
keep <- rowSums(counts(dds) >= 10) >= 3
dds <- dds[keep,]
# Normalize
vsd <- vst(dds, blind = FALSE) # Or rlog()
Step 3: Run Analysis
# For group comparison: GSEA
fgsea_res <- fgsea(pathways, gene_ranks)
# For unsupervised: GSVA
gsva_scores <- gsva(assay(vsd), pathways)
# For single samples: ssGSEA
ssgsea_scores <- gsva(assay(vsd), pathways, method = "ssgsea")
Step 4: Visualization
# GSEA enrichment plot
plotEnrichment(pathways[["HALLMARK_HYPOXIA"]], gene_ranks)
# GSVA heatmap
pheatmap(gsva_scores, scale = "row", clustering_distance_rows = "correlation")
# Pathway bar plot
top_pathways <- fgsea_res %>% filter(padj < 0.05) %>% top_n(20, abs(NES))
ggplot(top_pathways, aes(x = reorder(pathway, NES), y = NES, fill = NES > 0)) +
geom_col() +
coord_flip() +
labs(title = "Top Enriched Pathways")
Troubleshooting
"No pathways significant in GSEA"
Possible causes:
- Small effect sizes
- Not enough replicates
- Wrong gene ID format
Fix:
# Check gene ID conversion
sum(names(gene_ranks) %in% unlist(pathways)) # Should be >80%
# Relax FDR threshold for exploratory analysis
filter(fgsea_res, pval < 0.05)
GSVA scores all near zero
Cause: Expression data not properly normalized
Fix: Use variance-stabilized or log2-transformed data, not raw counts
Memory errors with large gene sets
Fix:
# Reduce gene set size
filtered_pathways <- pathways[sapply(pathways, length) < 500]
# Or use parallel processing
gsva(..., parallel.sz = 4)
Comparison Summary
Choose GSEA if:
- You have two groups and sufficient replicates
- Want to identify pathways driving condition differences
- Need statistical significance testing
Choose GSVA if:
- Exploratory analysis without predefined groups
- Want to cluster samples by pathway activity
- Correlating pathways with continuous variables
Choose ssGSEA if:
- Single-sample predictions
- Building pathway-based classifiers
- Heterogeneous datasets (e.g., tumor samples)
Resources
- fgsea R package: bioconductor.org/packages/fgsea
- GSVA R package: bioconductor.org/packages/GSVA
- MSigDB: gsea-msigdb.org
- GSEA tutorial: software.broadinstitute.org/gsea
See also:
Originally posted on WeChat Official Account: bioinfo_studyroom