通路富集GSEA分析¶
一句话概述:GSEA(基因集富集分析)不需要人为设定差异基因阈值,而是用所有基因的排名信息检测某基因集是否在排名的顶部或底部"扎堆",比ORA更灵敏。
核心知识点速览¶
| 概念 | 白话解释 |
|---|---|
| GSEA | 不需要先筛差异基因,直接用全部基因的排名做富集分析 |
| ORA vs GSEA | ORA只看"考满分的人",GSEA看"全班的排名分布" |
| 预排序基因列表 | 按统计量(如logFC×-log10p)排好序的全部基因 |
| ES(富集分数) | 基因集成员在排名中的分布偏移程度,越偏向两端越显著 |
| NES(标准化ES) | 消除基因集大小影响后的ES,可以跨基因集比较 |
| Leading Edge | 贡献最大的核心基因子集,是GSEA结果的"精华" |
| fgsea | 快速GSEA的R实现,比Broad GSEA快100倍 |
| MSigDB | 分子标签数据库,提供数千个预定义的基因集 |
| Hallmark基因集 | MSigDB精选的50个标志性通路,发文章最常用 |
| FDR | 假发现率,GSEA结果中<0.25即可认为有意义 |
一、GSEA原理详解¶
1.1 GSEA三步走¶
步骤1:对所有基因按某个统计量排序
→ 比如按 log2FC × (-log10 pvalue) 从大到小排
步骤2:从排名第1个基因开始"走楼梯"
→ 遇到基因集成员,分数+1步(上楼)
→ 遇到非成员,分数-1步(下楼)
→ 最大偏离零点就是ES(富集分数)
步骤3:通过排列检验(permutation)估算p值
→ 随机打乱基因顺序1000次
→ 看真实ES在随机分布中有多极端
1.2 GSEA vs ORA对比¶
| 特征 | ORA(过表示分析) | GSEA |
|---|---|---|
| 输入 | 差异基因列表(需要阈值筛选) | 全部基因的排名 |
| 统计方法 | 超几何检验 | 排列检验 |
| 信息利用 | 只用"是否差异"的二分信息 | 用全部基因的连续排名信息 |
| 灵敏度 | 可能遗漏小幅一致变化 | 能捕获"温和但协调"的变化 |
| 阈值依赖 | 强依赖(换阈值结果会变) | 无阈值依赖 |
| 适用场景 | 差异基因明确时 | 探索性分析或差异不显著时 |
二、fgsea实操流程¶
2.1 安装¶
# 安装fgsea包(2026最新版)
if (!require("BiocManager")) install.packages("BiocManager") # 安装BiocManager
BiocManager::install("fgsea") # 安装fgsea
BiocManager::install("msigdbr") # MSigDB基因集的R接口
BiocManager::install("org.Hs.eg.db") # 人类注释
library(fgsea) # 加载fgsea
library(msigdbr) # 加载MSigDB
library(ggplot2) # 画图
library(data.table) # 高效数据处理
2.2 准备排名基因列表¶
# 假设你已有DESeq2的差异分析结果
# deseq_result 包含 log2FoldChange, pvalue, padj 等列
# 方法1:用 -log10(pvalue) × sign(log2FC) 作为排名统计量
deseq_result$rank_metric <- -log10(deseq_result$pvalue) * sign(deseq_result$log2FoldChange)
# 方法2:直接用 log2FC(更简单,但信息量少)
# deseq_result$rank_metric <- deseq_result$log2FoldChange
# 方法3:用t统计量(如果有的话)
# deseq_result$rank_metric <- deseq_result$stat
# 构建命名向量(名字=基因名,值=排名统计量)
gene_ranks <- deseq_result$rank_metric # 取排名值
names(gene_ranks) <- deseq_result$gene_symbol # 用基因名命名
gene_ranks <- sort(gene_ranks, decreasing = TRUE) # 从大到小排序
gene_ranks <- gene_ranks[!is.na(gene_ranks)] # 去除NA值
gene_ranks <- gene_ranks[!duplicated(names(gene_ranks))] # 去除重复基因
head(gene_ranks) # 查看排名前几的基因
tail(gene_ranks) # 查看排名后几的基因
2.3 获取MSigDB基因集¶
# 获取Hallmark基因集(最常用,50个标志性通路)
hallmark_sets <- msigdbr(
species = "Homo sapiens", # 物种
category = "H" # H = Hallmark基因集
)
# 转换为fgsea需要的list格式
hallmark_list <- split(hallmark_sets$gene_symbol, hallmark_sets$gs_name) # 按基因集名分组
length(hallmark_list) # 查看有多少个基因集(应该是50个)
head(names(hallmark_list)) # 查看基因集名称
# 其他常用基因集类别
# C2: 策划基因集(包含KEGG、Reactome等)
c2_sets <- msigdbr(species = "Homo sapiens", category = "C2")
c2_list <- split(c2_sets$gene_symbol, c2_sets$gs_name)
# C5: GO基因集
c5_sets <- msigdbr(species = "Homo sapiens", category = "C5")
c5_list <- split(c5_sets$gene_symbol, c5_sets$gs_name)
MSigDB基因集分类¶
| 类别 | 名称 | 内容 |
|---|---|---|
| H | Hallmark | 50个标志性通路(推荐首选) |
| C1 | Positional | 按染色体位置分组 |
| C2 | Curated | 策划基因集(含KEGG、BioCarta、Reactome) |
| C3 | Regulatory | 调控靶标基因集(转录因子/miRNA) |
| C5 | Ontology | GO基因集(BP/CC/MF) |
| C6 | Oncogenic | 致癌标签 |
| C7 | Immunologic | 免疫学标签 |
| C8 | Cell type | 单细胞标记基因集 |
2.4 运行fgsea¶
# 运行fgsea分析(使用多层级蒙特卡洛方法)
gsea_result <- fgsea(
pathways = hallmark_list, # 基因集列表
stats = gene_ranks, # 排名统计量
minSize = 15, # 最小基因集大小(过滤太小的)
maxSize = 500, # 最大基因集大小(过滤太大的)
eps = 0 # 设为0可以计算任意小的p值
)
# 查看结果(按NES排序)
gsea_result <- gsea_result[order(gsea_result$NES, decreasing = TRUE), ] # NES降序排列
head(gsea_result, 10) # 看上调最显著的10个通路
# 结果列说明
# pathway: 基因集名称
# pval: 原始p值
# padj: BH校正后p值
# log2err: 对数误差估计
# ES: 富集分数
# NES: 标准化富集分数(跨基因集可比)
# size: 基因集中被检测到的基因数
# leadingEdge: 核心基因列表
2.5 筛选显著结果¶
# 筛选显著富集的通路(padj < 0.05)
sig_pathways <- gsea_result[gsea_result$padj < 0.05, ] # 取显著的
# 分别看上调和下调的通路
up_pathways <- sig_pathways[sig_pathways$NES > 0, ] # NES>0 = 上调富集
down_pathways <- sig_pathways[sig_pathways$NES < 0, ] # NES<0 = 下调富集
cat("上调通路数:", nrow(up_pathways), "\n") # 打印上调通路数
cat("下调通路数:", nrow(down_pathways), "\n") # 打印下调通路数
三、可视化¶
3.1 经典GSEA山峰图¶
# 单个通路的GSEA图(最经典的可视化)
plotEnrichment(
hallmark_list[["HALLMARK_TNFA_SIGNALING_VIA_NFKB"]], # 指定通路
gene_ranks # 排名统计量
) +
labs(title = "TNFa signaling via NFkB") + # 添加标题
theme_classic() # 经典主题
3.2 GSEA表格图¶
# 同时展示多个通路的GSEA图(表格格式)
topPathways <- gsea_result[head(order(gsea_result$pval), 10), ]$pathway # 取Top10通路
plotGseaTable(
hallmark_list[topPathways], # 通路列表
gene_ranks, # 排名
gsea_result, # 结果
gseaParam = 0.5 # ES计算参数
)
3.3 气泡图¶
# 自定义气泡图
library(ggplot2)
# 准备绘图数据
plot_data <- gsea_result[gsea_result$padj < 0.05, ] # 取显著通路
plot_data$pathway <- gsub("HALLMARK_", "", plot_data$pathway) # 去掉前缀
plot_data$pathway <- gsub("_", " ", plot_data$pathway) # 下划线换空格
# 画气泡图
ggplot(plot_data, aes(x = NES, y = reorder(pathway, NES))) + # NES为X轴
geom_point(aes(size = size, color = padj)) + # 大小=基因集大小,颜色=padj
scale_color_gradient(low = "red", high = "blue") + # 颜色梯度
labs(x = "Normalized Enrichment Score", # X轴标签
y = NULL, # Y轴不加标签
title = "GSEA Hallmark Pathways") + # 标题
theme_minimal() + # 简洁主题
geom_vline(xintercept = 0, linetype = "dashed") # 添加零线
3.4 用clusterProfiler做GSEA¶
library(clusterProfiler) # 加载clusterProfiler
# clusterProfiler也支持GSEA(底层调用fgsea)
gsea_go <- gseGO(
geneList = gene_ranks, # 排名基因列表
OrgDb = org.Hs.eg.db, # 注释数据库
keyType = "SYMBOL", # 基因ID类型
ont = "BP", # GO子类
minGSSize = 15, # 最小基因集
maxGSSize = 500, # 最大基因集
pvalueCutoff = 0.05, # p值阈值
eps = 0 # 精确p值
)
# GSEA-KEGG
gsea_kegg <- gseKEGG(
geneList = gene_ranks_entrez, # 需要Entrez ID的排名列表
organism = "hsa", # 物种
minGSSize = 15,
maxGSSize = 500,
pvalueCutoff = 0.05,
eps = 0
)
# clusterProfiler的GSEA可视化
gseaplot2(gsea_go, geneSetID = 1:3, pvalue_table = TRUE) # 前3个通路
dotplot(gsea_go, showCategory = 15) # 气泡图
ridgeplot(gsea_go, showCategory = 15) # 山脊图
四、实战技巧¶
4.1 排名统计量的选择¶
# 推荐方案(按推荐程度排序):
# 1. DESeq2的stat列(Wald统计量)— 最推荐,信息量最大
gene_ranks <- setNames(res$stat, res$gene)
# 2. -log10(pvalue) × sign(log2FC) — 通用性强
gene_ranks <- setNames(-log10(res$pvalue) * sign(res$log2FoldChange), res$gene)
# 3. log2FC — 最简单,但忽略了显著性信息
gene_ranks <- setNames(res$log2FoldChange, res$gene)
# 注意:必须去除NA和Inf值!
gene_ranks <- gene_ranks[!is.na(gene_ranks) & is.finite(gene_ranks)]
4.2 Leading Edge基因提取¶
# Leading Edge是GSEA结果的精华——贡献最大的核心基因
# 提取某个通路的leading edge基因
pathway_of_interest <- "HALLMARK_TNFA_SIGNALING_VIA_NFKB"
le_genes <- gsea_result[gsea_result$pathway == pathway_of_interest, ]$leadingEdge[[1]]
print(le_genes) # 打印核心基因
# 批量提取所有显著通路的leading edge
le_all <- gsea_result[gsea_result$padj < 0.05, c("pathway", "leadingEdge")]
常见报错与解决¶
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
There are ties in the preranked stats | 排名值有重复 | 添加微小随机数打破平局 |
All gene sets have fewer than minSize genes | 基因名不匹配 | 检查基因名类型是否与基因集一致 |
Error: NA/NaN/Inf in stats | 排名值有异常值 | 预处理时过滤NA/NaN/Inf |
fgsea: too few permutations | 基因集太小 | 增大minSize参数或检查数据 |
No significant pathways | 确实没有或数据问题 | 检查排名方向,放宽padj阈值看看 |
速查表¶
# fgsea快速流程
准备排名 → msigdbr()获取基因集 → fgsea() → plotEnrichment()
# clusterProfiler GSEA快速流程
准备排名 → gseGO()/gseKEGG() → gseaplot2()/dotplot()
# 排名统计量推荐
首选: DESeq2 stat列
次选: -log10(p) × sign(logFC)
简单: log2FC
# MSigDB常用类别
H: Hallmark(50个,首选)
C2: KEGG+Reactome
C5: GO基因集
# 显著性判断
fgsea原版: padj < 0.25(宽松)或 < 0.05(严格)
发文章推荐: padj < 0.05 + |NES| > 1
面试高频问题¶
Q1:GSEA和ORA的核心区别是什么?¶
答:①输入不同——ORA需要先筛差异基因(需设阈值),GSEA用全部基因的排名(无需阈值);②信息利用——ORA只用"是否差异"的二分信息,GSEA利用连续的变化幅度信息;③灵敏度——GSEA能检测到"很多基因都轻微上调"的协调变化模式,而这些基因单个都达不到差异阈值,ORA会完全漏掉。
Q2:GSEA的NES是什么?为什么需要标准化?¶
答:NES(Normalized Enrichment Score)是ES除以随机排列的期望ES得到的标准化分数。标准化的目的是消除基因集大小的影响——大基因集天然容易产生大的ES,不标准化就无法跨基因集比较。NES>0表示上调富集,NES<0表示下调富集。
Q3:什么是Leading Edge基因?¶
答:Leading Edge是GSEA结果中贡献最大的核心基因子集。具体来说,就是从排名最前(或最后)开始,到ES达到最大值时为止的那些基因集成员。它们是"真正推动这个通路富集"的基因,通常是后续实验验证的首选靶标。
Q4:fgsea和Broad GSEA有什么区别?¶
答:Broad GSEA是原始Java实现(Subramanian等2005年),fgsea是R/Bioconductor实现。主要区别:①速度——fgsea用多层级自适应蒙特卡洛算法,比Broad GSEA快100倍;②精度——fgsea可以计算任意小的p值(设eps=0),Broad受排列次数限制;③多重校正——fgsea用标准BH方法,比Broad GSEA的FDR估计更不保守;④集成性——fgsea是R包,方便整合到分析流程中。
Q5:GSEA分析需要注意什么?¶
答:①排名统计量的选择很重要——推荐DESeq2的stat或-log10(p)×sign(logFC);②必须去除NA和Inf值;③基因名要和基因集一致(都用Symbol或都用Entrez ID);④不要对已经筛选过的基因列表做GSEA(那应该用ORA);⑤结果解读要结合领域知识,不要只看p值。