summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
authorCharles Harris <charlesr.harris@gmail.com>2012-03-04 15:28:03 -0700
committerCharles Harris <charlesr.harris@gmail.com>2012-03-04 15:28:03 -0700
commit2c53bfaa654327cfd1176a0c2da428b66ccbf8d9 (patch)
tree370d57b07a38d6ceb5c5a4bc071a5f7c317da948 /numpy
parent1857ee3e9049260366634f350d67c3fe7609116c (diff)
parentb9f0f1f8b54d0e4cf74ea5e7d80893d66585c4a1 (diff)
downloadnumpy-2c53bfaa654327cfd1176a0c2da428b66ccbf8d9.tar.gz
Merge branch 'add-isclose-function'
* add-isclose-function: ENH: Made "isclose()" NA-aware, added release note and versionadded. TST: Test that isclose().all() == allclose() for all test cases. TST: Added masked array and scalar tests for "isclose()". ENH: Allow "isclose()" to work with subclasses of ndarray (such as masked arrays). ENH: Added "isclose()".
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/numeric.py90
-rw-r--r--numpy/core/tests/test_numeric.py124
2 files changed, 213 insertions, 1 deletions
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index bb13d573e..846429016 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -12,7 +12,7 @@ __all__ = ['newaxis', 'ndarray', 'flatiter', 'nditer', 'nested_iters', 'ufunc',
'array_repr', 'array_str', 'set_string_function',
'little_endian', 'require',
'fromiter', 'array_equal', 'array_equiv',
- 'indices', 'fromfunction',
+ 'indices', 'fromfunction', 'isclose',
'load', 'loads', 'isscalar', 'binary_repr', 'base_repr',
'ones', 'identity', 'allclose', 'compare_chararrays', 'putmask',
'seterr', 'geterr', 'setbufsize', 'getbufsize',
@@ -2024,6 +2024,94 @@ def allclose(a, b, rtol=1.e-5, atol=1.e-8):
y = y[~xinf]
return all(less_equal(absolute(x-y), atol + rtol * absolute(y)))
+def isclose(a, b, rtol=1.e-5, atol=1.e-8, equal_nan=False):
+ """
+ Returns a boolean array where two arrays are element-wise equal within a
+ tolerance.
+
+ The tolerance values are positive, typically very small numbers. The
+ relative difference (`rtol` * abs(`b`)) and the absolute difference
+ `atol` are added together to compare against the absolute difference
+ between `a` and `b`.
+
+ Parameters
+ ----------
+ a, b : array_like
+ Input arrays to compare.
+ rtol : float
+ The relative tolerance parameter (see Notes).
+ atol : float
+ The absolute tolerance parameter (see Notes).
+ equal_nan : bool
+ Whether to compare NaN's as equal. If True, NaN's in `a` will be
+ considered equal to NaN's in `b` in the output array.
+
+ Returns
+ -------
+ y : array_like
+ Returns a boolean array of where `a` and `b` are equal within the
+ given tolerance. If both `a` and `b` are scalars, returns a single
+ boolean value.
+
+ See Also
+ --------
+ allclose
+
+ Notes
+ -----
+ .. versionadded:: 1.7.0
+
+ For finite values, isclose uses the following equation to test whether
+ two floating point values are equivalent.
+
+ absolute(`a` - `b`) <= (`atol` + `rtol` * absolute(`b`))
+
+ The above equation is not symmetric in `a` and `b`, so that
+ `isclose(a, b)` might be different from `isclose(b, a)` in
+ some rare cases.
+
+ Examples
+ --------
+ >>> np.isclose([1e10,1e-7], [1.00001e10,1e-8])
+ array([True, False])
+ >>> np.isclose([1e10,1e-8], [1.00001e10,1e-9])
+ array([True, True])
+ >>> np.isclose([1e10,1e-8], [1.0001e10,1e-9])
+ array([False, True])
+ >>> np.isclose([1.0, np.nan], [1.0, np.nan])
+ array([True, False])
+ >>> np.isclose([1.0, np.nan], [1.0, np.nan], equal_nan=True)
+ array([True, True])
+ """
+ def within_tol(x, y, atol, rtol):
+ result = less_equal(abs(x-y), atol + rtol * abs(y))
+ if isscalar(a) and isscalar(b):
+ result = bool(result)
+ return result
+ x = array(a, copy=False, subok=True, ndmin=1)
+ y = array(b, copy=False, subok=True, ndmin=1)
+ xfin = isfinite(x)
+ yfin = isfinite(y)
+ if all(xfin, skipna=True) and all(yfin, skipna=True):
+ return within_tol(x, y, atol, rtol)
+ else:
+ finite = xfin & yfin
+ cond = zeros_like(finite, subok=True, maskna=finite.flags.maskna)
+ # Because we're using boolean indexing, x & y must be the same shape.
+ # Ideally, we'd just do x, y = broadcast_arrays(x, y). It's in
+ # lib.stride_tricks, though, so we can't import it here.
+ x = x * ones_like(cond)
+ y = y * ones_like(cond)
+ # Avoid subtraction with infinite/nan values and indexing with na...
+ finite[isna(finite)] = False
+ cond[finite] = within_tol(x[finite], y[finite], atol, rtol)
+ # Check for equality of infinite values...
+ cond[~finite] = (x[~finite] == y[~finite])
+ if equal_nan:
+ # Make NaN == NaN
+ cond[isnan(x) & isnan(y)] = True
+ return cond
+
def array_equal(a1, a2):
"""
True if two arrays have the same shape and elements, False otherwise.
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index de41f0c1f..5233d0f88 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -1220,6 +1220,130 @@ class TestAllclose(object):
assert_array_equal(y,array([0,inf]))
+class TestIsclose(object):
+ rtol = 1e-5
+ atol = 1e-8
+
+ def setup(self):
+ atol = self.atol
+ rtol = self.rtol
+ arr = array([100,1000])
+ aran = arange(125).reshape((5,5,5))
+
+ self.all_close_tests = [
+ ([1, 0], [1, 0]),
+ ([atol], [0]),
+ ([1], [1 + rtol + atol]),
+ (arr, arr + arr*rtol),
+ (arr, arr + arr*rtol + atol),
+ (aran, aran + aran*rtol),
+ (inf, inf),
+ (inf, [inf]),
+ ([inf, -inf], [inf, -inf]),
+ ]
+ self.none_close_tests = [
+ ([inf, 0], [1, inf]),
+ ([inf, -inf], [1, 0]),
+ ([inf, inf], [1, -inf]),
+ ([inf, inf], [1, 0]),
+ ([nan, 0], [nan, -inf]),
+ ([atol*2], [0]),
+ ([1], [1 + rtol + atol*2]),
+ (aran, aran + rtol*1.1*aran + atol*1.1),
+ (array([inf, 1]), array([0, inf])),
+ ]
+ self.some_close_tests = [
+ ([inf, 0], [inf, atol*2]),
+ ([atol, 1, 1e6*(1 + 2*rtol) + atol], [0, nan, 1e6]),
+ (arange(3), [0, 1, 2.1]),
+ (nan, [nan, nan, nan]),
+ ([0], [atol, inf, -inf, nan]),
+ (0, [atol, inf, -inf, nan]),
+ ]
+ self.some_close_results = [
+ [True, False],
+ [True, False, False],
+ [True, True, False],
+ [False, False, False],
+ [True, False, False, False],
+ [True, False, False, False],
+ ]
+
+ def test_ip_isclose(self):
+ self.setup()
+ tests = self.some_close_tests
+ results = self.some_close_results
+ for (x, y), result in zip(tests, results):
+ yield (assert_array_equal, isclose(x, y), result)
+
+ def tst_all_isclose(self, x, y):
+ assert_(all(isclose(x, y)), "%s and %s not close" % (x, y))
+
+ def tst_none_isclose(self, x, y):
+ msg = "%s and %s shouldn't be close"
+ assert_(not any(isclose(x, y)), msg % (x, y))
+
+ def tst_isclose_allclose(self, x, y):
+ msg = "isclose.all() and allclose aren't same for %s and %s"
+ assert_array_equal(isclose(x, y).all(), allclose(x, y), msg % (x, y))
+
+ def test_ip_all_isclose(self):
+ self.setup()
+ for (x,y) in self.all_close_tests:
+ yield (self.tst_all_isclose, x, y)
+
+ def test_ip_none_isclose(self):
+ self.setup()
+ for (x,y) in self.none_close_tests:
+ yield (self.tst_none_isclose, x, y)
+
+ def test_ip_isclose_allclose(self):
+ self.setup()
+ tests = (self.all_close_tests + self.none_close_tests +
+ self.some_close_tests)
+ for (x, y) in tests:
+ yield (self.tst_isclose_allclose, x, y)
+
+ def test_equal_nan(self):
+ assert_array_equal(isclose(nan, nan, equal_nan=True), [True])
+ arr = array([1.0, nan])
+ assert_array_equal(isclose(arr, arr, equal_nan=True), [True, True])
+
+ def test_masked_arrays(self):
+ x = np.ma.masked_where([True, True, False], np.arange(3))
+ assert_(type(x) == type(isclose(2, x)))
+
+ x = np.ma.masked_where([True, True, False], [nan, inf, nan])
+ assert_(type(x) == type(isclose(inf, x)))
+
+ x = np.ma.masked_where([True, True, False], [nan, nan, nan])
+ y = isclose(nan, x, equal_nan=True)
+ assert_(type(x) == type(y))
+ # Ensure that the mask isn't modified...
+ assert_array_equal([True, True, False], y.mask)
+
+ def test_maskna_arrays(self):
+ x = array([NA, 1, 2, 3])
+ y = array([0, 1, 2, NA])
+ assert_array_equal(isclose(x, y), array([NA, True, True, NA]))
+
+ assert_array_equal(isclose(NA, arange(3)), array([NA, NA, NA]))
+
+ x = array([NA, nan, 2, 3])
+ y = array([nan, 1, 2, NA])
+ assert_array_equal(isclose(x, y), array([NA, False, True, NA]))
+
+ def test_scalar_return(self):
+ assert_(isscalar(isclose(1, 1)))
+
+ def test_no_parameter_modification(self):
+ x = array([inf, 1])
+ y = array([0, inf])
+ isclose(x, y)
+ assert_array_equal(x, array([inf, 1]))
+ assert_array_equal(y, array([0, inf]))
+
+
class TestStdVar(TestCase):
def setUp(self):
self.A = array([1,-1,1,-1])