标准化与归一化:数据预处理的关键一步

好,我们直接进入正题。单细胞数据拿到手,第一步不是激动地跑聚类,而是先想清楚一个问题:你拿到的这些UMI计数,到底能不能直接用来比较?

答案显然是不能。原因很简单——每个细胞的捕获效率、测序深度都不一样。有的细胞测了5万条reads,有的只测了1万条,你如果直接拿原始计数去比较基因表达,那高测序深度的细胞天然就占优势,这显然不对。

所以,我们需要做标准化和归一化。这两个词经常被混用,但我个人习惯把它们拆开看:标准化是消除技术偏差,让细胞之间可比;归一化是让数据分布更符合统计模型的假设。

为什么不能直接比较原始UMI计数?

你想想看,一个细胞捕获了5000个UMI,另一个捕获了20000个,差异可能根本不是生物学上的,纯粹是技术噪音。我曾经遇到过一个项目,某批样本的测序深度明显偏高,如果不做标准化,这批细胞直接聚类成一个单独的群——嗯,典型的批次效应。

具体来说,原始计数存在三个主要问题:

  • 细胞间测序深度差异:每个细胞捕获的mRNA总量不同
  • 基因长度偏差:但这点在UMI计数里基本忽略,因为UMI是分子计数,不受基因长度影响
  • 过离散问题:单细胞数据比传统bulk RNA-seq更“散”,方差远大于均值

核心要点:标准化不是为了消除生物学差异,而是消除技术差异。你不想因为测得多就说某个基因表达高,对吧?

LogNormalize:最经典也是最常用的方法

LogNormalize是Seurat默认的方法。原理不复杂,两步走:

  1. 每个基因的UMI计数除以该细胞的总UMI计数,再乘以一个缩放因子(默认10000)
  2. 取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。

一张图理清决策流程

标准化 & 归一化方法选择流程 原始UMI计数矩阵 数据是否含强批次/高表达基因? 否 是 LogNormalize 缩放因子10000 + log1p SCTransform 负二项GLM + Pearson残差 结果均为归一化后的表达矩阵 ScaleData (Z-score标准化) PCA、聚类、Marker基因鉴定 💡 经验提示 • 先LogNormalize快速看整体分布,若发现高表达基因主导或稀有细胞被掩盖,再换SCTransform • 无论哪个方法,Normalization之后一定不要跳过ScaleData!

选择建议:别盲目,看数据说话

最后,我总结几条实战建议:

  • 数据量级:如果只有几千个细胞,SCTransform随便跑;如果有几十万,建议先用LogNormalize做质控
  • 细胞类型多样:有大量稀有细胞群?SCTransform能更好地保留这些信号
  • 批次效应问题:严重的批次效应下,SCTransform的回归可能“过头”,反而需要先用Harmony或CCA整合后再决定
  • 计算资源:学生或医院工作站,内存32G以下,我建议优先LogNormalize

我曾经踩过的一个坑:有一次跑10X数据,用了SCTransform,结果发现某个高表达基因(比如线粒体基因)的残差分布很奇怪。后来发现是因为原始数据中有大量双细胞,SCTransform把双细胞的双倍表达当成了“真实信号”。所以,做SCTransform之前,一定要先过滤双细胞!

好,标准化和归一化就讲到这里。记住一个简单原则:先做对,再做快。 数据质量不过关,再花哨的方法也是白搭。下次你处理单细胞数据时,不妨先问问自己:我的数据需要更强的去噪吗?如果答案是肯定的,试试SCTransform——但别盲目下单。