2. 单细胞数据分析流程概述

大家好,我是你们这门课的向导。上一章我们聊了聊单细胞测序的基本概念,今天咱们来点实际的。

你手头可能已经拿到了一堆测序数据,可能是 FASTQ 文件,也可能是处理好的计数矩阵。然后呢?怎么从这些密密麻麻的数字里,挖掘出有意义的生物学信息?

这就是咱们这章要解决的问题。我会带你走一遍标准的单细胞数据分析流程。说白了,就是一张路线图。有了它,你就知道每一步该做什么,该用什么工具,以及可能会踩什么坑。

流程全景图:先把全局看清楚

我个人习惯,开始任何分析之前,先画一张宏观流程图。这能让你心里有底,不至于做到一半迷失方向。

下面这张 SVG 图,就是单细胞数据分析的完整链条。你可以把它当成一份「藏宝图」来用。

单细胞数据分析完整流程图 原始测序数据 (FASTQ / BCL) 基因表达定量 (STARsolo / Cell Ranger) 数据质控过滤 (过滤双细胞/低质量) align / count 标准化与归一化 降维与聚类 (PCA / UMAP + 聚类) 细胞类型注释 (manual / database) 初步过滤后进入下游 差异表达分析 功能富集分析 生物学解释 (最终结论) 下游分析 迭代优化:质控与参数调整 关键要点 每一步都可能需要回溯调整参数;标准化不是可选项,是必选项。

第一步:从原始数据到表达矩阵

无论你用的是 10x Genomics 还是其他平台,第一步都是拿到 raw data。通常是 FASTQ 文件。

你需要用一个比对工具,把测序 reads 比对到参考基因组上。常用的有 Cell Ranger(10x 官方工具)、STARsolo 或者 kallisto-bustools。

我个人推荐初学者先用 Cell Ranger。为什么?因为它的 pipeline 非常完整,一条命令就能跑完。

cellranger count --id=sample1 \
  --transcriptome=/path/to/refdata-gex-GRCh38-2020-A \
  --fastqs=/path/to/fastq_dir \
  --sample=sample_name

跑完之后,你会得到一个 filtered_feature_bc_matrix 文件夹。里面有三个文件:barcodes.tsv(细胞条形码)、features.tsv(基因名)、matrix.mtx(表达量矩阵)。

小提示: 你也可以用 STARSolo。它的好处是速度快,而且能处理更灵活的定量需求。我有时候会用它处理非标准建库的数据。

第二步:读入数据与创建 Seurat 对象

拿到矩阵之后,我们就要把它请进 R 环境,用 Seurat 来处理了。

读入数据通常用 Read10X() 函数,它专门用来读取 10x 格式的输出。

library(Seurat)
data_dir <- "filtered_feature_bc_matrix"
counts <- Read10X(data.dir = data_dir)
seurat_obj <- CreateSeuratObject(counts = counts,
                                 project = "MyProject",
                                 min.cells = 3,
                                 min.features = 200)

这里有两个过滤参数:min.cells 表示一个基因至少在多少个细胞中表达,min.features 表示一个细胞至少检测到多少个基因。为什么这么设?是为了剔除那些几乎不表达的基因,和那些没有捕获到足够信息的空液滴。

注意: 这一步的过滤只是第一道粗糙的门槛。后面我们会做更精细的质控。别想着一步到位,单细胞分析就是一个「不断往回看」的过程。

第三步:质控过滤——剔除坏细胞

质控这步非常关键。可以说,你后面分析的好坏,很大程度上取决于这步做得认不认真。

主要看三个指标:

  • nCount_RNA:每个细胞的 UMI 总数。太低说明细胞活性差或测序深度不够;太高可能是双细胞团或一个液滴里有两个细胞。
  • nFeature_RNA:每个细胞检测到的基因数。标准同上。
  • percent.mt:线粒体基因表达占比。太高(>20% 或 >30%)说明细胞膜破裂,RNA 泄漏了。

你可能会问,我怎么计算线粒体基因比例?Seurat 提供了方便的计算函数:

<code>seurat_obj[["percent.mt"]] <- PercentageFeatureSet(seurat_obj, pattern = "^MT-")

然后画个小提琴图看看分布:

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

根据图形,设定过滤阈值。比如:

seurat_obj <- subset(seurat_obj, subset = nFeature_RNA > 200 &
                       nFeature_RNA < 3000 &
                       percent.mt < 20)

