From c607456d9eac8743e5280adeec5496fd2621ae6a Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 27 Apr 2017 23:01:49 +0100 Subject: [PATCH 1/2] MAINT: Remove weird create-a-copy dance --- numpy/lib/nanfunctions.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index c96e925aafe1..d9df9563f556 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -61,14 +61,10 @@ def _replace_nan(a, val): NaNs, otherwise return None. """ - is_new = not isinstance(a, np.ndarray) - if is_new: - a = np.array(a) + a = np.array(a, subok=True, copy=True) + if not issubclass(a.dtype.type, np.inexact): return a, None - if not is_new: - # need copy - a = np.array(a, subok=True) mask = np.isnan(a) np.copyto(a, val, where=mask) From a77709c67051a1d19ed4caa83b0b7686ba22bfc4 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 27 Apr 2017 23:02:43 +0100 Subject: [PATCH 2/2] BUG: Fix incorrect behavior of nanfunctions on object arrays Fixes gh-8974 and gh-9008 --- numpy/lib/nanfunctions.py | 24 ++++++++++++++++-------- numpy/lib/tests/test_nanfunctions.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index d9df9563f556..1e342b9329ce 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -63,11 +63,17 @@ def _replace_nan(a, val): """ a = np.array(a, subok=True, copy=True) - if not issubclass(a.dtype.type, np.inexact): - return a, None + if a.dtype == np.object_: + # object arrays do not support `isnan` (gh-9009), so make a guess + mask = a != a + elif issubclass(a.dtype.type, np.inexact): + mask = np.isnan(a) + else: + mask = None + + if mask is not None: + np.copyto(a, val, where=mask) - mask = np.isnan(a) - np.copyto(a, val, where=mask) return a, mask @@ -228,8 +234,9 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims - if not isinstance(a, np.ndarray) or type(a) is np.ndarray: - # Fast, but not safe for subclasses of ndarray + if type(a) is np.ndarray and a.dtype != np.object_: + # Fast, but not safe for subclasses of ndarray, or object arrays, + # which do not implement isnan (gh-9009), or fmin correctly (gh-8975) res = np.fmin.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): warnings.warn("All-NaN axis encountered", RuntimeWarning, stacklevel=2) @@ -335,8 +342,9 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims - if not isinstance(a, np.ndarray) or type(a) is np.ndarray: - # Fast, but not safe for subclasses of ndarray + if type(a) is np.ndarray and a.dtype != np.object_: + # Fast, but not safe for subclasses of ndarray, or object arrays, + # which do not implement isnan (gh-9009), or fmax correctly (gh-8975) res = np.fmax.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): warnings.warn("All-NaN slice encountered", RuntimeWarning, stacklevel=2) diff --git a/numpy/lib/tests/test_nanfunctions.py b/numpy/lib/tests/test_nanfunctions.py index 1678e1091891..466ceefb5e99 100644 --- a/numpy/lib/tests/test_nanfunctions.py +++ b/numpy/lib/tests/test_nanfunctions.py @@ -152,6 +152,18 @@ def test_matrices(self): assert_(res != np.nan) assert_(len(w) == 0) + def test_object_array(self): + arr = np.array([[1.0, 2.0], [np.nan, 4.0], [np.nan, np.nan]], dtype=object) + assert_equal(np.nanmin(arr), 1.0) + assert_equal(np.nanmin(arr, axis=0), [1.0, 2.0]) + + with warnings.catch_warnings(record=True) as w: + warnings.simplefilter('always') + # assert_equal does not work on object arrays of nan + assert_equal(list(np.nanmin(arr, axis=1)), [1.0, 4.0, np.nan]) + assert_(len(w) == 1, 'no warning raised') + assert_(issubclass(w[0].category, RuntimeWarning)) + class TestNanFunctions_ArgminArgmax(TestCase):