diff options
Diffstat (limited to 'numpy/lib/nanfunctions.py')
-rw-r--r-- | numpy/lib/nanfunctions.py | 203 |
1 files changed, 162 insertions, 41 deletions
diff --git a/numpy/lib/nanfunctions.py b/numpy/lib/nanfunctions.py index 2c2c3435b..08d9b42bb 100644 --- a/numpy/lib/nanfunctions.py +++ b/numpy/lib/nanfunctions.py @@ -160,8 +160,12 @@ def _remove_nan_1d(arr1d, overwrite_input=False): True if `res` can be modified in place, given the constraint on the input """ + if arr1d.dtype == object: + # object arrays do not support `isnan` (gh-9009), so make a guess + c = np.not_equal(arr1d, arr1d, dtype=bool) + else: + c = np.isnan(arr1d) - c = np.isnan(arr1d) s = np.nonzero(c)[0] if s.size == arr1d.size: warnings.warn("All-NaN slice encountered", RuntimeWarning, @@ -214,19 +218,25 @@ def _divide_by_count(a, b, out=None): return np.divide(a, b, out=out, casting='unsafe') else: if out is None: - return a.dtype.type(a / b) + # Precaution against reduced object arrays + try: + return a.dtype.type(a / b) + except AttributeError: + return a / b else: # This is questionable, but currently a numpy scalar can # be output to a zero dimensional array. return np.divide(a, b, out=out, casting='unsafe') -def _nanmin_dispatcher(a, axis=None, out=None, keepdims=None): +def _nanmin_dispatcher(a, axis=None, out=None, keepdims=None, + initial=None, where=None): return (a, out) @array_function_dispatch(_nanmin_dispatcher) -def nanmin(a, axis=None, out=None, keepdims=np._NoValue): +def nanmin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=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 @@ -258,6 +268,16 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + The maximum value of an output element. Must be present to allow + computation on empty slice. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to compare for the minimum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -313,6 +333,11 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if initial is not np._NoValue: + kwargs['initial'] = initial + if where is not np._NoValue: + kwargs['where'] = where + 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) @@ -328,6 +353,7 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): return res # Check for all-NaN axis + kwargs.pop("initial", None) mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) @@ -336,12 +362,14 @@ def nanmin(a, axis=None, out=None, keepdims=np._NoValue): return res -def _nanmax_dispatcher(a, axis=None, out=None, keepdims=None): +def _nanmax_dispatcher(a, axis=None, out=None, keepdims=None, + initial=None, where=None): return (a, out) @array_function_dispatch(_nanmax_dispatcher) -def nanmax(a, axis=None, out=None, keepdims=np._NoValue): +def nanmax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue, + where=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 @@ -373,6 +401,16 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + The minimum value of an output element. Must be present to allow + computation on empty slice. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to compare for the maximum. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -428,6 +466,11 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): kwargs = {} if keepdims is not np._NoValue: kwargs['keepdims'] = keepdims + if initial is not np._NoValue: + kwargs['initial'] = initial + if where is not np._NoValue: + kwargs['where'] = where + 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) @@ -443,6 +486,7 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): return res # Check for all-NaN axis + kwargs.pop("initial", None) mask = np.all(mask, axis=axis, **kwargs) if np.any(mask): res = _copyto(res, np.nan, mask) @@ -451,12 +495,12 @@ def nanmax(a, axis=None, out=None, keepdims=np._NoValue): return res -def _nanargmin_dispatcher(a, axis=None): +def _nanargmin_dispatcher(a, axis=None, out=None, *, keepdims=None): return (a,) @array_function_dispatch(_nanargmin_dispatcher) -def nanargmin(a, axis=None): +def nanargmin(a, axis=None, out=None, *, keepdims=np._NoValue): """ Return the indices of the minimum values in the specified axis ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Warning: the results @@ -468,6 +512,17 @@ def nanargmin(a, axis=None): Input data. axis : int, optional Axis along which to operate. By default flattened input is used. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + + .. versionadded:: 1.22.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 array. + + .. versionadded:: 1.22.0 Returns ------- @@ -492,20 +547,20 @@ def nanargmin(a, axis=None): """ a, mask = _replace_nan(a, np.inf) - res = np.argmin(a, axis=axis) if mask is not None: mask = np.all(mask, axis=axis) if np.any(mask): raise ValueError("All-NaN slice encountered") + res = np.argmin(a, axis=axis, out=out, keepdims=keepdims) return res -def _nanargmax_dispatcher(a, axis=None): +def _nanargmax_dispatcher(a, axis=None, out=None, *, keepdims=None): return (a,) @array_function_dispatch(_nanargmax_dispatcher) -def nanargmax(a, axis=None): +def nanargmax(a, axis=None, out=None, *, keepdims=np._NoValue): """ Return the indices of the maximum values in the specified axis ignoring NaNs. For all-NaN slices ``ValueError`` is raised. Warning: the @@ -518,6 +573,17 @@ def nanargmax(a, axis=None): Input data. axis : int, optional Axis along which to operate. By default flattened input is used. + out : array, optional + If provided, the result will be inserted into this array. It should + be of the appropriate shape and dtype. + + .. versionadded:: 1.22.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 array. + + .. versionadded:: 1.22.0 Returns ------- @@ -542,20 +608,22 @@ def nanargmax(a, axis=None): """ a, mask = _replace_nan(a, -np.inf) - res = np.argmax(a, axis=axis) if mask is not None: mask = np.all(mask, axis=axis) if np.any(mask): raise ValueError("All-NaN slice encountered") + res = np.argmax(a, axis=axis, out=out, keepdims=keepdims) return res -def _nansum_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None): +def _nansum_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): return (a, out) @array_function_dispatch(_nansum_dispatcher) -def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Return the sum of array elements over a given axis treating Not a Numbers (NaNs) as zero. @@ -600,6 +668,14 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): does not implement `keepdims` any exceptions will be raised. .. versionadded:: 1.8.0 + initial : scalar, optional + Starting value for the sum. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to include in the sum. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -645,15 +721,18 @@ def nansum(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ a, mask = _replace_nan(a, 0) - return np.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.sum(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + initial=initial, where=where) -def _nanprod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None): +def _nanprod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + initial=None, where=None): return (a, out) @array_function_dispatch(_nanprod_dispatcher) -def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + initial=np._NoValue, where=np._NoValue): """ Return the product of array elements over a given axis treating Not a Numbers (NaNs) as ones. @@ -687,6 +766,16 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): 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`. + initial : scalar, optional + The starting value for this product. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 + where : array_like of bool, optional + Elements to include in the product. See `~numpy.ufunc.reduce` + for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -715,7 +804,8 @@ def nanprod(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ a, mask = _replace_nan(a, 1) - return np.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.prod(a, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + initial=initial, where=where) def _nancumsum_dispatcher(a, axis=None, dtype=None, out=None): @@ -855,12 +945,14 @@ def nancumprod(a, axis=None, dtype=None, out=None): return np.cumprod(a, axis=axis, dtype=dtype, out=out) -def _nanmean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None): +def _nanmean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, + *, where=None): return (a, out) @array_function_dispatch(_nanmean_dispatcher) -def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): +def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the arithmetic mean along the specified axis, ignoring NaNs. @@ -898,6 +990,10 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): `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. + where : array_like of bool, optional + Elements to include in the mean. See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -936,7 +1032,8 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): """ arr, mask = _replace_nan(a, 0) if mask is None: - return np.mean(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + return np.mean(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) if dtype is not None: dtype = np.dtype(dtype) @@ -945,8 +1042,10 @@ def nanmean(a, axis=None, dtype=None, out=None, keepdims=np._NoValue): if out is not None and not issubclass(out.dtype.type, np.inexact): raise TypeError("If a is inexact, then out must be inexact") - cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=keepdims) - tot = np.sum(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) + cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=keepdims, + where=where) + tot = np.sum(arr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) avg = _divide_by_count(tot, cnt, out=out) isbad = (cnt == 0) @@ -967,7 +1066,7 @@ def _nanmedian1d(arr1d, overwrite_input=False): ) if arr1d_parsed.size == 0: - # Ensure that a nan-esque scalar of the appropiate type (and unit) + # Ensure that a nan-esque scalar of the appropriate type (and unit) # is returned for `timedelta64` and `complexfloating` return arr1d[-1] @@ -1413,19 +1512,21 @@ def _nanquantile_1d(arr1d, q, overwrite_input=False, interpolation='linear'): arr1d, overwrite_input = _remove_nan_1d(arr1d, overwrite_input=overwrite_input) if arr1d.size == 0: - return np.full(q.shape, np.nan)[()] # convert to scalar + # convert to scalar + return np.full(q.shape, np.nan, dtype=arr1d.dtype)[()] return function_base._quantile_unchecked( arr1d, q, overwrite_input=overwrite_input, interpolation=interpolation) -def _nanvar_dispatcher( - a, axis=None, dtype=None, out=None, ddof=None, keepdims=None): +def _nanvar_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): return (a, out) @array_function_dispatch(_nanvar_dispatcher) -def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the variance along the specified axis, while ignoring NaNs. @@ -1462,7 +1563,11 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): 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`. + where : array_like of bool, optional + Elements to include in the variance. See `~numpy.ufunc.reduce` for + details. + .. versionadded:: 1.22.0 Returns ------- @@ -1518,7 +1623,7 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): arr, mask = _replace_nan(a, 0) if mask is None: return np.var(arr, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + keepdims=keepdims, where=where) if dtype is not None: dtype = np.dtype(dtype) @@ -1537,21 +1642,29 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): # 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) + cnt = np.sum(~mask, axis=axis, dtype=np.intp, keepdims=_keepdims, + where=where) + avg = np.sum(arr, axis=axis, dtype=dtype, keepdims=_keepdims, where=where) avg = _divide_by_count(avg, cnt) # Compute squared deviation from mean. - np.subtract(arr, avg, out=arr, casting='unsafe') + np.subtract(arr, avg, out=arr, casting='unsafe', where=where) arr = _copyto(arr, 0, mask) if issubclass(arr.dtype.type, np.complexfloating): - sqr = np.multiply(arr, arr.conj(), out=arr).real + sqr = np.multiply(arr, arr.conj(), out=arr, where=where).real else: - sqr = np.multiply(arr, arr, out=arr) + sqr = np.multiply(arr, arr, out=arr, where=where) # Compute variance. - var = np.sum(sqr, axis=axis, dtype=dtype, out=out, keepdims=keepdims) - if var.ndim < cnt.ndim: + var = np.sum(sqr, axis=axis, dtype=dtype, out=out, keepdims=keepdims, + where=where) + + # Precaution against reduced object arrays + try: + var_ndim = var.ndim + except AttributeError: + var_ndim = np.ndim(var) + if var_ndim < cnt.ndim: # Subclasses of ndarray may ignore keepdims, so check here. cnt = cnt.squeeze(axis) dof = cnt - ddof @@ -1567,13 +1680,14 @@ def nanvar(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): return var -def _nanstd_dispatcher( - a, axis=None, dtype=None, out=None, ddof=None, keepdims=None): +def _nanstd_dispatcher(a, axis=None, dtype=None, out=None, ddof=None, + keepdims=None, *, where=None): return (a, out) @array_function_dispatch(_nanstd_dispatcher) -def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): +def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, + *, where=np._NoValue): """ Compute the standard deviation along the specified axis, while ignoring NaNs. @@ -1617,6 +1731,11 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): as-is to the relevant functions of the sub-classes. If these functions do not have a `keepdims` kwarg, a RuntimeError will be raised. + where : array_like of bool, optional + Elements to include in the standard deviation. + See `~numpy.ufunc.reduce` for details. + + .. versionadded:: 1.22.0 Returns ------- @@ -1668,9 +1787,11 @@ def nanstd(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue): """ var = nanvar(a, axis=axis, dtype=dtype, out=out, ddof=ddof, - keepdims=keepdims) + keepdims=keepdims, where=where) if isinstance(var, np.ndarray): std = np.sqrt(var, out=var) - else: + elif hasattr(var, 'dtype'): std = var.dtype.type(np.sqrt(var)) + else: + std = np.sqrt(var) return std |