Skip to content

Commit 6333db4

Browse files
author
Joao Veiga
committed
ENH: Allow relative and/or absolute precision in assert_almost_equal
This commit makes `assert_almost_equal` accept both relative and absolute precision when comparing numbers, through two new keyword arguments: `rtol`, and `atol`, respectively. Under the hood, `_libs.testing.assert_almost_equal` is now calling `math.isclose`, instead of an adaptaion of [numpy.testing.assert_almost_equal](https://docs.scipy.org/doc/numpy-1.17.0/reference/generated/numpy.testing.assert_almost_equal.html).
1 parent 28e0f18 commit 6333db4

15 files changed

+337
-134
lines changed

doc/source/whatsnew/v1.1.0.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,10 @@ Deprecations
210210
- Setting values with ``.loc`` using a positional slice is deprecated and will raise in a future version. Use ``.loc`` with labels or ``.iloc`` with positions instead (:issue:`31840`)
211211
- :meth:`DataFrame.to_dict` has deprecated accepting short names for ``orient`` in future versions (:issue:`32515`)
212212
- :meth:`Categorical.to_dense` is deprecated and will be removed in a future version, use ``np.asarray(cat)`` instead (:issue:`32639`)
213+
- :meth:`util.testing.assert_almost_equal` now accepts both relative and absolute
214+
precision through the ``rtol``, and ``atol`` parameters, thus deprecating the
215+
``check_less_precise`` parameter. (:issue:`13357`).
216+
-
213217

214218
.. ---------------------------------------------------------------------------
215219

pandas/_libs/testing.pyx

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import math
2+
13
import numpy as np
24

35
from pandas.core.dtypes.missing import isna, array_equivalent
@@ -38,13 +40,6 @@ cdef bint is_dictlike(obj):
3840
return hasattr(obj, 'keys') and hasattr(obj, '__getitem__')
3941

4042

41-
cdef bint decimal_almost_equal(double desired, double actual, int decimal):
42-
# Code from
43-
# http://docs.scipy.org/doc/numpy/reference/generated
44-
# /numpy.testing.assert_almost_equal.html
45-
return abs(desired - actual) < (0.5 * 10.0 ** -decimal)
46-
47-
4843
cpdef assert_dict_equal(a, b, bint compare_keys=True):
4944
assert is_dictlike(a) and is_dictlike(b), (
5045
"Cannot compare dict objects, one or both is not dict-like"
@@ -63,7 +58,7 @@ cpdef assert_dict_equal(a, b, bint compare_keys=True):
6358

6459

6560
cpdef assert_almost_equal(a, b,
66-
check_less_precise=False,
61+
rtol=1.e-5, atol=1.e-8,
6762
bint check_dtype=True,
6863
obj=None, lobj=None, robj=None, index_values=None):
6964
"""
@@ -73,31 +68,33 @@ cpdef assert_almost_equal(a, b,
7368
----------
7469
a : object
7570
b : object
76-
check_less_precise : bool or int, default False
77-
Specify comparison precision.
78-
5 digits (False) or 3 digits (True) after decimal points are
79-
compared. If an integer, then this will be the number of decimal
80-
points to compare
71+
rtol : float, default 1e-5
72+
Relative tolerance.
73+
74+
.. versionadded:: 1.1.0
75+
atol : float, default 1e-8
76+
Absolute tolerance.
77+
78+
.. versionadded:: 1.1.0
8179
check_dtype: bool, default True
82-
check dtype if both a and b are np.ndarray
80+
check dtype if both a and b are np.ndarray.
8381
obj : str, default None
8482
Specify object name being compared, internally used to show
85-
appropriate assertion message
83+
appropriate assertion message.
8684
lobj : str, default None
8785
Specify left object name being compared, internally used to show
88-
appropriate assertion message
86+
appropriate assertion message.
8987
robj : str, default None
9088
Specify right object name being compared, internally used to show
91-
appropriate assertion message
89+
appropriate assertion message.
9290
index_values : ndarray, default None
9391
Specify shared index values of objects being compared, internally used
94-
to show appropriate assertion message
92+
to show appropriate assertion message.
9593
9694
.. versionadded:: 1.1.0
9795
9896
"""
9997
cdef:
100-
int decimal
10198
double diff = 0.0
10299
Py_ssize_t i, na, nb
103100
double fa, fb
@@ -108,8 +105,6 @@ cpdef assert_almost_equal(a, b,
108105
if robj is None:
109106
robj = b
110107

111-
assert isinstance(check_less_precise, (int, bool))
112-
113108
if isinstance(a, dict) or isinstance(b, dict):
114109
return assert_dict_equal(a, b)
115110

@@ -167,8 +162,7 @@ cpdef assert_almost_equal(a, b,
167162

168163
for i in range(len(a)):
169164
try:
170-
assert_almost_equal(a[i], b[i],
171-
check_less_precise=check_less_precise)
165+
assert_almost_equal(a[i], b[i], rtol=rtol, atol=atol)
172166
except AssertionError:
173167
is_unequal = True
174168
diff += 1
@@ -200,24 +194,11 @@ cpdef assert_almost_equal(a, b,
200194
# inf comparison
201195
return True
202196

203-
if check_less_precise is True:
204-
decimal = 3
205-
elif check_less_precise is False:
206-
decimal = 5
207-
else:
208-
decimal = check_less_precise
209-
210197
fa, fb = a, b
211198

212-
# case for zero
213-
if abs(fa) < 1e-5:
214-
if not decimal_almost_equal(fa, fb, decimal):
215-
assert False, (f'(very low values) expected {fb:.5f} '
216-
f'but got {fa:.5f}, with decimal {decimal}')
217-
else:
218-
if not decimal_almost_equal(1, fb / fa, decimal):
219-
assert False, (f'expected {fb:.5f} but got {fa:.5f}, '
220-
f'with decimal {decimal}')
199+
if not math.isclose(fa, fb, rel_tol=rtol, abs_tol=atol):
200+
assert False, (f"expected {fb:.5f} but got {fa:.5f}, "
201+
f"with rtol={rtol}, atol={atol}")
221202
return True
222203

223204
raise AssertionError(f"{a} != {b}")

0 commit comments

Comments
 (0)