4. 数据预处理:标准化、中心化、对数变换对可视化结果的影响

做批次效应可视化之前,有个绕不开的步骤——数据预处理。这一步做不好,后面PCoA和UMAP画出来的图,很可能只是「漂亮的垃圾」。

我刚开始做生物信息分析那会儿,接过一份别人的数据,直接跑PCoA。结果出来,样本按批次完美分开,我当时还以为是模型结果好。后来才发现,那个分开是因为不同批次测序深度不一样,跟生物学差异没半毛钱关系。嗯,从那以后,我再也不敢跳过预处理步骤了。

本章核心问题:标准化、中心化、对数变换,到底在做什么?它们对PCoA和UMAP的可视化结果,分别有什么影响?

4.1 为什么要预处理?批次效应的根源在数据尺度差异

原始测序数据,说白了就是一堆计数矩阵。不同样本的总读数可能差几倍甚至几十倍。你想想看,如果把这种尺度差异直接扔进距离计算,高表达量的样本天然就会聚在一起,跟生物学分组没关系。

批次效应的本质,恰恰是这种非生物的技术差异。所以预处理的目的很明确:把技术噪声压下去,让真正的生物学信号浮上来。

数据问题技术来源对可视化影响
测序深度差异不同批次、不同文库样本按测序量分层,掩盖真实分组
基因表达量跨度大高表达vs低表达基因差几个数量级距离矩阵被高表达基因主导
方差不均匀RNA-seq计数数据本身有均值-方差关系PCoA/UMAP对异常值敏感

4.2 三种常见预处理方法:到底怎么选?

4.2.1 对数变换(Log Transformation)

这是最常用的一步。为什么?因为测序计数数据是典型的「过离散」分布——方差随均值一起涨。不压一下,高表达基因会把距离矩阵带偏。

我常用的公式是 log2(x + 1) 或者 log10(x + 1)。加 1 是为了避免 log(0) 报错。

import numpy as np

# 假设 expression_matrix 是 基因×样本 的计数矩阵
# 对数变换(log2)
log_expr = np.log2(expression_matrix + 1)

# 不加 1 会怎样?遇到 0 就直接报错,或者产生 -inf
# 这是新手最容易踩的坑
什么时候用 log 变换?处理 RNA-seq、scRNA-seq 这类计数数据时,我几乎必做。但如果是芯片数据(已经是对数信号),就不用重复做了。

对比效果:没做 log 变换之前,PCoA 的第一主成分往往对应总表达量;做了之后,PCoA 才能把生物学差异放到前面。我在项目中遇到过几次,不做 log 变换直接跑 UMAP,结果就是一团乱麻,怎么看都没有分组。

4.2.2 中心化(Centering / Mean Centering)

中心化就是把每个基因的表达量减去它的均值,让表达谱的中心落在零点。这一步对 PCA/PCoA 的影响非常大——因为 PCA 本身就是在找最大方差方向,而中心化能确保方差是从协方差结构来的,而不是从均值水平来的。

# 中心化
centered_expr = log_expr - log_expr.mean(axis=0)  # 按样本中心化
# 或者按基因中心化
centered_expr_gene = log_expr - log_expr.mean(axis=1, keepdims=True)

我个人的习惯是:先做 log 变换,再做中心化。这样既能压制方差,又能对齐数据维度。为什么要按基因中心化?因为不同基因的基线表达量差异太大,不中心化的话,高表达基因在距离计算中权重过高。

注意:中心化不会改变样本间的相对距离,但会改变 PC 的载荷方向。如果只是做 UMAP 这种基于邻居的降维,中心化的影响比 PCA/PCoA 小很多。不过,为了统一流程,我还是建议做一下。

4.2.3 标准化(Scaling / Z-score)

标准化的定义是:(原始值 - 均值) / 标准差。做完之后,每个基因的表达谱变成均值为 0、方差为 1 的标准正态分布。

from scipy.stats import zscore

# Z-score 标准化
scaled_expr = zscore(log_expr, axis=1)  # 按基因标准化

# 注意:zscore 默认 axis=0(按列),这里我们按基因走,所以传 axis=1

标准化最极端的地方在于:它会强制每个基因的方差完全一致。这意味着「所有基因在距离计算中权重相同」。这听起来很公平,但有个副作用——噪声基因(低表达、高变异)会跟高信号基因平起平坐。我一般在做无监督聚类或者 UMAP 之前,会先过滤掉低表达基因,再标准化,不然噪声会被放大。

