数据标准化与批次校正
好,咱们直接进入第三个课题。数据标准化和批次校正,这步要是没做好,后面的模型训练就是跑偏。我刚开始做空间转录组项目时,就吃过这个亏——两个芯片的数据死活整合不到一起,折腾了两周才发现是标准化方法选错了。
说白了,标准化的任务是让不同细胞、不同 spots 之间的表达量具有可比性。而批次校正,是消除不同实验批次带来的技术偏差。这两个概念不一样,但经常一起处理。
为什么需要标准化?
原始 count 数据有几个毛病:
- 文库大小差异——有的 spot 捕获的 RNA 多,有的少,这不代表生物学差异。
- 基因长度偏差——虽然空间转录组的 UMIs 计数比 bulk RNA-seq 好一些,但长基因仍然倾向于产生更多 counts。
- 方差结构复杂——高表达基因方差大,低表达基因方差小,但很多统计模型假设方差齐性。
所以我们需要把数据“拉到一个公平的起跑线上”。
标准化方法,我常用的几种
| 方法 | 适用场景 | 我踩过的坑 |
|---|---|---|
| LogNormalize | 绝大多数空间转录组数据 | 对低表达噪音敏感 |
| SCTransform | 数据高度零膨胀时 | 计算量巨大,谨慎上全量数据 |
| SCANPY 的 pp.normalize_total | 快速批处理、Python 生态 | 参数 target_sum 设不好会扭曲 |
| Raw counts + 方差稳定 | 某些深度学习模型需要 | 不是万能的 |
我的经验:对于 10x Visium 数据,我习惯先用 LogNormalize 看看全局分布。如果发现很多基因表达模式像“二极管”(要么不表达,要么爆发),那就换成 SCTransform。但 SCTransform 跑 10 万 spots 会很慢,记得上 GPU 或者用 mini-batch。
注意:千万不要在标准化之前直接做降维。我见过有人直接用原始 counts 跑 PCA,结果前两个主成分全是文库大小。这就像两个人比身高,一个站在凳子上一个站在地上——没意义。
批次校正,不解决会出大事
批次效应是个很烦的东西。两个相同组织切片,同一套实验方案,但不同时间做的,数据就会有差异。你说它是批次效应还是真实差异?有时候真分不清。
我这里整理了几种主流方法的对比:
| 方法 | 核心思想 | Python 实现 | 我项目中的评价 |
|---|---|---|---|
| Harmony | 在 PCA 空间做软聚类调整 | harmony-pytorch | 快,适合大样本,但参数多 |
| ComBat | 贝叶斯范式,调整均值和方差 | combat.py | 经典方法,对批次少时有效 |
| scVI | 变分自编码器,隐空间对齐 | scvi-tools | 效果好但训练时间长 |
| BBKNN | 基于图的批次校正 | bbknn | 保留局部结构,适合聚类 |
实践建议:我个人在空间转录组项目中,如果样本量小于 5 张切片,建议先试 ComBat。它简单,假设少,而且经常很管用。样本多了以后,Harmony 或 scVI 更合适。
Harmony 怎么用
Harmony 是我的老朋友了。它的思路是先做 PCA 降维,然后迭代优化:找出每个细胞/spot 属于哪个批次,再调整它们在 PCA 空间的位置,让不同批次的分布对齐。说白了,就是“软聚类 + 平移”。
我常用的代码示例:
import harmonypy
import pandas as pd
import numpy as np
# 假设 pca_embedding 是 (n_cells, 30) 的 PCA 结果
# meta_data 包含批次信息列 'batch'
ho = harmonypy.run_harmony(
pca_embedding,
meta_data,
vars_use=['batch'],
max_iter_harmony=10,
do_pca=False # 已经做过 PCA 了
)
corrected_embedding = ho.Z_corr.T
这里有个坑:max_iter_harmony 的默认值是 10,但我遇到过一个样本量特大的数据集,20 次迭代才收敛。你可以在训练过程中监控 loss 变化,如果连续 3 次迭代 loss 没下降,就停了。
ComBat 的实现细节
ComBat 更传统一些。它假设每个基因在不同批次的均值或方差存在偏移,然后用经验贝叶斯去估计这些偏移。说白了,就是把每个基因的分布“校正”到同一水平线上。
看看这段代码:
import pandas as pd
import numpy as np
from combat.pycombat import pycombat
# expression_data: 基因×样本的矩阵
# batch: 样本对应的批次标签
corrected_data = pycombat(expression_data, batch)
# 返回同样形状的校正后矩阵
重要提醒:ComBat 要求输入是完整矩阵。如果你的数据有大量缺失值(空间转录组有些基因在很多 spots 都是零),它可能会报错。我遇到过这种情况:有些基因在某个批次中全部为零,ComBat 直接崩了。解决方法是先过滤掉那些在所有批次中表达太低的基因。
我画的一张图,理清整个流程
下面这张 SVG 图展示了我常用的标准化与批次校正的工作流。从原始数据到最终可用特征,中间会经历好几个关卡。你仔细看每个节点对应的方法和注意事项。
你看这个流程的走向:先标准化,再判断是否需要批次校正,最后得到干净的矩阵。这个顺序不能乱。我试过先校正再标准化,结果算法里大量零值导致内存爆炸。
实战中常踩的坑
- 过度校正——调和过头,真实的生物学差异也被抹掉了。你要保留一些生物学差异!
- 忽略空间信息——普通单细胞的方法照搬用到空间转录组,往往效果差。空间数据有额外的空间自相关性。
- 批次信息不完整——有些实验室只记得记录“batch 1”和“batch 2”,但忘了记录哪个样本用的试剂盒不一样。这会导致批次效应无法完全消除。
我的一次翻车经历:有一次两个批次的数据,我用了默认参数的 Harmony,校正完一看,UMAP 上两叠数据完美重合。我当时还很高兴,以为问题解决了。结果下游差异基因分析,一个 real marker 都找不到。后来才发现,我把真实的组织区域差异也当做批次效应给校正掉了。所以现在我做校正,都会预留 10% 的基因不参与校正,用来做验证。
参数调优的快问快答
- Harmony 的 theta 参数——控制校正力度。越小校正越强。我一般从 1.0 开始,如果批次效应还没消失,降到 0.5。
- ComBat 的 par.prior 参数——是否使用参数化先验。样本量大用 True,否则 False。
- scVI 的 n_latent——不要设太大,10-20 就够。太大容易过拟合批次特征。
# Harmony 调参示例
ho = harmonypy.run_harmony(
pca_embedding,
meta_data,
vars_use=['batch'],
theta=1.0, # 控制力度
lambda_val=1.0, # 岭回归正则化
sigma=0.1, # 软聚类带宽
max_iter_harmony=10,
verbose=False
)
小技巧:任何批次校正做完后,都跑一遍 UMAP,用颜色标记批次。如果不同批次混在一起,但生物学分组(如不同组织区域)也能清晰分开,说明校正效果很好。如果生物学差异也被压平了,赶紧调整参数。