summaryrefslogtreecommitdiffstats
path: root/venv/lib/python3.9/site-packages/numpy/ma/extras.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.9/site-packages/numpy/ma/extras.py')
-rw-r--r--venv/lib/python3.9/site-packages/numpy/ma/extras.py2045
1 files changed, 2045 insertions, 0 deletions
diff --git a/venv/lib/python3.9/site-packages/numpy/ma/extras.py b/venv/lib/python3.9/site-packages/numpy/ma/extras.py
new file mode 100644
index 00000000..41bce0f2
--- /dev/null
+++ b/venv/lib/python3.9/site-packages/numpy/ma/extras.py
@@ -0,0 +1,2045 @@
+"""
+Masked arrays add-ons.
+
+A collection of utilities for `numpy.ma`.
+
+:author: Pierre Gerard-Marchant
+:contact: pierregm_at_uga_dot_edu
+:version: $Id: extras.py 3473 2007-10-29 15:18:13Z jarrod.millman $
+
+"""
+__all__ = [
+ 'apply_along_axis', 'apply_over_axes', 'atleast_1d', 'atleast_2d',
+ 'atleast_3d', 'average', 'clump_masked', 'clump_unmasked', 'column_stack',
+ 'compress_cols', 'compress_nd', 'compress_rowcols', 'compress_rows',
+ 'count_masked', 'corrcoef', 'cov', 'diagflat', 'dot', 'dstack', 'ediff1d',
+ 'flatnotmasked_contiguous', 'flatnotmasked_edges', 'hsplit', 'hstack',
+ 'isin', 'in1d', 'intersect1d', 'mask_cols', 'mask_rowcols', 'mask_rows',
+ 'masked_all', 'masked_all_like', 'median', 'mr_', 'ndenumerate',
+ 'notmasked_contiguous', 'notmasked_edges', 'polyfit', 'row_stack',
+ 'setdiff1d', 'setxor1d', 'stack', 'unique', 'union1d', 'vander', 'vstack',
+ ]
+
+import itertools
+import warnings
+
+from . import core as ma
+from .core import (
+ MaskedArray, MAError, add, array, asarray, concatenate, filled, count,
+ getmask, getmaskarray, make_mask_descr, masked, masked_array, mask_or,
+ nomask, ones, sort, zeros, getdata, get_masked_subclass, dot,
+ mask_rowcols
+ )
+
+import numpy as np
+from numpy import ndarray, array as nxarray
+from numpy.core.multiarray import normalize_axis_index
+from numpy.core.numeric import normalize_axis_tuple
+from numpy.lib.function_base import _ureduce
+from numpy.lib.index_tricks import AxisConcatenator
+
+
+def issequence(seq):
+ """
+ Is seq a sequence (ndarray, list or tuple)?
+
+ """
+ return isinstance(seq, (ndarray, tuple, list))
+
+
+def count_masked(arr, axis=None):
+ """
+ Count the number of masked elements along the given axis.
+
+ Parameters
+ ----------
+ arr : array_like
+ An array with (possibly) masked elements.
+ axis : int, optional
+ Axis along which to count. If None (default), a flattened
+ version of the array is used.
+
+ Returns
+ -------
+ count : int, ndarray
+ The total number of masked elements (axis=None) or the number
+ of masked elements along each slice of the given axis.
+
+ See Also
+ --------
+ MaskedArray.count : Count non-masked elements.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> a = np.arange(9).reshape((3,3))
+ >>> a = ma.array(a)
+ >>> a[1, 0] = ma.masked
+ >>> a[1, 2] = ma.masked
+ >>> a[2, 1] = ma.masked
+ >>> a
+ masked_array(
+ data=[[0, 1, 2],
+ [--, 4, --],
+ [6, --, 8]],
+ mask=[[False, False, False],
+ [ True, False, True],
+ [False, True, False]],
+ fill_value=999999)
+ >>> ma.count_masked(a)
+ 3
+
+ When the `axis` keyword is used an array is returned.
+
+ >>> ma.count_masked(a, axis=0)
+ array([1, 1, 1])
+ >>> ma.count_masked(a, axis=1)
+ array([0, 2, 1])
+
+ """
+ m = getmaskarray(arr)
+ return m.sum(axis)
+
+
+def masked_all(shape, dtype=float):
+ """
+ Empty masked array with all elements masked.
+
+ Return an empty masked array of the given shape and dtype, where all the
+ data are masked.
+
+ Parameters
+ ----------
+ shape : int or tuple of ints
+ Shape of the required MaskedArray, e.g., ``(2, 3)`` or ``2``.
+ dtype : dtype, optional
+ Data type of the output.
+
+ Returns
+ -------
+ a : MaskedArray
+ A masked array with all data masked.
+
+ See Also
+ --------
+ masked_all_like : Empty masked array modelled on an existing array.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> ma.masked_all((3, 3))
+ masked_array(
+ data=[[--, --, --],
+ [--, --, --],
+ [--, --, --]],
+ mask=[[ True, True, True],
+ [ True, True, True],
+ [ True, True, True]],
+ fill_value=1e+20,
+ dtype=float64)
+
+ The `dtype` parameter defines the underlying data type.
+
+ >>> a = ma.masked_all((3, 3))
+ >>> a.dtype
+ dtype('float64')
+ >>> a = ma.masked_all((3, 3), dtype=np.int32)
+ >>> a.dtype
+ dtype('int32')
+
+ """
+ a = masked_array(np.empty(shape, dtype),
+ mask=np.ones(shape, make_mask_descr(dtype)))
+ return a
+
+
+def masked_all_like(arr):
+ """
+ Empty masked array with the properties of an existing array.
+
+ Return an empty masked array of the same shape and dtype as
+ the array `arr`, where all the data are masked.
+
+ Parameters
+ ----------
+ arr : ndarray
+ An array describing the shape and dtype of the required MaskedArray.
+
+ Returns
+ -------
+ a : MaskedArray
+ A masked array with all data masked.
+
+ Raises
+ ------
+ AttributeError
+ If `arr` doesn't have a shape attribute (i.e. not an ndarray)
+
+ See Also
+ --------
+ masked_all : Empty masked array with all elements masked.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> arr = np.zeros((2, 3), dtype=np.float32)
+ >>> arr
+ array([[0., 0., 0.],
+ [0., 0., 0.]], dtype=float32)
+ >>> ma.masked_all_like(arr)
+ masked_array(
+ data=[[--, --, --],
+ [--, --, --]],
+ mask=[[ True, True, True],
+ [ True, True, True]],
+ fill_value=1e+20,
+ dtype=float32)
+
+ The dtype of the masked array matches the dtype of `arr`.
+
+ >>> arr.dtype
+ dtype('float32')
+ >>> ma.masked_all_like(arr).dtype
+ dtype('float32')
+
+ """
+ a = np.empty_like(arr).view(MaskedArray)
+ a._mask = np.ones(a.shape, dtype=make_mask_descr(a.dtype))
+ return a
+
+
+#####--------------------------------------------------------------------------
+#---- --- Standard functions ---
+#####--------------------------------------------------------------------------
+class _fromnxfunction:
+ """
+ Defines a wrapper to adapt NumPy functions to masked arrays.
+
+
+ An instance of `_fromnxfunction` can be called with the same parameters
+ as the wrapped NumPy function. The docstring of `newfunc` is adapted from
+ the wrapped function as well, see `getdoc`.
+
+ This class should not be used directly. Instead, one of its extensions that
+ provides support for a specific type of input should be used.
+
+ Parameters
+ ----------
+ funcname : str
+ The name of the function to be adapted. The function should be
+ in the NumPy namespace (i.e. ``np.funcname``).
+
+ """
+
+ def __init__(self, funcname):
+ self.__name__ = funcname
+ self.__doc__ = self.getdoc()
+
+ def getdoc(self):
+ """
+ Retrieve the docstring and signature from the function.
+
+ The ``__doc__`` attribute of the function is used as the docstring for
+ the new masked array version of the function. A note on application
+ of the function to the mask is appended.
+
+ Parameters
+ ----------
+ None
+
+ """
+ npfunc = getattr(np, self.__name__, None)
+ doc = getattr(npfunc, '__doc__', None)
+ if doc:
+ sig = self.__name__ + ma.get_object_signature(npfunc)
+ doc = ma.doc_note(doc, "The function is applied to both the _data "
+ "and the _mask, if any.")
+ return '\n\n'.join((sig, doc))
+ return
+
+ def __call__(self, *args, **params):
+ pass
+
+
+class _fromnxfunction_single(_fromnxfunction):
+ """
+ A version of `_fromnxfunction` that is called with a single array
+ argument followed by auxiliary args that are passed verbatim for
+ both the data and mask calls.
+ """
+ def __call__(self, x, *args, **params):
+ func = getattr(np, self.__name__)
+ if isinstance(x, ndarray):
+ _d = func(x.__array__(), *args, **params)
+ _m = func(getmaskarray(x), *args, **params)
+ return masked_array(_d, mask=_m)
+ else:
+ _d = func(np.asarray(x), *args, **params)
+ _m = func(getmaskarray(x), *args, **params)
+ return masked_array(_d, mask=_m)
+
+
+class _fromnxfunction_seq(_fromnxfunction):
+ """
+ A version of `_fromnxfunction` that is called with a single sequence
+ of arrays followed by auxiliary args that are passed verbatim for
+ both the data and mask calls.
+ """
+ def __call__(self, x, *args, **params):
+ func = getattr(np, self.__name__)
+ _d = func(tuple([np.asarray(a) for a in x]), *args, **params)
+ _m = func(tuple([getmaskarray(a) for a in x]), *args, **params)
+ return masked_array(_d, mask=_m)
+
+
+class _fromnxfunction_args(_fromnxfunction):
+ """
+ A version of `_fromnxfunction` that is called with multiple array
+ arguments. The first non-array-like input marks the beginning of the
+ arguments that are passed verbatim for both the data and mask calls.
+ Array arguments are processed independently and the results are
+ returned in a list. If only one array is found, the return value is
+ just the processed array instead of a list.
+ """
+ def __call__(self, *args, **params):
+ func = getattr(np, self.__name__)
+ arrays = []
+ args = list(args)
+ while len(args) > 0 and issequence(args[0]):
+ arrays.append(args.pop(0))
+ res = []
+ for x in arrays:
+ _d = func(np.asarray(x), *args, **params)
+ _m = func(getmaskarray(x), *args, **params)
+ res.append(masked_array(_d, mask=_m))
+ if len(arrays) == 1:
+ return res[0]
+ return res
+
+
+class _fromnxfunction_allargs(_fromnxfunction):
+ """
+ A version of `_fromnxfunction` that is called with multiple array
+ arguments. Similar to `_fromnxfunction_args` except that all args
+ are converted to arrays even if they are not so already. This makes
+ it possible to process scalars as 1-D arrays. Only keyword arguments
+ are passed through verbatim for the data and mask calls. Arrays
+ arguments are processed independently and the results are returned
+ in a list. If only one arg is present, the return value is just the
+ processed array instead of a list.
+ """
+ def __call__(self, *args, **params):
+ func = getattr(np, self.__name__)
+ res = []
+ for x in args:
+ _d = func(np.asarray(x), **params)
+ _m = func(getmaskarray(x), **params)
+ res.append(masked_array(_d, mask=_m))
+ if len(args) == 1:
+ return res[0]
+ return res
+
+
+atleast_1d = _fromnxfunction_allargs('atleast_1d')
+atleast_2d = _fromnxfunction_allargs('atleast_2d')
+atleast_3d = _fromnxfunction_allargs('atleast_3d')
+
+vstack = row_stack = _fromnxfunction_seq('vstack')
+hstack = _fromnxfunction_seq('hstack')
+column_stack = _fromnxfunction_seq('column_stack')
+dstack = _fromnxfunction_seq('dstack')
+stack = _fromnxfunction_seq('stack')
+
+hsplit = _fromnxfunction_single('hsplit')
+
+diagflat = _fromnxfunction_single('diagflat')
+
+
+#####--------------------------------------------------------------------------
+#----
+#####--------------------------------------------------------------------------
+def flatten_inplace(seq):
+ """Flatten a sequence in place."""
+ k = 0
+ while (k != len(seq)):
+ while hasattr(seq[k], '__iter__'):
+ seq[k:(k + 1)] = seq[k]
+ k += 1
+ return seq
+
+
+def apply_along_axis(func1d, axis, arr, *args, **kwargs):
+ """
+ (This docstring should be overwritten)
+ """
+ arr = array(arr, copy=False, subok=True)
+ nd = arr.ndim
+ axis = normalize_axis_index(axis, nd)
+ ind = [0] * (nd - 1)
+ i = np.zeros(nd, 'O')
+ indlist = list(range(nd))
+ indlist.remove(axis)
+ i[axis] = slice(None, None)
+ outshape = np.asarray(arr.shape).take(indlist)
+ i.put(indlist, ind)
+ res = func1d(arr[tuple(i.tolist())], *args, **kwargs)
+ # if res is a number, then we have a smaller output array
+ asscalar = np.isscalar(res)
+ if not asscalar:
+ try:
+ len(res)
+ except TypeError:
+ asscalar = True
+ # Note: we shouldn't set the dtype of the output from the first result
+ # so we force the type to object, and build a list of dtypes. We'll
+ # just take the largest, to avoid some downcasting
+ dtypes = []
+ if asscalar:
+ dtypes.append(np.asarray(res).dtype)
+ outarr = zeros(outshape, object)
+ outarr[tuple(ind)] = res
+ Ntot = np.product(outshape)
+ k = 1
+ while k < Ntot:
+ # increment the index
+ ind[-1] += 1
+ n = -1
+ while (ind[n] >= outshape[n]) and (n > (1 - nd)):
+ ind[n - 1] += 1
+ ind[n] = 0
+ n -= 1
+ i.put(indlist, ind)
+ res = func1d(arr[tuple(i.tolist())], *args, **kwargs)
+ outarr[tuple(ind)] = res
+ dtypes.append(asarray(res).dtype)
+ k += 1
+ else:
+ res = array(res, copy=False, subok=True)
+ j = i.copy()
+ j[axis] = ([slice(None, None)] * res.ndim)
+ j.put(indlist, ind)
+ Ntot = np.product(outshape)
+ holdshape = outshape
+ outshape = list(arr.shape)
+ outshape[axis] = res.shape
+ dtypes.append(asarray(res).dtype)
+ outshape = flatten_inplace(outshape)
+ outarr = zeros(outshape, object)
+ outarr[tuple(flatten_inplace(j.tolist()))] = res
+ k = 1
+ while k < Ntot:
+ # increment the index
+ ind[-1] += 1
+ n = -1
+ while (ind[n] >= holdshape[n]) and (n > (1 - nd)):
+ ind[n - 1] += 1
+ ind[n] = 0
+ n -= 1
+ i.put(indlist, ind)
+ j.put(indlist, ind)
+ res = func1d(arr[tuple(i.tolist())], *args, **kwargs)
+ outarr[tuple(flatten_inplace(j.tolist()))] = res
+ dtypes.append(asarray(res).dtype)
+ k += 1
+ max_dtypes = np.dtype(np.asarray(dtypes).max())
+ if not hasattr(arr, '_mask'):
+ result = np.asarray(outarr, dtype=max_dtypes)
+ else:
+ result = asarray(outarr, dtype=max_dtypes)
+ result.fill_value = ma.default_fill_value(result)
+ return result
+apply_along_axis.__doc__ = np.apply_along_axis.__doc__
+
+
+def apply_over_axes(func, a, axes):
+ """
+ (This docstring will be overwritten)
+ """
+ val = asarray(a)
+ N = a.ndim
+ if array(axes).ndim == 0:
+ axes = (axes,)
+ for axis in axes:
+ if axis < 0:
+ axis = N + axis
+ args = (val, axis)
+ res = func(*args)
+ if res.ndim == val.ndim:
+ val = res
+ else:
+ res = ma.expand_dims(res, axis)
+ if res.ndim == val.ndim:
+ val = res
+ else:
+ raise ValueError("function is not returning "
+ "an array of the correct shape")
+ return val
+
+
+if apply_over_axes.__doc__ is not None:
+ apply_over_axes.__doc__ = np.apply_over_axes.__doc__[
+ :np.apply_over_axes.__doc__.find('Notes')].rstrip() + \
+ """
+
+ Examples
+ --------
+ >>> a = np.ma.arange(24).reshape(2,3,4)
+ >>> a[:,0,1] = np.ma.masked
+ >>> a[:,1,:] = np.ma.masked
+ >>> a
+ masked_array(
+ data=[[[0, --, 2, 3],
+ [--, --, --, --],
+ [8, 9, 10, 11]],
+ [[12, --, 14, 15],
+ [--, --, --, --],
+ [20, 21, 22, 23]]],
+ mask=[[[False, True, False, False],
+ [ True, True, True, True],
+ [False, False, False, False]],
+ [[False, True, False, False],
+ [ True, True, True, True],
+ [False, False, False, False]]],
+ fill_value=999999)
+ >>> np.ma.apply_over_axes(np.ma.sum, a, [0,2])
+ masked_array(
+ data=[[[46],
+ [--],
+ [124]]],
+ mask=[[[False],
+ [ True],
+ [False]]],
+ fill_value=999999)
+
+ Tuple axis arguments to ufuncs are equivalent:
+
+ >>> np.ma.sum(a, axis=(0,2)).reshape((1,-1,1))
+ masked_array(
+ data=[[[46],
+ [--],
+ [124]]],
+ mask=[[[False],
+ [ True],
+ [False]]],
+ fill_value=999999)
+ """
+
+
+def average(a, axis=None, weights=None, returned=False, *,
+ keepdims=np._NoValue):
+ """
+ Return the weighted average of array over the given axis.
+
+ Parameters
+ ----------
+ a : array_like
+ Data to be averaged.
+ Masked entries are not taken into account in the computation.
+ axis : int, optional
+ Axis along which to average `a`. If None, averaging is done over
+ the flattened array.
+ weights : array_like, optional
+ The importance that each element has in the computation of the average.
+ The weights array can either be 1-D (in which case its length must be
+ the size of `a` along the given axis) or of the same shape as `a`.
+ If ``weights=None``, then all data in `a` are assumed to have a
+ weight equal to one. The 1-D calculation is::
+
+ avg = sum(a * weights) / sum(weights)
+
+ The only constraint on `weights` is that `sum(weights)` must not be 0.
+ returned : bool, optional
+ Flag indicating whether a tuple ``(result, sum of weights)``
+ should be returned as output (True), or just the result (False).
+ Default is 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 `a`.
+ *Note:* `keepdims` will not work with instances of `numpy.matrix`
+ or other classes whose methods do not support `keepdims`.
+
+ .. versionadded:: 1.23.0
+
+ Returns
+ -------
+ average, [sum_of_weights] : (tuple of) scalar or MaskedArray
+ The average along the specified axis. When returned is `True`,
+ return a tuple with the average as the first element and the sum
+ of the weights as the second element. The return type is `np.float64`
+ if `a` is of integer type and floats smaller than `float64`, or the
+ input data-type, otherwise. If returned, `sum_of_weights` is always
+ `float64`.
+
+ Examples
+ --------
+ >>> a = np.ma.array([1., 2., 3., 4.], mask=[False, False, True, True])
+ >>> np.ma.average(a, weights=[3, 1, 0, 0])
+ 1.25
+
+ >>> x = np.ma.arange(6.).reshape(3, 2)
+ >>> x
+ masked_array(
+ data=[[0., 1.],
+ [2., 3.],
+ [4., 5.]],
+ mask=False,
+ fill_value=1e+20)
+ >>> avg, sumweights = np.ma.average(x, axis=0, weights=[1, 2, 3],
+ ... returned=True)
+ >>> avg
+ masked_array(data=[2.6666666666666665, 3.6666666666666665],
+ mask=[False, False],
+ fill_value=1e+20)
+
+ With ``keepdims=True``, the following result has shape (3, 1).
+
+ >>> np.ma.average(x, axis=1, keepdims=True)
+ masked_array(
+ data=[[0.5],
+ [2.5],
+ [4.5]],
+ mask=False,
+ fill_value=1e+20)
+ """
+ a = asarray(a)
+ m = getmask(a)
+
+ # inspired by 'average' in numpy/lib/function_base.py
+
+ if keepdims is np._NoValue:
+ # Don't pass on the keepdims argument if one wasn't given.
+ keepdims_kw = {}
+ else:
+ keepdims_kw = {'keepdims': keepdims}
+
+ if weights is None:
+ avg = a.mean(axis, **keepdims_kw)
+ scl = avg.dtype.type(a.count(axis))
+ else:
+ wgt = asarray(weights)
+
+ if issubclass(a.dtype.type, (np.integer, np.bool_)):
+ result_dtype = np.result_type(a.dtype, wgt.dtype, 'f8')
+ else:
+ result_dtype = np.result_type(a.dtype, wgt.dtype)
+
+ # Sanity checks
+ if a.shape != wgt.shape:
+ if axis is None:
+ raise TypeError(
+ "Axis must be specified when shapes of a and weights "
+ "differ.")
+ if wgt.ndim != 1:
+ raise TypeError(
+ "1D weights expected when shapes of a and weights differ.")
+ if wgt.shape[0] != a.shape[axis]:
+ raise ValueError(
+ "Length of weights not compatible with specified axis.")
+
+ # setup wgt to broadcast along axis
+ wgt = np.broadcast_to(wgt, (a.ndim-1)*(1,) + wgt.shape, subok=True)
+ wgt = wgt.swapaxes(-1, axis)
+
+ if m is not nomask:
+ wgt = wgt*(~a.mask)
+ wgt.mask |= a.mask
+
+ scl = wgt.sum(axis=axis, dtype=result_dtype, **keepdims_kw)
+ avg = np.multiply(a, wgt,
+ dtype=result_dtype).sum(axis, **keepdims_kw) / scl
+
+ if returned:
+ if scl.shape != avg.shape:
+ scl = np.broadcast_to(scl, avg.shape).copy()
+ return avg, scl
+ else:
+ return avg
+
+
+def median(a, axis=None, out=None, overwrite_input=False, keepdims=False):
+ """
+ Compute the median along the specified axis.
+
+ Returns the median of the array elements.
+
+ Parameters
+ ----------
+ a : array_like
+ Input array or object that can be converted to an array.
+ axis : int, optional
+ Axis along which the medians are computed. The default (None) is
+ to compute the median along a flattened version of the array.
+ out : ndarray, optional
+ 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.
+ overwrite_input : bool, optional
+ If True, then allow use of memory of input array (a) for
+ calculations. The input array will be modified by the call to
+ median. This will save memory when you do not need to preserve
+ the contents of the input array. Treat the input as undefined,
+ but it will probably be fully or partially sorted. Default is
+ False. Note that, if `overwrite_input` is True, and the input
+ 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 input array.
+
+ .. versionadded:: 1.10.0
+
+ Returns
+ -------
+ median : ndarray
+ A new array holding the result is returned unless out is
+ specified, in which case a reference to out is returned.
+ Return data-type is `float64` for integers and floats smaller than
+ `float64`, or the input data-type, otherwise.
+
+ See Also
+ --------
+ mean
+
+ Notes
+ -----
+ Given a vector ``V`` with ``N`` non masked values, the median of ``V``
+ is the middle value of a sorted copy of ``V`` (``Vs``) - i.e.
+ ``Vs[(N-1)/2]``, when ``N`` is odd, or ``{Vs[N/2 - 1] + Vs[N/2]}/2``
+ when ``N`` is even.
+
+ Examples
+ --------
+ >>> x = np.ma.array(np.arange(8), mask=[0]*4 + [1]*4)
+ >>> np.ma.median(x)
+ 1.5
+
+ >>> x = np.ma.array(np.arange(10).reshape(2, 5), mask=[0]*6 + [1]*4)
+ >>> np.ma.median(x)
+ 2.5
+ >>> np.ma.median(x, axis=-1, overwrite_input=True)
+ masked_array(data=[2.0, 5.0],
+ mask=[False, False],
+ fill_value=1e+20)
+
+ """
+ if not hasattr(a, 'mask'):
+ m = np.median(getdata(a, subok=True), axis=axis,
+ out=out, overwrite_input=overwrite_input,
+ keepdims=keepdims)
+ if isinstance(m, np.ndarray) and 1 <= m.ndim:
+ return masked_array(m, copy=False)
+ else:
+ return m
+
+ return _ureduce(a, func=_median, keepdims=keepdims, axis=axis, out=out,
+ overwrite_input=overwrite_input)
+
+
+def _median(a, axis=None, out=None, overwrite_input=False):
+ # when an unmasked NaN is present return it, so we need to sort the NaN
+ # values behind the mask
+ if np.issubdtype(a.dtype, np.inexact):
+ fill_value = np.inf
+ else:
+ fill_value = None
+ if overwrite_input:
+ if axis is None:
+ asorted = a.ravel()
+ asorted.sort(fill_value=fill_value)
+ else:
+ a.sort(axis=axis, fill_value=fill_value)
+ asorted = a
+ else:
+ asorted = sort(a, axis=axis, fill_value=fill_value)
+
+ if axis is None:
+ axis = 0
+ else:
+ axis = normalize_axis_index(axis, asorted.ndim)
+
+ if asorted.shape[axis] == 0:
+ # for empty axis integer indices fail so use slicing to get same result
+ # as median (which is mean of empty slice = nan)
+ indexer = [slice(None)] * asorted.ndim
+ indexer[axis] = slice(0, 0)
+ indexer = tuple(indexer)
+ return np.ma.mean(asorted[indexer], axis=axis, out=out)
+
+ if asorted.ndim == 1:
+ idx, odd = divmod(count(asorted), 2)
+ mid = asorted[idx + odd - 1:idx + 1]
+ if np.issubdtype(asorted.dtype, np.inexact) and asorted.size > 0:
+ # avoid inf / x = masked
+ s = mid.sum(out=out)
+ if not odd:
+ s = np.true_divide(s, 2., casting='safe', out=out)
+ s = np.lib.utils._median_nancheck(asorted, s, axis)
+ else:
+ s = mid.mean(out=out)
+
+ # if result is masked either the input contained enough
+ # minimum_fill_value so that it would be the median or all values
+ # masked
+ if np.ma.is_masked(s) and not np.all(asorted.mask):
+ return np.ma.minimum_fill_value(asorted)
+ return s
+
+ counts = count(asorted, axis=axis, keepdims=True)
+ h = counts // 2
+
+ # duplicate high if odd number of elements so mean does nothing
+ odd = counts % 2 == 1
+ l = np.where(odd, h, h-1)
+
+ lh = np.concatenate([l,h], axis=axis)
+
+ # get low and high median
+ low_high = np.take_along_axis(asorted, lh, axis=axis)
+
+ def replace_masked(s):
+ # Replace masked entries with minimum_full_value unless it all values
+ # are masked. This is required as the sort order of values equal or
+ # larger than the fill value is undefined and a valid value placed
+ # elsewhere, e.g. [4, --, inf].
+ if np.ma.is_masked(s):
+ rep = (~np.all(asorted.mask, axis=axis, keepdims=True)) & s.mask
+ s.data[rep] = np.ma.minimum_fill_value(asorted)
+ s.mask[rep] = False
+
+ replace_masked(low_high)
+
+ if np.issubdtype(asorted.dtype, np.inexact):
+ # avoid inf / x = masked
+ s = np.ma.sum(low_high, axis=axis, out=out)
+ np.true_divide(s.data, 2., casting='unsafe', out=s.data)
+
+ s = np.lib.utils._median_nancheck(asorted, s, axis)
+ else:
+ s = np.ma.mean(low_high, axis=axis, out=out)
+
+ return s
+
+
+def compress_nd(x, axis=None):
+ """Suppress slices from multiple dimensions which contain masked values.
+
+ Parameters
+ ----------
+ x : array_like, MaskedArray
+ The array to operate on. If not a MaskedArray instance (or if no array
+ elements are masked), `x` is interpreted as a MaskedArray with `mask`
+ set to `nomask`.
+ axis : tuple of ints or int, optional
+ Which dimensions to suppress slices from can be configured with this
+ parameter.
+ - If axis is a tuple of ints, those are the axes to suppress slices from.
+ - If axis is an int, then that is the only axis to suppress slices from.
+ - If axis is None, all axis are selected.
+
+ Returns
+ -------
+ compress_array : ndarray
+ The compressed array.
+ """
+ x = asarray(x)
+ m = getmask(x)
+ # Set axis to tuple of ints
+ if axis is None:
+ axis = tuple(range(x.ndim))
+ else:
+ axis = normalize_axis_tuple(axis, x.ndim)
+
+ # Nothing is masked: return x
+ if m is nomask or not m.any():
+ return x._data
+ # All is masked: return empty
+ if m.all():
+ return nxarray([])
+ # Filter elements through boolean indexing
+ data = x._data
+ for ax in axis:
+ axes = tuple(list(range(ax)) + list(range(ax + 1, x.ndim)))
+ data = data[(slice(None),)*ax + (~m.any(axis=axes),)]
+ return data
+
+
+def compress_rowcols(x, axis=None):
+ """
+ Suppress the rows and/or columns of a 2-D array that contain
+ masked values.
+
+ The suppression behavior is selected with the `axis` parameter.
+
+ - If axis is None, both rows and columns are suppressed.
+ - If axis is 0, only rows are suppressed.
+ - If axis is 1 or -1, only columns are suppressed.
+
+ Parameters
+ ----------
+ x : array_like, MaskedArray
+ The array to operate on. If not a MaskedArray instance (or if no array
+ elements are masked), `x` is interpreted as a MaskedArray with
+ `mask` set to `nomask`. Must be a 2D array.
+ axis : int, optional
+ Axis along which to perform the operation. Default is None.
+
+ Returns
+ -------
+ compressed_array : ndarray
+ The compressed array.
+
+ Examples
+ --------
+ >>> x = np.ma.array(np.arange(9).reshape(3, 3), mask=[[1, 0, 0],
+ ... [1, 0, 0],
+ ... [0, 0, 0]])
+ >>> x
+ masked_array(
+ data=[[--, 1, 2],
+ [--, 4, 5],
+ [6, 7, 8]],
+ mask=[[ True, False, False],
+ [ True, False, False],
+ [False, False, False]],
+ fill_value=999999)
+
+ >>> np.ma.compress_rowcols(x)
+ array([[7, 8]])
+ >>> np.ma.compress_rowcols(x, 0)
+ array([[6, 7, 8]])
+ >>> np.ma.compress_rowcols(x, 1)
+ array([[1, 2],
+ [4, 5],
+ [7, 8]])
+
+ """
+ if asarray(x).ndim != 2:
+ raise NotImplementedError("compress_rowcols works for 2D arrays only.")
+ return compress_nd(x, axis=axis)
+
+
+def compress_rows(a):
+ """
+ Suppress whole rows of a 2-D array that contain masked values.
+
+ This is equivalent to ``np.ma.compress_rowcols(a, 0)``, see
+ `compress_rowcols` for details.
+
+ See Also
+ --------
+ compress_rowcols
+
+ """
+ a = asarray(a)
+ if a.ndim != 2:
+ raise NotImplementedError("compress_rows works for 2D arrays only.")
+ return compress_rowcols(a, 0)
+
+
+def compress_cols(a):
+ """
+ Suppress whole columns of a 2-D array that contain masked values.
+
+ This is equivalent to ``np.ma.compress_rowcols(a, 1)``, see
+ `compress_rowcols` for details.
+
+ See Also
+ --------
+ compress_rowcols
+
+ """
+ a = asarray(a)
+ if a.ndim != 2:
+ raise NotImplementedError("compress_cols works for 2D arrays only.")
+ return compress_rowcols(a, 1)
+
+
+def mask_rows(a, axis=np._NoValue):
+ """
+ Mask rows of a 2D array that contain masked values.
+
+ This function is a shortcut to ``mask_rowcols`` with `axis` equal to 0.
+
+ See Also
+ --------
+ mask_rowcols : Mask rows and/or columns of a 2D array.
+ masked_where : Mask where a condition is met.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> a = np.zeros((3, 3), dtype=int)
+ >>> a[1, 1] = 1
+ >>> a
+ array([[0, 0, 0],
+ [0, 1, 0],
+ [0, 0, 0]])
+ >>> a = ma.masked_equal(a, 1)
+ >>> a
+ masked_array(
+ data=[[0, 0, 0],
+ [0, --, 0],
+ [0, 0, 0]],
+ mask=[[False, False, False],
+ [False, True, False],
+ [False, False, False]],
+ fill_value=1)
+
+ >>> ma.mask_rows(a)
+ masked_array(
+ data=[[0, 0, 0],
+ [--, --, --],
+ [0, 0, 0]],
+ mask=[[False, False, False],
+ [ True, True, True],
+ [False, False, False]],
+ fill_value=1)
+
+ """
+ if axis is not np._NoValue:
+ # remove the axis argument when this deprecation expires
+ # NumPy 1.18.0, 2019-11-28
+ warnings.warn(
+ "The axis argument has always been ignored, in future passing it "
+ "will raise TypeError", DeprecationWarning, stacklevel=2)
+ return mask_rowcols(a, 0)
+
+
+def mask_cols(a, axis=np._NoValue):
+ """
+ Mask columns of a 2D array that contain masked values.
+
+ This function is a shortcut to ``mask_rowcols`` with `axis` equal to 1.
+
+ See Also
+ --------
+ mask_rowcols : Mask rows and/or columns of a 2D array.
+ masked_where : Mask where a condition is met.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> a = np.zeros((3, 3), dtype=int)
+ >>> a[1, 1] = 1
+ >>> a
+ array([[0, 0, 0],
+ [0, 1, 0],
+ [0, 0, 0]])
+ >>> a = ma.masked_equal(a, 1)
+ >>> a
+ masked_array(
+ data=[[0, 0, 0],
+ [0, --, 0],
+ [0, 0, 0]],
+ mask=[[False, False, False],
+ [False, True, False],
+ [False, False, False]],
+ fill_value=1)
+ >>> ma.mask_cols(a)
+ masked_array(
+ data=[[0, --, 0],
+ [0, --, 0],
+ [0, --, 0]],
+ mask=[[False, True, False],
+ [False, True, False],
+ [False, True, False]],
+ fill_value=1)
+
+ """
+ if axis is not np._NoValue:
+ # remove the axis argument when this deprecation expires
+ # NumPy 1.18.0, 2019-11-28
+ warnings.warn(
+ "The axis argument has always been ignored, in future passing it "
+ "will raise TypeError", DeprecationWarning, stacklevel=2)
+ return mask_rowcols(a, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- arraysetops ---
+#####--------------------------------------------------------------------------
+
+def ediff1d(arr, to_end=None, to_begin=None):
+ """
+ Compute the differences between consecutive elements of an array.
+
+ This function is the equivalent of `numpy.ediff1d` that takes masked
+ values into account, see `numpy.ediff1d` for details.
+
+ See Also
+ --------
+ numpy.ediff1d : Equivalent function for ndarrays.
+
+ """
+ arr = ma.asanyarray(arr).flat
+ ed = arr[1:] - arr[:-1]
+ arrays = [ed]
+ #
+ if to_begin is not None:
+ arrays.insert(0, to_begin)
+ if to_end is not None:
+ arrays.append(to_end)
+ #
+ if len(arrays) != 1:
+ # We'll save ourselves a copy of a potentially large array in the common
+ # case where neither to_begin or to_end was given.
+ ed = hstack(arrays)
+ #
+ return ed
+
+
+def unique(ar1, return_index=False, return_inverse=False):
+ """
+ Finds the unique elements of an array.
+
+ Masked values are considered the same element (masked). The output array
+ is always a masked array. See `numpy.unique` for more details.
+
+ See Also
+ --------
+ numpy.unique : Equivalent function for ndarrays.
+
+ Examples
+ --------
+ >>> import numpy.ma as ma
+ >>> a = [1, 2, 1000, 2, 3]
+ >>> mask = [0, 0, 1, 0, 0]
+ >>> masked_a = ma.masked_array(a, mask)
+ >>> masked_a
+ masked_array(data=[1, 2, --, 2, 3],
+ mask=[False, False, True, False, False],
+ fill_value=999999)
+ >>> ma.unique(masked_a)
+ masked_array(data=[1, 2, 3, --],
+ mask=[False, False, False, True],
+ fill_value=999999)
+ >>> ma.unique(masked_a, return_index=True)
+ (masked_array(data=[1, 2, 3, --],
+ mask=[False, False, False, True],
+ fill_value=999999), array([0, 1, 4, 2]))
+ >>> ma.unique(masked_a, return_inverse=True)
+ (masked_array(data=[1, 2, 3, --],
+ mask=[False, False, False, True],
+ fill_value=999999), array([0, 1, 3, 1, 2]))
+ >>> ma.unique(masked_a, return_index=True, return_inverse=True)
+ (masked_array(data=[1, 2, 3, --],
+ mask=[False, False, False, True],
+ fill_value=999999), array([0, 1, 4, 2]), array([0, 1, 3, 1, 2]))
+ """
+ output = np.unique(ar1,
+ return_index=return_index,
+ return_inverse=return_inverse)
+ if isinstance(output, tuple):
+ output = list(output)
+ output[0] = output[0].view(MaskedArray)
+ output = tuple(output)
+ else:
+ output = output.view(MaskedArray)
+ return output
+
+
+def intersect1d(ar1, ar2, assume_unique=False):
+ """
+ Returns the unique elements common to both arrays.
+
+ Masked values are considered equal one to the other.
+ The output is always a masked array.
+
+ See `numpy.intersect1d` for more details.
+
+ See Also
+ --------
+ numpy.intersect1d : Equivalent function for ndarrays.
+
+ Examples
+ --------
+ >>> x = np.ma.array([1, 3, 3, 3], mask=[0, 0, 0, 1])
+ >>> y = np.ma.array([3, 1, 1, 1], mask=[0, 0, 0, 1])
+ >>> np.ma.intersect1d(x, y)
+ masked_array(data=[1, 3, --],
+ mask=[False, False, True],
+ fill_value=999999)
+
+ """
+ if assume_unique:
+ aux = ma.concatenate((ar1, ar2))
+ else:
+ # Might be faster than unique( intersect1d( ar1, ar2 ) )?
+ aux = ma.concatenate((unique(ar1), unique(ar2)))
+ aux.sort()
+ return aux[:-1][aux[1:] == aux[:-1]]
+
+
+def setxor1d(ar1, ar2, assume_unique=False):
+ """
+ Set exclusive-or of 1-D arrays with unique elements.
+
+ The output is always a masked array. See `numpy.setxor1d` for more details.
+
+ See Also
+ --------
+ numpy.setxor1d : Equivalent function for ndarrays.
+
+ """
+ if not assume_unique:
+ ar1 = unique(ar1)
+ ar2 = unique(ar2)
+
+ aux = ma.concatenate((ar1, ar2))
+ if aux.size == 0:
+ return aux
+ aux.sort()
+ auxf = aux.filled()
+# flag = ediff1d( aux, to_end = 1, to_begin = 1 ) == 0
+ flag = ma.concatenate(([True], (auxf[1:] != auxf[:-1]), [True]))
+# flag2 = ediff1d( flag ) == 0
+ flag2 = (flag[1:] == flag[:-1])
+ return aux[flag2]
+
+
+def in1d(ar1, ar2, assume_unique=False, invert=False):
+ """
+ Test whether each element of an array is also present in a second
+ array.
+
+ The output is always a masked array. See `numpy.in1d` for more details.
+
+ We recommend using :func:`isin` instead of `in1d` for new code.
+
+ See Also
+ --------
+ isin : Version of this function that preserves the shape of ar1.
+ numpy.in1d : Equivalent function for ndarrays.
+
+ Notes
+ -----
+ .. versionadded:: 1.4.0
+
+ """
+ if not assume_unique:
+ ar1, rev_idx = unique(ar1, return_inverse=True)
+ ar2 = unique(ar2)
+
+ ar = ma.concatenate((ar1, ar2))
+ # We need this to be a stable sort, so always use 'mergesort'
+ # here. The values from the first array should always come before
+ # the values from the second array.
+ order = ar.argsort(kind='mergesort')
+ sar = ar[order]
+ if invert:
+ bool_ar = (sar[1:] != sar[:-1])
+ else:
+ bool_ar = (sar[1:] == sar[:-1])
+ flag = ma.concatenate((bool_ar, [invert]))
+ indx = order.argsort(kind='mergesort')[:len(ar1)]
+
+ if assume_unique:
+ return flag[indx]
+ else:
+ return flag[indx][rev_idx]
+
+
+def isin(element, test_elements, assume_unique=False, invert=False):
+ """
+ Calculates `element in test_elements`, broadcasting over
+ `element` only.
+
+ The output is always a masked array of the same shape as `element`.
+ See `numpy.isin` for more details.
+
+ See Also
+ --------
+ in1d : Flattened version of this function.
+ numpy.isin : Equivalent function for ndarrays.
+
+ Notes
+ -----
+ .. versionadded:: 1.13.0
+
+ """
+ element = ma.asarray(element)
+ return in1d(element, test_elements, assume_unique=assume_unique,
+ invert=invert).reshape(element.shape)
+
+
+def union1d(ar1, ar2):
+ """
+ Union of two arrays.
+
+ The output is always a masked array. See `numpy.union1d` for more details.
+
+ See Also
+ --------
+ numpy.union1d : Equivalent function for ndarrays.
+
+ """
+ return unique(ma.concatenate((ar1, ar2), axis=None))
+
+
+def setdiff1d(ar1, ar2, assume_unique=False):
+ """
+ Set difference of 1D arrays with unique elements.
+
+ The output is always a masked array. See `numpy.setdiff1d` for more
+ details.
+
+ See Also
+ --------
+ numpy.setdiff1d : Equivalent function for ndarrays.
+
+ Examples
+ --------
+ >>> x = np.ma.array([1, 2, 3, 4], mask=[0, 1, 0, 1])
+ >>> np.ma.setdiff1d(x, [1, 2])
+ masked_array(data=[3, --],
+ mask=[False, True],
+ fill_value=999999)
+
+ """
+ if assume_unique:
+ ar1 = ma.asarray(ar1).ravel()
+ else:
+ ar1 = unique(ar1)
+ ar2 = unique(ar2)
+ return ar1[in1d(ar1, ar2, assume_unique=True, invert=True)]
+
+
+###############################################################################
+# Covariance #
+###############################################################################
+
+
+def _covhelper(x, y=None, rowvar=True, allow_masked=True):
+ """
+ Private function for the computation of covariance and correlation
+ coefficients.
+
+ """
+ x = ma.array(x, ndmin=2, copy=True, dtype=float)
+ xmask = ma.getmaskarray(x)
+ # Quick exit if we can't process masked data
+ if not allow_masked and xmask.any():
+ raise ValueError("Cannot process masked data.")
+ #
+ if x.shape[0] == 1:
+ rowvar = True
+ # Make sure that rowvar is either 0 or 1
+ rowvar = int(bool(rowvar))
+ axis = 1 - rowvar
+ if rowvar:
+ tup = (slice(None), None)
+ else:
+ tup = (None, slice(None))
+ #
+ if y is None:
+ xnotmask = np.logical_not(xmask).astype(int)
+ else:
+ y = array(y, copy=False, ndmin=2, dtype=float)
+ ymask = ma.getmaskarray(y)
+ if not allow_masked and ymask.any():
+ raise ValueError("Cannot process masked data.")
+ if xmask.any() or ymask.any():
+ if y.shape == x.shape:
+ # Define some common mask
+ common_mask = np.logical_or(xmask, ymask)
+ if common_mask is not nomask:
+ xmask = x._mask = y._mask = ymask = common_mask
+ x._sharedmask = False
+ y._sharedmask = False
+ x = ma.concatenate((x, y), axis)
+ xnotmask = np.logical_not(np.concatenate((xmask, ymask), axis)).astype(int)
+ x -= x.mean(axis=rowvar)[tup]
+ return (x, xnotmask, rowvar)
+
+
+def cov(x, y=None, rowvar=True, bias=False, allow_masked=True, ddof=None):
+ """
+ Estimate the covariance matrix.
+
+ Except for the handling of missing data this function does the same as
+ `numpy.cov`. For more details and examples, see `numpy.cov`.
+
+ By default, masked values are recognized as such. If `x` and `y` have the
+ same shape, a common mask is allocated: if ``x[i,j]`` is masked, then
+ ``y[i,j]`` will also be masked.
+ Setting `allow_masked` to False will raise an exception if values are
+ missing in either of the input arrays.
+
+ Parameters
+ ----------
+ x : array_like
+ A 1-D or 2-D array containing multiple variables and observations.
+ Each row of `x` represents a variable, and each column a single
+ observation of all those variables. Also see `rowvar` below.
+ y : array_like, optional
+ An additional set of variables and observations. `y` has the same
+ shape as `x`.
+ rowvar : bool, optional
+ If `rowvar` is True (default), then each row represents a
+ variable, with observations in the columns. Otherwise, the relationship
+ is transposed: each column represents a variable, while the rows
+ contain observations.
+ bias : bool, optional
+ Default normalization (False) is by ``(N-1)``, where ``N`` is the
+ number of observations given (unbiased estimate). If `bias` is True,
+ then normalization is by ``N``. This keyword can be overridden by
+ the keyword ``ddof`` in numpy versions >= 1.5.
+ allow_masked : bool, optional
+ If True, masked values are propagated pair-wise: if a value is masked
+ in `x`, the corresponding value is masked in `y`.
+ If False, raises a `ValueError` exception when some values are missing.
+ ddof : {None, int}, optional
+ If not ``None`` normalization is by ``(N - ddof)``, where ``N`` is
+ the number of observations; this overrides the value implied by
+ ``bias``. The default value is ``None``.
+
+ .. versionadded:: 1.5
+
+ Raises
+ ------
+ ValueError
+ Raised if some values are missing and `allow_masked` is False.
+
+ See Also
+ --------
+ numpy.cov
+
+ """
+ # Check inputs
+ if ddof is not None and ddof != int(ddof):
+ raise ValueError("ddof must be an integer")
+ # Set up ddof
+ if ddof is None:
+ if bias:
+ ddof = 0
+ else:
+ ddof = 1
+
+ (x, xnotmask, rowvar) = _covhelper(x, y, rowvar, allow_masked)
+ if not rowvar:
+ fact = np.dot(xnotmask.T, xnotmask) * 1. - ddof
+ result = (dot(x.T, x.conj(), strict=False) / fact).squeeze()
+ else:
+ fact = np.dot(xnotmask, xnotmask.T) * 1. - ddof
+ result = (dot(x, x.T.conj(), strict=False) / fact).squeeze()
+ return result
+
+
+def corrcoef(x, y=None, rowvar=True, bias=np._NoValue, allow_masked=True,
+ ddof=np._NoValue):
+ """
+ Return Pearson product-moment correlation coefficients.
+
+ Except for the handling of missing data this function does the same as
+ `numpy.corrcoef`. For more details and examples, see `numpy.corrcoef`.
+
+ Parameters
+ ----------
+ x : array_like
+ A 1-D or 2-D array containing multiple variables and observations.
+ Each row of `x` represents a variable, and each column a single
+ observation of all those variables. Also see `rowvar` below.
+ y : array_like, optional
+ An additional set of variables and observations. `y` has the same
+ shape as `x`.
+ rowvar : bool, optional
+ If `rowvar` is True (default), then each row represents a
+ variable, with observations in the columns. Otherwise, the relationship
+ is transposed: each column represents a variable, while the rows
+ contain observations.
+ bias : _NoValue, optional
+ Has no effect, do not use.
+
+ .. deprecated:: 1.10.0
+ allow_masked : bool, optional
+ If True, masked values are propagated pair-wise: if a value is masked
+ in `x`, the corresponding value is masked in `y`.
+ If False, raises an exception. Because `bias` is deprecated, this
+ argument needs to be treated as keyword only to avoid a warning.
+ ddof : _NoValue, optional
+ Has no effect, do not use.
+
+ .. deprecated:: 1.10.0
+
+ See Also
+ --------
+ numpy.corrcoef : Equivalent function in top-level NumPy module.
+ cov : Estimate the covariance matrix.
+
+ Notes
+ -----
+ This function accepts but discards arguments `bias` and `ddof`. This is
+ for backwards compatibility with previous versions of this function. These
+ arguments had no effect on the return values of the function and can be
+ safely ignored in this and previous versions of numpy.
+ """
+ msg = 'bias and ddof have no effect and are deprecated'
+ if bias is not np._NoValue or ddof is not np._NoValue:
+ # 2015-03-15, 1.10
+ warnings.warn(msg, DeprecationWarning, stacklevel=2)
+ # Get the data
+ (x, xnotmask, rowvar) = _covhelper(x, y, rowvar, allow_masked)
+ # Compute the covariance matrix
+ if not rowvar:
+ fact = np.dot(xnotmask.T, xnotmask) * 1.
+ c = (dot(x.T, x.conj(), strict=False) / fact).squeeze()
+ else:
+ fact = np.dot(xnotmask, xnotmask.T) * 1.
+ c = (dot(x, x.T.conj(), strict=False) / fact).squeeze()
+ # Check whether we have a scalar
+ try:
+ diag = ma.diagonal(c)
+ except ValueError:
+ return 1
+ #
+ if xnotmask.all():
+ _denom = ma.sqrt(ma.multiply.outer(diag, diag))
+ else:
+ _denom = diagflat(diag)
+ _denom._sharedmask = False # We know return is always a copy
+ n = x.shape[1 - rowvar]
+ if rowvar:
+ for i in range(n - 1):
+ for j in range(i + 1, n):
+ _x = mask_cols(vstack((x[i], x[j]))).var(axis=1)
+ _denom[i, j] = _denom[j, i] = ma.sqrt(ma.multiply.reduce(_x))
+ else:
+ for i in range(n - 1):
+ for j in range(i + 1, n):
+ _x = mask_cols(
+ vstack((x[:, i], x[:, j]))).var(axis=1)
+ _denom[i, j] = _denom[j, i] = ma.sqrt(ma.multiply.reduce(_x))
+ return c / _denom
+
+#####--------------------------------------------------------------------------
+#---- --- Concatenation helpers ---
+#####--------------------------------------------------------------------------
+
+class MAxisConcatenator(AxisConcatenator):
+ """
+ Translate slice objects to concatenation along an axis.
+
+ For documentation on usage, see `mr_class`.
+
+ See Also
+ --------
+ mr_class
+
+ """
+ concatenate = staticmethod(concatenate)
+
+ @classmethod
+ def makemat(cls, arr):
+ # There used to be a view as np.matrix here, but we may eventually
+ # deprecate that class. In preparation, we use the unmasked version
+ # to construct the matrix (with copy=False for backwards compatibility
+ # with the .view)
+ data = super().makemat(arr.data, copy=False)
+ return array(data, mask=arr.mask)
+
+ def __getitem__(self, key):
+ # matrix builder syntax, like 'a, b; c, d'
+ if isinstance(key, str):
+ raise MAError("Unavailable for masked array.")
+
+ return super().__getitem__(key)
+
+
+class mr_class(MAxisConcatenator):
+ """
+ Translate slice objects to concatenation along the first axis.
+
+ This is the masked array version of `lib.index_tricks.RClass`.
+
+ See Also
+ --------
+ lib.index_tricks.RClass
+
+ Examples
+ --------
+ >>> np.ma.mr_[np.ma.array([1,2,3]), 0, 0, np.ma.array([4,5,6])]
+ masked_array(data=[1, 2, 3, ..., 4, 5, 6],
+ mask=False,
+ fill_value=999999)
+
+ """
+ def __init__(self):
+ MAxisConcatenator.__init__(self, 0)
+
+mr_ = mr_class()
+
+
+#####--------------------------------------------------------------------------
+#---- Find unmasked data ---
+#####--------------------------------------------------------------------------
+
+def ndenumerate(a, compressed=True):
+ """
+ Multidimensional index iterator.
+
+ Return an iterator yielding pairs of array coordinates and values,
+ skipping elements that are masked. With `compressed=False`,
+ `ma.masked` is yielded as the value of masked elements. This
+ behavior differs from that of `numpy.ndenumerate`, which yields the
+ value of the underlying data array.
+
+ Notes
+ -----
+ .. versionadded:: 1.23.0
+
+ Parameters
+ ----------
+ a : array_like
+ An array with (possibly) masked elements.
+ compressed : bool, optional
+ If True (default), masked elements are skipped.
+
+ See Also
+ --------
+ numpy.ndenumerate : Equivalent function ignoring any mask.
+
+ Examples
+ --------
+ >>> a = np.ma.arange(9).reshape((3, 3))
+ >>> a[1, 0] = np.ma.masked
+ >>> a[1, 2] = np.ma.masked
+ >>> a[2, 1] = np.ma.masked
+ >>> a
+ masked_array(
+ data=[[0, 1, 2],
+ [--, 4, --],
+ [6, --, 8]],
+ mask=[[False, False, False],
+ [ True, False, True],
+ [False, True, False]],
+ fill_value=999999)
+ >>> for index, x in np.ma.ndenumerate(a):
+ ... print(index, x)
+ (0, 0) 0
+ (0, 1) 1
+ (0, 2) 2
+ (1, 1) 4
+ (2, 0) 6
+ (2, 2) 8
+
+ >>> for index, x in np.ma.ndenumerate(a, compressed=False):
+ ... print(index, x)
+ (0, 0) 0
+ (0, 1) 1
+ (0, 2) 2
+ (1, 0) --
+ (1, 1) 4
+ (1, 2) --
+ (2, 0) 6
+ (2, 1) --
+ (2, 2) 8
+ """
+ for it, mask in zip(np.ndenumerate(a), getmaskarray(a).flat):
+ if not mask:
+ yield it
+ elif not compressed:
+ yield it[0], masked
+
+
+def flatnotmasked_edges(a):
+ """
+ Find the indices of the first and last unmasked values.
+
+ Expects a 1-D `MaskedArray`, returns None if all values are masked.
+
+ Parameters
+ ----------
+ a : array_like
+ Input 1-D `MaskedArray`
+
+ Returns
+ -------
+ edges : ndarray or None
+ The indices of first and last non-masked value in the array.
+ Returns None if all values are masked.
+
+ See Also
+ --------
+ flatnotmasked_contiguous, notmasked_contiguous, notmasked_edges
+ clump_masked, clump_unmasked
+
+ Notes
+ -----
+ Only accepts 1-D arrays.
+
+ Examples
+ --------
+ >>> a = np.ma.arange(10)
+ >>> np.ma.flatnotmasked_edges(a)
+ array([0, 9])
+
+ >>> mask = (a < 3) | (a > 8) | (a == 5)
+ >>> a[mask] = np.ma.masked
+ >>> np.array(a[~a.mask])
+ array([3, 4, 6, 7, 8])
+
+ >>> np.ma.flatnotmasked_edges(a)
+ array([3, 8])
+
+ >>> a[:] = np.ma.masked
+ >>> print(np.ma.flatnotmasked_edges(a))
+ None
+
+ """
+ m = getmask(a)
+ if m is nomask or not np.any(m):
+ return np.array([0, a.size - 1])
+ unmasked = np.flatnonzero(~m)
+ if len(unmasked) > 0:
+ return unmasked[[0, -1]]
+ else:
+ return None
+
+
+def notmasked_edges(a, axis=None):
+ """
+ Find the indices of the first and last unmasked values along an axis.
+
+ If all values are masked, return None. Otherwise, return a list
+ of two tuples, corresponding to the indices of the first and last
+ unmasked values respectively.
+
+ Parameters
+ ----------
+ a : array_like
+ The input array.
+ axis : int, optional
+ Axis along which to perform the operation.
+ If None (default), applies to a flattened version of the array.
+
+ Returns
+ -------
+ edges : ndarray or list
+ An array of start and end indexes if there are any masked data in
+ the array. If there are no masked data in the array, `edges` is a
+ list of the first and last index.
+
+ See Also
+ --------
+ flatnotmasked_contiguous, flatnotmasked_edges, notmasked_contiguous
+ clump_masked, clump_unmasked
+
+ Examples
+ --------
+ >>> a = np.arange(9).reshape((3, 3))
+ >>> m = np.zeros_like(a)
+ >>> m[1:, 1:] = 1
+
+ >>> am = np.ma.array(a, mask=m)
+ >>> np.array(am[~am.mask])
+ array([0, 1, 2, 3, 6])
+
+ >>> np.ma.notmasked_edges(am)
+ array([0, 6])
+
+ """
+ a = asarray(a)
+ if axis is None or a.ndim == 1:
+ return flatnotmasked_edges(a)
+ m = getmaskarray(a)
+ idx = array(np.indices(a.shape), mask=np.asarray([m] * a.ndim))
+ return [tuple([idx[i].min(axis).compressed() for i in range(a.ndim)]),
+ tuple([idx[i].max(axis).compressed() for i in range(a.ndim)]), ]
+
+
+def flatnotmasked_contiguous(a):
+ """
+ Find contiguous unmasked data in a masked array.
+
+ Parameters
+ ----------
+ a : array_like
+ The input array.
+
+ Returns
+ -------
+ slice_list : list
+ A sorted sequence of `slice` objects (start index, end index).
+
+ .. versionchanged:: 1.15.0
+ Now returns an empty list instead of None for a fully masked array
+
+ See Also
+ --------
+ flatnotmasked_edges, notmasked_contiguous, notmasked_edges
+ clump_masked, clump_unmasked
+
+ Notes
+ -----
+ Only accepts 2-D arrays at most.
+
+ Examples
+ --------
+ >>> a = np.ma.arange(10)
+ >>> np.ma.flatnotmasked_contiguous(a)
+ [slice(0, 10, None)]
+
+ >>> mask = (a < 3) | (a > 8) | (a == 5)
+ >>> a[mask] = np.ma.masked
+ >>> np.array(a[~a.mask])
+ array([3, 4, 6, 7, 8])
+
+ >>> np.ma.flatnotmasked_contiguous(a)
+ [slice(3, 5, None), slice(6, 9, None)]
+ >>> a[:] = np.ma.masked
+ >>> np.ma.flatnotmasked_contiguous(a)
+ []
+
+ """
+ m = getmask(a)
+ if m is nomask:
+ return [slice(0, a.size)]
+ i = 0
+ result = []
+ for (k, g) in itertools.groupby(m.ravel()):
+ n = len(list(g))
+ if not k:
+ result.append(slice(i, i + n))
+ i += n
+ return result
+
+
+def notmasked_contiguous(a, axis=None):
+ """
+ Find contiguous unmasked data in a masked array along the given axis.
+
+ Parameters
+ ----------
+ a : array_like
+ The input array.
+ axis : int, optional
+ Axis along which to perform the operation.
+ If None (default), applies to a flattened version of the array, and this
+ is the same as `flatnotmasked_contiguous`.
+
+ Returns
+ -------
+ endpoints : list
+ A list of slices (start and end indexes) of unmasked indexes
+ in the array.
+
+ If the input is 2d and axis is specified, the result is a list of lists.
+
+ See Also
+ --------
+ flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges
+ clump_masked, clump_unmasked
+
+ Notes
+ -----
+ Only accepts 2-D arrays at most.
+
+ Examples
+ --------
+ >>> a = np.arange(12).reshape((3, 4))
+ >>> mask = np.zeros_like(a)
+ >>> mask[1:, :-1] = 1; mask[0, 1] = 1; mask[-1, 0] = 0
+ >>> ma = np.ma.array(a, mask=mask)
+ >>> ma
+ masked_array(
+ data=[[0, --, 2, 3],
+ [--, --, --, 7],
+ [8, --, --, 11]],
+ mask=[[False, True, False, False],
+ [ True, True, True, False],
+ [False, True, True, False]],
+ fill_value=999999)
+ >>> np.array(ma[~ma.mask])
+ array([ 0, 2, 3, 7, 8, 11])
+
+ >>> np.ma.notmasked_contiguous(ma)
+ [slice(0, 1, None), slice(2, 4, None), slice(7, 9, None), slice(11, 12, None)]
+
+ >>> np.ma.notmasked_contiguous(ma, axis=0)
+ [[slice(0, 1, None), slice(2, 3, None)], [], [slice(0, 1, None)], [slice(0, 3, None)]]
+
+ >>> np.ma.notmasked_contiguous(ma, axis=1)
+ [[slice(0, 1, None), slice(2, 4, None)], [slice(3, 4, None)], [slice(0, 1, None), slice(3, 4, None)]]
+
+ """
+ a = asarray(a)
+ nd = a.ndim
+ if nd > 2:
+ raise NotImplementedError("Currently limited to at most 2D array.")
+ if axis is None or nd == 1:
+ return flatnotmasked_contiguous(a)
+ #
+ result = []
+ #
+ other = (axis + 1) % 2
+ idx = [0, 0]
+ idx[axis] = slice(None, None)
+ #
+ for i in range(a.shape[other]):
+ idx[other] = i
+ result.append(flatnotmasked_contiguous(a[tuple(idx)]))
+ return result
+
+
+def _ezclump(mask):
+ """
+ Finds the clumps (groups of data with the same values) for a 1D bool array.
+
+ Returns a series of slices.
+ """
+ if mask.ndim > 1:
+ mask = mask.ravel()
+ idx = (mask[1:] ^ mask[:-1]).nonzero()
+ idx = idx[0] + 1
+
+ if mask[0]:
+ if len(idx) == 0:
+ return [slice(0, mask.size)]
+
+ r = [slice(0, idx[0])]
+ r.extend((slice(left, right)
+ for left, right in zip(idx[1:-1:2], idx[2::2])))
+ else:
+ if len(idx) == 0:
+ return []
+
+ r = [slice(left, right) for left, right in zip(idx[:-1:2], idx[1::2])]
+
+ if mask[-1]:
+ r.append(slice(idx[-1], mask.size))
+ return r
+
+
+def clump_unmasked(a):
+ """
+ Return list of slices corresponding to the unmasked clumps of a 1-D array.
+ (A "clump" is defined as a contiguous region of the array).
+
+ Parameters
+ ----------
+ a : ndarray
+ A one-dimensional masked array.
+
+ Returns
+ -------
+ slices : list of slice
+ The list of slices, one for each continuous region of unmasked
+ elements in `a`.
+
+ Notes
+ -----
+ .. versionadded:: 1.4.0
+
+ See Also
+ --------
+ flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges
+ notmasked_contiguous, clump_masked
+
+ Examples
+ --------
+ >>> a = np.ma.masked_array(np.arange(10))
+ >>> a[[0, 1, 2, 6, 8, 9]] = np.ma.masked
+ >>> np.ma.clump_unmasked(a)
+ [slice(3, 6, None), slice(7, 8, None)]
+
+ """
+ mask = getattr(a, '_mask', nomask)
+ if mask is nomask:
+ return [slice(0, a.size)]
+ return _ezclump(~mask)
+
+
+def clump_masked(a):
+ """
+ Returns a list of slices corresponding to the masked clumps of a 1-D array.
+ (A "clump" is defined as a contiguous region of the array).
+
+ Parameters
+ ----------
+ a : ndarray
+ A one-dimensional masked array.
+
+ Returns
+ -------
+ slices : list of slice
+ The list of slices, one for each continuous region of masked elements
+ in `a`.
+
+ Notes
+ -----
+ .. versionadded:: 1.4.0
+
+ See Also
+ --------
+ flatnotmasked_edges, flatnotmasked_contiguous, notmasked_edges
+ notmasked_contiguous, clump_unmasked
+
+ Examples
+ --------
+ >>> a = np.ma.masked_array(np.arange(10))
+ >>> a[[0, 1, 2, 6, 8, 9]] = np.ma.masked
+ >>> np.ma.clump_masked(a)
+ [slice(0, 3, None), slice(6, 7, None), slice(8, 10, None)]
+
+ """
+ mask = ma.getmask(a)
+ if mask is nomask:
+ return []
+ return _ezclump(mask)
+
+
+###############################################################################
+# Polynomial fit #
+###############################################################################
+
+
+def vander(x, n=None):
+ """
+ Masked values in the input array result in rows of zeros.
+
+ """
+ _vander = np.vander(x, n)
+ m = getmask(x)
+ if m is not nomask:
+ _vander[m] = 0
+ return _vander
+
+vander.__doc__ = ma.doc_note(np.vander.__doc__, vander.__doc__)
+
+
+def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False):
+ """
+ Any masked values in x is propagated in y, and vice-versa.
+
+ """
+ x = asarray(x)
+ y = asarray(y)
+
+ m = getmask(x)
+ if y.ndim == 1:
+ m = mask_or(m, getmask(y))
+ elif y.ndim == 2:
+ my = getmask(mask_rows(y))
+ if my is not nomask:
+ m = mask_or(m, my[:, 0])
+ else:
+ raise TypeError("Expected a 1D or 2D array for y!")
+
+ if w is not None:
+ w = asarray(w)
+ if w.ndim != 1:
+ raise TypeError("expected a 1-d array for weights")
+ if w.shape[0] != y.shape[0]:
+ raise TypeError("expected w and y to have the same length")
+ m = mask_or(m, getmask(w))
+
+ if m is not nomask:
+ not_m = ~m
+ if w is not None:
+ w = w[not_m]
+ return np.polyfit(x[not_m], y[not_m], deg, rcond, full, w, cov)
+ else:
+ return np.polyfit(x, y, deg, rcond, full, w, cov)
+
+polyfit.__doc__ = ma.doc_note(np.polyfit.__doc__, polyfit.__doc__)