记得有一次处理一个心脏样本,我发现过滤参数设得太宽松,结果混入了大量双细胞团。后来花了整整两天重新跑,后悔啊。

避坑指南: 建议先做一个 lenient(宽松)的过滤,保留尽可能多的细胞。然后在后续聚类中发现奇怪的小群,再回头严格过滤。迭代式过滤是最靠谱的。

第四步:标准化与归一化

质控完后,我们拿到的原始表达矩阵,每个细胞的文库大小差别很大。所以必须标准化。

Seurat 默认用的是 LogNormalize 方法:

seurat_obj <- NormalizeData(seurat_obj, normalization.method = "LogNormalize", scale.factor = 10000)

它的原理是:先计算每个基因在细胞中的相对表达(除以该细胞总 UMI 数),再乘以 10000,最后做 log 变换。这样就能把不同文库大小的细胞拉到同一个水平上。

但我得说一句,近年来 SCTransform 方法越来越流行。它基于正则化负二项回归,能同时处理标准化和方差稳定化。用起来也方便:

seurat_obj <- SCTransform(seurat_obj, vars.to.regress = "percent.mt")

我个人现在大部分项目都改用 SCTransform 了。效果确实比 LogNormalize 好,尤其对于高变异基因的筛选。

第五步:降维与聚类

这是 Seurat 的核心功能。咱们这节课的主战场。

PCA 降维

高维数据直接聚类是不行的。我们先用 PCA 把几千个基因压缩成几十个主成分。Seurat 会先选择一个子集的高变基因来做 PCA。

seurat_obj <- FindVariableFeatures(seurat_obj, selection.method = "vst", nfeatures = 2000)
seurat_obj <- ScaleData(seurat_obj)
seurat_obj <- RunPCA(seurat_obj, npcs = 30)

为什么选 30 个主成分?不一定。有些人用 20,有些用 50。可以通过 ElbowPlot() 看看肘部在哪里。

UMAP 可视化

PCA 的结果是一种线性降维,但细胞之间的关系往往是非线性的。所以我们会再用 UMAP 或 t-SNE 做最终的二维可视化。

seurat_obj <- RunUMAP(seurat_obj, dims = 1:20)
DimPlot(seurat_obj, reduction = "umap")

你会看到细胞被分成了一个个小团。这就是初步的聚类结果。

基于图的聚类

Seurat 内部用的是基于图的聚类算法(Louvain / Leiden)。它会构建一个 KNN 图,然后通过社区发现算法把细胞分成不同的簇。

seurat_obj <- FindNeighbors(seurat_obj, dims = 1:20)
seurat_obj <- FindClusters(seurat_obj, resolution = 0.5)

resolution 这个参数决定了聚类的粒度。值越大,得到的细胞亚群越多。一般从 0.4 到 1.2 之间调试。

嗯,这里要注意。不要一上来就用很高的分辨率。我见过很多新手一口气设成 1.5,结果得到 30 个簇,根本解释不了。从低分辨率开始,慢慢往上加。

第六步:差异分析与生物学解释

聚好类之后,你就可以来找各个 cluster 的 marker 基因了:

all.markers <- FindAllMarkers(seurat_obj, only.pos = TRUE, min.pct = 0.25, logfc.threshold = 0.25)

比如,cluster 0 高表达 CD3E、CD4,那它很可能是 CD4+ T 细胞。cluster 2 高表达 FCGR3A、NKG7,那可能是 NK 细胞。

这就是从纯数字到生物学含义的桥梁。你做完这一步,就可以回答生物学问题了:

  • 这个疾病样本里,哪些细胞亚群的数量变了?
  • 某个亚群内部,哪些通路发生了异常?
  • 细胞之间的通讯是不是失调了?
核心原则: 单细胞分析的最终目的,不是画出漂亮的 UMAP 图。而是用它来回答一个具体的生物学问题。所有分析步骤都朝这个目标发力。

一点额外提醒:质量控制不是一锤子买卖

做完整套分析之后,你可能会发现,之前设定的阈值太宽松了。比如有一个 cluster 的 percent.mt 异常高,这说明里面混入了碎片细胞。

这时候就需要回头调整过滤阈值,重新跑一遍 pipeline。别嫌烦,我几乎在每个项目里都要迭代 2-3 遍。

最后,送你一句话:单细胞分析,方法重要,但对数据的理解更重要。 多看图,多思考,多动手。咱们下一章见(哦不对,不能预告下一章)。今天就先到这里。