分辨率设多少:聚成几群才合理
跟完这篇,您能给单细胞聚类挑出一个站得住脚的分辨率(resolution):知道它怎么影响群数,一眼看出「过聚」和「欠聚」两种失败长什么样,并学会用已知 marker 和聚类稳定性两把尺子,把「聚成几群」这件事从拍脑袋变成有据可依。
聚类里最让人纠结的参数就是 resolution:调大分得细、调小分得粗,同一份数据能被聚成 8 群,也能被聚成 25 群。它没有唯一「正确值」——合适与否,取决于您要区分到多细的细胞类型,以及分出来的群能不能被生物学解释。所以这篇不给您一个数字,而是给您一套挑数字的办法。
resolution 到底在调什么
Seurat(以及大多数单细胞工具)的图聚类分两步:先在降维空间上建近邻图,再用 Louvain / Leiden 这类算法在图上找「社群」。resolution 就是社群划分的松紧旋钮:
- 调大:算法倾向于切出更多、更小的群 —— 群数变多。
- 调小:算法倾向于把邻近的细胞并到一起 —— 群数变少。
library(Seurat)
# 前提:seu 已完成 NormalizeData / RunPCA / FindNeighbors
seu <- FindNeighbors(seu, dims = 1:30) # dims 按肘图定,非固定值
# resolution 越大,分出的群越多;0.8 是 Seurat 默认值
seu <- FindClusters(seu, resolution = 0.8)
几个要点,先记住:
resolution = 0.8是 Seurat 的默认值,常用范围大致在0.4 ~ 1.2之间。这是惯例、按数据调,不是铁律:细胞数越多、异质性越高,往往需要更大的分辨率才能把该分开的类型分开。resolution只影响「怎么分群」,不影响 UMAP 的坐标。UMAP 只用于看图,改分辨率时点的位置不动,变的只是每个点被染成哪一群的颜色。- 群的编号(0、1、2……)只是标签,本身没有生物学含义。别被「聚出了 20 群」这个数字唬住 —— 群多不等于分得好。
先扫一遍:一次跑多个分辨率
定分辨率的第一步,不是猜一个值,而是在一个范围里连扫几个值,把候选摊开来比。FindClusters 的 resolution 直接接受一个向量,一次算完,结果分别存进 meta.data 的不同列:
# 一口气跑一串分辨率,各自存成一列
seu <- FindClusters(
seu,
resolution = c(0.2, 0.4, 0.6, 0.8, 1.0, 1.2)
)
# 每个 resolution 的结果存在 <assay>_snn_res.<值> 这样的列里
# 例如 RNA_snn_res.0.4、RNA_snn_res.0.8 …
colnames(seu@meta.data)
先看每个分辨率各分出多少群,心里有个数:
# 逐个分辨率数群数
sapply(
grep("_snn_res\\.", colnames(seu@meta.data), value = TRUE),
function(col) length(unique(seu@meta.data[[col]]))
)
再把相邻的几个分辨率并排画出来,直观感受「多分的那一群是从哪切出来的」:
library(patchwork)
p1 <- DimPlot(seu, group.by = "RNA_snn_res.0.4", label = TRUE) + ggtitle("res 0.4")
p2 <- DimPlot(seu, group.by = "RNA_snn_res.0.8", label = TRUE) + ggtitle("res 0.8")
p1 + p2
扫的过程中会看到一个规律:分辨率从小到大,群数先快速增加、后趋于平缓。平缓段往往意味着「真实的类型差不多都分出来了,再加分辨率只是在切噪声」——这个拐点附近,通常就是值得重点考察的区间。
过聚与欠聚:两种失败长什么样
分辨率错了有两个方向,症状正好相反。学会认这两副面孔,比记任何默认值都有用:
| 欠聚(分辨率过低) | 过聚(分辨率过高) | |
|---|---|---|
| 群数 | 偏少 | 偏多 |
| 典型表现 | 一群里混着互斥的 marker | 一种细胞被切成好几群 |
| marker 信号 | 同一群同时高表达 CD3D(T)和 CD14(单核) | 相邻几群的 marker 几乎一模一样 |
| 差异检验 | 群内异质,下游比较被稀释 | 相邻群之间几乎找不到显著差异基因 |
| UMAP 上看 | 一团点明显该分开却被涂成一色 | 一片连续的点被硬切成好几块 |
| 怎么办 | 调大 resolution,或对该群单独再聚类 | 调小 resolution,或合并 marker 高度重叠的相邻群 |
判断的抓手就两句话:
- 欠聚看「一群里有没有本不该共存的 marker」。如果某一群同时冒出两套互斥谱系的标志基因,说明两类细胞被并在了一起,该往上调。
- 过聚看「相邻群之间还分不分得开」。如果两群互相之间几乎找不到差异基因、marker 高度重叠,说明它们本是一类,被无谓地切碎了,该往下调或合并。
提醒:稀有细胞类型天生就少、也可能天生就该独立成一小群。「群小」不等于「过聚」——关键看它有没有自己特异的 marker,而不是看它有几个细胞。
用 marker 判断:分对了吗
数群数、看 UMAP 都只是外围观察,真正定案要回到生物学:每一群能不能用已知 marker 讲清楚它是什么。这是判断分辨率最硬的一把尺子。
做法是对候选分辨率下的分群跑一遍 marker,看各群是否各有特异标志:
library(dplyr)
# 先把 Idents 设成要考察的那个分辨率
Idents(seu) <- "RNA_snn_res.0.8"
markers <- FindAllMarkers(
seu,
only.pos = TRUE, # 只看上调,即本群相对高表达的基因
min.pct = 0.25, # 至少在 25% 的本群细胞里检出:常用惯例,按需调
logfc.threshold = 0.25 # log2FC 门槛:常用惯例,非铁律
)
# 每群取区分度最高的前几个 marker
top <- markers %>% group_by(cluster) %>% slice_max(avg_log2FC, n = 5)
拿到 marker 后,用两条准则对照分辨率:
- 每群都有自己特异的 marker(
pct.1高、pct.2低) —— 说明这个分辨率分出的群是「有身份」的,分得住。 - 相邻两群的 top marker 几乎完全重叠 —— 过聚的信号,这两群该合。
- 一群里同时挤着两套互斥 marker —— 欠聚的信号,这群该拆。
把关心的经典 marker 画成 DotPlot,一眼就能看出「每群是否各占一个 marker」:
# 一个 marker 只在某一群又大又深、别群又小又浅,才说明这群分得干净
DotPlot(seu, features = c("CD3D", "CD8A", "MS4A1", "CD14", "NKG7", "LYZ")) +
RotatedAxis()
一个实用的判定标准是:合适的分辨率,应当让您已知的、真正关心的细胞类型「恰好各成一群」——既没有把一类劈成两半,也没有把两类揉在一起。marker 面板怎么挑、注释怎么核对,展开见 细胞注释:手动 marker 与自动 SingleR。
用稳定性判断:clustree 看分裂脉络
marker 回答「分得对不对」,稳定性回答「分得稳不稳」——一个可信的群,应该在分辨率小幅变动时基本保持原样,而不是稍微一调就散架重组。clustree 是看这件事最直观的工具:它把不同分辨率的分群串成一棵树,每个分辨率一层,箭头表示细胞从上一层的群流向下一层的群。
library(clustree)
# prefix 对应 meta.data 里那批列的共同前缀
clustree(seu, prefix = "RNA_snn_res.")
看这棵树,重点关注两种信号:
- 干净的一分为二:某一层里,一个群清清楚楚地裂成两个稳定的子群,箭头几乎不交叉 —— 这往往是真实的亚型被分辨率提高后揭示出来,是可信的细分。
- 来回横跳的乱麻:某一层往上一调,箭头就大量交叉、细胞在几个群之间反复搬家 —— 这说明分群已经不稳,多半是在切噪声,是过聚的预警。
稳定性还可以用重采样来量化:对细胞做有放回抽样(bootstrap)重新聚类,看原来的群有多大比例被复现出来。复现率高的群稳、低的群可疑。这类做法在
bluster等包里有现成实现,思路都是「扰动一下,看群还在不在」。这里不展开,记住原则即可:经得起小扰动的分群,才值得信。
综合起来:怎么定一个合适的值
把上面几把尺子合到一起,定分辨率其实是一套可复现的流程,而不是玄学:
- 连扫一个范围:如
c(0.2, 0.4, 0.6, 0.8, 1.0, 1.2),先看群数随分辨率怎么涨,找到增长趋缓的拐点区间。 - 在拐点附近取候选:不必逐个细究,锁定两三个值重点考察即可。
- 用 marker 验身份:候选分辨率下,每群是否各有特异 marker?有没有该拆的、该合的?以「已知类型恰好各成一群」为准。
- 用 clustree 验稳定性:候选值处的分群,是干净的分裂还是来回横跳的乱麻?
- 落到能解释的那个:选一个既能把您关心的类型分开、又足够稳、每群都讲得通的值。宁可稍偏保守(略欠聚、群更纯),也别为追求「分得细」而过聚出一堆解释不了的群。
几条贯穿始终的原则:
- 没有全局最优的分辨率,只有匹配问题的分辨率。 想区分大类(T / B / 髓系),低分辨率就够;想抠某类细胞的亚群,就需要更高分辨率,甚至把这类细胞单独拎出来再聚一次。
- 分辨率是可以分层的。 常见做法是先用较低分辨率分出大类,再对感兴趣的大类单独做 subcluster、用更高分辨率细分。这比强行用一个全局高分辨率把所有类型一次切到底更稳、更好解释。
- 让生物学有最终发言权。 数群数、看树都是辅助,一个群站不站得住,最终看它能不能用 marker 讲成一个说得通的细胞类型。
常见坑
| 坑 | 表现 | 怎么办 |
|---|---|---|
| 默认值当标准答案 | 直接用 resolution = 0.8 就定稿,从不比较 | 至少连扫一个范围,用 marker 和 clustree 验一遍再定 |
| 群越多越好 | 一味调大分辨率,追求「分得细」 | 群多不等于分得好;相邻群 marker 重叠、找不到差异,就是过聚 |
| 把稀有类型当噪声 | 见到小群就调低分辨率想合掉 | 小群只要有自己特异的 marker,就是真类型,别误伤 |
| 只调分辨率不查上游 | 分不开时只在 resolution 上硬调 | 先确认 dims(主成分数)、高变基因、批次效应没问题,分辨率救不了坏输入 |
| 过度解读 UMAP 距离 | 按 UMAP 上簇的远近判断该分几群 | UMAP 只用于看图,簇间距离不代表定量关系,分群以 marker 为准 |
| 一个分辨率打天下 | 用同一个全局值又要分大类又要抠亚群 | 分层做:低分辨率分大类,感兴趣的类单独 subcluster 再细分 |
关于批次效应会不会假性拉高群数、需不需要先做整合,见 批次效应怎么识别与校正。
在平台上跑一遍
不想装 Seurat、clustree,也能完成这套「扫分辨率 —— 验 marker —— 看稳定性」的流程:在 百沐一下 传入已降维的单细胞对象,用一句话说清目的——「帮我在 0.2 到 1.2 之间扫几个分辨率,比较群数、各群 marker 和聚类稳定性,推荐一个合适的值」,平台会自动跑多组分辨率、算各群 marker、画 clustree 与 DotPlot,并连同可复现的 R 代码一起交付,过聚或欠聚的迹象会替您标出来。用法见 组学分析助手使用指南。
相关
- 单细胞分析全流程:Seurat 标准 pipeline —— 分辨率所在的降维聚类步在整条流程里的位置
- 细胞注释:手动 marker 与自动 SingleR —— 用 marker 给分好的群定身份,也是验分辨率的尺子
- 单细胞质控怎么做 —— 坏细胞没清干净会假性影响分群,聚类前的必修课
- 批次效应怎么识别与校正 —— 未整合的批次会假性拉高群数