diff --git a/doc/release/1.11.0-notes.rst b/doc/release/1.11.0-notes.rst index e95225a7c0d5..aa11cdf0789f 100644 --- a/doc/release/1.11.0-notes.rst +++ b/doc/release/1.11.0-notes.rst @@ -142,6 +142,7 @@ FutureWarning to changed behavior due to a bug, sometimes no warning was raised and the dimensions were already preserved. + C API ~~~~~ diff --git a/doc/release/1.12.0-notes.rst b/doc/release/1.12.0-notes.rst index d093e0542c86..ce606e5b5366 100644 --- a/doc/release/1.12.0-notes.rst +++ b/doc/release/1.12.0-notes.rst @@ -41,6 +41,30 @@ XXX the two coincide. Previous behavior of 'lower' + 0.5 is fixed. +``keepdims`` kwarg is passed through to user-class methods +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +numpy functions that take a ``keepdims`` kwarg now pass the value +through to the corresponding methods on ndarray sub-classes. Previously the +``keepdims`` keyword would be silently dropped. These functions now have +the following behavior: + +1. If user does not provide ``keepdims``, no keyword is passed to the underlying + method. +2. Any user-provided value of ``keepdims`` is passed through as a keyword + argument to the method. + +This will raise in the case where the method does not support a +``keepdims`` kwarg and the user explicitly passes in ``keepdims``. + + +The following functions are changed: ``sum``, ``product``, +``sometrue``, ``alltrue``, ``any``, ``all``, ``amax``, ``amin``, +``prod``, ``mean``, ``std``, ``var``, ``nanmin``, ``nanmax``, +``nansum``, ``nanprod``, ``nanmean``, ``nanmedian``, ``nanvar``, +``nanstd`` + + DeprecationWarning to error ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -73,4 +97,3 @@ Changes Deprecations ============ - diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 4faeb557a6d3..52a15e30d7d1 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -17,7 +17,6 @@ _dt_ = nt.sctype2char - # functions that are methods __all__ = [ 'alen', 'all', 'alltrue', 'amax', 'amin', 'any', 'argmax', @@ -1380,6 +1379,7 @@ def trace(a, offset=0, axis1=0, axis2=1, dtype=None, out=None): return asanyarray(a).trace(offset, axis1, axis2, dtype, out) + def ravel(a, order='C'): """Return a contiguous flattened array. @@ -1740,7 +1740,7 @@ def clip(a, a_min, a_max, out=None): return clip(a_min, a_max, out) -def sum(a, axis=None, dtype=None, out=None, keepdims=False): +def sum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Sum of array elements over a given axis. @@ -1770,9 +1770,15 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): the same shape as the expected output, but the type of the output values will be cast if necessary. keepdims : bool, optional - If this is set to True, the axes which are reduced are left in the - result as dimensions with size one. With this option, the result - will broadcast correctly against the input array. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `arr`. + + If the default value is passed, then `keepdims` will not be + passed through to the `sum` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. Returns ------- @@ -1821,26 +1827,27 @@ def sum(a, axis=None, dtype=None, out=None, keepdims=False): -128 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if isinstance(a, _gentype): res = _sum_(a) if out is not None: out[...] = res return out return res - elif type(a) is not mu.ndarray: + if type(a) is not mu.ndarray: try: sum = a.sum except AttributeError: - return _methods._sum(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameters here... - return sum(axis=axis, dtype=dtype, out=out) - else: - return _methods._sum(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + pass + else: + return sum(axis=axis, dtype=dtype, out=out, **kwargs) + return _methods._sum(a, axis=axis, dtype=dtype, + out=out, **kwargs) -def product(a, axis=None, dtype=None, out=None, keepdims=False): +def product(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the product of array elements over a given axis. @@ -1849,11 +1856,13 @@ def product(a, axis=None, dtype=None, out=None, keepdims=False): prod : equivalent function; see for details. """ - return um.multiply.reduce(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return um.multiply.reduce(a, axis=axis, dtype=dtype, out=out, **kwargs) -def sometrue(a, axis=None, out=None, keepdims=False): +def sometrue(a, axis=None, out=None, keepdims=np._NoValue): """ Check whether some values are true. @@ -1865,14 +1874,13 @@ def sometrue(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.any(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.any(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.any(axis=axis, out=out, **kwargs) -def alltrue(a, axis=None, out=None, keepdims=False): +def alltrue(a, axis=None, out=None, keepdims=np._NoValue): """ Check if all elements of input array are true. @@ -1882,14 +1890,13 @@ def alltrue(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.all(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.all(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.all(axis=axis, out=out, **kwargs) -def any(a, axis=None, out=None, keepdims=False): +def any(a, axis=None, out=None, keepdims=np._NoValue): """ Test whether any array element along a given axis evaluates to True. @@ -1915,11 +1922,18 @@ def any(a, axis=None, out=None, keepdims=False): (e.g., if it is of type float, then it will remain so, returning 1.0 for True and 0.0 for False, regardless of the type of `a`). See `doc.ufuncs` (Section "Output arguments") for details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `any` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- any : bool or ndarray @@ -1963,14 +1977,13 @@ def any(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.any(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.any(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.any(axis=axis, out=out, **kwargs) -def all(a, axis=None, out=None, keepdims=False): +def all(a, axis=None, out=None, keepdims=np._NoValue): """ Test whether all array elements along a given axis evaluate to True. @@ -1994,11 +2007,18 @@ def all(a, axis=None, out=None, keepdims=False): type is preserved (e.g., if ``dtype(out)`` is float, the result will consist of 0.0's and 1.0's). See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `all` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- all : ndarray, bool @@ -2037,11 +2057,10 @@ def all(a, axis=None, out=None, keepdims=False): """ arr = asanyarray(a) - - try: - return arr.all(axis=axis, out=out, keepdims=keepdims) - except TypeError: - return arr.all(axis=axis, out=out) + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + return arr.all(axis=axis, out=out, **kwargs) def cumsum(a, axis=None, dtype=None, out=None): @@ -2177,7 +2196,7 @@ def ptp(a, axis=None, out=None): return ptp(axis, out) -def amax(a, axis=None, out=None, keepdims=False): +def amax(a, axis=None, out=None, keepdims=np._NoValue): """ Return the maximum of an array or maximum along an axis. @@ -2197,11 +2216,18 @@ def amax(a, axis=None, out=None, keepdims=False): Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `amax` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- amax : ndarray or scalar @@ -2255,20 +2281,23 @@ def amax(a, axis=None, out=None, keepdims=False): 4.0 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: amax = a.max except AttributeError: - return _methods._amax(a, axis=axis, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameter - return amax(axis=axis, out=out) - else: - return _methods._amax(a, axis=axis, - out=out, keepdims=keepdims) + pass + else: + return amax(axis=axis, out=out, **kwargs) + + return _methods._amax(a, axis=axis, + out=out, **kwargs) -def amin(a, axis=None, out=None, keepdims=False): +def amin(a, axis=None, out=None, keepdims=np._NoValue): """ Return the minimum of an array or minimum along an axis. @@ -2288,11 +2317,18 @@ def amin(a, axis=None, out=None, keepdims=False): Alternative output array in which to place the result. Must be of the same shape and buffer length as the expected output. See `doc.ufuncs` (Section "Output arguments") for more details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `amin` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- amin : ndarray or scalar @@ -2346,17 +2382,19 @@ def amin(a, axis=None, out=None, keepdims=False): 0.0 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: amin = a.min except AttributeError: - return _methods._amin(a, axis=axis, - out=out, keepdims=keepdims) - # NOTE: Dropping the keepdims parameter - return amin(axis=axis, out=out) - else: - return _methods._amin(a, axis=axis, - out=out, keepdims=keepdims) + pass + else: + return amin(axis=axis, out=out, **kwargs) + + return _methods._amin(a, axis=axis, + out=out, **kwargs) def alen(a): @@ -2392,7 +2430,7 @@ def alen(a): return len(array(a, ndmin=1)) -def prod(a, axis=None, dtype=None, out=None, keepdims=False): +def prod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the product of array elements over a given axis. @@ -2427,6 +2465,12 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False): result as dimensions with size one. With this option, the result will broadcast correctly against the input array. + If the default value is passed, then `keepdims` will not be + passed through to the `prod` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- product_along_axis : ndarray, see `dtype` parameter above. @@ -2484,16 +2528,19 @@ def prod(a, axis=None, dtype=None, out=None, keepdims=False): True """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: prod = a.prod except AttributeError: - return _methods._prod(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) - return prod(axis=axis, dtype=dtype, out=out) - else: - return _methods._prod(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + pass + else: + return prod(axis=axis, dtype=dtype, out=out, **kwargs) + + return _methods._prod(a, axis=axis, dtype=dtype, + out=out, **kwargs) def cumprod(a, axis=None, dtype=None, out=None): @@ -2793,7 +2840,7 @@ def round_(a, decimals=0, out=None): return round(decimals, out) -def mean(a, axis=None, dtype=None, out=None, keepdims=False): +def mean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Compute the arithmetic mean along the specified axis. @@ -2823,11 +2870,18 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): is ``None``; if provided, it must have the same shape as the expected output, but the type will be cast if necessary. See `doc.ufuncs` for details. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `mean` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- m : ndarray, see dtype parameter above @@ -2874,18 +2928,22 @@ def mean(a, axis=None, dtype=None, out=None, keepdims=False): 0.55000000074505806 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims if type(a) is not mu.ndarray: try: mean = a.mean - return mean(axis=axis, dtype=dtype, out=out) except AttributeError: pass + else: + return mean(axis=axis, dtype=dtype, out=out, **kwargs) return _methods._mean(a, axis=axis, dtype=dtype, - out=out, keepdims=keepdims) + out=out, **kwargs) -def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the standard deviation along the specified axis. @@ -2922,6 +2980,12 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `std` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- standard_deviation : ndarray, see dtype parameter above. @@ -2981,19 +3045,23 @@ def std(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): 0.44999999925494177 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: std = a.std - return std(axis=axis, dtype=dtype, out=out, ddof=ddof) except AttributeError: pass + else: + return std(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) return _methods._std(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + **kwargs) -def var(a, axis=None, dtype=None, out=None, ddof=0, - keepdims=False): +def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the variance along the specified axis. @@ -3031,6 +3099,12 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, in the result as dimensions with size one. With this option, the result will broadcast correctly against the original `arr`. + If the default value is passed, then `keepdims` will not be + passed through to the `var` method of sub-classes of + `ndarray`, however any non-default value will be. If the + sub-classes `sum` method does not implement `keepdims` any + exceptions will be raised. + Returns ------- variance : ndarray, see dtype parameter above @@ -3089,12 +3163,18 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, 0.2025 """ + kwargs = {} + if keepdims is not np._NoValue: + kwargs['keepdims'] = keepdims + if type(a) is not mu.ndarray: try: var = a.var - return var(axis=axis, dtype=dtype, out=out, ddof=ddof) + except AttributeError: pass + else: + return var(axis=axis, dtype=dtype, out=out, ddof=ddof, **kwargs) return _methods._var(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + **kwargs) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 7309cf2249c0..e22a5e193434 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -2472,5 +2472,17 @@ def test_number_of_arguments(self): assert_equal(mit.numiter, j) +class TestKeepdims(TestCase): + + class sub_array(np.ndarray): + def sum(self, axis=None, dtype=None, out=None): + return np.ndarray.sum(self, axis, dtype, out, keepdims=True) + + def test_raise(self): + sub_class = self.sub_array + x = np.arange(30).view(sub_class) + assert_raises(TypeError, np.sum, x, keepdims=True) + + if __name__ == "__main__": run_module_suite() diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index 8fe7afd46fb5..56f0010afedb 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -23,6 +23,7 @@ import numpy as np from numpy.lib.function_base import _ureduce as _ureduce + __all__ = [ 'nansum', 'nanmax', 'nanmin', 'nanargmax', 'nanargmin', 'nanmean', 'nanmedian', 'nanpercentile', 'nanvar', 'nanstd', 'nanprod', @@ -141,7 +142,7 @@ def _divide_by_count(a, b, out=None): return np.divide(a, b, out=out, casting='unsafe') -def nanmin(a, axis=None, out=None, keepdims=False): +def nanmin(a, axis=None, out=None, keepdims=np._NoValue): """ Return minimum of an array or minimum along an axis, ignoring any NaNs. When all-NaN slices are encountered a ``RuntimeWarning`` is raised and @@ -163,9 +164,14 @@ def nanmin(a, axis=None, out=None, keepdims=False): .. versionadded:: 1.8.0 keepdims : bool, optional - If this is set to True, the axes which are reduced are left in the - result as dimensions with size one. With this option, the result - will broadcast correctly against the original `a`. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + If the value is anything but the default, then + `keepdims` will be passed through to the `min` method + of sub-classes of `ndarray`. If the sub-classes methods + does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 @@ -220,27 +226,30 @@ def nanmin(a, axis=None, out=None, keepdims=False): -inf """ + 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 - res = np.fmin.reduce(a, axis=axis, out=out, keepdims=keepdims) + res = np.fmin.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): warnings.warn("All-NaN axis encountered", RuntimeWarning) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, +np.inf) - res = np.amin(a, axis=axis, out=out, keepdims=keepdims) + res = np.amin(a, axis=axis, out=out, **kwargs) if mask is None: return res # Check for all-NaN axis - mask = np.all(mask, axis=axis, keepdims=keepdims) + mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) warnings.warn("All-NaN axis encountered", RuntimeWarning) return res -def nanmax(a, axis=None, out=None, keepdims=False): +def nanmax(a, axis=None, out=None, keepdims=np._NoValue): """ Return the maximum of an array or maximum along an axis, ignoring any NaNs. When all-NaN slices are encountered a ``RuntimeWarning`` is @@ -262,9 +271,14 @@ def nanmax(a, axis=None, out=None, keepdims=False): .. versionadded:: 1.8.0 keepdims : bool, optional - If this is set to True, the axes which are reduced are left in the - result as dimensions with size one. With this option, the result - will broadcast correctly against the original `a`. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + If the value is anything but the default, then + `keepdims` will be passed through to the `max` method + of sub-classes of `ndarray`. If the sub-classes methods + does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 @@ -319,20 +333,23 @@ def nanmax(a, axis=None, out=None, keepdims=False): inf """ + 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 - res = np.fmax.reduce(a, axis=axis, out=out, keepdims=keepdims) + res = np.fmax.reduce(a, axis=axis, out=out, **kwargs) if np.isnan(res).any(): warnings.warn("All-NaN slice encountered", RuntimeWarning) else: # Slow, but safe for subclasses of ndarray a, mask = _replace_nan(a, -np.inf) - res = np.amax(a, axis=axis, out=out, keepdims=keepdims) + res = np.amax(a, axis=axis, out=out, **kwargs) if mask is None: return res # Check for all-NaN axis - mask = np.all(mask, axis=axis, keepdims=keepdims) + mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) warnings.warn("All-NaN axis encountered", RuntimeWarning) @@ -428,7 +445,7 @@ def nanargmax(a, axis=None): return res -def nansum(a, axis=None, dtype=None, out=None, keepdims=0): +def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the sum of array elements over a given axis treating Not a Numbers (NaNs) as zero. @@ -462,9 +479,15 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=0): .. versionadded:: 1.8.0 keepdims : bool, optional - If True, the axes which are reduced are left in the result as - dimensions with size one. With this option, the result will - broadcast correctly against the original `arr`. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + + If the value is anything but the default, then + `keepdims` will be passed through to the `mean` or `sum` methods + of sub-classes of `ndarray`. If the sub-classes methods + does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 @@ -513,7 +536,7 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=0): return np.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) -def nanprod(a, axis=None, dtype=None, out=None, keepdims=0): +def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Return the product of array elements over a given axis treating Not a Numbers (NaNs) as zero. @@ -583,7 +606,7 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=0): return np.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) -def nanmean(a, axis=None, dtype=None, out=None, keepdims=False): +def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ Compute the arithmetic mean along the specified axis, ignoring NaNs. @@ -613,9 +636,14 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=False): expected output, but the type will be cast if necessary. See `doc.ufuncs` for details. keepdims : bool, optional - If this is set to True, the axes which are reduced are left in the - result as dimensions with size one. With this option, the result - will broadcast correctly against the original `arr`. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + If the value is anything but the default, then + `keepdims` will be passed through to the `mean` or `sum` methods + of sub-classes of `ndarray`. If the sub-classes methods + does not implement `keepdims` any exceptions will be raised. Returns ------- @@ -727,6 +755,7 @@ def _nanmedian(a, axis=None, out=None, overwrite_input=False): out[...] = result return result + def _nanmedian_small(a, axis=None, out=None, overwrite_input=False): """ sort + indexing median, faster for small medians along multiple @@ -743,7 +772,8 @@ def _nanmedian_small(a, axis=None, out=None, overwrite_input=False): return out return m.filled(np.nan) -def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=False): + +def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=np._NoValue): """ Compute the median along the specified axis, while ignoring NaNs. @@ -772,9 +802,15 @@ def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=False): False. If `overwrite_input` is ``True`` and `a` is not already an `ndarray`, an error will be raised. keepdims : bool, optional - If this is set to True, the axes which are reduced are left in - the result as dimensions with size one. With this option, the - result will broadcast correctly against the original `arr`. + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + If this is anything but the default value it will be passed + through (in the special case of an empty array) to the + `mean` function of the underlying array. If the array is + a sub-class and `mean` does not have the kwarg `keepdims` this + will raise a RuntimeError. Returns ------- @@ -829,14 +865,14 @@ def nanmedian(a, axis=None, out=None, overwrite_input=False, keepdims=False): r, k = _ureduce(a, func=_nanmedian, axis=axis, out=out, overwrite_input=overwrite_input) - if keepdims: + if keepdims and keepdims is not np._NoValue: return r.reshape(k) else: return r def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=False): + interpolation='linear', keepdims=np._NoValue): """ Compute the qth percentile of the data along the specified axis, while ignoring nan values. @@ -883,9 +919,21 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, * nearest: ``i`` or ``j``, whichever is nearest. * midpoint: ``(i + j) / 2``. keepdims : bool, optional +<<<<<<< 35b5f5be1ffffada84c8be207e7b8b196a58f786 If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, the result will broadcast correctly against the original array `a`. +======= + If this is set to True, the axes which are reduced are left + in the result as dimensions with size one. With this option, + the result will broadcast correctly against the original `a`. + + If this is anything but the default value it will be passed + through (in the special case of an empty array) to the + `mean` function of the underlying array. If the array is + a sub-class and `mean` does not have the kwarg `keepdims` this + will raise a RuntimeError. +>>>>>>> BUG: many functions silently drop `keepdims` kwarg Returns ------- @@ -893,7 +941,7 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, If `q` is a single percentile and `axis=None`, then the result is a scalar. If multiple percentiles are given, first axis of the result corresponds to the percentiles. The other axes are - the axes that remain after the reduction of `a`. If the input + the axes that remain after the reduction of `a`. If the input contains integers or floats smaller than ``float64``, the output data-type is ``float64``. Otherwise, the output data-type is the same as that of the input. If `out` is specified, that array is @@ -954,7 +1002,7 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, r, k = _ureduce(a, func=_nanpercentile, q=q, axis=axis, out=out, overwrite_input=overwrite_input, interpolation=interpolation) - if keepdims: + if keepdims and keepdims is not np._NoValue: if q.ndim == 0: return r.reshape(k) else: @@ -964,7 +1012,7 @@ def nanpercentile(a, q, axis=None, out=None, overwrite_input=False, def _nanpercentile(a, q, axis=None, out=None, overwrite_input=False, - interpolation='linear', keepdims=False): + interpolation='linear'): """ Private function that doesn't support extended axis or keepdims. These methods are extended to this function using _ureduce @@ -981,7 +1029,7 @@ def _nanpercentile(a, q, axis=None, out=None, overwrite_input=False, # Move that axis to the beginning to match percentile's # convention. if q.ndim != 0: - result = np.rollaxis(result, axis) + result = np.rollaxis(result, axis) if out is not None: out[...] = result @@ -1020,7 +1068,7 @@ def _nanpercentile1d(arr1d, q, overwrite_input=False, interpolation='linear'): interpolation=interpolation) -def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the variance along the specified axis, while ignoring NaNs. @@ -1056,7 +1104,8 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, - the result will broadcast correctly against the original `arr`. + the result will broadcast correctly against the original `a`. + Returns ------- @@ -1095,6 +1144,9 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): below). Specifying a higher-accuracy accumulator using the ``dtype`` keyword can alleviate this issue. + For this function to work on sub-classes of ndarray, they must define + `sum` with the kwarg `keepdims` + Examples -------- >>> a = np.array([[1, np.nan], [3, 4]]) @@ -1122,8 +1174,17 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): warnings.simplefilter('ignore') # Compute mean - cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=True) - avg = np.sum(arr, axis=axis, dtype=dtype, keepdims=True) + if type(arr) is np.matrix: + _keepdims = np._NoValue + else: + _keepdims = True + # we need to special case matrix for reverse compatibility + # in order for this to work, these sums need to be called with + # keepdims=True, however matrix now raises an error in this case, but + # the reason that it drops the keepdims kwarg is to force keepdims=True + # so this used to work by serendipity. + cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=_keepdims) + avg = np.sum(arr, axis=axis, dtype=dtype, keepdims=_keepdims) avg = _divide_by_count(avg, cnt) # Compute squared deviation from mean. @@ -1151,7 +1212,7 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): return var -def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): +def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ Compute the standard deviation along the specified axis, while ignoring NaNs. @@ -1185,10 +1246,16 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=False): Means Delta Degrees of Freedom. The divisor used in calculations is ``N - ddof``, where ``N`` represents the number of non-NaN elements. By default `ddof` is zero. + keepdims : bool, optional If this is set to True, the axes which are reduced are left in the result as dimensions with size one. With this option, - the result will broadcast correctly against the original `arr`. + the result will broadcast correctly against the original `a`. + + If this value is anything but the default it is passed through + as-is to the relevant functions of the sub-classes. If these + functions do not have a `keepdims` kwarg, a RuntimeError will + be raised. Returns -------