4.3 这几种方法对可视化到底有什么影响?

我拿一个真实的批量效应模拟数据来说说。假设我们有三个生物学分组,但混了两个批次的测序数据。不同预处理下,PCoA 的差异非常大。

预处理组合PCoA 上的批次效应形态UMAP 上的样本结构
不做任何预处理样本按总表达量分层,批次效应被放大UMAP 可能只有一两个点稠密簇,完全看不出分组
仅 log 变换第一批方差被压制,但均值差异仍在分组开始出现,但批次内样本仍明显分开
log + 中心化样本集中到原点周围,批次效应明显减轻分组清晰,批次之间开始重叠
log + 中心化 + 标准化样本更加均匀分布,所有基因等权分组判别能力可能提升,但噪声基因会「稀释」信号

为什么会这样?说白了就是:log 变换主要解决方差问题,中心化解决偏移问题,标准化解决权重问题。三者联用,能把技术差异压缩到最小,让生物学差异站出来。

4.4 我的实战流程与避坑指南

我现在做一个批次效应可视化的标准流程是:

  1. 过滤低表达基因:比如在至少 10% 的样本中表达量 > 0 的基因才保留。这个没标准答案,我一般看数据的稀疏程度。
  2. log 变换:log2(x+1),必须加 1。
  3. 中心化:按基因减去均值,让所有基因的表达谱对齐到零点。
  4. 可选标准化:如果做 PCA/PCoA 做批次校正(如 ComBat),我不做标准化;如果直接扔给 UMAP 做下游可视化,我会标准化,同时加大过滤力度。
核心经验:预处理要做,但不要做过头。标准化不是万能的,它可能会让原本清晰的分布变得模糊。我在一个宏基因组的 16S 数据中做过对比——标准化之后,原本在 PCoA 上明显分开的两个生态型,反而混在了一起。后来发现,标准化让丰度低的 OTU 跟高丰度 OTU 等权,把关键差异稀释了。

4.5 知识体系:预处理与可视化路径总图

下面这张图总结了从原始数据到 PCoA/UMAP 可视化的完整预处理路径,以及每个环节对结果的直接影响。

数据预处理与可视化路径图 数据预处理与可视化核心路径 原始计数矩阵 过滤低表达基因 / 低丰度特征 log 变换 压制方差,拉平表达量尺度 中心化 去除基线偏移,对齐数据维度 标准化 (Z-score) 等权所有基因,放大噪声需谨慎 联合预处理后的表达矩阵 PCoA (基于距离矩阵) 受中心化、标准化影响大 UMAP (基于邻居结构) 受 log 变换、过滤影响更大 预处理对两种可视化方法的影响权重不同:PCoA 更依赖数据分布假设,UMAP 更依赖局部密度结构。
图中很重要的一点:PCoA 和 UMAP 对预处理的敏感度不同。PCoA 基于欧氏距离或 Bray-Curtis 距离,中心化和标准化的影响非常直接;而 UMAP 虽然也受距离影响,但它的「邻居结构」对尺度变化有一定鲁棒性。我见过一个极端例子——不标准化,PCoA 一塌糊涂,但 UMAP 还能看出分组趋势。

4.6 总结三个关键点

  1. 先过滤,再变换:不先去掉低表达基因,log 变换后噪声会被放大。我自己至少过滤掉在 5%~10% 样本中不表达的基因。
  2. 中心化是 PCoA 的好朋友:如果不做中心化,PCoA 第一主成分往往只反映均值差异。中心化之后,生物学结构才会出现在前几个轴上。
  3. 标准化要慎重:不要无脑 Z-score。如果数据稀疏、基因数多,标准化很可能让 UMAP 结果更分散——看起来「漂亮」,但实际分错了。记得对标标准化前后的聚类效果。
我最惨的一次教训:做了一个 scRNA-seq 的整合分析,跳过过滤直接标准化,然后跑 UMAP 可视化。结果每个细胞亚群都被「拉伸」得很开,我兴奋地以为是发现了新亚型。后来一查,那些分叉全是噪声基因贡献的。自那以后,我就把这一步写死在流程脚本里了。

好了,关于预处理的三种方法和对可视化路径的影响,就聊到这里。下一步,我们会把这些预处理后的数据,真正放进 PCoA 和 UMAP 里,看看效果如何。不过那就是另一章的事了。