第二章:单细胞转录组数据预处理——QC、标准化、聚类

拿到单细胞数据之后,第一件事不是急着做通信网络图。你想想看,原始数据里的细胞状态良莠不齐,有死细胞,有双细胞,还有一大堆技术噪音。我刚开始做单细胞分析时,就吃过这个亏——直接跑下游分析,结果出来一堆假阳性,排查了半天才发现是质控没做好。

所以,咱们先把地基打牢。这一章我会带你走完预处理的三板斧:质控(QC)、标准化、聚类。每一步我都会给些实际项目里的注意点,帮你少走弯路。

1. 质控(Quality Control)——剔除垃圾细胞

质控的目标很明确:把低质量的细胞、双细胞、空液滴踢出去。常用的指标有三个:

  • 基因数(nFeature_RNA):一个细胞里检测到的基因数量。
  • UMI总数(nCount_RNA):唯一分子标识的总量。
  • 线粒体基因占比(percent.mt):线粒体基因的UMI占总UMI的比例。

为什么看这三个?我解释一下:

  • 基因数太低 → 可能是空液滴或细胞破裂。
  • 基因数太高 → 很可能是双细胞(两个细胞被抓在一个液滴里)。
  • 线粒体占比高(比如>20%)→ 细胞膜受损,胞浆内容物漏出,只剩线粒体基因还在。

我的经验:阈值没有标准答案。10x Genomics 官方指南建议线粒体占比控制在20%以下,但不同组织差异很大。我在做脂肪组织样本时,线粒体占比普遍偏高(15%~25%),硬切到20%会丢掉一半细胞。稳妥的做法是先画分布图,再根据拐点定阈值。

具体操作,以 Seurat 为例:

library(Seurat)
pbmc <- Read10X(data.dir = "filtered_feature_bc_matrix/")
pbmc <- CreateSeuratObject(counts = pbmc, project = "pbmc3k", min.cells = 3, min.features = 200)

# 计算线粒体基因比例
pbmc[["percent.mt"]] <- PercentageFeatureSet(pbmc, pattern = "^MT-")

# 画图看分布
VlnPlot(pbmc, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)

# 质控过滤
pbmc <- subset(pbmc, subset = nFeature_RNA > 200 & nFeature_RNA < 2500 & percent.mt < 5)

特别注意:我曾经踩过一个坑——过滤条件里写了 nCount_RNA 的下限,结果把所有低 UMI 的细胞都清掉了,后来发现这批数据本身就是低启动率(比如10x v2试剂)。后来我改用 nFeature_RNA 加 percent.mt 做主筛选,nCount 只用来剔除极低值的异常点滴。

2. 标准化(Normalization)——消除技术差异

质控完,数据还是原始的 UMI 计数矩阵。不同细胞捕获效率不一样,测序深度也不同,直接拿原始计数比较会出问题。标准化就是为了把细胞拉到同一水平线上。

主流方法有几种:

方法 原理 适用场景
LogNormalize 每个细胞的总UMI归一化,再log变换 快速入门,Seurat默认
SCTransform 正则化负二项回归,处理方差关系 高噪音数据,推荐
scran deconvolution 利用临近细胞池估计尺度因子 低表达基因较多的数据

我个人习惯先用 SCTransform。它比 LogNormalize 能保留更多生物学变异,而且把标准化和找高变基因一步完成了。不过注意,SCTransform 对内存消耗较大,如果你的数据超过10万个细胞,还是用 LogNormalize 加 ScaleData 吧。

# SCTransform 标准化(Seurat v5)
pbmc <- SCTransform(pbmc, vars.to.regress = "percent.mt", verbose = FALSE)

标准化之后,记得做一步 ScaleData(如果用了 LogNormalize),把每个基因的表达量中心化并缩放,让后续PCA不受高表达基因主导。

核心认知:标准化不是“标准化数据本身”,而是让细胞之间可比,消除测序深度差异。不要在这个阶段过度缩放,否则会损失真实的表达差异。

3. 聚类(Clustering)——给细胞分群

标准化之后,我们要对细胞进行无监督聚类,找到不同的细胞亚群。这一步直接影响后续通信分析的细胞类型分配,必须做扎实。

Seurat 的聚类流程通常是:

  1. PCA降维 → 提取主成分,减少噪音。
  2. 选择主成分数 → 用 Elbow Plot 或 JackStraw 确定维数。
  3. 构建KNN图 → 基于PCA空间计算细胞间距离。
  4. Louvain/Leiden聚类 → 图算法划分模块。
# PCA
pbmc <- RunPCA(pbmc, npcs = 30, verbose = FALSE)

# 确定主成分数(看肘部图)
ElbowPlot(pbmc, ndims = 30)

# 聚类(分辨率参数控制分群粒度)
pbmc <- FindNeighbors(pbmc, dims = 1:15)
pbmc <- FindClusters(pbmc, resolution = 0.5)

# UMAP 可视化
pbmc <- RunUMAP(pbmc, dims = 1:15)
DimPlot(pbmc, reduction = "umap", label = TRUE)

分辨率怎么调? 没有固定公式。我一般先从0.5开始,看UMAP图,如果分群太粗(比如B细胞和T细胞混在一起),就提高到0.8~1.0;如果出现过多小群(看起来像噪音),就降到0.3~0.4。另外,可以试试 clustree 包,它能在不同分辨率下展示聚类的稳定性,非常有帮助。

聚类完成后,别急着走。我建议先用已知的 marker 基因验证一下分群的生物学合理性。比如免疫细胞数据,检查 CD3D(T细胞)、CD14(单核细胞)、MS4A1(B细胞)是否分配到了正确的群。这一步能快速发现因为批次效应或聚类参数不合理导致的分群错误。

避坑: 不要只用 resolution=0.5 就跑完全程。不同组织的最优分辨率差异巨大。我记得在分析肿瘤样本时,0.5分辨率把肿瘤细胞和正常上皮细胞分在同一个群,后来提高到0.8才把恶性克隆单独分出来。所以,多跑几个分辨率,结合生物学知识做判断。

知识框架一览

下面这张图把本章的核心流程串起来了——从原始计数到聚类分群,每一步的关键操作和产出物都在里面。

单细胞数据预处理流程 原始计数矩阵 QC质控 nFeature / nCount / percent.mt 标准化 LogNormalize / SCTransform 降维 + 聚类 PCA → KNN → Louvain/Leiden 细胞分群(cluster) 为什么先处理数据? 原始计数包含大量技术噪音 不预处理 → 下游分析全歪 本章流程确保数据干净规范

总结一下预处理的关键点

  • QC 阶段不要死套阈值,一定要结合数据分布和生物学背景。
  • 标准化选 SCTransform 能省事,但注意内存;大项目用 LogNormalize + ScaleData。
  • 聚类分辨率是用 “生物学验证+统计稳定性” 双重标准确定的,不是找个神奇数字。
  • 跑完聚类,记得用 marker 基因验证一下,别让后面的通信网络建立在错误的分群上。

好了,数据预处理到这基本就稳了。下一章我们会进到细胞类型注释,那是连接预处理和通信分析的桥梁——你得给每个 cluster 贴上生物学名字。

(注意:本章内容仅供教学参考,实际分析请根据数据特点灵活调整。)