如何在assertDictEqual中引入relative/absolute容忍度?

How to introduce relative/absolute tolerance in assertDictEqual?

是否有内置方法,或者是否有一种快速有效的方法让 assertDictEqual() 具有与 rtol and/or atol 相同的功能对于 asset_frame_equal()pandas.testing?

我希望能够比较两个字典中对应于同一个键的值是否相等,当值在给定的公差限制内彼此接近时,它们作为相等传递。类似于 atol/rtol 对 frame_equal 的作用。

MRE:

import numpy as np
def assert_dicts_almost_equal(d1, d2, rtol=0.01, atol=0.1):
  assert len(d1) == len(d2), 'Unequal number of elements.'
  for key in d1:
    try:
      np.testing.assert_allclose(d1[key], d2[key], rtol=rtol, atol=atol)
    except AssertionError as msg:
      print('Assertion Error for {key}'.format(key=key))
      print(msg)

数据:

d = {'A': 0.49, 'B': 0.51}
d0 = {'A': 0.4999999999999991, 'B': 0.5000000000000007, 'C': np.nan}
d1 = {'A': 0.3105572709904508, 'B': 0.5030302993151613, 'C': np.nan}
d2 = {'A': 0.4813463081397519, 'B': 0.5104397084554964, 'C': np.nan}
d3 = {'A': 0.4740668937066489, 'B': 0.5144020381674881, 'C': np.nan}

测试:

assert_dicts_almost_equal(d0, d)
assert_dicts_almost_equal(d0, d1)
assert_dicts_almost_equal(d0, d2)
assert_dicts_almost_equal(d0, d3)

只有前两个会引发断言错误,其余的都会通过。

Numpy 的 assert_allclose 类似。这是演示的玩具示例。

import numpy as np

dict_a = {'A': np.array([1,2,3]), 'B': np.array([4,5,6]), 'C': np.array([7,8,9])}

dict_b = {'A': np.array([1,2,3]), 'B': np.array([4,5,20]), 'C': np.array([7,8,9])}

for k, v in dict_a.items():
    try:
        np.testing.assert_allclose(dict_a[k], dict_b[k], atol=2)
        print('{k} is close'.format(k=k))
        
    except AssertionError as msg:
        print('Assertion Error for {k}'.format(k=k))
        print(msg)

输出:

A is close
Assertion Error for B

Not equal to tolerance rtol=1e-07, atol=2

Mismatched elements: 1 / 3 (33.3%)
Max absolute difference: 14
Max relative difference: 0.7
 x: array([4, 5, 6])
 y: array([ 4,  5, 20])
C is close

在此示例中,atol 参数设置为更高的 25,其 B 值在绝对公差范围内。

import numpy as np

dict_a = {'A': np.array([1,2,3]), 'B': np.array([4,5,6]), 'C': np.array([7,8,9])}

dict_b = {'A': np.array([1,2,3]), 'B': np.array([4,5,20]), 'C': np.array([7,8,9])}

for k, v in dict_a.items():
    try:
        np.testing.assert_allclose(dict_a[k], dict_b[k], atol=25)
        print('{k} is close'.format(k=k))
        
    except AssertionError as msg:
        print('Assertion Error for {k}'.format(k=k))
        print(msg)

输出:

A is close
B is close
C is close

编辑评论:

我根据您的评论对此进行了一些更改。现在原始字典是列表和单个值的混合,数组转换发生在循环中。 Numpy 更灵活,因为与无法处理列表的 math.isclose() 相比,它可以处理列表或单个值。

import numpy as np
import math

dict_a = {'A': [1,2,3], 'B': [1,2,3], 'C': [1,2,3]}

dict_b = {'A': [1,2,3], 'B': 66, 'C': [1,2,3]}

for k, v in dict_a.items():
    try:
        np.testing.assert_allclose(np.array(dict_a[k]), np.array(dict_b[k]), atol=25)
        #math.isclose(dict_a[k], dict_b[k])
        print('{k} is close'.format(k=k))
        
    except AssertionError as msg:
        print('Assertion Error for {k}'.format(k=k))
        print(msg)

输出:

A is close
Assertion Error for B

Not equal to tolerance rtol=1e-07, atol=25

Mismatched elements: 3 / 3 (100%)
Max absolute difference: 65
Max relative difference: 0.98484848
 x: array([1, 2, 3])
 y: array(66)
C is close