2. 单细胞RNA-seq数据预处理流程归纳总结

预处理是单细胞RNA-seq分析的地基。地基没打牢,后面所有批次校正、聚类、差异表达全是白搭。我个人习惯是,拿到新数据后先拉一遍完整的预处理管线,再回头看样本质量。今天我就把这套流程拆开来给你捋一遍。

2.1 原始数据到表达矩阵:核心三步

先说大框架。不管你用10x、Smart-seq2还是Drop-seq,最终都要走到同一个目标:把测序仪吐出来的fastq文件,转成基因×细胞的计数矩阵。我把它总结成三条主线——

步骤输入输出常用工具
1. 原始数据质控fastqclean fastqFastQC, fastp, Cutadapt
2. 比对/定量clean fastq + 参考基因组BAM / 基因-UMI计数STARsolo, Cell Ranger, kallisto|bustools
3. 生成计数矩阵基因-UMI计数文件cells × genes 稀疏矩阵Cell Ranger output, emptyDrops

嗯,你可能要问:为什么跳过比对直接定量也行?因为现在很多工具是pseudo-alignment(伪比对),比如kallisto,速度是STAR的5-10倍,准确度并不差。我在项目里试过STARsolo和kallisto,对肿瘤样本这种高异质性的数据,两种方法的结果差异很小,但kallisto跑得快多了。

2.2 细胞筛选:别把垃圾当宝贝

矩阵拿到了,第一件事不是急着分析,而是把空液滴、死细胞、双细胞踢出去。这个环节我吃过亏——有次肿瘤样本里混了大量血小板黏连的颗粒,结果聚类出来一个“假亚群”,折腾了两周才发现是双细胞。避坑建议从这里开始。

我曾经踩过的坑:只用nCount_RNA一个指标筛细胞,结果把低质量的真细胞也踢掉了。后来加了线粒体比例和核糖体比例两个维度,才把真正的单细胞筛干净。

常用的过滤指标有三个:

  • 基因数(nFeature_RNA):一般200-2500之间。肿瘤细胞往往表达多样,上限可以放宽到4000。
  • UMI数(nCount_RNA):与基因数高度相关,一般去双细胞时会用它做联合筛选。
  • 线粒体比例(percent.mt):死细胞会漏出线粒体RNA,通常阈值设在10%-20%。肿瘤样本我建议放到15%——因为肿瘤细胞代谢旺盛,线粒体表达本来就高。

下面是我常用的一组过滤代码(Seurat风格),你可以直接套用:

# 过滤低质量细胞(单样本)
selected_cells <- which(
  pbmc$nFeature_RNA > 200 &
  pbmc$nFeature_RNA < 4000 &
  pbmc$percent.mt < 15
)
pbmc <- pbmc[, selected_cells]

小技巧:过滤前先画一个violin plot看分布。如果线粒体比例在10%处有个明显拐点,就别死守15%了。数据告诉我们阈值在哪,我们只是执行者。

2.3 标准化与归一化:让细胞之间能比较

原始UMI counts受测序深度影响很大——某个细胞测了5万reads,另一个只测了1万,单纯比counts是不公平的。标准化就是把这个坑填平。

主流的标准化方法分两大派:

方法核心思想适用场景
LogNormalize (Seurat默认)每个细胞的counts除以总counts,乘以scale.factor,再log1p快速、内存友好,适合初步探索
SCTransform用负二项回归去除深度影响,同时保留生物变异肿瘤样本(高异质性)、批次效应明显的场景
sctransform + v2改进版,速度更快,正则化更强大规模数据集(1万以上细胞)

我个人偏好:如果样本量小于5000细胞,我会用SCTransform,因为它在保留稀有亚群方面明显优于LogNormalize。有一次我在髓系细胞分析中发现,LogNormalize直接抹掉了一个只有0.5%比例的巨噬细胞亚群,而SCTransform能清晰保留。

2.4 高变基因选择:降维前的关键一步

