浮動小数点の計算結果は、完全一致だけで判定すると丸め誤差まで不一致になる。np.iscloseで許容誤差を明示し、比較前にshapeを確認し、不一致の位置を取り出すと原因を調べやすい。許容誤差はエラーを消すためではなく、値の単位や求める精度に合わせて決める。
最小例で確かめる
以下は説明用に作った小さなデータである。実測データや実行速度の測定結果ではない。コード全体をexample.pyとして保存すれば、入力ファイルを別途用意せずに実行できる。assertは、この例で成り立つべき形や値を確認するために入れてある。
import numpy as np
reference = np.array([0., 1., 1000., np.nan])
actual = np.array([1e-9, 1.000001, 1000.02, np.nan])
if actual.shape != reference.shape:
raise ValueError("shape mismatch")
close = np.isclose(actual, reference, rtol=1e-5, atol=1e-8,
equal_nan=True)
print("close:", close.tolist())
print("mismatch indices:", np.flatnonzero(~close).tolist())
print("absolute difference:", np.abs(actual[:3] - reference[:3]).round(9).tolist())
forward = np.isclose(1., 2., rtol=0.6, atol=0.)
reverse = np.isclose(2., 1., rtol=0.6, atol=0.)
print("asymmetric example:", bool(forward), bool(reverse))
np.testing.assert_array_equal(close, [True, True, False, True])
assert forward and not reverse
assert not np.isclose(np.nan, np.nan)
assert np.isclose(np.inf, np.inf)
assert not np.isclose(np.inf, -np.inf)
実行結果
close: [True, True, False, True]
mismatch indices: [2]
absolute difference: [1e-09, 1e-06, 0.02]
asymmetric example: True False
絶対誤差と相対誤差を組み合わせる
有限値に対する判定は、差の絶対値がatol + rtol * abs(reference)以下かどうかで決まる。atolは元の値と同じ単位を持つ絶対許容誤差、rtolは大きさに応じた相対許容誤差である。参照値がゼロに近いと相対項は小さくなるため、atolの選び方が特に重要になる。
例の参照値1000では相対項が約0.01である。実際の差は0.02なので不一致になる。一方、参照値0では差1e-9がatol=1e-8以内のため一致とする。この許容値が適切かは用途による。ナノ単位の信号を比較しているなら、1e-8という絶対値は大きすぎるかもしれない。
比較の向きとshapeを固定する
np.iscloseは第2引数を基準として相対誤差を計算するため、一般には対称ではない。例の大きなrtolを用いた実験では、引数を逆にすると判定が変わる。実際の検査では参照値を第2引数へ置く、と決めておくと読みやすい。双方を対等に扱う基準が必要なら、その定義を別途決める。
また、iscloseはbroadcastingするので、(n, 1)と(n,)を渡すと全組合せの比較になることがある。要素ごとの対応が前提なら比較前にshapeを検査する。np.allcloseのTrueだけを見ても、軸やラベルの対応が正しいかは分からない。数値の近さと対応関係は別に確かめたい。
不一致を位置と値で確認する
np.flatnonzero(~close)は1次元配列の不一致位置を返す。多次元ならnp.argwhere(~close)などで各軸の添字を取り出せる。最初から不一致を集約してTrueかFalseだけにしてしまうより、該当する参照値、実測値、差、許容幅を並べると単位違いや系統的なずれを見分けやすい。
例のround(9)は表示を短くするためだけであり、判定前の値を丸めてはいない。表示の小数点以下の桁数を合わせてから比較すると、許容する誤差の根拠が曖昧になりやすい。計算は元の精度で行い、許容基準と表示書式を切り離す方がよい。
NaNと無限大の方針を明示する
NaNは標準では同じ位置に二つあっても不一致である。equal_nan=Trueを使えば欠測位置同士を一致として扱えるが、これは欠測パターンの検査であって数値が正しく求まったという意味ではない。計算結果にNaNが出てはいけない処理なら、最初にnp.isfiniteなどで入力・出力を検査する。
同じ符号の無限大は一致し、正負が異なれば一致しない。許容誤差で非有限値の問題を覆い隠さないようにする。また、テストを通すためにrtolを大きくする前に、単位、並び、dtype、集約軸の違いを確認したい。参照値そのものの不確かさも含め、求める精度を先に決めておくと判定を説明しやすい。
動作確認環境と参考資料
Linux・CPython 3.12.14、NumPy 2.3.5、pandas 2.2.3、SciPy 1.17.0、Matplotlib 3.10.8の環境で掲載コードを実行した。使用するライブラリはコード冒頭のimportを参照してほしい。公式資料の最新版と、この実行確認版は区別している。数値の末尾や表の表示幅は環境によって変わることがある。
