标准化与归一化:LogNormalize、SCTransform、NormalizeData
各位好,我是做单细胞分析的。今天咱们聊聊数据预处理里最基础但也最容易被忽视的一环——标准化与归一化。说实话,我见过不少项目,数据扫了一堆,结果分析发现批次效应比生物学差异还大,最后发现是标准化方法选错了。你想想看,CellChat 要依赖的基因表达量背景,如果底层数据就没对齐,那后面的配体-受体推断基本是空中楼阁。
这一章我重点讲三个常用方法:LogNormalize、SCTransform 和 NormalizeData。它们不是并列的,而是不同层面的工具。我会结合个人踩过的坑,给你理清楚什么时候该用哪个,怎么用才能让批次效应模型药到病除。
1. LogNormalize —— 最经典的基准方法
LogNormalize 是 Seurat 和 Scanpy 的默认标准化方式。它的核心计算非常简单:每个细胞的每个基因 UMI 计数,除以该细胞总 UMI 数,再乘以 10000,最后取 log(1 + x)。说白了就是把绝对计数变成相对表达量,让不同细胞在测序深度差异下的可比性提高。
我个人习惯,在数据量不大(细胞少于 5 万)且技术批次差异不极端时,用 LogNormalize 就够了。我在一个 10x 样本合并项目里遇到过,直接用 LogNormalize 后跑 Harmony,批次效应基本被抹平。但要注意:如果样本间总 UMI 差异超过 3 倍,LogNormalize 后仍然会残留明显的批次特征。
适用场景:测序深度较均匀、同一平台、细胞数量中等。
避坑:我曾经只做了 LogNormalize 就送进 CellChat,结果两个批次之间的平均表达量相差很大,最后出来的配体-受体互作结果几乎全是批次伪影。后来我才意识到,在标准化之前就应该先做简单的总 UMI 归一化,或者直接换 SCTransform。
2. SCTransform —— 一次性搞定方差稳定化
SCTransform 是基于负二项回归的标准化方法。它把每个基因的 UMI 计数建模成对总 UMI 和基因长度的依赖,同时估计方差的噪声模型。这样做的好处:它不仅能做标准归一化,还能同时完成方差稳定化和基因选择。对于 CellChat 分析而言,SCTransform 可以减少低表达基因的波动,使得细胞间的信号强度更鲁棒。
我最近一个跨平台项目(10x vs Smart-seq2),用 SCTransform 后不用再做额外的批次校正,直接跑 CellChat 居然也能看出明显的生物学信号。但要注意一点:SCTransform 是“有状态的”——它会在 Seurat 对象中创建一个新的 assay(SCT),里面存储了校正后的残差和归一化值。你需要确保后续 CellChat 创建对象时使用的是正确的 assay。
技巧:如果你打算用 SCTransform,建议先对每个样本单独做 SCTransform,然后再进行整合(Integration)。这样每个样本内部的标准化尺度是一致的,整合时不容易引入新的批次偏差。
3. NormalizeData —— 可切换后背的瑞士军刀
很多新手以为 NormalizeData 是一种具体方法,其实它是 Seurat 里的函数,可以通过 method 参数切换不同的标准化算法。默认是 LogNormalize,但也可以指定 CLR(针对 miRNA 或免疫组库)、RC(相对计数,不做 log)等。
在批次效应场景中,我一般不会直接用 NormalizeData 本身的切换来做批次消除——因为它无论选哪个,都只是全局缩放,无法处理技术差异。但有一个例外:如果批次间细胞组成极度不均衡(比如一个样本全是 T 细胞,另一个全是 B 细胞),CLR 方法可以减轻这种细胞类型比例带来的偏差。
| 方法 | 原理 | 对批次效应的影响 | 推荐场景 |
|---|---|---|---|
| LogNormalize | 计数 / 总 UMI × 1e4 → log1p | 仅消除测序深度差异,不处理技术噪声 | 批次间测序深度相近 |
| SCTransform | 负二项回归 + 方差稳定 | 同时消除技术噪声和基因表达方差,可弱化线性批次差异 | 跨平台、细胞异质性大 |
| NormalizeData (method=CLR) | 每个基因除以几何均值后取对数 | 对细胞类型比例偏差敏感,可用于特定数据 | 免疫组库、超低表达数据 |
4. 三种方法在 CellChat 流程中的实际选择
在实战中,我建议你根据数据来源做一个快速决策树:
- 如果只有单批次数据:直接 LogNormalize 就够,不需要额外处理批次。
- 如果有两个及以上批次,且测序平台相同:先用 SCTransform 分别处理每个样本,再通过 Seurat 的 FindIntegrationAnchors/IntegrateData 合并,最后用合并后的 SCT assay 创建 CellChat 对象。
- 如果平台不同(10x vs Smart-seq2):SCTransform 是首选,因为它可以有效压缩技术差异。但别忘了做完 SCTransform 后还要检查降维图(如 UMAP)中批次是否混得足够好。
- 如果遇到极端低表达基因多:可以尝试 NormalizeData(method="CLR"),然后搭配 Harmony 做批次校正。
核心结论:标准化只是预处理的第一步,它决定了批次效应消除方法(如 Harmony、CCA)的输入质量。如果你去批次之前的数据分布本身就扭曲了,后面再好的算法也无能为力。
5. 示例代码:三种标准化方式在 Seurat 中的调用
下面给出一个微型的 R 代码片段,展示如何用不同方法准备数据。注意,我假设你已经有一个 Seurat 对象 seu,并且 seu$batch 中记录了批次信息。
# 方法一:LogNormalize(Seurat 默认)
seu <- NormalizeData(seu, normalization.method = "LogNormalize",
scale.factor = 10000)
# 方法二:SCTransform(推荐用于多批次前处理)
seu <- SCTransform(seu, vars.to.regress = "percent.mt",
verbose = FALSE)
# 方法三:CLR(可用于免疫组库数据)
seu <- NormalizeData(seu, normalization.method = "CLR",
margin = 2) # margin=2 表示按基因标准化
注意:SCTransform 会替换掉原来的 RNA assay,生成一个新的 "SCT" assay。后续整合批次时请指定 assay="SCT",否则默认用的是未经标准化的 RNA 数据,那会出大问题。
6. 我的压箱底经验
最后分享一个我血泪教训过的案例:有个项目用了 8 个 10x 样本,我图省事直接用 LogNormalize → FindVariableFeatures → RunCCA → Integrate。结果 UMAP 上批次完全没混,调了参数也不行。后来我换成每个样本先做 SCTransform,再跑 Integration,这次批次彻底消失。你看,第一步选对了,后面一切顺利。这个经验让我之后做 CellChat 之前,都会先扫一眼降维图的批次混合程度,如果不好,立马退回标准化这一步重新来过。
好吧,关于标准化与归一化就先聊到这儿。记住:数据喂给 CellChat 之前,先把表达量洗洗干净。下一层,我们就要啃批次效应消除的核心算法了。