标准化与归一化:数据预处理的关键一步
好,我们直接进入正题。单细胞数据拿到手,第一步不是激动地跑聚类,而是先想清楚一个问题:你拿到的这些UMI计数,到底能不能直接用来比较?
答案显然是不能。原因很简单——每个细胞的捕获效率、测序深度都不一样。有的细胞测了5万条reads,有的只测了1万条,你如果直接拿原始计数去比较基因表达,那高测序深度的细胞天然就占优势,这显然不对。
所以,我们需要做标准化和归一化。这两个词经常被混用,但我个人习惯把它们拆开看:标准化是消除技术偏差,让细胞之间可比;归一化是让数据分布更符合统计模型的假设。
为什么不能直接比较原始UMI计数?
你想想看,一个细胞捕获了5000个UMI,另一个捕获了20000个,差异可能根本不是生物学上的,纯粹是技术噪音。我曾经遇到过一个项目,某批样本的测序深度明显偏高,如果不做标准化,这批细胞直接聚类成一个单独的群——嗯,典型的批次效应。
具体来说,原始计数存在三个主要问题:
- 细胞间测序深度差异:每个细胞捕获的mRNA总量不同
- 基因长度偏差:但这点在UMI计数里基本忽略,因为UMI是分子计数,不受基因长度影响
- 过离散问题:单细胞数据比传统bulk RNA-seq更“散”,方差远大于均值
核心要点:标准化不是为了消除生物学差异,而是消除技术差异。你不想因为测得多就说某个基因表达高,对吧?
LogNormalize:最经典也是最常用的方法
LogNormalize是Seurat默认的方法。原理不复杂,两步走:
- 每个基因的UMI计数除以该细胞的总UMI计数,再乘以一个缩放因子(默认10000)
- 取log1p(也就是ln(count+1))
代码实现长得像这样:
# Seurat内部实现逻辑(不是让你直接跑的代码)
norm_counts <- log1p(t(t(counts) / colSums(counts)) * 10000)
为什么是10000?说白了,这是个老习惯。早期单细胞测序每个细胞平均深度也就几千到一万,乘以10000相当于把每个细胞都“缩放”到一万的总分子数。现在超高通量数据动辄几万,但习惯了,大家也就沿用下来。
我早期做scRNA-seq时,用LogNormalize处理过一批免疫细胞数据。效果还行,但也踩过坑——如果你数据里有非常强的表达基因(比如T细胞的CD3),这个基因会过度拉低其他基因的相对表达。所以,LogNormalize适合大多数情况,但不是万能的。
使用建议:
- 数据质量一般、批次效应不明显时,LogNormalize够用
- 运行速度快,内存占用低
- 对高表达基因敏感,可能引入假象
SCTransform:新一代正规化技术
SCTransform是2019年出的方法,说白了就是拿广义线性模型(GLM)来拟合UMI计数。它不再用一个全局的缩放因子,而是为每个基因学出一个合适的模型参数。
它的核心思路是:
UMI计数 ~ 正则化负二项分布,并且考虑细胞测序深度作为协变量。
# 用Seurat跑SCTransform
pbmc <- SCTransform(pbmc, vars.to.regress = "percent.mt", verbose = FALSE)
你看,一行代码而已。但背后做了很多事:
- 为每个基因拟合一个负二项GLM
- 把细胞测序深度的影响回归掉
- 返回Pearson残差作为标准化后的表达值
我曾经拿SCTransform处理一批包含多种细胞类型的PBMC数据,效果确实好:高表达基因不再“压制”低表达基因,稀有细胞群(比如pDC)也能被明显识别出来。但代价是速度慢——处理一万个细胞,LogNormalize可能十几秒就完了,SCTransform要几分钟甚至更久。
注意:SCTransform不是免费的午餐。如果你有严重的批次效应,或者数据极度稀疏(比如双细胞太多),它可能会过度回归掉真实的生物学信号。我见过一个案例,SCTransform把某种细胞类型特有的基因差异也归为“技术噪音”给去掉了……嗯,这是个真实陷阱。
两种方法的对比
好,直接上表格看看它们到底差在哪:
| 对比维度 | LogNormalize | SCTransform |
|---|---|---|
| 基本原理 | 缩放 + log变换 | 正则化负二项GLM残差 |
| 是否考虑基因变异性 | 否,全局缩放 | 是,每个基因独立建模 |
| 对高表达基因的处理 | 易被支配 | 更稳健 |
| 速度 | 快 | 慢(10x用时差异约5~20倍) |
| 适用场景 | 快速探索&常规分析 | 需要去噪、稀有细胞检测 |
你可能会问:那到底用哪个?我个人看法是:先用LogNormalize跑一趟,看看数据整体的分离度。如果发现高表达基因主导了聚类,或者你怀疑有隐藏的稀有细胞群,再换成SCTransform。
归一化 vs 标准化:别搞混
很多人会问:Normalization和Standardization有什么区别?
我简单解释一下:
- Normalization(归一化):让数据在细胞间可比,比如LogNormalize就是“归一化”,因为它统一了细胞总表达量
- Standardization(标准化):让数据在基因间可比,通常涉及减去均值除以标准差,比如scale data
完整的流程其实是:原始计数 → Normalization → Standardization。你想想看,如果只做Normalization不做Standardization,基因之间的表达量尺度可能差很多(比如一个高表达基因和低表达基因差100倍),PCA或聚类时高表达基因会主导结果。
所以在Seurat里,做完LogNormalize或SCTransform之后,还要跑一句ScaleData:
# 标准化基因表达值
pbmc <- ScaleData(pbmc, features = rownames(pbmc))
这个步骤做的是:每个基因在所有细胞上的表达值减去均值并除以标准差,让每个基因的表达平均为0,方差为1。
一张图理清决策流程
选择建议:别盲目,看数据说话
最后,我总结几条实战建议:
- 数据量级:如果只有几千个细胞,SCTransform随便跑;如果有几十万,建议先用LogNormalize做质控
- 细胞类型多样:有大量稀有细胞群?SCTransform能更好地保留这些信号
- 批次效应问题:严重的批次效应下,SCTransform的回归可能“过头”,反而需要先用Harmony或CCA整合后再决定
- 计算资源:学生或医院工作站,内存32G以下,我建议优先LogNormalize
我曾经踩过的一个坑:有一次跑10X数据,用了SCTransform,结果发现某个高表达基因(比如线粒体基因)的残差分布很奇怪。后来发现是因为原始数据中有大量双细胞,SCTransform把双细胞的双倍表达当成了“真实信号”。所以,做SCTransform之前,一定要先过滤双细胞!
好,标准化和归一化就讲到这里。记住一个简单原则:先做对,再做快。 数据质量不过关,再花哨的方法也是白搭。下次你处理单细胞数据时,不妨先问问自己:我的数据需要更强的去噪吗?如果答案是肯定的,试试SCTransform——但别盲目下单。