全基因组3万个基因,大部分是“噪音”。我们只取那些在细胞之间表达差异最大的基因(高变基因,HVGs)做后续PCA。这步做不好,降维后全是技术噪声。

Seurat的FindVariableFeatures默认选2000个,你想想看,2000个够用吗?

  • 如果样本非常均质(比如纯化的T细胞),1000个就够了。
  • 肿瘤样本里细胞类型多、状态复杂,我建议用3000-4000。
  • 另外,选择方法上我常用vst(基于方差稳定变换),对0膨胀的数据处理得更好。
# Seurat 高变基因选择(vst方法,取3000个)
pbmc <- FindVariableFeatures(
  pbmc,
  selection.method = "vst",
  nfeatures = 3000
)

2.5 PCA与UMAP:把高维塞进二维

标准化后的表达矩阵仍然是高维的(细胞数×高变基因数)。我们需要降维。

PCA是线性降维,保留全局结构。一般取30-50个主成分。多少合适?跑一个ElbowPlot,看拐点在哪。我习惯取50个——多几个主成分不会引入太多噪声,但能保留更细的变异。

UMAP是非线性降维,专门用于可视化。UMAP的参数里,n.neighbors和min.dist对肿瘤异质性展示影响很大。我记得有次调整n.neighbors从15降到5,一个原本被埋没的应激反应亚群忽然就分开了。

关键理解:PCA是做分析的,UMAP是做图用的。不要在UMAP上判断聚类是否稳定——用PCA的聚类结果做下游分析,UMAP只给你一个视觉直觉。

# PCA + UMAP 标准流程(Seurat)
pbmc <- RunPCA(pbmc, npcs = 50)
pbmc <- FindNeighbors(pbmc, dims = 1:50)
pbmc <- FindClusters(pbmc, resolution = 0.8)
pbmc <- RunUMAP(pbmc, dims = 1:50, n.neighbors = 10, min.dist = 0.3)

2.6 预处理全流程SVG总览

下面这张图把以上五个环节串成一条管线。你可以把它贴在工位旁,每次跑数据前扫一眼,保证不丢步骤。

单细胞RNA-seq预处理全流程管线 原始数据质控 fastq→clean fastq 比对/定量 STARsolo/kallisto 生成计数矩阵 cells×genes 细胞筛选 nCount/nFeature/mt% 标准化+降维 LogNorm/SCTransform 🔍 质控检查点 1. 测序饱和度 2. 比对率 3. 细胞条形码透镜图 ⚙️ 常见参数速查 HVGs: 3000 | PCA: 50 | UMAP: n.neighbors=10 ⚠️ 肿瘤样本特殊处理 线粒体阈值15% | HVGs取4000 | 警惕双细胞

2.7 预处理常见坑(以及我怎么爬出来的)

做肿瘤样本预处理这些年,我至少在以下三个地方摔过跟头:

  1. 双细胞过滤太松:肿瘤细胞本身个头大、表达丰度高,容易形成异型双细胞。我用DoubletFinder检测后发现,未经任何处理的样本里双细胞比例竟高达8%。后来我在过滤步骤之前,专门跑了一遍scrublet。
  2. 忽略线粒体RNA的肿瘤生物学意义:前面说线粒体阈值设在15%,但如果是缺氧肿瘤区域,线粒体基因激活可能本身就是生物信号。我建议做一个“高线粒体细胞子集”的单独分析,不要无脑踢掉。
  3. 标准化选择错误:用LogNormalize处理混合肿瘤-正常样本时,由于深度差异大(肿瘤细胞UMI是正常细胞的3倍),会引入严重的批次效应。改用SCTransform后干净多了。

一句话总结本章:预处理不是一步到位的机械操作,而是一个“分析-验证-纠偏”的迭代过程。拿到数据后先跑完上述5个环节,画出QC图,再决定要不要调整某个阈值。这也是为什么我把它们归到同一章——它们是连在一起的拳头,不是分开的手指。