summaryrefslogtreecommitdiff
path: root/numpy/lib/nanfunctions.py
diff options
context:
space:
mode:
authorThomas A Caswell <tcaswell@bnl.gov>2014-04-14 08:43:20 -0400
committerThomas A Caswell <tcaswell@gmail.com>2016-02-07 14:26:55 -0500
commit75ae820c74e927679af82ef634a8f3ef0f97503f (patch)
treeb2b5df40c39feded29b9213ae6fdf77a654a121e /numpy/lib/nanfunctions.py
parent35b5f5be1ffffada84c8be207e7b8b196a58f786 (diff)
downloadnumpy-75ae820c74e927679af82ef634a8f3ef0f97503f.tar.gz
BUG: many functions silently drop `keepdims` kwarg
change test from `type(a) is not mu.ndarray` to `not isinstance(a, mu.ndarray)` Because every sub-class of ndarray is not guaranteed to implement `keepdims` as a kwarg, when wrapping these methods care must be taken. The previous behavior was to silently eat the kwarg when dealing with a sub-class of ndarray. Now, if `keepdims=np._NoValue` (the new default) it is not passed through to the underlying function call (so the default value of `keepdims` is now controlled by the sub-class). If `keepdims` is not `np._NoValue` then it is passed through and will raise an exception if the sub-class does not support the kwarg. A special case in nanvar was required to deal with `matrix` that previously relied on `fromnumeric` silently dropping `keepdims`.
Diffstat (limited to 'numpy/lib/nanfunctions.py')
-rw-r--r--numpy/lib/nanfunctions.py145
1 files changed, 106 insertions, 39 deletions
diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py
index 8fe7afd46..56f0010af 100644
--- a/numpy/lib/nanfunctions.py
+++ b/numpy/lib/nanfunctions.py
@@ -23,6 +23,7 @@ import warnings
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
-------