Skip to content

Commit c1dc48f

Browse files
Use assert_allclose instead of assert_almost_equal
1 parent 62025b2 commit c1dc48f

File tree

4 files changed

+43
-24
lines changed

4 files changed

+43
-24
lines changed

_episodes/24-diagnosing-issues-improving-robustness.md

Lines changed: 43 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -120,37 +120,49 @@ to check that the normalisation function is correct for some test data.
120120
])
121121
def test_patient_normalise(test, expected):
122122
"""Test normalisation works for arrays of one and positive integers.
123-
Assumption that test accuracy of two decimal places is sufficient."""
123+
Test with a relative and absolute tolerance of 0.01."""
124124
from inflammation.models import patient_normalise
125-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
125+
result = patient_normalise(np.array(test))
126+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
126127
~~~
127128
{: .language-python}
128129

129-
Note that we are using the `assert_almost_equal()` Numpy testing function
130+
Note that we are using the `assert_allclose()` Numpy testing function
130131
instead of `assert_array_equal()`,
131-
since it allows us to test against values that are *almost* equal.
132+
since it allows us to test against values that are **close** to each other.
132133
This is very useful when we have numbers with arbitrary decimal places
133134
and are only concerned with a certain degree of precision,
134-
like the test case above,
135-
where we make the assumption that a test accuracy of two decimal places is sufficient.
135+
like the test case above.
136+
137+
> ## Relative and absolute tolerance
138+
> **Relative tolerance** in unit testing means that the acceptable difference between the expected and actual results
139+
> depends on the size of the expected result itself. So, if your expected result is 100,
140+
> a relative tolerance of 0.1 (or 10%) means the actual result can be anywhere from 90 to 110 and still be considered correct.
141+
>
142+
> **Absolute tolerance**, on the other hand,
143+
> sets a fixed allowable difference regardless of the magnitude of the expected result.
144+
> For example, if you set an absolute tolerance of 5,
145+
> it means the actual result can be within 5 units of the expected result,
146+
> regardless of whether the expected result is 10 or 1000.
147+
{: .callout}
136148

137149
Run the tests again using `python -m pytest tests/test_models.py`
138150
and you will note that the new test is failing,
139151
with an error message that does not give many clues as to what went wrong.
140152

141153
~~~
142-
E AssertionError:
143-
E Arrays are not almost equal to 2 decimals
154+
E AssertionError:
155+
E Not equal to tolerance rtol=0.01, atol=0.01
144156
E
145-
E Mismatched elements: 6 / 9 (66.7%)
146-
E Max absolute difference: 0.57142857
147-
E Max relative difference: 1.345
148-
E x: array([[0.14, 0.29, 0.43],
149-
E [0.5 , 0.62, 0.75],
150-
E [0.78, 0.89, 1. ]])
151-
E y: array([[0.33, 0.67, 1. ],
152-
E [0.67, 0.83, 1. ],
153-
E [0.78, 0.89, 1. ]])
157+
E Mismatched elements: 6 / 9 (66.7%)
158+
E Max absolute difference: 0.57142857
159+
E Max relative difference: 0.57356077
160+
E x: array([[0.142857, 0.285714, 0.428571],
161+
E [0.5 , 0.625 , 0.75 ],
162+
E [0.777778, 0.888889, 1. ]])
163+
E y: array([[0.33, 0.67, 1. ],
164+
E [0.67, 0.83, 1. ],
165+
E [0.78, 0.89, 1. ]])
154166
155167
tests/test_models.py:53: AssertionError
156168
~~~
@@ -407,7 +419,7 @@ due to the division by zero as we predicted.
407419

408420
~~~
409421
E AssertionError:
410-
E Arrays are not almost equal to 2 decimals
422+
E Not equal to tolerance rtol=0.01, atol=0.01
411423
E
412424
E x and y nan location mismatch:
413425
E x: array([[nan, nan, nan],
@@ -489,7 +501,8 @@ def patient_normalise(data):
489501
> > def test_patient_normalise(test, expected):
490502
> > """Test normalisation works for arrays of one and positive integers."""
491503
> > from inflammation.models import patient_normalise
492-
> > npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
504+
> > result = patient_normalise(np.array(test))
505+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
493506
> > ...
494507
> > ~~~
495508
> > {: .language-python}
@@ -569,11 +582,14 @@ but of an inappropriate value.
569582
def test_patient_normalise(test, expected, expect_raises):
570583
"""Test normalisation works for arrays of one and positive integers."""
571584
from inflammation.models import patient_normalise
585+
572586
if expect_raises is not None:
573587
with pytest.raises(expect_raises):
574-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
588+
result = patient_normalise(np.array(test))
589+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
575590
else:
576-
npt.assert_almost_equal(patient_normalise(np.array(test)), np.array(expected), decimal=2)
591+
result = patient_normalise(np.array(test))
592+
npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
577593
~~~
578594
{: .language-python}
579595
@@ -651,14 +667,17 @@ Be sure to commit your changes so far and push them to GitHub.
651667
> > test = np.array(test)
652668
> > if expect_raises is not None:
653669
> > with pytest.raises(expect_raises):
654-
> > npt.assert_almost_equal(patient_normalise(test), np.array(expected), decimal=2)
670+
> > result = patient_normalise(test)
671+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
672+
> >
655673
> > else:
656-
> > npt.assert_almost_equal(patient_normalise(test), np.array(expected), decimal=2)
674+
> > result = patient_normalise(test)
675+
> > npt.assert_allclose(result, np.array(expected), rtol=1e-2, atol=1e-2)
657676
> > ...
658677
> > ~~~
659678
> >
660679
> > Note the conversion from `list` to `np.array` has been moved
661-
> > out of the call to `npt.assert_almost_equal()` within the test function,
680+
> > out of the call to `npt.assert_allclose()` within the test function,
662681
> > and is now only applied to list items (rather than all items).
663682
> > This allows for greater flexibility with our test inputs,
664683
> > since this wouldn't work in the test case that uses a string.

fig/pytest-pycharm-all-tests-pass.png

-198 KB
Loading
-53.5 KB
Loading

fig/pytest-pycharm-run-tests.png

-207 KB
Loading

0 commit comments

Comments
 (0)