| from __future__ import annotations |
|
|
| import warnings |
|
|
| import numpy |
|
|
| import cupy |
| from cupy import _core |
| from cupy._core import _routines_statistics as _statistics |
| from cupy._core import _fusion_thread_local |
| from cupy._logic import content |
|
|
|
|
| def amin(a, axis=None, out=None, keepdims=False): |
| """Returns the minimum of an array or the minimum along an axis. |
| |
| .. note:: |
| |
| When at least one element is NaN, the corresponding min value will be |
| NaN. |
| |
| Args: |
| a (cupy.ndarray): Array to take the minimum. |
| axis (int): Along which axis to take the minimum. The flattened array |
| is used by default. |
| out (cupy.ndarray): Output array. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| |
| Returns: |
| cupy.ndarray: The minimum of ``a``, along the axis if specified. |
| |
| .. note:: |
| When cuTENSOR accelerator is used, the output value might be collapsed |
| for reduction axes that have one or more NaN elements. |
| |
| .. seealso:: :func:`numpy.amin` |
| |
| """ |
| if _fusion_thread_local.is_fusing(): |
| if keepdims: |
| raise NotImplementedError( |
| 'cupy.amin does not support `keepdims` in fusion yet.') |
| return _fusion_thread_local.call_reduction( |
| _statistics.amin, a, axis=axis, out=out) |
|
|
| |
| return a.min(axis=axis, out=out, keepdims=keepdims) |
|
|
|
|
| def amax(a, axis=None, out=None, keepdims=False): |
| """Returns the maximum of an array or the maximum along an axis. |
| |
| .. note:: |
| |
| When at least one element is NaN, the corresponding min value will be |
| NaN. |
| |
| Args: |
| a (cupy.ndarray): Array to take the maximum. |
| axis (int): Along which axis to take the maximum. The flattened array |
| is used by default. |
| out (cupy.ndarray): Output array. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| |
| Returns: |
| cupy.ndarray: The maximum of ``a``, along the axis if specified. |
| |
| .. note:: |
| When cuTENSOR accelerator is used, the output value might be collapsed |
| for reduction axes that have one or more NaN elements. |
| |
| .. seealso:: :func:`numpy.amax` |
| |
| """ |
| if _fusion_thread_local.is_fusing(): |
| if keepdims: |
| raise NotImplementedError( |
| 'cupy.amax does not support `keepdims` in fusion yet.') |
| return _fusion_thread_local.call_reduction( |
| _statistics.amax, a, axis=axis, out=out) |
|
|
| |
| return a.max(axis=axis, out=out, keepdims=keepdims) |
|
|
|
|
| def nanmin(a, axis=None, out=None, keepdims=False): |
| """Returns the minimum of an array along an axis ignoring NaN. |
| |
| When there is a slice whose elements are all NaN, a :class:`RuntimeWarning` |
| is raised and NaN is returned. |
| |
| Args: |
| a (cupy.ndarray): Array to take the minimum. |
| axis (int): Along which axis to take the minimum. The flattened array |
| is used by default. |
| out (cupy.ndarray): Output array. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| |
| Returns: |
| cupy.ndarray: The minimum of ``a``, along the axis if specified. |
| |
| .. warning:: |
| |
| This function may synchronize the device. |
| |
| .. seealso:: :func:`numpy.nanmin` |
| |
| """ |
| |
| res = _core.nanmin(a, axis=axis, out=out, keepdims=keepdims) |
| if content.isnan(res).any(): |
| warnings.warn('All-NaN slice encountered', RuntimeWarning) |
| return res |
|
|
|
|
| def nanmax(a, axis=None, out=None, keepdims=False): |
| """Returns the maximum of an array along an axis ignoring NaN. |
| |
| When there is a slice whose elements are all NaN, a :class:`RuntimeWarning` |
| is raised and NaN is returned. |
| |
| Args: |
| a (cupy.ndarray): Array to take the maximum. |
| axis (int): Along which axis to take the maximum. The flattened array |
| is used by default. |
| out (cupy.ndarray): Output array. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| |
| Returns: |
| cupy.ndarray: The maximum of ``a``, along the axis if specified. |
| |
| .. warning:: |
| |
| This function may synchronize the device. |
| |
| .. seealso:: :func:`numpy.nanmax` |
| |
| """ |
| |
| res = _core.nanmax(a, axis=axis, out=out, keepdims=keepdims) |
| if content.isnan(res).any(): |
| warnings.warn('All-NaN slice encountered', RuntimeWarning) |
| return res |
|
|
|
|
| def ptp(a, axis=None, out=None, keepdims=False): |
| """Returns the range of values (maximum - minimum) along an axis. |
| |
| .. note:: |
| |
| The name of the function comes from the acronym for 'peak to peak'. |
| |
| When at least one element is NaN, the corresponding ptp value will be |
| NaN. |
| |
| Args: |
| a (cupy.ndarray): Array over which to take the range. |
| axis (int): Axis along which to take the minimum. The flattened |
| array is used by default. |
| out (cupy.ndarray): Output array. |
| keepdims (bool): If ``True``, the axis is retained as an axis of |
| size one. |
| |
| Returns: |
| cupy.ndarray: The minimum of ``a``, along the axis if specified. |
| |
| .. note:: |
| When cuTENSOR accelerator is used, the output value might be collapsed |
| for reduction axes that have one or more NaN elements. |
| |
| .. seealso:: :func:`numpy.amin` |
| |
| """ |
| return a.ptp(axis=axis, out=out, keepdims=keepdims) |
|
|
|
|
| def _quantile_unchecked(a, q, axis=None, out=None, |
| overwrite_input=False, |
| method='linear', |
| keepdims=False): |
| dtype = cupy.result_type(a, q) |
| q = cupy.asarray(q) |
|
|
| if q.ndim == 0: |
| q = q[None] |
| zerod = True |
| else: |
| zerod = False |
| if q.ndim > 1: |
| raise ValueError('Expected q to have a dimension of 1.\n' |
| 'Actual: {} != 1'.format(q.ndim)) |
| if isinstance(axis, int): |
| axis = axis, |
| if keepdims: |
| if axis is None: |
| keepdim = (1,) * a.ndim |
| else: |
| keepdim = list(a.shape) |
| for ax in axis: |
| keepdim[ax % a.ndim] = 1 |
| keepdim = tuple(keepdim) |
| if axis is None: |
| if overwrite_input: |
| ap = a.ravel() |
| else: |
| ap = a.flatten() |
| nkeep = 0 |
| else: |
| |
| axis = tuple(ax % a.ndim for ax in axis) |
| keep = set(range(a.ndim)) - set(axis) |
| nkeep = len(keep) |
| for i, s in enumerate(sorted(keep)): |
| a = a.swapaxes(i, s) |
| if overwrite_input: |
| ap = a.reshape(a.shape[:nkeep] + (-1,)) |
| else: |
| ap = a.reshape(a.shape[:nkeep] + (-1,)).copy() |
|
|
| axis = -1 |
| ap.sort(axis=axis) |
| Nx = ap.shape[axis] |
| indices = q * (Nx - 1.) |
|
|
| if method in ['inverted_cdf', 'averaged_inverted_cdf', |
| 'closest_observation', 'interpolated_inverted_cdf', |
| 'hazen', 'weibull', 'median_unbiased', 'normal_unbiased']: |
| |
| raise ValueError(f'\'{method}\' method is not yet supported. ' |
| 'Please use any other method.') |
| elif method == 'lower': |
| indices = cupy.floor(indices).astype(cupy.int32) |
| elif method == 'higher': |
| indices = cupy.ceil(indices).astype(cupy.int32) |
| elif method == 'midpoint': |
| indices = 0.5 * (cupy.floor(indices) + cupy.ceil(indices)) |
| elif method == 'nearest': |
| indices = cupy.around(indices).astype(cupy.int32) |
| elif method == 'linear': |
| pass |
| else: |
| raise ValueError('Unexpected interpolation method.\n' |
| 'Actual: \'{}\' not in (\'linear\', \'lower\', ' |
| '\'higher\', \'midpoint\')'.format(method)) |
|
|
| if indices.dtype == cupy.int32: |
| ret = cupy.rollaxis(ap, axis) |
| ret = ret.take(indices, axis=0, out=out) |
| else: |
| if out is None: |
| ret = cupy.empty(ap.shape[:-1] + q.shape, dtype=dtype) |
| else: |
| ret = cupy.rollaxis(out, 0, out.ndim) |
|
|
| cupy.ElementwiseKernel( |
| 'S idx, raw T a, raw int32 offset, raw int32 size', 'U ret', |
| ''' |
| ptrdiff_t idx_below = floor(idx); |
| U weight_above = idx - idx_below; |
| |
| ptrdiff_t max_idx = size - 1; |
| ptrdiff_t offset_bottom = _ind.get()[0] * offset + idx_below; |
| ptrdiff_t offset_top = min(offset_bottom + 1, max_idx); |
| |
| U diff = a[offset_top] - a[offset_bottom]; |
| |
| if (weight_above < 0.5) { |
| ret = a[offset_bottom] + diff * weight_above; |
| } else { |
| ret = a[offset_top] - diff * (1 - weight_above); |
| } |
| ''', |
| 'cupy_percentile_weightnening' |
| )(indices, ap, ap.shape[-1] if ap.ndim > 1 else 0, ap.size, ret) |
| ret = cupy.rollaxis(ret, -1) |
|
|
| if zerod: |
| ret = ret.squeeze(0) |
| if keepdims: |
| if q.size > 1: |
| keepdim = (-1,) + keepdim |
| ret = ret.reshape(keepdim) |
|
|
| return _core._internal_ascontiguousarray(ret) |
|
|
|
|
| def _quantile_is_valid(q): |
| xp = cupy if isinstance(q, cupy.ndarray) else numpy |
| return xp.count_nonzero(0.0 <= q) and xp.count_nonzero(q <= 1.0) |
|
|
|
|
| def percentile(a, q, axis=None, out=None, |
| overwrite_input=False, |
| method='linear', |
| keepdims=False, |
| *, |
| interpolation=None): |
| """Computes the q-th percentile of the data along the specified axis. |
| |
| Args: |
| a (cupy.ndarray): Array for which to compute percentiles. |
| q (float, tuple of floats or cupy.ndarray): Percentiles to compute |
| in the range between 0 and 100 inclusive. |
| axis (int or tuple of ints): Along which axis or axes to compute the |
| percentiles. The flattened array is used by default. |
| out (cupy.ndarray): Output array. |
| overwrite_input (bool): If True, then allow the input array `a` |
| to be modified by the intermediate calculations, to save |
| memory. In this case, the contents of the input `a` after this |
| function completes is undefined. |
| method (str): Interpolation method when a quantile lies between |
| two data points. ``linear`` interpolation is used by default. |
| Supported interpolations are ``lower``, ``higher``, ``midpoint``, |
| ``nearest`` and ``linear``. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| interpolation (str): Deprecated name for the method keyword argument. |
| |
| Returns: |
| cupy.ndarray: The percentiles of ``a``, along the axis if specified. |
| |
| .. seealso:: :func:`numpy.percentile` |
| """ |
| if interpolation is not None: |
| method = _check_interpolation_as_method( |
| method, interpolation, 'percentile') |
| if isinstance(q, (tuple, list)): |
| |
| |
| q = numpy.asarray(q) |
| q = q / 100 |
| if not _quantile_is_valid(q): |
| raise ValueError('Percentiles must be in the range [0, 100]') |
| return _quantile_unchecked( |
| a, q, axis=axis, out=out, |
| overwrite_input=overwrite_input, |
| method=method, |
| keepdims=keepdims) |
|
|
|
|
| def quantile(a, q, axis=None, out=None, |
| overwrite_input=False, |
| method='linear', |
| keepdims=False, |
| *, |
| interpolation=None): |
| """Computes the q-th quantile of the data along the specified axis. |
| |
| Args: |
| a (cupy.ndarray): Array for which to compute quantiles. |
| q (float, tuple of floats or cupy.ndarray): Quantiles to compute |
| in the range between 0 and 1 inclusive. |
| axis (int or tuple of ints): Along which axis or axes to compute the |
| quantiles. The flattened array is used by default. |
| out (cupy.ndarray): Output array. |
| overwrite_input (bool): If True, then allow the input array `a` |
| to be modified by the intermediate calculations, to save |
| memory. In this case, the contents of the input `a` after this |
| function completes is undefined. |
| method (str): Interpolation method when a quantile lies between |
| two data points. ``linear`` interpolation is used by default. |
| Supported interpolations are ``lower``, ``higher``, ``midpoint``, |
| ``nearest`` and ``linear``. |
| keepdims (bool): If ``True``, the axis is remained as an axis of |
| size one. |
| interpolation (str): Deprecated name for the method keyword argument. |
| |
| Returns: |
| cupy.ndarray: The quantiles of ``a``, along the axis if specified. |
| |
| .. seealso:: :func:`numpy.quantile` |
| """ |
| if interpolation is not None: |
| method = _check_interpolation_as_method( |
| method, interpolation, 'quantile') |
| if isinstance(q, (tuple, list)): |
| |
| |
| q = numpy.asarray(q) |
| if not _quantile_is_valid(q): |
| raise ValueError('Quantiles must be in the range [0, 1]') |
| return _quantile_unchecked( |
| a, q, axis=axis, out=out, |
| overwrite_input=overwrite_input, |
| method=method, |
| keepdims=keepdims) |
|
|
|
|
| |
| def _check_interpolation_as_method(method, interpolation, fname): |
| |
| warnings.warn( |
| f"the `interpolation=` argument to {fname} was renamed to " |
| "`method=`, which has additional options.\n" |
| "Users of the modes 'nearest', 'lower', 'higher', or " |
| "'midpoint' are encouraged to review the method they. " |
| "(Deprecated NumPy 1.22)", |
| DeprecationWarning, stacklevel=3) |
| if method != "linear": |
| |
| raise TypeError( |
| "You shall not pass both `method` and `interpolation`!\n" |
| "(`interpolation` is Deprecated in favor of `method`)") |
| return interpolation |
|
|