numpy 如何用 np.nan_to_num 处理 inf/nan 时的边界行为

np.nan_to_num 默认将 np.nan 替换为 0.0,np.inf 替换为 dtype 对应的 finfo.max(如 float64 下约 1.798e308),-np.inf 替换为 finfo.min。

np.nan_to_num 默认如何处理 inf 和 nan

np.nan_to_num 默认把 np.nan 替换成 0.0,把 np.inf 替换成当前浮点类型的极大值(如 np.finfo(float).max),-np.inf 替换成极小值(np.finfo(float).min)。这不是“截断”或“丢弃”,而是有明确数值映射的填充。

常见误解是它只管 nan,其实 inf 类型默认也参与转换——这点容易被忽略,尤其在做数据清洗时误以为 inf 会保留原样。

  • 默认行为等价于:np.nan_to_num(x, nan=0.0, posinf=None, neginf=None),其中 posinfneginfNone 时才触发极值替换
  • 若显式传入 posinf=1e308,则 np.inf 被替换成 1e308,不再用 finfo.max
  • 若传 posinf=np.nan,那 np.inf 反而变成 nan,后续可能被二次处理

posinf/neginf 设为 None 时的实际边界值是什么

posinfneginfNone(即未指定),np.nan_to_num 会查当前 dtype 的 finfo。对 float64np.finfo(np.float64).max ≈ 1.798e308;对 float32,则是 ≈ 3.403e38。这些值不是 magic number,而是 IEEE 754 规定的可表示最大有限值。

关键点:这个替换发生在“数值域内”,不引发 overflow(因为本身就是上限),但下游计算若再做乘法或指数运算,仍可能立刻溢出为 inf

  • 检查方式:np.finfo(x.dtype).max,别硬记常量
  • 混合 dtype 数组(如含 float32float64)会先 upcast,再取对应 finfo
  • 整数数组不处理 inf(本身不能存 inf),但 nan 会报错,需先转 float

为什么有时 np.nan_to_num(x, nan=0) 后仍有 inf

最常见原因是:你传入的是整数数组,或数组里根本没 inf,但你以为有;或者你用了旧版 NumPy(posinf/neginf 参数尚未支持 None 默认行为(那时默认不处理 inf)。

另一个隐蔽原因:输入含 Python 原生 float('inf'),但数组 dtype 是 object ——此时 np.nan_to_num 不递归处理 object 元素,直接返回原数组。

  • 确认 dtype:x.dtype,非 object 且为浮点型才有效
  • 检查 inf 是否真实存在:np.isinf(x).any(),别靠 print 猜
  • 旧版本兼容写法:np.nan_to_num(x, nan=0.0, posinf=1e308, neginf=-1e308)

替代方案:何时不该用 np.nan_to_num

如果目标只是“去掉异常值用于绘图或统计”,用 np.nan_to_num 可能掩盖问题——把 inf 塞进一个极大但合法的数,mean、std 会被严重扭曲。这时候更稳妥的是显式掩码或截断。

例如,用 np.clip 限定范围,或用布尔索引清空:x[np.isinf(x)] = np.nan 再统一处理;又或者用 scipy.stats.mstats.trimmed_mean 避开极端值。

  • np.nan_to_num 适合“快速兜底 + 进入下游数值计算流程”的场景,比如喂给 sklearn 模型前的预处理
  • 不适合探索性分析——它不报 warning,也不留 trace,静默替换后你可能很久才发现结果异常
  • 若需日志记录哪些位置被替换,得自己封装一层,检查 np.isnan(x) | np.isinf(

    x)
    再操作