DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
晚安云;香港 CN2 ¥15/月
雨云;给项目一台云服务器

pandas reindex 含 NaN 的 object 索引后填错行

数值 Series 对含 NaN 的 object 目标索引执行 ffill 时,后续标签可能取到错误位置;None 还可能触发比较异常。先分离缺失标签,再对有效数值标签重建索引。

数值索引已经递增,reindex(method="ffill") 仍把目标 3.5 对应到错误行,而且不抛异常。若目标索引是 object 类型,中间还夹着 NaN,要把目标标签的缺失处理与普通填充分开检查。

下面的组合在 pandas 3.0.6,以及 pandas 3.1.0 配合 NumPy 2.5.4 的环境中出现过错误结果:

import numpy as np
import pandas as pd

source = pd.Series([10, 20, 30, 40], index=[1.0, 2.0, 3.0, 4.0])
target = pd.Index([2.5, np.nan, 3.5], dtype=object)
result = source.reindex(target, method="ffill")

按向前匹配规则,两个有效标签应得到 20、30,缺失标签应保留缺失。受影响路径可能把最后一个结果变成 10。如果换成 None,还可能出现 TypeError: '<' not supported between instances of 'float' and 'NoneType'。

缺失标签不应参与位置搜索

pandas 3.1.0 在这条回退路径中,先处理精确匹配,再将剩余目标交给有序查找。object 数组中的 NaN、None 没有普通数值那样的比较关系;不先隔离它们,可能影响其他目标的位置。nearest 也会调用左右两侧的填充索引器,不能只因为方法名称不同就排除同类条件。

这里要求有序的是源索引;不能把问题解释成“目标必须递增”,再通过给目标排序掩盖错误。排序还可能改变业务要求的输出次序。普通数据单元格里有空值,也不等同于本题的目标标签缺失。

对有效数值标签单独重建索引

如果业务输入明确是数值标签,可以先留下缺失位置,只把有效目标转为源索引的数值 dtype,再把结果放回原位置:

valid = ~target.isna()
values = np.full(len(target), np.nan, dtype=float)
valid_target = pd.Index(target[valid], dtype=source.index.dtype)
values[valid] = source.reindex(valid_target, method="ffill").to_numpy()
result = pd.Series(values, index=target, name=source.name)

这段代码接续上面的 source 与 target,输出顺序和目标标签保持不变,缺失标签位置仍为空。它特意使用浮点结果容纳 NaN,只适合这里的小型数值示例;大整数精度、可空整数、字符串值、时区索引或混合标签需要各自设计结果容器和转换规则,不能整段直接套用。

转换之前应确认有效标签确实属于相同数值域。不要用强制转数值并把失败值悄悄变成缺失的方式“清洗”未知标签,也不要用零填补目标缺失,那会变成查找标签 0。源索引还须满足所选填充方法的单调性等要求;隔离缺失目标不能修复重复源标签或错误索引类型。

比较每个标签对应的行

核对时保留一份只有有效目标的基准结果,逐项比较含缺失目标时的对应值。缺失标签放在开头、中间、末尾,包含多个缺失标签以及超出源索引范围的目标,都应保持各自规则。若实际使用 bfill、nearest、容差或填充数量限制,应对那个真实调用分别验收,不能仅凭本例的 ffill 结果推断。

这类错误可能产生完整且看似合理的表格,因此只检查没有异常、行数相同或缺失数相同都不够。已按错误位置生成的下游统计,需要从保留输入重算;单独更改索引处理不会自动纠正历史输出。