单基因泛癌分析
跟完这篇,您能拿一个感兴趣的基因,在 TCGA 的 33 个癌种里系统扫一遍它的表达、预后、免疫浸润、突变四件事,把散落的分癌种结果拼成一条能讲的线索,并且清楚知道这条线索的分量到哪为止、下一步该往哪深挖。
泛癌分析在做什么
泛癌分析(pan-cancer analysis)就一句话:拿一个基因,横跨多种癌症统一看它的行为,而不是只盯着一种病。
它的默认舞台是 TCGA(The Cancer Genome Atlas)。TCGA 把肿瘤样本按癌种整理成 33 个项目,每个项目一个癌种,用四个字母的缩写标识:
| 缩写 | 癌种 | 缩写 | 癌种 |
|---|---|---|---|
BRCA | 乳腺癌 | LUAD | 肺腺癌 |
LIHC | 肝细胞癌 | LUSC | 肺鳞癌 |
COAD | 结肠腺癌 | STAD | 胃腺癌 |
KIRC | 肾透明细胞癌 | PRAD | 前列腺腺癌 |
(上表只列了常见的几个,完整是 33 个。)
「泛癌」的价值在于看一致性:如果一个基因只在某一种癌里异常,可能是个案;但如果它在十几个癌种里都往同一个方向变,这种跨癌种的一致信号,往往更值得往下追。所以泛癌分析在您研究流程里的位置很明确——它是筛线索、提假设的第一步,不是下结论的最后一步。
具体到一个基因,一次标准分析通常就围绕这四个维度展开,逐个维度在每个癌种里各算一遍,再横向拼起来看:
| 维度 | 问的问题 | 常用做法 |
|---|---|---|
| 表达差异 | 肿瘤 vs 正常,这个基因高了还是低了 | 分癌种做 Wilcoxon 秩和检验,看方向和显著性 |
| 预后 | 表达高低和病人活得久不久有没有关系 | 按表达中位数分高/低组,做 KM + log-rank,或用 Cox 求风险比 |
| 免疫浸润 | 表达和肿瘤里免疫细胞的多少相不相关 | 和免疫浸润评分做 Spearman 相关 |
| 突变 | 基因或其所在通路的突变状态与表达有没有关联 | 比较突变型 vs 野生型的表达,或与 TMB 相关 |
下面就按「准备数据 → 四步各跑一遍 → 拼成线索」的顺序走一遍,代码用标准 R 包,改一个基因名就能套到您自己的分析上。
准备:选基因、取数据
您几乎不用自己从原始测序数据算起。主流两个入口,先看用哪个:
| 入口 | 拿到的是什么 | 适合 |
|---|---|---|
| GDC(portal.gdc.cancer.gov) | TCGA 官方门户,字段最全、最原始 | 要最完整数据做深入分析;R 里常用 TCGAbiolinks 批量下载 |
| UCSC Xena(xenabrowser.net) | 已归一化、开箱即用的规整矩阵 | 想快速把一个基因扫一遍、出泛癌图(推荐上手) |
Xena 还专门提供了一个把 TCGA 肿瘤和 GTEx 正常组织统一处理的合并队列(常记作 TCGA TARGET GTEx),正好补上 TCGA 正常样本太少的短板——这是做「肿瘤 vs 正常」泛癌图最常用的数据来源。这里就以 Xena 下载下来的文本矩阵为例。
先把三张表读进 R:
library(data.table)
gene <- "TP53" # 换成您关心的基因
# 从 UCSC Xena 下载的文本矩阵,本地读入即可(文件名以您下载到的为准)
# expr:行是基因,列是样本;值是已做 log2 归一化的表达量
expr <- fread("expression.tsv", data.table = FALSE)
# pheno:每个样本一行,含癌种、样本类型(肿瘤 / 正常)等注释
pheno <- fread("phenotype.tsv", data.table = FALSE)
# surv:每个样本一行,含生存时间与结局(0 = 存活 / 1 = 死亡)
surv <- fread("survival.tsv", data.table = FALSE)
后面几步都建立在同一批对象上,先记清它们各自「一行是什么」:
| 对象 | 一行是什么 | 关键列 |
|---|---|---|
expr | 一个基因 | 各样本列的表达值 |
pheno | 一个样本 | 癌种、样本类型(肿瘤 / 正常) |
surv | 一个样本 | 生存时间、结局(0/1) |
infil | 一个样本 | 各免疫细胞评分(第三步再拿到) |
再把目标基因那一行取出来,拼成「每行一个样本」的长表,方便按癌种分组:
library(dplyr)
# 取出目标基因所在行,转成每样本一行
dat <- data.frame(
sample = colnames(expr)[-1],
expr = as.numeric(expr[expr[[1]] == gene, -1]),
type = pheno$sample_type, # "Tumor" / "Normal"
cancer = pheno$cancer
)
数据下下来后怎么整理成规范矩阵(去重、对齐样本、处理缺失),见 表达矩阵整理。
第一步:肿瘤 vs 正常的表达差异
最基础的一件事——这个基因在肿瘤里是不是异常表达。泛癌的做法不是只比一次,而是逐癌种各比一次,再横着看:
res <- dat %>%
filter(!is.na(type)) %>%
group_by(cancer) %>%
summarise(p = wilcox.test(expr ~ type)$p.value, .groups = "drop") %>%
mutate(padj = p.adjust(p, method = "BH")) # 跨癌种做多重检验校正
这里用 Wilcoxon 秩和检验,是因为要在几十个癌种上快速统一比,非参数、不挑分布最省心。多个癌种一起检验,一定要做多重比较校正(这里用 BH 法);padj<0.05 是常用惯例、不是铁律,按数据和检验数量调整。
呈现上,跨癌种表达差异约定俗成用分组箱线图(每个癌种一对肿瘤/正常):
library(ggplot2)
ggplot(filter(dat, !is.na(type)), aes(cancer, expr, fill = type)) +
geom_boxplot(outlier.size = 0.3) +
labs(x = NULL, y = "log2 表达量", fill = NULL) +
theme(axis.text.x = element_text(angle = 90, hjust = 1))
两点提醒:
- TCGA 正常样本很少,很多癌种只有个位数甚至没有配对正常组织。补 GTEx 正常组织(即上一节的合并队列)能救急,但那是两个来源拼在一起,批次差异要留个心眼。
- Wilcoxon 适合「快速扫一遍」。若要在某一个癌种里做严谨的差异表达,应回到原始 read count 用 DESeq2/edgeR,见 用 DESeq2 做差异表达。
第二步:表达与预后
把每个癌种的病人按目标基因表达切成高、低两组,看表达高低和活得久不久有没有关系。泛癌里最常用 Cox 求风险比 HR,一个数就能横向比较各癌种:
library(survival)
cancers <- unique(surv$cancer)
hr <- lapply(cancers, function(cc) {
d <- merge(subset(surv, cancer == cc), dat[, c("sample", "expr")], by = "sample")
# 按中位数分高 / 低组,指定低表达为参照,HR 即「高 vs 低」
d$group <- factor(ifelse(d$expr > median(d$expr), "high", "low"),
levels = c("low", "high"))
s <- summary(coxph(Surv(time, event) ~ group, data = d))
data.frame(cancer = cc,
HR = s$conf.int[1, "exp(coef)"],
lower = s$conf.int[1, "lower .95"],
upper = s$conf.int[1, "upper .95"],
p = s$coefficients[1, "Pr(>|z|)"])
})
hr <- do.call(rbind, hr)
按中位数切是最稳、最好复现的做法。想更直观地看「高低两组的生存曲线拉不拉得开」,再补 KM + log-rank(survdiff)。生存分析的门道(删失、log-rank、最佳截断为什么容易过拟合)见 生存分析。
预后结果的约定画法是森林图:一张图看尽各癌种的 HR 与置信区间,HR = 1 的竖线是「无差异」的分界。
ggplot(hr, aes(x = HR, y = reorder(cancer, HR))) +
geom_point() +
geom_errorbarh(aes(xmin = lower, xmax = upper), height = 0.2) +
geom_vline(xintercept = 1, linetype = "dashed") +
scale_x_log10() + # HR 用对数轴,左右才对称
labs(x = "风险比 HR(高表达 vs 低表达)", y = NULL)
读图:点落在竖线右侧(HR>1)且置信区间不跨过 1,说明在该癌种里高表达关联更差的生存;落在左侧则相反。
第三步:免疫浸润相关
想知道这个基因和肿瘤微环境里的免疫细胞有没有关系,标准做法分两步:先用成熟算法从表达谱反推每个样本的免疫浸润,再和目标基因表达做相关。常见的反推算法:
| 算法 | 给出什么 |
|---|---|
| CIBERSORT | 22 种免疫细胞的相对比例 |
| ESTIMATE | 免疫评分(ImmuneScore)与基质评分(StromalScore) |
| ssGSEA / MCP-counter / xCell | 各有一套免疫细胞打分 |
假设您已经算出(或从工具导出)一张 infil 表——每样本一行、各免疫细胞评分为列。把它和表达并到一起,逐癌种、逐细胞类型做 Spearman 相关:
library(tidyr)
m <- dat %>% inner_join(infil, by = "sample") # 表达 + 各免疫细胞评分
corr <- m %>%
pivot_longer(c(CD8_Tcell, Macrophage_M2, Treg), # 举例几类细胞
names_to = "cell", values_to = "score") %>%
group_by(cancer, cell) %>%
summarise(rho = cor(expr, score, method = "spearman"),
p = cor.test(expr, score, method = "spearman")$p.value,
.groups = "drop")
多用 Spearman(不假设线性、对离群值更稳)。结果的约定画法是气泡热图:行是癌种、列是免疫细胞,颜色示相关方向、点大小示显著性。
ggplot(corr, aes(cell, cancer)) +
geom_point(aes(size = -log10(p), color = rho)) +
scale_color_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
labs(x = NULL, y = NULL, color = "Spearman ρ", size = "-log10(p)")
不想自己跑算法,也有现成网页工具(如 TIMER2.0)能直接查一个基因和免疫浸润的跨癌种相关。无论哪条路,都要记住:相关不等于因果,这里得到的只是「表达和某类免疫细胞丰度同向或反向变化」,不能直接说谁调控谁。
第四步:突变关联
看目标基因的突变状态或整体突变负荷与表达的关系:比较突变型与野生型样本的表达差异,或和肿瘤突变负荷(TMB)、微卫星不稳定性(MSI)做相关。
# status:每个样本目标基因的突变状态("Mutant" / "WT")
wilcox.test(expr ~ status, data = mut_LUAD)
# 或与肿瘤突变负荷 TMB 做相关
cor.test(m$expr, m$TMB, method = "spearman")
这一维度的解读要更保守:单基因突变频率在多数癌种里都不高,样本一分组就很小,结论务必留余地,别在小样本上硬讲显著。
把四步串成一条线索
四步各自跑完,您手上是一堆分癌种的表格和三张图。真正有价值的一步,是把它们横向拼起来,看能不能连成一条自洽的线索。一个站得住的叙事骨架是这样递进的:
- 一致性:先看第一步。这个基因是不是在多个癌种里都朝同一方向异常(比如在十几种癌里都显著高表达)?跨癌种的一致,是整条故事的地基。
- 临床相关:叠上第二步。在那些高表达的癌种里,森林图上的 HR 是不是也大多关联更差的生存?表达异常又与结局相关,线索就从「有变化」升级到「可能有意义」。
- 机制线索:接着看第三、四步,给这条线索一个可能的方向。比如表达与某类免疫细胞浸润稳定相关,提示它可能牵涉免疫微环境——注意这只是提示方向,不是证明机制。
- 收敛到一个癌种:泛癌图把面铺开,真正能深挖的是点。根据前三步,挑一个信号最强、又最贴合您研究背景的癌种,作为后续单癌种深入研究的落点。
把这条线讲出来,大致就是:「基因 X 在多个癌种中一致高表达,且高表达普遍关联更差预后,同时与免疫浸润相关,提示它可能是一个值得深入研究的候选。」 注意每句都落在「关联」「提示」「候选」这类词上——泛癌分析支撑得起的,就是到这个分量为止。三张图各司其职:箱线图给一致性,森林图给临床相关,气泡热图给机制线索。
边界:别过度解读
这一节比前面都重要。泛癌分析最容易被过度解读,用之前请把下面几条钉在心里。
- 它是广撒网筛线索,不是下定论。 泛癌的产出是假设:哪个基因、在哪些癌种里、值得深入看。它本身不构成机制证明,也替代不了单癌种的精细分析和实验验证。
- 同时扫几十个癌种,多重比较被放大。 一个基因在 33 个癌种里各做一次检验,纯靠运气也会撞出几个「显著」。跨癌种汇报务必做多重检验校正(如 BH 法),
padj<0.05只是常用惯例、非铁律。 - 相关不是因果。 免疫浸润相关、预后关联,都是观察到的共变,不能推出调控或驱动关系。措辞老实地停在「相关」「关联」。
- 数据来源的坑要认。 TCGA 正常样本少、拼 GTEx 会引入批次效应;不同癌种样本量差异巨大(小样本癌种的结论天然更不稳);表达是 bulk 的平均值,掩盖了细胞异质性。这些都会左右结论,报告时该讲清就讲清。
- 一致 ≠ 一样。 就算一个基因在多个癌种里都高表达,它在每种癌里的生物学角色也可能完全不同。泛癌看到的是统计上的共性,落到具体某种癌,还得回到那个癌种的背景里重新理解。
一句话收束:把泛癌分析当成「地图」,它告诉您值得往哪走;真正的发现,在您照着地图选定一个癌种、深入挖下去之后。
在平台上跑一遍
上面这四步,在百沐一下不用写代码也能做:说清您要分析的基因(比如「帮我对 TP53 做泛癌分析」),平台会自动从公共数据库取数、分癌种跑完表达、预后、免疫浸润这几步,把箱线图、森林图、相关性热图一并给您,并附上可复现的 R 代码。您要做的,是核对癌种选得对不对、把结果读成一条自己能讲的线索。想连自己的数据一起分析也可以,直接上传即可。
上手见 组学智能体使用指南,或直接开始:https://app.baimuyixia.com 。