NumPy nanquantile 调换行顺序后,结果为什么变成 float32
float32 数据的第一个切片全是 NaN 时,nanquantile 可能按该分支分配输出并舍入其他切片。计算前明确使用 float64,保留 NaN 与轴语义,不在结果上事后补精度。
同一份 float32 数据,只把行顺序倒过来,np.nanquantile() 的输出却从 float64 变成 float32;除了 NaN 所在行,其他有效行的结果也随之舍入。这种差异可能没有异常提示,不能只用“程序没报错”判断计算正常。
这条路径在 NumPy 2.4.6、2.5.3 中有明确记录,2.5.4 的相关正式实现仍保留相同分支。关键条件是沿指定轴处理多个切片、q 是数组,以及第一个参与处理的一维切片全是 NaN。
第一个切片影响了输出分配
普通切片会进入分位数计算;全 NaN 切片则走另一条分支,用输入切片的 dtype 创建一个全 NaN 结果。外层使用 apply_along_axis() 组合各切片结果时,会按第一个结果决定输出数组的类型。
若第一个切片恰好全 NaN,输出可能被分配成 float32。后续有效切片即使算出 float64 结果,写进这个输出时也会被转换。改变切片顺序,便可能改变整份结果的 dtype 与舍入。
下面仅用于说明触发条件,预期比较的是两次调用的 dtype,而不是把交换顺序当成修复:
import numpy as np
a = np.array([[1.0, 2.0], [np.nan, np.nan]], dtype=np.float32)
q = np.array([0.3])
forward = np.nanquantile(a, q, axis=1)
reversed_rows = np.nanquantile(a[::-1], q, axis=1)
在上述受影响路径中,forward 可以是 float64,而 reversed_rows 成为 float32。这里“第一个”由实际 axis 和切片遍历决定,不一定就是业务界面显示的第一行。多维影像或批次数据需要先对照维度含义。
计算前明确采用 float64
若统计流程要求双精度输出,可以在调用前把工作输入转换为 float64,使全 NaN 分支与正常切片都建立在相同的输入精度上:
work = np.asarray(a, dtype=np.float64)
result = np.nanquantile(work, q, axis=1)
work 是用于这次计算的工作数组,原来的 a 仍可保留。大型数据需要评估额外存储和已有分块策略,不能不看内存预算就把整个数据集复制一遍。
在已经得到 float32 结果后,再调用 result.astype(np.float64),只能换成较宽的容器,不能恢复之前已经舍掉的低位。需要精度时,转换要发生在计算之前。
也不要因为普通 np.quantile() 返回了期望 dtype 就直接替换。它与 nanquantile() 对 NaN 的处理含义不同。把 NaN 填成零、删除全 NaN 行或重排数据,同样可能改变统计对象、坐标对应或缺失值含义。
把顺序不变性写进验收
保存原数据类型、axis、q 的形状与类型、method 和缺失值分布。在隔离数据中分别安排全 NaN 切片位于首个位置与其他位置,计算后把顺序还原,再比较对应有效切片的结果与 dtype。允许的数值误差应来自项目的统计要求,不能只依赖打印出来的小数位。
全 NaN 切片按约定得到 NaN,以及可能出现的相关警告,是另一项需要明确的业务行为。不要为了让验收安静而全局关闭警告;单独记录缺失切片,并确认坐标、掩码与输出轴的位置没有变化。
截至 2026 年 10 月 22 日,不能仅凭问题记录被关闭,就认定某个正式发行版已经修复。后续升级仍需核对具体实现,并用同一组顺序对照样本复查。
回退工作数组的精度调整前,保留原计算参数与结果记录。已经进入后续分析或落库的低精度结果,需要按任务标识确定重算范围,不能只恢复代码就把旧结果视为重新计算过。


