4. 数据质量控制 (QC) 与过滤
到了这一步,其实有个灵魂拷问摆在你面前:原始数据敢直接用吗?
我个人习惯是,先扫一眼数据质量再动手跑任何聚类。因为如果底子就是烂的,后面调出一朵花来也没用。做这个项目的时候,我一开始拿到的那套公开肿瘤数据,直接丢进去算,结果发现标记数分布和线粒体比例都很不对劲——嗯,下面会详细说这个问题。
4.1 为什么先做数据质量控制?
Visium 平台的原理其实就是原位捕获 + 测序。它是切片贴片方式,难免有些点(spot)质量不好,比如组织边缘的 spot 可能捕获细胞太少,或部分 spot 捕获了掉下来的细胞碎片。
- 标记数太少:统计信息弱,后续聚不了类。
- UMI 重复太高:表示扩增偏差,不是真实的生物信号。
- 线粒体比例过高:通常是细胞受损或凋亡的标志。
在一个真实项目里,我见过一份数据,一次切片大约 3%-5% 的 spot 是碎片区。如果不过滤掉,这些“低质量邻居”会干扰降维和聚类算法,最后出来的图有奇怪的噪声簇。
4.2 检查哪些指标?
无非三件事:总 UMI 数、检测到的基因数、线粒体百分比。你想想看,这三个指标基本覆盖了“捕获够不够”、“信噪比高不高”、“细胞是否健康”。
| 指标 | 含义 | 通常低质量判断 |
|---|---|---|
| nCount_UMI | 每个 spot 的总 UMI 计数 | 过低(< 500)或极端高异常 |
| nFeature_RNA | 每个 spot 检测到的独有基因数 | 过低(< 200 或 100) |
| percent.mt | 线粒体基因 UMI 占总 UMI 比例 | 过高(> 20% 或 25%) |
其实,我习惯在过滤前先画一个 Violin 图和 Scatter 图看一下分布。你一眼就能看出哪些是尾巴、哪些是极端值。
4.3 过滤脚本怎么写?
在 R 里,以 Seurat 为例,你可以这样做:
# 计算线粒体比例
tumor_data[["percent.mt"]] <- PercentageFeatureSet(tumor_data, pattern = "^MT-")
# 可视化
VlnPlot(tumor_data, features = c("nCount_UMI", "nFeature_RNA", "percent.mt"),
ncol = 3, pt.size = 0.01)
# 手动过滤
tumor_data <- subset(tumor_data,
subset = nFeature_RNA > 200 &
percent.mt < 20 &
nCount_UMI > 500)
嗯,这里要注意:如果你用 Python 的 Scanpy,语法略有不同,但逻辑一样。
import scanpy as sc
# 读入 h5 文件
adata = sc.read_visium("path/to/spaceranger/output")
# 计算 QC 指标
adata.var["mt"] = adata.var_names.str.startswith("MT-")
sc.pp.calculate_qc_metrics(adata, qc_vars=["mt"], percent_top=None, log1p=False)
# 过滤
adata = adata[adata.obs.n_genes_by_counts > 200, :]
adata = adata[adata.obs.pct_counts_mt < 20, :]
adata = adata[adata.obs.total_counts > 500, :]
还记得吗?我曾经过滤完忘记更新高变基因列表,结果后面 PCA 降维时总感觉不对劲。后来就养成了习惯:过滤后一定要重新跑一下找高变基因和归一化。
4.4 流程图:数据进至 QC 过滤
画个流程来总结一下这一节。
流程其实很直接,但每一步的阈值可视化不能省。为什么?因为你凭肉眼看到分布才知道哪些 spot 是离群。
4.5 避坑指南:过滤标准不要太死
我记得刚用 Visium 数据那会儿,总喜欢参考公共数据集的通用阈值。但 Visium 的捕获效率、切片厚度、组织类型不同,UMI 分布差异会很大。
- 组织类型敏感:比如脑组织的线粒体比例通常较高,可以适当放宽松一些到 25%。
- nCount 上限也要看:过高的 nCount 可能是来自组织边缘污染的双 spot,我一般会画箱线图去掉上分位数+3 IQR 以上的点。
- 千万不要一开始就过滤掉低表达基因:那个可以在后面找高变基因时再做。
4.6 检查过滤效果
过滤完了,我喜欢再做一个数据分布的总结,比如看看还剩多少个 spot,以及 UMI 分布的情况。
# 在 R 里
dim(tumor_data) # 看看过滤前后的 spot 数量变化
summary(tumor_data$nFeature_RNA)
summary(tumor_data$percent.mt)
# 在 Python 里
print(adata.shape)
sc.pl.violin(adata, 'n_genes_by_counts')
sc.pl.violin(adata, 'pct_counts_mt')
如果过滤后 spot 数量骤减(比如超过了 20%),我建议你回头检查一下阈值是不是太严,或者组织切片本身有没有物理瑕疵。
4.7 整个章节关键点总结
1. 过滤不是为了让数据变漂亮,而是剔除明显的技术噪声。
2. 三个关键指标:总 UMI、基因数量、线粒体占比。
3. 阈值不准教条,必须看数据分布。
4. 始终画图确认过滤前后差异。
好,QC 这块先说到这。准备好了干净的数据,下一步才能放心调那个最佳分辨率参数。