跳转到内容

缺失值、统计与数值稳定性

计算机基础 · NumPy

前置知识:广播、axis 与向量化

本章目录

本章学习目标

  • 区分缺失、无穷和有效零
  • 明确均值、标准差和分母口径
  • 避免除零、溢出与未来信息泄漏

一、缺失值不是任意可以填掉的空白

NaN 表示缺失或无效浮点结果,常见来源包括原始缺失、零除零等。NaN 与自身也不相等,因此 x==np.nan 不能正确检测缺失,应用 np.isnan;排除无穷时用 np.isfinite。零收益、零成交量可能是有效观测,不应因为看起来“空”就过滤。缺失的原因也影响处理:停牌、抓取失败和新用户尚无行为,不能在统计意义上全部视作同一种情况。

以 [1,NaN,3] 为例,普通 mean 得到 NaN,nanmean 忽略 NaN 得到二,分母是两个有效值而不是三个。这个自动忽略规则必须在指标说明中出现,否则读者可能以为缺失已填零。全缺失组没有有效分母,nanmean 会产生警告并返回 NaN;业务上应标记无观测,而不是用零均值替代。nanmean 也不会自动忽略正负无穷。

二、统计量必须带着口径使用

均值描述总量除以有效数量,中位数描述排序后中间位置,二者对极端值反应不同;例如 [1,2,100] 的均值约三十四点三,中位数是二。截去异常值会改变分析对象,先确认错误还是合法大额订单,不能因为结果“不好看”就删掉尾部。np.quantile 的插值或方法会影响小样本分位数,应记录使用方法。

标准差 std 默认 ddof=0,分母是 n;ddof=1 的样本口径分母是 n-1。对于 [1,3],均值二,总体标准差一,样本标准差为根号二。只有一个有效观测时样本标准差没有足够自由度,必须说明边界。不同口径的结果不能直接比较成优劣。在收益统计中还需要说明收益是否对数、周期是否一致、年化因子来自多少有效交易日。

三、安全除法需要设计输出

计算比例时 np.where(den!=0,num/den,np.nan) 看似保护了除零,但 num/den 通常先完整求值,仍可能产生警告。可先建立填 NaN 的输出,再使用 np.divide(num,den,out=result,where=den!=0) 只在允许位置计算。这个 where 是计算掩码,不是自动缺失规则;还需要检查分子分母有限性以及负分母是否业务允许。

例如分子 [2,0,1]、分母 [4,0,0],结果约定为 [0.5,NaN,NaN]。零除零与正数除零都没有合法业务比例,不能默认返回零或无穷。若比例是转化率,还应检查分子不大于分母。掩盖警告可以用 errstate,但它只改变告警,不改变结果是否合理;正确做法是先定义有效条件,再建立输出并统计无效数量。

四、稳定变换与浮点比较

较大数值上直接 exp 可能溢出,softmax 应先按行减去最大值;减同一常数不改变最终比例,却把最大指数控制为一。对于 [[1000,1001]],直接 exp 很危险,减最大值后计算 exp([-1,0]),再归一化即可得到约零点二六九与零点七三一。模型训练会使用更成熟的损失实现,但理解这个变换有助于诊断 NaN。

接近零的小量计算 log(1+x) 时使用 log1p,exp(x)-1 时使用 expm1,减少相消误差。比较浮点数组用 allclose,但相对和绝对容差必须结合单位决定;金额以元与以万元表示时默认阈值的业务意义不同。不要通过 round 把所有中间值提前截断,那可能累计误差;通常保留计算精度,仅在最终展示时格式化。

五、用不变量审查统计流水线

配套代码验证有效均值、两种标准差、安全比例和稳定 softmax,并检查 softmax 每行总和为一、全部值有限。新增空数组或全缺失组时,程序应给出明确缺失结果;统计过程可同时返回有效数量,让下游知道估计可靠性。只报告均值而不报告样本数,会掩盖一组千条、一组一条的巨大差异。

