Skip to content

Commit e464967

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 18ab444 commit e464967

15 files changed

+348
-137
lines changed

doc/source/whatsnew/v1.1.0.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,9 @@ Deprecations
753753
instead (:issue:`34191`).
754754
- The ``squeeze`` keyword in the ``groupby`` function is deprecated and will be removed in a future version (:issue:`32380`)
755755
- The ``tz`` keyword in :meth:`Period.to_timestamp` is deprecated and will be removed in a future version; use `per.to_timestamp(...).tz_localize(tz)`` instead (:issue:`34522`)
756+
- :meth:`util.testing.assert_almost_equal` now accepts both relative and absolute
757+
precision through the ``rtol``, and ``atol`` parameters, thus deprecating the
758+
``check_less_precise`` parameter. (:issue:`13357`).
756759

757760
.. ---------------------------------------------------------------------------
758761

pandas/_libs/testing.pyx

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import math
2+
13
import numpy as np
24
from numpy cimport import_array
35
import_array()
@@ -42,11 +44,6 @@ cdef bint is_dictlike(obj):
4244
return hasattr(obj, 'keys') and hasattr(obj, '__getitem__')
4345

4446

45-
cdef bint decimal_almost_equal(double desired, double actual, int decimal):
46-
# Code from
47-
# https://numpy.org/doc/stable/reference/generated/numpy.testing.assert_almost_equal.html
48-
return abs(desired - actual) < (0.5 * 10.0 ** -decimal)
49-
5047

5148
cpdef assert_dict_equal(a, b, bint compare_keys=True):
5249
assert is_dictlike(a) and is_dictlike(b), (
@@ -66,7 +63,7 @@ cpdef assert_dict_equal(a, b, bint compare_keys=True):
6663

6764

6865
cpdef assert_almost_equal(a, b,
69-
check_less_precise=False,
66+
rtol=1.e-5, atol=1.e-8,
7067
bint check_dtype=True,
7168
obj=None, lobj=None, robj=None, index_values=None):
7269
"""
@@ -76,31 +73,33 @@ cpdef assert_almost_equal(a, b,
7673
----------
7774
a : object
7875
b : object
79-
check_less_precise : bool or int, default False
80-
Specify comparison precision.
81-
5 digits (False) or 3 digits (True) after decimal points are
82-
compared. If an integer, then this will be the number of decimal
83-
points to compare
76+
rtol : float, default 1e-5
77+
Relative tolerance.
78+
79+
.. versionadded:: 1.1.0
80+
atol : float, default 1e-8
81+
Absolute tolerance.
82+
83+
.. versionadded:: 1.1.0
8484
check_dtype: bool, default True
85-
check dtype if both a and b are np.ndarray
85+
check dtype if both a and b are np.ndarray.
8686
obj : str, default None
8787
Specify object name being compared, internally used to show
88-
appropriate assertion message
88+
appropriate assertion message.
8989
lobj : str, default None
9090
Specify left object name being compared, internally used to show
91-
appropriate assertion message
91+
appropriate assertion message.
9292
robj : str, default None
9393
Specify right object name being compared, internally used to show
94-
appropriate assertion message
94+
appropriate assertion message.
9595
index_values : ndarray, default None
9696
Specify shared index values of objects being compared, internally used
97-
to show appropriate assertion message
97+
to show appropriate assertion message.
9898
9999
.. versionadded:: 1.1.0
100100
101101
"""
102102
cdef:
103-
int decimal
104103
double diff = 0.0
105104
Py_ssize_t i, na, nb
106105
double fa, fb
@@ -111,8 +110,6 @@ cpdef assert_almost_equal(a, b,
111110
if robj is None:
112111
robj = b
113112

114-
assert isinstance(check_less_precise, (int, bool))
115-
116113
if isinstance(a, dict) or isinstance(b, dict):
117114
return assert_dict_equal(a, b)
118115

@@ -170,8 +167,7 @@ cpdef assert_almost_equal(a, b,
170167

171168
for i in range(len(a)):
172169
try:
173-
assert_almost_equal(a[i], b[i],
174-
check_less_precise=check_less_precise)
170+
assert_almost_equal(a[i], b[i], rtol=rtol, atol=atol)
175171
except AssertionError:
176172
is_unequal = True
177173
diff += 1
@@ -203,24 +199,11 @@ cpdef assert_almost_equal(a, b,
203199
# inf comparison
204200
return True
205201

206-
if check_less_precise is True:
207-
decimal = 3
208-
elif check_less_precise is False:
209-
decimal = 5
210-
else:
211-
decimal = check_less_precise
212-
213202
fa, fb = a, b
214203

215-
# case for zero
216-
if abs(fa) < 1e-5:
217-
if not decimal_almost_equal(fa, fb, decimal):
218-
assert False, (f'(very low values) expected {fb:.5f} '
219-
f'but got {fa:.5f}, with decimal {decimal}')
220-
else:
221-
if not decimal_almost_equal(1, fb / fa, decimal):
222-
assert False, (f'expected {fb:.5f} but got {fa:.5f}, '
223-
f'with decimal {decimal}')
204+
if not math.isclose(fa, fb, rel_tol=rtol, abs_tol=atol):
205+
assert False, (f"expected {fb:.5f} but got {fa:.5f}, "
206+
f"with rtol={rtol}, atol={atol}")
224207
return True
225208

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

0 commit comments

Comments
 (0)