DEBUG.WIKI
雨云;给项目一台云服务器
丽萨主机;为跨境业务选好 IP;原生 / 住宅 IP · 按套餐选择;查看 IP →
OnlineSIM:在线接收短信,虚拟号码租用

pandas 断言两边都显示 1.0,却说数值不同

pandas 3.1.0 的断言消息会按 display.precision 格式化浮点序列。先提高诊断显示精度或查看 float.hex,保留原有比较规则,别为消掉报错直接放宽容差。

assert_frame_equal() 报数值不同,但消息中的 [left] 和 [right] 都是 [1.0],不一定是断言判断错了。pandas 3.1.0 在格式化序列中的浮点数时使用 display.precision;小于显示精度的差异可能被一起显示成相同文本。

例如,两个值分别是 1.0 和 1.000000001,调用使用 check_exact=True。它们确实不同,断言失败符合要求,但默认六位显示精度可能让错误消息失去区分度。这与把字符串 "1.0" 和数值 1.0 比较、索引不一致或 dtype 不同,需要分开检查。

提高诊断精度,保持断言条件

可以只在需要查看这次失败的局部上下文提高精度,不改整套程序的全局显示选项:

import pandas as pd

left = pd.DataFrame({"x": [1.0]})
right = pd.DataFrame({"x": [1.000000001]})

with pd.option_context("display.precision", 17):
    pd.testing.assert_frame_equal(left, right, check_exact=True)

这段示例仍应抛 AssertionError。它的目的只是让消息更容易辨认差异,不是让原断言通过。显示精度不会改变 DataFrame 中保存的值,也不会把已经产生的浮点计算误差消除。

对于某个已定位的 Python 浮点值,还可以查看二进制浮点的十六进制表示:

a = float(left.loc[0, "x"])
b = float(right.loc[0, "x"])
print(repr(a), repr(b))
print(a.hex(), b.hex())

这适合检查已经确认的标量,不意味着所有扩展 dtype、缺失值或 Decimal 都应先转成 float;转换本身可能丢失原类型信息。大表也不必为了这一次错误把全部业务数据打印进日志,先定位列与行再输出必要的诊断值。

比较容差应来自数据要求

如果业务要求逐位一致,就保留 check_exact=True,继续追查输入、运算顺序或序列化过程。如果允许数值误差,才为 check_exact=False 配置有依据的 rtol 和 atol。容差是比较规则,display.precision 是诊断展示;把两者同时改掉,很难确认究竟修复了数据还是放宽了门槛。

对于大数与接近零的数,相对容差和绝对容差各自的作用不同。不能只因这组示例差了十亿分之一,就给项目所有浮点列统一加一个宽松阈值。需要严格比较的标识、计数或金额表示,也应先核对实际类型与业务契约。

pandas 3.1.0 的正式实现仍有这条显示精度路径,不能把问题已被登记理解成已经发行修补。后续升级应保留原始比较条件与失败样本,检查消息是否清晰,并确认真正相同与真正不同的数据仍分别得到正确结果。