特征标准化、缺失填补和异常阈值通常都从训练集拟合,不能先对全部数据估计再切分;时间序列还要确保每个时点只使用过去可得信息。本章不要求所有统计方法,但要求能说清数据纳入规则、分母、自由度、非有限值策略与数值稳定性。出现 NaN 时先定位第一处产生的位置,不要在整个流程末尾无条件 nan_to_num,让真正的问题消失。

交互演示:观察机制

完整示例与实际输出

import numpy as np
x=np.array([1.,np.nan,3.])
assert np.isnan(x).tolist()==[False,True,False]
assert np.nanmean(x)==2
print('mean/valid count',float(np.nanmean(x)),int(np.isfinite(x).sum()))
sample=np.array([1.,3.])
assert np.isclose(sample.std(ddof=0),1)
assert np.isclose(sample.std(ddof=1),np.sqrt(2))
print('std population/sample',sample.std(ddof=0),sample.std(ddof=1))
num=np.array([2.,0.,1.]); den=np.array([4.,0.,0.])
ratio=np.full(num.shape,np.nan)
np.divide(num,den,out=ratio,where=np.isfinite(num)&np.isfinite(den)&(den!=0))
assert ratio[0]==.5 and np.isnan(ratio[1:]).all()
print('safe ratio',ratio.tolist())
logits=np.array([[1000.,1001.],[0.,0.]])
shifted=logits-logits.max(axis=1,keepdims=True)
ex=np.exp(shifted); probs=ex/ex.sum(axis=1,keepdims=True)
assert np.isfinite(probs).all() and np.allclose(probs.sum(axis=1),1)
print('stable softmax',np.round(probs,6).tolist())

实际运行通过CPU Python execution.

mean/valid count 2.0 2
std population/sample 1.0 1.4142135623730951
safe ratio [0.5, nan, nan]
stable softmax [[0.268941, 0.731059], [0.5, 0.5]]

五道自测:先回答,再核对

01 · x==np.nan 为什么检测不了 NaN?

查看提示

NaN 与自身比较。

查看参考答案

NaN 不等于自身,使用 np.isnan。

02 · [1,NaN,3] 的 nanmean 分母是多少?

查看提示

只纳入有效值。

查看参考答案

两个,结果为二。

03 · [1,3] 的 ddof=0 与 ddof=1 标准差?

查看提示

分母分别为二和一。

查看参考答案

分别为一和根号二。

04 · np.where 能保证 num/den 不先求值吗?

查看提示

函数参数先求值。

查看参考答案

不能;安全除法可用 np.divide 的 out 和 where。

05 · softmax 为什么先减去每行最大值?

查看提示

指数溢出与比例不变。

查看参考答案

控制指数范围,同时不改变归一化概率。

两项动手任务

任务 1 · 统计 [[1,NaN],[3,NaN]] 每列有效个数和均值,全缺失列保留 NaN 且避免警告。

提示

sum 与 count 后做带 where 的除法。

完整参考解答与检查方式

第一列两项均值二,第二列零项均值缺失。

import numpy as np
X=np.array([[1.,np.nan],[3.,np.nan]])
valid=np.isfinite(X); counts=valid.sum(axis=0)
sums=np.where(valid,X,0).sum(axis=0)
means=np.full(X.shape[1],np.nan)
np.divide(sums,counts,out=means,where=counts>0)
assert counts.tolist()==[2,0] and means[0]==2 and np.isnan(means[1])
print(counts.tolist(),means.tolist())

任务 2 · 用稳定 softmax 处理 [10000,10001,10002],验证概率总和并说明是否需四舍五入后计算。

提示

先减最大值。

完整参考解答与检查方式

不提前舍入;稳定指数归一化,显示时再控制小数位。

import numpy as np
x=np.array([10000.,10001.,10002.]); e=np.exp(x-x.max()); p=e/e.sum()
assert np.isfinite(p).all() and np.isclose(p.sum(),1)
assert p[2]>p[1]>p[0]
print(np.round(p,6).tolist())

官方资料与掌握状态

完成两项任务后,按独立完成程度记录。状态会尽量保存在当前浏览器,可导出备份。

动手任务记录

掌握状态与复习

关掉资料独立完成后记 A;提示后完成记 B;需要重学记 C。

记录尽量保存在当前浏览器,建议定期导出备份。