第三章 创建Seurat对象:QC指标与过滤,标准化初步认识

好,我们正式开始动手。单细胞分析的第一步,就是把原始数据装进R里,弄成一个标准的Seurat对象。很多人觉得这步就是跑个函数,没什么技术含量。我早年也这么想,结果吃过不少暗亏。你想想看,数据本身质量不行,后面分析再花哨也是白搭。

说白了,Seurat对象就是个聪明的大容器。它能把表达矩阵、细胞元数据、基因信息、降维结果、聚类标签等等全塞在一起,而且每个部分还互相索引。你需要取某个基因在某个细胞里的表达量,一行代码就搞定。

核心一句话: Seurat对象是单细胞分析的数据基石,所有后续操作都围绕它展开。

3.1 从一个矩阵开始

我们先做一个小规模的演示。假设你从Cell Ranger或者STARsolo拿到了标准的三个输出文件:barcodes.tsv、features.tsv、matrix.mtx。读取并创建Seurat对象的经典操作如下:

library(Seurat)

# 读取10X标准输出
raw_data <- Read10X(data.dir = "path/to/your/filtered_feature_bc_matrix")

# 创建Seurat对象
sce <- CreateSeuratObject(
  counts = raw_data,
  project = "my_project",
  min.cells = 3,      # 至少在3个细胞中有表达的基因才留下
  min.features = 200  # 至少检测到200个基因的细胞才保留
)

我个人习惯,min.cells设3,min.features设200,是前人总结的经验值,比较稳妥。如果你的数据测得很深,可以把min.features提到300甚至500,但初学阶段用默认就够。

小提示: 如果你只有非10X平台的数据(比如Smart-seq2),可以用CreateSeuratObject(counts = your_matrix)直接传入一个稀疏矩阵dgCMatrix,格式不对会报错。我踩过这个坑。

创建完对象后,你可以用sce看看它内部长什么样:

sce
# An object of class Seurat
# 2000 features across 100 samples within 1 assay
# Active assay: RNA (2000 features, 0 variable features)

看到这个结构,心里就有底了。但光有对象还不够,我们需要知道这些细胞的质量怎么样。

3.2 QC指标——好细胞长什么样

单细胞数据质量把控,我总结就三件事:

  • 每个细胞检测到的基因数(nFeature_RNA)——太少可能是死细胞或空液滴,太多可能是双细胞或多细胞。
  • 每个细胞的UMI总数(nCount_RNA)——与nFeature_RNA有正相关性,异常偏高可能是双细胞。
  • 线粒体基因占比(percent.mt)——比例过高说明细胞膜破损,细胞质内容物泄露,线粒体基因相对富集。

计算线粒体占比,Seurat有现成函数:

sce[["percent.mt"]] <- PercentageFeatureSet(sce, pattern = "^MT-")
注意: 人的线粒体基因名以^MT-开头。小鼠是^mt-。斑马鱼、大鼠等物种的前缀不同,一定不要写错。我曾经一个项目,代码检查了三遍才发现物种前缀给用错了。

计算完后,用VlnPlot可视化,一眼就能看出离群点:

VlnPlot(sce, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)

这个箱线图+散点图的组合,我建议你保存一份。它是你最原始的数据快照,将来写论文汇报时总要用到。

3.3 过滤——做减法比做加法难

过滤看起来是剔除细胞,但目的其实是保留高质量的信号。常见的过滤标准如下表:

指标 常见下界 常见上界 说明
nFeature_RNA 200 5000 少于200个基因可能是空液滴;多于5000可能是双细胞
nCount_RNA 视测序深度而定 视测序深度而定 一般结合nFeature_RNA做散点图判断
percent.mt — 20% 或 25% 超过20%的细胞很可能已经破碎

实际操作时,我建议你对照VlnPlot的图形来定阈值,不要盲目套用别人的数字。过滤代码很简单:

sce <- subset(sce, subset = nFeature_RNA > 200 & nFeature_RNA < 5000 & percent.mt < 20)

做完这一步,你大概会剔除掉5%-15%的细胞数据集,具体看样本质量。我经历过一个肿瘤样本,坏死区域比例很高,mt占比超过50%的细胞占了近四分之一,那一刀切得我肉疼,但必须切。

3.4 标准化——让数据具有可比性

标准化是为了消除细胞间测序深度的差异。本来一个细胞测得深,基因表达量就高,但不代表它真的表达得多。你得把它拉到同一把尺子上。

Seurat默认的标准化方法是LogNormalize,但在我这门课——《SCTransform批次校正方法应用》里,我们要重点介绍SCTransform。SCTransform比传统方法好在哪里?它用一个正则化负二项回归模型,同时完成了标准化、方差稳定化和高变基因选择。一步到位,非常高效。

它的用法简洁到令人震惊:

sce <- SCTransform(sce, vars.to.regress = "percent.mt", verbose = FALSE)
小技巧: 如果你有多个样本批次,可以在vars.to.regress里加上批次变量,比如vars.to.regress = c("percent.mt", "batch")。我早期做多批次整合时就踩过一个坑,只在标准化后做校正,但批次效应在标准化前就已存在。SCTransform直接回归掉,效果立竿见影。

运行完成后,Seurat对象里会增加一个名为SCT的assay。后续的降维、聚类、差异分析都默认使用这个assay的数据。你不必再手动做一次FindVariableFeatures,因为SCTransform自动帮你选好了前3000个高变基因。

3.5 本章小结:一张图看懂流程

下面这张SVG图总结了从原始数据到Seurat对象、QC过滤、标准化的完整脉络。你可以把它当作一张检查清单。

第三章知识体系:Seurat对象创建·QC·标准化 Cell Ranger 输出 CreateSeuratObject() min.cells / min.features QC 指标计算 nFeature / nCount / percent.mt subset() 过滤 剔除低质量/双细胞 SCTransform() 标准化 + 高变基因选择 说明:每步都可能需要根据数据特点调整参数,不要死记硬背阈值。
一点个人体会: QC和标准化看起来像流水线,但恰恰是考验分析者经验的地方。我做过的项目里,有两次都是因为过滤参数不同,下游找出的差异基因截然不同。后来我把每一步的过滤逻辑都写成了可重复执行的脚本,每次跑完自动生成QC报告,再人工看一眼图,再决定是否调整阈值。

走到这一步,你的Seurat对象已经干净、标准化,准备进入降维和聚类了。但别急,在进入那些性感的分析前,先花几分钟确认你的QC策略是否稳健。这是日后避免返工最有效的投资。