4、寻找标记基因(FindAllMarkers)
聚类分完了,细胞群也有了。然后呢?
你得知道每个簇里到底有什么基因在“撑场面”。说白了,就是找到每个cluster相对于其他细胞高表达的特征基因。Seurat 里做这个事儿的函数叫 FindAllMarkers。
我个人特别喜欢把这步比作“给每个细胞群写身份证”。你的后续细胞类型注释、差异热图绘制,全靠这份身份证列表。弄不好,后面全白搭。
为什么不用手动跑差异?
FindAllMarkers 本质上是把每个簇跟剩下所有细胞做一次差异表达分析。你想想看,如果手动写循环,十几二十个簇,代码又臭又长,还容易漏参数。Seurat 帮你一把梭,返回一个干净的数据框,直接拿来用。
我在项目里遇到过那种“每个簇只找10个基因”的粗暴做法,结果注释出来好几个簇都写成“T细胞”。嗯,后来我学乖了,老老实实用默认参数先扫一遍,再调阈值。
核心用法
假设你已经有了一个处理好的 Seurat 对象,叫 seu,并且 seu$seurat_clusters 已经设好。跑下面这句就行:
markers <- FindAllMarkers(seu, only.pos = TRUE, logfc.threshold = 0.25, min.pct = 0.1)
参数不多,但每个都挺关键。我来拆开说。
参数详解(这张表我经常贴在工位上)
| 参数 | 默认值 | 说明 |
|---|---|---|
only.pos | FALSE | 设为 TRUE 只返回在每个簇中上调的基因。我 99% 的情况都开,因为标记基因就是要找高表达的,下调的对于细胞注释没意义。 |
logfc.threshold | 0.25 | 平均 logFC 的绝对值门槛。太小会混入大量噪声,太大又可能漏掉一些弱但特异的标记。0.25 是官方推荐,我一般在 0.25~0.5 之间试。 |
min.pct | 0.1 | 基因在簇内和簇外的最小表达比例。0.1 比较宽松,如果你数据量大或者想严格点,可以提到 0.25。 |
test.use | "wilcox" | 差异检验方法。Wilcoxon 秩和检验又快又稳。我有时候会用 MAST 或 DESeq2,但跑几十万细胞时 Wilcox 是首选。 |
assay | 默认当前 assay | 指定哪个 assay 的数据。一般是 "RNA",如果你用了 SCT 标准化,记得切成 "SCT"。 |
小技巧: 如果你不知道设多少阈值合适,先跑一遍不加过滤(
logfc.threshold = 0, min.pct = 0)看输出的 logFC 和 pct 分布,再决定切点。这招我在一个 10x 数据上用过,发现大部分基因 logFC 在 0.1~0.3,于是把阈值设成 0.3 刚刚好。
返回结果长什么样?
跑完后 markers 是一个 data.frame,每行一个基因-簇对。列名如下:
- p_val — 原始 p-value。
- avg_log2FC — 该簇相对于其他细胞的平均 log2 倍变化。
- pct.1 — 该簇内表达该基因的细胞比例。
- pct.2 — 其他簇中表达该基因的细胞比例。
- p_val_adj — 多重假设检验校正后的 p-value。看这个!假的看 raw p 会哭。
- cluster — 所属的簇编号。
- gene — 基因符号。
注意:avg_log2FC 在 Seurat v5 里改叫 avg_log2FC,老版本可能叫 avg_logFC。这坑我踩过,脚本跑着跑着报错找不到列名,后来发现版本问题。
我曾经历过…… 有一次我图省事,直接用 raw p-value 排序挑前 10 个基因做热图。结果簇 5 和簇 6 的标记基因完全重叠,注释根本分不开。后来发现是 p_val_adj 没控制,假阳性太多。从那以后我只看
p_val_adj < 0.05 的结果。
筛选优质标记的常用套路
拿到 markers 后,我一般这样过滤:
top_markers <- markers %>%
filter(p_val_adj < 0.05) %>%
group_by(cluster) %>%
slice_max(n = 10, order_by = avg_log2FC)
每个簇取 logFC 最高的 10 个基因,同时保证校正显著。这种列表画热图时横轴不会太长,也够你做初步注释。
流程图:从 Seurat 对象到标记基因列表
下面这张图展示了整个 FindAllMarkers 的数据流。我习惯把它放在实验记录本的第一页,提醒自己每一步的意义。
避坑指南(用钱换来的教训)
- 千万别忘了 only.pos=TRUE。默认是 FALSE,会给出双向的基因,结果里一半是下调的,画热图时颜色倒过来很别扭。我有个同事就是没设,被老板追问“为什么这个标记基因在所有簇里都低表达?” 尴尬。
- 大数据集跑得慢? 几十万细胞时 Wilcox 检验也很慢。可以试试
test.use = "poisson"或者先在FindAllMarkers里增加max.cells.per.ident = 500参数,只采样 500 个细胞做差异,精度损失不大但速度快 10 倍。 - 结果行数爆炸。如果不设阈值,一个 20 簇的数据可能输出几十万行。你筛选的时候记得先把 p_val_adj 过滤掉,否则内存撑不住。
一句话总结: FindAllMarkers 是你从无监督聚类走向有监督注释的桥梁。参数调得好,热图画得早;参数调得糙,注释两行泪。