2. 单细胞数据分析流程概述
大家好,我是你们这门课的向导。上一章我们聊了聊单细胞测序的基本概念,今天咱们来点实际的。
你手头可能已经拿到了一堆测序数据,可能是 FASTQ 文件,也可能是处理好的计数矩阵。然后呢?怎么从这些密密麻麻的数字里,挖掘出有意义的生物学信息?
这就是咱们这章要解决的问题。我会带你走一遍标准的单细胞数据分析流程。说白了,就是一张路线图。有了它,你就知道每一步该做什么,该用什么工具,以及可能会踩什么坑。
流程全景图:先把全局看清楚
我个人习惯,开始任何分析之前,先画一张宏观流程图。这能让你心里有底,不至于做到一半迷失方向。
下面这张 SVG 图,就是单细胞数据分析的完整链条。你可以把它当成一份「藏宝图」来用。
第一步:从原始数据到表达矩阵
无论你用的是 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(表达量矩阵)。
第二步:读入数据与创建 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)
记得有一次处理一个心脏样本,我发现过滤参数设得太宽松,结果混入了大量双细胞团。后来花了整整两天重新跑,后悔啊。
第四步:标准化与归一化
质控完后,我们拿到的原始表达矩阵,每个细胞的文库大小差别很大。所以必须标准化。
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 细胞。
这就是从纯数字到生物学含义的桥梁。你做完这一步,就可以回答生物学问题了:
- 这个疾病样本里,哪些细胞亚群的数量变了?
- 某个亚群内部,哪些通路发生了异常?
- 细胞之间的通讯是不是失调了?
一点额外提醒:质量控制不是一锤子买卖
做完整套分析之后,你可能会发现,之前设定的阈值太宽松了。比如有一个 cluster 的 percent.mt 异常高,这说明里面混入了碎片细胞。
这时候就需要回头调整过滤阈值,重新跑一遍 pipeline。别嫌烦,我几乎在每个项目里都要迭代 2-3 遍。
最后,送你一句话:单细胞分析,方法重要,但对数据的理解更重要。 多看图,多思考,多动手。咱们下一章见(哦不对,不能预告下一章)。今天就先到这里。