summaryrefslogtreecommitdiff
path: root/numpy/ma/core.py
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/ma/core.py')
-rw-r--r--numpy/ma/core.py183
1 files changed, 128 insertions, 55 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index 4fb7d8c28..82e5e7155 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -40,7 +40,6 @@ from numpy.compat import (
from numpy import expand_dims
from numpy.core.numeric import normalize_axis_tuple
from numpy.core._internal import recursive
-from numpy.compat import pickle
__all__ = [
@@ -399,10 +398,10 @@ def _recursive_set_fill_value(fillvalue, dt):
Parameters
----------
- fillvalue: scalar or array_like
+ fillvalue : scalar or array_like
Scalar or array representing the fill value. If it is of shorter
length than the number of fields in dt, it will be resized.
- dt: dtype
+ dt : dtype
The structured dtype for which to create the fill value.
Returns
@@ -914,7 +913,7 @@ class _MaskedUnaryOperation(_MaskedUFunc):
"""
def __init__(self, mufunc, fill=0, domain=None):
- super(_MaskedUnaryOperation, self).__init__(mufunc)
+ super().__init__(mufunc)
self.fill = fill
self.domain = domain
ufunc_domain[mufunc] = domain
@@ -996,7 +995,7 @@ class _MaskedBinaryOperation(_MaskedUFunc):
abfunc(x, filly) = x for all x to enable reduce.
"""
- super(_MaskedBinaryOperation, self).__init__(mbfunc)
+ super().__init__(mbfunc)
self.fillx = fillx
self.filly = filly
ufunc_domain[mbfunc] = None
@@ -1142,7 +1141,7 @@ class _DomainedBinaryOperation(_MaskedUFunc):
"""abfunc(fillx, filly) must be defined.
abfunc(x, filly) = x for all x to enable reduce.
"""
- super(_DomainedBinaryOperation, self).__init__(dbfunc)
+ super().__init__(dbfunc)
self.domain = domain
self.fillx = fillx
self.filly = filly
@@ -1936,6 +1935,10 @@ def masked_where(condition, a, copy=True):
result = a.view(cls)
# Assign to *.mask so that structured masks are handled correctly.
result.mask = _shrink_mask(cond)
+ # There is no view of a boolean so when 'a' is a MaskedArray with nomask
+ # the update to the result's mask has no effect.
+ if not copy and hasattr(a, '_mask') and getmask(a) is nomask:
+ a._mask = result._mask.view()
return result
@@ -2859,8 +2862,9 @@ class MaskedArray(ndarray):
elif isinstance(data, (tuple, list)):
try:
# If data is a sequence of masked array
- mask = np.array([getmaskarray(np.asanyarray(m, dtype=mdtype))
- for m in data], dtype=mdtype)
+ mask = np.array(
+ [getmaskarray(np.asanyarray(m, dtype=_data.dtype))
+ for m in data], dtype=mdtype)
except ValueError:
# If data is nested
mask = nomask
@@ -3399,7 +3403,7 @@ class MaskedArray(ndarray):
# Define so that we can overwrite the setter.
@property
def dtype(self):
- return super(MaskedArray, self).dtype
+ return super().dtype
@dtype.setter
def dtype(self, dtype):
@@ -3415,7 +3419,7 @@ class MaskedArray(ndarray):
@property
def shape(self):
- return super(MaskedArray, self).shape
+ return super().shape
@shape.setter
def shape(self, shape):
@@ -3894,7 +3898,7 @@ class MaskedArray(ndarray):
# Force the condition to a regular ndarray and forget the missing
# values.
- condition = np.array(condition, copy=False, subok=False)
+ condition = np.asarray(condition)
_new = _data.compress(condition, axis=axis, out=out).view(type(self))
_new._update_from(self)
@@ -4984,8 +4988,8 @@ class MaskedArray(ndarray):
#!!!: implement out + test!
m = self._mask
if m is nomask:
- result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
- axis2=axis2, out=out)
+ result = super().trace(offset=offset, axis1=axis1, axis2=axis2,
+ out=out)
return result.astype(dtype)
else:
D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
@@ -5220,7 +5224,7 @@ class MaskedArray(ndarray):
--------
numpy.ndarray.mean : corresponding function for ndarrays
numpy.mean : Equivalent function
- numpy.ma.average: Weighted average.
+ numpy.ma.average : Weighted average.
Examples
--------
@@ -5236,8 +5240,7 @@ class MaskedArray(ndarray):
kwargs = {} if keepdims is np._NoValue else {'keepdims': keepdims}
if self._mask is nomask:
- result = super(MaskedArray, self).mean(axis=axis,
- dtype=dtype, **kwargs)[()]
+ result = super().mean(axis=axis, dtype=dtype, **kwargs)[()]
else:
dsum = self.sum(axis=axis, dtype=dtype, **kwargs)
cnt = self.count(axis=axis, **kwargs)
@@ -5314,8 +5317,8 @@ class MaskedArray(ndarray):
# Easy case: nomask, business as usual
if self._mask is nomask:
- ret = super(MaskedArray, self).var(axis=axis, dtype=dtype, out=out,
- ddof=ddof, **kwargs)[()]
+ ret = super().var(axis=axis, dtype=dtype, out=out, ddof=ddof,
+ **kwargs)[()]
if out is not None:
if isinstance(out, MaskedArray):
out.__setmask__(nomask)
@@ -5439,7 +5442,7 @@ class MaskedArray(ndarray):
When the array contains unmasked values at the same extremes of the
datatype, the ordering of these values and the masked values is
undefined.
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used internally for the masked values.
If ``fill_value`` is not None, it supersedes ``endwith``.
@@ -5497,7 +5500,7 @@ class MaskedArray(ndarray):
axis : {None, integer}
If None, the index is into the flattened array, otherwise along
the specified axis
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used to fill in the masked values. If None, the output of
minimum_fill_value(self._data) is used instead.
out : {None, array}, optional
@@ -5543,7 +5546,7 @@ class MaskedArray(ndarray):
axis : {None, integer}
If None, the index is into the flattened array, otherwise along
the specified axis
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used to fill in the masked values. If None, the output of
maximum_fill_value(self._data) is used instead.
out : {None, array}, optional
@@ -5594,7 +5597,7 @@ class MaskedArray(ndarray):
When the array contains unmasked values sorting at the same extremes of the
datatype, the ordering of these values and the masked values is
undefined.
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used internally for the masked values.
If ``fill_value`` is not None, it supersedes ``endwith``.
@@ -5665,7 +5668,7 @@ class MaskedArray(ndarray):
out : array_like, optional
Alternative output array in which to place the result. Must be of
the same shape and buffer length as the expected output.
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used to fill in the masked values.
If None, use the output of `minimum_fill_value`.
keepdims : bool, optional
@@ -5799,7 +5802,7 @@ class MaskedArray(ndarray):
out : array_like, optional
Alternative output array in which to place the result. Must
be of the same shape and buffer length as the expected output.
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used to fill in the masked values.
If None, use the output of maximum_fill_value().
keepdims : bool, optional
@@ -5876,7 +5879,7 @@ class MaskedArray(ndarray):
Alternative output array in which to place the result. It must
have the same shape and buffer length as the expected output
but the type will be cast if necessary.
- fill_value : {var}, optional
+ fill_value : scalar or None, optional
Value used to fill in the masked values.
keepdims : bool, optional
If this is set to True, the axes which are reduced are left
@@ -5946,13 +5949,13 @@ class MaskedArray(ndarray):
warnings.warn("Warning: 'partition' will ignore the 'mask' "
f"of the {self.__class__.__name__}.",
stacklevel=2)
- return super(MaskedArray, self).partition(*args, **kwargs)
+ return super().partition(*args, **kwargs)
def argpartition(self, *args, **kwargs):
warnings.warn("Warning: 'argpartition' will ignore the 'mask' "
f"of the {self.__class__.__name__}.",
stacklevel=2)
- return super(MaskedArray, self).argpartition(*args, **kwargs)
+ return super().argpartition(*args, **kwargs)
def take(self, indices, axis=None, out=None, mode='raise'):
"""
@@ -6178,7 +6181,7 @@ class MaskedArray(ndarray):
"""
cf = 'CF'[self.flags.fnc]
- data_state = super(MaskedArray, self).__reduce__()[2]
+ data_state = super().__reduce__()[2]
return data_state + (getmaskarray(self).tobytes(cf), self._fill_value)
def __setstate__(self, state):
@@ -6194,7 +6197,7 @@ class MaskedArray(ndarray):
"""
(_, shp, typ, isf, raw, msk, flv) = state
- super(MaskedArray, self).__setstate__((shp, typ, isf, raw))
+ super().__setstate__((shp, typ, isf, raw))
self._mask.__setstate__((shp, make_mask_descr(typ), isf, msk))
self.fill_value = flv
@@ -6255,7 +6258,7 @@ class mvoid(MaskedArray):
@property
def _data(self):
# Make sure that the _data part is a np.void
- return super(mvoid, self)._data[()]
+ return super()._data[()]
def __getitem__(self, indx):
"""
@@ -6292,7 +6295,7 @@ class mvoid(MaskedArray):
return str(self._data)
rdtype = _replace_dtype_fields(self._data.dtype, "O")
- data_arr = super(mvoid, self)._data
+ data_arr = super()._data
res = data_arr.astype(rdtype)
_recursive_printoption(res, self._mask, masked_print_option)
return str(res)
@@ -6454,7 +6457,7 @@ class MaskedConstant(MaskedArray):
if not self.__has_singleton():
# this handles the `.view` in __new__, which we want to copy across
# properties normally
- return super(MaskedConstant, self).__array_finalize__(obj)
+ return super().__array_finalize__(obj)
elif self is self.__singleton:
# not clear how this can happen, play it safe
pass
@@ -6528,14 +6531,14 @@ class MaskedConstant(MaskedArray):
def __setattr__(self, attr, value):
if not self.__has_singleton():
# allow the singleton to be initialized
- return super(MaskedConstant, self).__setattr__(attr, value)
+ return super().__setattr__(attr, value)
elif self is self.__singleton:
raise AttributeError(
f"attributes of {self!r} are not writeable")
else:
# duplicate instance - we can end up here from __array_finalize__,
# where we set the __class__ attribute
- return super(MaskedConstant, self).__setattr__(attr, value)
+ return super().__setattr__(attr, value)
masked = masked_singleton = MaskedConstant()
@@ -6627,7 +6630,7 @@ class _extrema_operation(_MaskedUFunc):
"""
def __init__(self, ufunc, compare, fill_value):
- super(_extrema_operation, self).__init__(ufunc)
+ super().__init__(ufunc)
self.compare = compare
self.fill_value_func = fill_value
@@ -6913,8 +6916,7 @@ def compressed(x):
See Also
--------
- ma.MaskedArray.compressed
- Equivalent method.
+ ma.MaskedArray.compressed : Equivalent method.
"""
return asanyarray(x).compressed()
@@ -7339,16 +7341,16 @@ def where(condition, x=_NoValue, y=_NoValue):
def choose(indices, choices, out=None, mode='raise'):
"""
- Use an index array to construct a new array from a set of choices.
+ Use an index array to construct a new array from a list of choices.
- Given an array of integers and a set of n choice arrays, this method
+ Given an array of integers and a list of n choice arrays, this method
will create a new array that merges each of the choice arrays. Where a
- value in `a` is i, the new array will have the value that choices[i]
+ value in `index` is i, the new array will have the value that choices[i]
contains in the same place.
Parameters
----------
- a : ndarray of ints
+ indices : ndarray of ints
This array must contain integers in ``[0, n-1]``, where n is the
number of choices.
choices : sequence of arrays
@@ -7870,9 +7872,14 @@ def allclose(a, b, masked_equal=True, rtol=1e-5, atol=1e-8):
# make sure y is an inexact type to avoid abs(MIN_INT); will cause
# casting of x later.
- dtype = np.result_type(y, 1.)
- if y.dtype != dtype:
- y = masked_array(y, dtype=dtype, copy=False)
+ # NOTE: We explicitly allow timedelta, which used to work. This could
+ # possibly be deprecated. See also gh-18286.
+ # timedelta works if `atol` is an integer or also a timedelta.
+ # Although, the default tolerances are unlikely to be useful
+ if y.dtype.kind != "m":
+ dtype = np.result_type(y, 1.)
+ if y.dtype != dtype:
+ y = masked_array(y, dtype=dtype, copy=False)
m = mask_or(getmask(x), getmask(y))
xinf = np.isinf(masked_array(x, copy=False, mask=m)).filled(False)
@@ -8087,22 +8094,51 @@ class _convert2ma:
"""
__doc__ = None
- def __init__(self, funcname, params=None):
+ def __init__(self, funcname, np_ret, np_ma_ret, params=None):
self._func = getattr(np, funcname)
- self.__doc__ = self.getdoc()
+ self.__doc__ = self.getdoc(np_ret, np_ma_ret)
self._extras = params or {}
- def getdoc(self):
+ def getdoc(self, np_ret, np_ma_ret):
"Return the doc of the function (from the doc of the method)."
doc = getattr(self._func, '__doc__', None)
sig = get_object_signature(self._func)
if doc:
+ doc = self._replace_return_type(doc, np_ret, np_ma_ret)
# Add the signature of the function at the beginning of the doc
if sig:
sig = "%s%s\n" % (self._func.__name__, sig)
doc = sig + doc
return doc
+ def _replace_return_type(self, doc, np_ret, np_ma_ret):
+ """
+ Replace documentation of ``np`` function's return type.
+
+ Replaces it with the proper type for the ``np.ma`` function.
+
+ Parameters
+ ----------
+ doc : str
+ The documentation of the ``np`` method.
+ np_ret : str
+ The return type string of the ``np`` method that we want to
+ replace. (e.g. "out : ndarray")
+ np_ma_ret : str
+ The return type string of the ``np.ma`` method.
+ (e.g. "out : MaskedArray")
+ """
+ if np_ret not in doc:
+ raise RuntimeError(
+ f"Failed to replace `{np_ret}` with `{np_ma_ret}`. "
+ f"The documentation string for return type, {np_ret}, is not "
+ f"found in the docstring for `np.{self._func.__name__}`. "
+ f"Fix the docstring for `np.{self._func.__name__}` or "
+ "update the expected string for return type."
+ )
+
+ return doc.replace(np_ret, np_ma_ret)
+
def __call__(self, *args, **params):
# Find the common parameters to the call and the definition
_extras = self._extras
@@ -8118,20 +8154,57 @@ class _convert2ma:
result._hardmask = bool(_extras.get("hard_mask", False))
return result
-arange = _convert2ma('arange', params=dict(fill_value=None, hardmask=False))
+
+arange = _convert2ma(
+ 'arange',
+ params=dict(fill_value=None, hardmask=False),
+ np_ret='arange : ndarray',
+ np_ma_ret='arange : MaskedArray',
+)
clip = np.clip
diff = np.diff
-empty = _convert2ma('empty', params=dict(fill_value=None, hardmask=False))
-empty_like = _convert2ma('empty_like')
-frombuffer = _convert2ma('frombuffer')
-fromfunction = _convert2ma('fromfunction')
+empty = _convert2ma(
+ 'empty',
+ params=dict(fill_value=None, hardmask=False),
+ np_ret='out : ndarray',
+ np_ma_ret='out : MaskedArray',
+)
+empty_like = _convert2ma(
+ 'empty_like',
+ np_ret='out : ndarray',
+ np_ma_ret='out : MaskedArray',
+)
+frombuffer = _convert2ma(
+ 'frombuffer',
+ np_ret='out : ndarray',
+ np_ma_ret='out: MaskedArray',
+)
+fromfunction = _convert2ma(
+ 'fromfunction',
+ np_ret='fromfunction : any',
+ np_ma_ret='fromfunction: MaskedArray',
+)
identity = _convert2ma(
- 'identity', params=dict(fill_value=None, hardmask=False))
+ 'identity',
+ params=dict(fill_value=None, hardmask=False),
+ np_ret='out : ndarray',
+ np_ma_ret='out : MaskedArray',
+)
indices = np.indices
-ones = _convert2ma('ones', params=dict(fill_value=None, hardmask=False))
+ones = _convert2ma(
+ 'ones',
+ params=dict(fill_value=None, hardmask=False),
+ np_ret='out : ndarray',
+ np_ma_ret='out : MaskedArray',
+)
ones_like = np.ones_like
squeeze = np.squeeze
-zeros = _convert2ma('zeros', params=dict(fill_value=None, hardmask=False))
+zeros = _convert2ma(
+ 'zeros',
+ params=dict(fill_value=None, hardmask=False),
+ np_ret='out : ndarray',
+ np_ma_ret='out : MaskedArray',
+)
zeros_like = np.zeros_like