2. 什么是 Rank Normalization?基本概念归纳总结

我记得刚踏入生物信息学这行时,最头疼的问题之一就是 批次效应。明明是同一种细胞,A 实验室测出来的表达量跟 B 实验室的完全对不上。你想想看,这数据还能用吗?

后来我接触到一个非常巧妙的方法——Rank Normalization。说白了,它就是把数据的「数值大小」变成了「排名位置」。举个最直接的例子:两名学生的考试成绩分别是 85 分和 90 分,转换成排名就是第 2 名和第 1 名。Rank Normalization 就是把这个逻辑应用到整个数据矩阵上。

2.1 排序这件事

我们聊一下最基础的概念:排序。在生物学数据中,每个样本有成千上万个特征(比如基因表达量、蛋白质丰度、代谢物浓度)。这些特征值有高有低。Rank Normalization 的第一步,就是把这些数值按照大小排个序。

  • 升序排列:最小值排第 1,最大值排最后。
  • 降序排列:最大值排第 1,最小值排最后。
  • 秩次分配:相同数值取平均排名(这个细节后面会讲)。
我个人的习惯:大多数情况下用升序。因为在质控中,我们通常关注表达量的整体分布,升序排序更直观。

2.2 什么叫 Rank Normalization?

Rank Normalization 的数学定义其实很简单。对于一个包含 n 个观测值的向量 x = [x₁, x₂, ..., xₙ],我们把它转换成秩向量 R = [R₁, R₂, ..., Rₙ]。

变换公式如下:

Rᵢ = rank(xᵢ)

其中 rank(xᵢ) 表示 xᵢ 在所有观测值中的顺序位置。

举个例子:

原始数值: [0.2, 1.5, 3.8, 2.1, 0.9]
排序后:    [0.2(1), 0.9(2), 1.5(3), 2.1(4), 3.8(5)]
秩次向量: [1, 3, 5, 4, 2]

看到没?原始数值的具体大小消失了,只保留了它们之间的相对顺序。

2.3 Rank Normalization 的算法步骤

实际做起来,步骤也就三步。

  1. 排序:对每个样本(或每个特征)的特征值进行排序,得到排序索引。
  2. 分配秩次:将排序索引映射为秩次。如果有相等值,取中位秩(比如两个第1名,都取1.5)。
  3. 归一化(可选):将秩次缩放到一个固定范围(比如 [0, 1] 或 [-3, 3])。
关键点:第 2 步中如何处理相等值(tie),直接影响后续分析。我曾在项目中遇到过因为忽略 tie 处理,导致整体分布偏移的情况。当时花了一周才找到原因——就是排名时没做平均。

2.4 深入一点:分位数标准化

你可能听说过另一个概念:Quantile Normalization(分位数标准化)。跟 Rank Normalization 有什么区别?

特性 Rank Normalization Quantile Normalization
核心操作 只排序,不改变分布形状 强制所有样本相同分布
输出类型 秩次(整数或小数) 映射后的标准化值(连续)
适用范围 非参数检验、批次效应校正 微阵列数据、RNA-seq 中低表达基因处理
对离群值影响 不敏感(只看位置) 可能会拉偏整体分布
计算复杂度 O(n log n) O(n log n + n*k)
小心一点:不要混淆了 Rank Normalization 和 Quantile Normalization。前者得到的是秩次,后者得到的是标准化后的表达量。我之前见过有人用错了,把秩次当成了标准化后的数值做下游差异分析,结果全错了。

2.5 核心知识体系

下面的图展示了 Rank Normalization 在多批次质控中的位置。

Rank Normalization 核心知识体系 原始多批次数据 Rank Normalization 处理流程 步骤1: 每个样本排序 步骤2: 分配秩次 步骤3: 归一化(可选) 标准化的秩次矩阵 下游应用 批次效应校正 非参数差异分析 分布一致性检验

2.6 为什么 Rank Normalization 能消除批次效应?

这个问题很关键。批次效应本质上是系统性的偏移——A 批次的整体表达量比 B 批次高 20%。你想想看,如果我们只看数值大小,那 A 批次的每个基因都比 B 批次高,这会造成假阳性。

但 Rank Normalization 只关心基因在各自样本内的相对排序。A 批次中表达量最高的基因,在 A 批次的排序一定是第 1 位。B 批次中表达量最高的基因,在 B 批次的排序也一定是第 1 位。这样一来,批次间的系统偏移就被消除了,只保留了生物上的相对差异。

我建议:在做多批次质控时,先对每个批次内部做 Rank Normalization,然后再合并批次。这样能最大程度保留生物学变异,同时去除技术噪声。

2.7 避坑指南

  • 秩次平局陷阱:我曾经在分析蛋白质组学数据时,发现很多肽段的丰度值是 0。排序后,所有值为 0 的肽段都变成了平局。正确的做法是取平均秩次,比如两个 0 值,都取 1.5。不这么做的话,下游检验的统计量会失真。
  • 样本量太小不行:如果某个批次只有 3 个样本,那排序后的秩次就 1, 2, 3 三个值,信息量严重丢失。一般来说,每个批次至少要有 10 个样本才能有效使用 Rank Normalization。
  • 不要用在差异表达基因筛选后:Rank Normalization 应该在 原始表达量矩阵上使用,而不是筛选后的基因集上。否则会破坏整体分布结构。
一个小技巧:处理甲基化数据时,Beta 值天然在 [0,1] 范围,直接做 Rank Normalization 效果很好。但如果是 RNA-seq 的 counts 数据,建议先做 log2 或 TMM 归一化,再转秩次——这样能减弱低表达基因的干扰。

2.8 实战示例(Python)

最后,我顺手给一个 Python 的实现,方便你理解:

import numpy as np
from scipy.stats import rankdata

def rank_normalization(matrix, axis=1):
    """
    对矩阵进行 Rank Normalization
    matrix: numpy array, shape (n_samples, n_features)
    axis: 0 表示对每个特征排序,1 表示对每个样本排序
    """
    ranks = np.apply_along_axis(rankdata, axis=axis, arr=matrix)
    # 可选:缩放到 [0, 1]
    n = matrix.shape[axis]
    ranks_normalized = (ranks - 1) / (n - 1)
    return ranks_normalized

# 示例数据(3个样本,5个基因)
data = np.array([
    [0.2, 1.5, 3.8, 2.1, 0.9],
    [0.5, 1.2, 4.0, 2.5, 0.8],
    [0.3, 1.8, 3.5, 2.0, 1.0]
])

result = rank_normalization(data, axis=1)
print(result)
# 输出类似:
# [[0.   0.75 1.   0.5  0.25]
#  [0.   0.5  1.   0.75 0.25]
#  [0.   0.75 0.5  0.25 1.  ]]

注意看,每个样本(每行)的最小值都变成了 0,最大值变成了 1。这就是 Rank Normalization 的魔力——把不同样本的数值映射到同一个尺度上。