第3章:质量控制总览:从原始数据到过滤后数据的完整QC流程框架
质量控制这事儿,说白了就是给你的空间转录组数据“体检”。不做体检直接跑下游分析?嗯,我踩过这个坑,浪费了整整两周时间在那堆“假信号”上打转。今天我就把这份完整流程框架摊开来跟你聊,看看从原始数据到最后能用的过滤数据,中间到底经历了什么。
3.1 为什么需要一套完整的QC流程?
空间转录组数据有个特点——它不像单细胞数据那么“干净”。组织切片的质量、透化时间、芯片批次、甚至操作员的手法,都会留下一堆“坑”。你想想看,一张切片上可能同时存在:
- 技术性噪音:背景荧光、非特异性结合、探针残留
- 物理异常:组织折叠、气泡、撕裂边缘
- 生物混杂:坏死区域、血细胞污染、高凋亡区域
我刚开始做空间转录组时,天真地以为只要UMI count够高就能用。结果呢?那些“高表达”的spot有一半是组织边缘的严重坏死区,RNA降解得厉害,高表达的其实是线粒体基因。所以我后来做项目,第一件事就是搭一套系统化的QC流程。
3.2 从原始数据到过滤数据:四步走框架
这四步是我根据多个项目经验总结出来的,缺一步都不行。每一步都有明确的输入、处理和输出,跑完之后你才能安心往下走。
核心原则:宁可多过滤,不放过一个“脏”spot。一个坏点污染一个邻域,就能毁掉整个空间模式。
第一步:原始数据拆包与初步校验
这一步往往被很多人忽略,觉得“数据拿到手直接跑就行”。其实原始数据的质量决定了后续所有分析的天花板。我建议你至少检查:
- 空间坐标完整性:所有spot是否都有x, y坐标?有没有坐标重合或空值?
- 文库复杂度:UMI counts的分布是否合理?有没有奇高或奇低的spot?
- 基因覆盖度:每个spot检测到的基因数是不是在预期范围内?
这里有个小技巧。我习惯先跑一段简单的代码来快速摸排:
# 快速质量概览 - Python碎片
import scanpy as sc
# 加载原始数据
adata = sc.read_10x_h5('filtered_feature_bc_matrix.h5')
# 空间坐标必须存在
print(f"总SPOT数: {adata.n_obs}")
print(f"总基因数: {adata.n_vars}")
print(f"UMI统计: min={adata.obs['total_counts'].min()}, max={adata.obs['total_counts'].max()}")
别小看这几行代码,它能在几分钟内告诉你数据的水有多深。
我遇到过的坑:有一次数据UMI分布看起来完美,但死活跑不出空间模式。后来发现是坐标轴顺序反了——x和y列被交换。所以,阅读meta数据时,务必确认坐标方向与官方文档一致。
第二步:多维度指标计算与联合过滤
这一步是整个QC的核心。常用的指标我整理成了一张表,方便你对照:
| 指标 | 含义 | 常见阈值 | 异常提示 |
|---|---|---|---|
| UMI Count | 每个spot的Unique Molecular Identifiers总数 | 500 - 20000 | 过低=空point/凋亡;过高=双胞或多细胞团 |
| Genes Count | 每个spot检测到的基因种类数 | 200 - 5000 | 过低=RNA降解/细胞量少;过高=组织重叠 |
| 线粒体比例 | 线粒体基因表达占总表达的百分比 | < 20-30% | 高=细胞损伤/凋亡/坏死 |
| 核糖体比例 | 核糖体蛋白基因表达占比 | < 15-25% | 高≠异常;但极低可能有meta异常 |
| 文库复杂度 | 基因数对UMI数的log-log回归斜率 | > 0.6 | 低复杂度=扩增偏差或RNA降解 |
| 组织区域标记 | 基于组织学染色的spot类型(如H&E、免疫荧光) | 仅保留组织内区域 | 组织外=无意义背景;组织内但异常形态=折叠/撕裂 |
记得,阈值不是死的。我在不同组织类型上用过完全不同的参数:
- 脑组织:线粒体比例可以放到30%,因为神经元代谢活跃。
- 肿瘤组织:UMI count的下限可以放宽到300,因为肿瘤微环境复杂,有些区域细胞少但信号重要。
第三步:可视化诊断与手动校验
数据指标没有问题的数据,不一定真的可以用。我强烈建议你画几张基本图来“眼见为实”:
- 空间分布图:用原始坐标画出UMI count、线粒体比例的空间热图,肉眼看一下有没有明显异常区域。
- QC指标相关性散点图:比如Genes vs. UMI。如果存在大量低基因数+高UMI的点,说明扩增发生了偏好。
- 生物标记物表达分布:用已知的组织特异基因(如脑的SNAP25、肿瘤的KRT19)表达图验证过滤后的数据是否保留了预期模式。
别忘了这一步:有些spot的QC指标完全合规,但处在组织边缘或组织折叠区域,它周围会掺入背景信号。这种点我建议手动标记剔除,即使它的UMI count有5000。
我习惯将可视化诊断的结果存成一份简短的报告:
# 生成QC诊断图
sc.pl.spatial(adata, color=['total_counts', 'pct_mito', 'n_genes_by_counts'],
ncols=3, show=False, save='QC_diagnostic.pdf')
第四步:过滤与再平衡
这一步我要纠正一个常见错误——很多人filters直接写死固定阈值,然后跑一次就完事了。我的做法是分两轮:
- 硬过滤:先用表格里的保守阈值去除明显异常点。
- 检查再平衡:把过滤后的数据重新计算各项统计量,看看有没有引入新的偏倚。如果过滤掉太多数据(>30%),说明实验质量堪忧,这时候应该回退到实验流程,而不是强行过滤。
# 第二轮检查代码片段
adata_filtered = adata[adata.obs.pct_mito < 20].copy()
print(f"过滤后剩余SPOT数: {adata_filtered.n_obs}")
# 检查线粒体分布是否还有长尾
adata_filtered.obs['pct_mito'].hist(bins=50)
3.3 QC流程框架图:一张图讲清整体流程
为了让你对这套流程有一种全局感,我画了一张完整的框架图。先看看,然后再单步理解各个模块的衔接关系。
3.4 最终输出:过滤后的数据长什么样?
经过上述四步之后,你拿到的数据应该具有如下特征:
- 每个spot有可靠的空间坐标(没有空值、无错位)。
- UMI count和基因数的分布呈正常的右偏单峰——既没有拖尾也没有异常尖峰。
- 线粒体比例、核糖体比例都在正常的生物范围之内。
- 组织学标记显示过滤后的spot几乎都落在组织区域内,没有异常边缘效应。
我个人的经验:我第一次做完这套流程后,直接去跑了空间差异表达分析,结果与组织H&E染色的视觉判断高度一致。再回去对比没过滤的版本——纯粹的信噪比提升了一个档次。
3.5 避坑指南:千万别踩的雷
我干这行几年,踩过的坑比走对的桥还多。在这给你几个避坑点:
- 不要单独用某个指标下结论。比如线粒体比例超过20%不一定不好,如果同时基因数和UMI count都很高,说明可能是正常的高代谢区域。
- 不要在一开始就定死阈值。先画分布图,看数据本身的“脾气”,再基于分布特征选择边界。我习惯用5%和95%分位数作为初步边界。
- 过滤后务必重新检查。有一次我过滤完发现死亡区域虽然没了,但分离了正常组织的一个亚结构。后来我调整了阈值,才保留了完整的空间模式。
- 不能靠自动化一劳永逸。不同组织(脑、肿瘤、肝脏)QC参数完全不同。依赖默认值的分析,等于闭着眼开盲盒。
嗯,以上就是你从原始空间转录组数据到能用的过滤数据之间必须要搞清楚的完整流程。这份框架是我自己在做了十几个项目之后打磨出来的,你直接拿去用就行。