正态分布分位数归一化是将数据经验分位数严格对齐标准正态分布理论分位数的过程:先排序、用blom公式计算稳健概率、经norm.ppf映射、再按原秩还原,输出近似n(0,1)且保留原始秩结构的数组。

什么是正态分布分位数归一化(Quantile Normalization to Normal)
它不是把数据变成均匀分布再映射到正态,而是让原始数据的经验分位数严格匹配标准正态分布 scipy.stats.norm.ppf 的理论分位数——结果是输出服从 N(0,1) 的近似连续分布,且保留原始数据的秩结构。关键在于:必须用样本分位数对齐理论分位数,不能直接调用 scipy.stats.rankdata 后线性缩放。
核心步骤:排序 → 插值 → 映射
假设你有一维数组 x(长度为 n),目标是得到 z,使得 z 的经验 CDF 尽可能贴近标准正态 CDF。
- 先对
x升序排序,得x_sorted - 计算对应的经验概率(避免端点问题,用
(i + 0.5) / n而非i/n) - 用
scipy.stats.norm.ppf将这些概率转为标准正态分位数值,得z_sorted - 最后按原
x的排序索引,把z_sorted“填回去” —— 这步必须用np.argsort配合高级索引,不能用argsort().argsort()(会出错)
示例代码:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
import numpy as np from scipy import stats <p>def norm_quantile_normalize(x): x = np.asarray(x) n = len(x) if n == 0: return x.copy()</p><h1>获取原始索引顺序(用于还原)</h1><pre class="brush:python;toolbar:false;">orig_idx = np.argsort(x) x_sorted = x[orig_idx] # 经验概率:Blom公式更稳健,用 (i+0.25)/(n+0.5) p = (np.arange(n) + 0.25) / (n + 0.5) z_sorted = stats.norm.ppf(p) # 还原:z[i] = z_sorted[ 在排序后数组中排第几 ] z = np.empty_like(z_sorted) z[orig_idx] = z_sorted return z
为什么不用 sklearn.preprocessing.QuantileTransformer
它默认输出均匀分布,设 output_distribution='normal' 确实能做类似事,但有三个硬伤:
- 内部使用样条插值拟合分位数函数,不是精确的
norm.ppf,小样本下偏差明显 - 对重复值(ties)默认用「平均秩」处理,会人为制造平局,破坏原始数据的微小差异
- 训练/变换分离,若只处理单个数组,需额外调用
fit_transform,而纯 NumPy 方案一行z = norm_quantile_normalize(x)更轻量
NaN 和重复值怎么处理
原始 np.argsort 对 NaN 默认排在末尾(kind='quicksort'),但 stats.norm.ppf 不接受 NaN 或超范围概率(如 0 或 1)。所以必须显式剥离:
- 用
np.isnan(x)提前标记nan_mask - 只对非
NaN子集做排序和映射 - 最终结果中,
NaN位置仍填np.nan,不参与分位对齐 - 重复值无需特殊处理 ——
argsort稳定,相同值按出现顺序排序,ppf输入概率不同(因i不同),输出自然略有差异,符合秩归一化本意
这点容易被忽略:很多人直接对含 NaN 的数组调用 argsort,后续 ppf 报 ValueError: quantiles must be in [0, 1],其实错在没过滤。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










