Z-score标准化需先筛选数值列并校验每列唯一值>1且非全空,否则设0;MinMaxScaler比手写公式更可靠,避免除零和数据泄露;比例归一化应使用df.div(df.sum(axis=0), axis=0)并验证每列和≈1。

直接用 df.sub().div() 做 Z-score 标准化,但必须先过滤和校验
看似 df.sub(df.mean()).div(df.std()) 一行能搞定,实际在真实数据上极易产出全 nan 或 inf 列。根本原因是:df.std() 对只含一个非空值(或全相同)的列返回 0,除零后整列崩;对全 NaN 列返回 NaN,导致整列变 NaN。
- 务必先用
df.select_dtypes(include='number')锁定数值列,避免对datetime、category、object列误操作 - 对每列显式检查:只有
x.nunique() > 1且not x.isna().all()才执行标准化,否则设为 0 或跳过 - 用
ddof=0避免样本标准差校正(df.std(ddof=0)),防止常量列因校正后 std 更小而放大误差
MinMaxScaler 比手写 (df - df.min()) / (df.max() - df.min()) 可靠得多
手写公式在生产环境几乎必报 ZeroDivisionError: float division by zero——只要某列 max == min(常量列),分母就为 0。更隐蔽的是:训练集的 min/max 若被异常值带偏,测试数据超出该范围时,归一化结果会溢出 [0, 1],破坏模型输入假设。
-
MinMaxScaler内置保护:常量列默认输出feature_range[0](通常是 0),不崩溃 - 必须对训练集调用
fit_transform(),对测试集只调用transform();绝不能对测试集单独fit_transform(),否则数据泄露 - 若需反归一化,必须保存 scaler 实例(它存了
min_和scale_),别依赖原始df.min()——后者无法还原训练时的状态
用 df.div(df.sum(axis=0), axis=0) 做比例归一化,别用 apply
想让每列加起来等于 1,本质是每列除以自身列和。错误写法如 df.apply(lambda x: x / df.sum().sum()) 是把所有数除以总和,列间耦合,结果全错。
- 正确且高效写法是
df.div(df.sum(axis=0), axis=0):axis=0表示按列广播,自动对齐索引,向量化快,无隐式类型转换风险 -
apply在纯比例归一化中是冗余的:它底层是 Python 循环,慢;且若列混有字符串,apply可能静默跳过,埋下隐患 - 遇到某列全 0 或全
NaN时,x.sum()返回 0 → 除零。应提前用df.replace(0, np.nan).fillna(0)清理,或用np.where(df.sum(axis=0) != 0, ..., 0)包裹
验证归一化结果是否真“生效”,不能只看有没有报错
浮点误差会让 df.sum() 看似不是严格 1(比如 0.9999999999999999),但这不是重点;真正容易忽略的是逻辑错配或类型污染。
立即学习“Python免费学习笔记(深入)”;
- 用
np.allclose(df.sum(), 1, atol=1e-10)验证比例归一化,比== 1可靠 - 若用了
axis=1错配,会出现每行和为 1(而非每列),结果完全不对,但也不报错——这点最易被忽略 - 归一化前漏掉非数值列?用
df.select_dtypes(include=np.number)过滤后再操作,否则字符串列会触发TypeError或静默失败
归一化本身不难,难的是边界条件处理——常量列、全空列、类型混杂、训练/测试不一致,这些地方一旦漏掉,模型上线后可能悄无声息地失效。


















