跳到正文
百沐一下百沐一下
本节目录

分辨率设多少:聚成几群才合理

跟完这篇,您能给单细胞聚类挑出一个站得住脚的分辨率(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 群」这个数字唬住 —— 群多不等于分得好。

先扫一遍:一次跑多个分辨率

定分辨率的第一步,不是猜一个值,而是在一个范围里连扫几个值,把候选摊开来比。FindClustersresolution 直接接受一个向量,一次算完,结果分别存进 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 高度重叠的相邻群

判断的抓手就两句话:

  1. 欠聚看「一群里有没有本不该共存的 marker」。如果某一群同时冒出两套互斥谱系的标志基因,说明两类细胞被并在了一起,该往上调。
  2. 过聚看「相邻群之间还分不分得开」。如果两群互相之间几乎找不到差异基因、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.")

看这棵树,重点关注两种信号:

  1. 干净的一分为二:某一层里,一个群清清楚楚地裂成两个稳定的子群,箭头几乎不交叉 —— 这往往是真实的亚型被分辨率提高后揭示出来,是可信的细分。
  2. 来回横跳的乱麻:某一层往上一调,箭头就大量交叉、细胞在几个群之间反复搬家 —— 这说明分群已经不稳,多半是在切噪声,是过聚的预警

稳定性还可以用重采样来量化:对细胞做有放回抽样(bootstrap)重新聚类,看原来的群有多大比例被复现出来。复现率高的群稳、低的群可疑。这类做法在 bluster 等包里有现成实现,思路都是「扰动一下,看群还在不在」。这里不展开,记住原则即可:经得起小扰动的分群,才值得信。

综合起来:怎么定一个合适的值

把上面几把尺子合到一起,定分辨率其实是一套可复现的流程,而不是玄学:

  1. 连扫一个范围:如 c(0.2, 0.4, 0.6, 0.8, 1.0, 1.2),先看群数随分辨率怎么涨,找到增长趋缓的拐点区间。
  2. 在拐点附近取候选:不必逐个细究,锁定两三个值重点考察即可。
  3. 用 marker 验身份:候选分辨率下,每群是否各有特异 marker?有没有该拆的、该合的?以「已知类型恰好各成一群」为准。
  4. 用 clustree 验稳定性:候选值处的分群,是干净的分裂还是来回横跳的乱麻?
  5. 落到能解释的那个:选一个既能把您关心的类型分开、又足够稳、每群都讲得通的值。宁可稍偏保守(略欠聚、群更纯),也别为追求「分得细」而过聚出一堆解释不了的群。

几条贯穿始终的原则:

  • 没有全局最优的分辨率,只有匹配问题的分辨率。 想区分大类(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 代码一起交付,过聚或欠聚的迹象会替您标出来。用法见 组学分析助手使用指南

相关