about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/numpy/ma/extras.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/numpy/ma/extras.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-4a52a71956a8d46fcb7294ac71734504bb09bcc2.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/numpy/ma/extras.py')
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/ma/extras.py2133
1 files changed, 2133 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/numpy/ma/extras.py b/.venv/lib/python3.12/site-packages/numpy/ma/extras.py
new file mode 100644
index 00000000..8a6246c3
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/ma/extras.py
@@ -0,0 +1,2133 @@
+"""
+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
+    )
+
+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.prod(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.prod(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_rowcols(a, axis=None):
+    """
+    Mask rows and/or columns of a 2D array that contain masked values.
+
+    Mask whole rows and/or columns of a 2D array that contain
+    masked values.  The masking behavior is selected using the
+    `axis` parameter.
+
+      - If `axis` is None, rows *and* columns are masked.
+      - If `axis` is 0, only rows are masked.
+      - If `axis` is 1 or -1, only columns are masked.
+
+    Parameters
+    ----------
+    a : array_like, MaskedArray
+        The array to mask.  If not a MaskedArray instance (or if no array
+        elements are masked), the result is a MaskedArray with `mask` set
+        to `nomask` (False). Must be a 2D array.
+    axis : int, optional
+        Axis along which to perform the operation. If None, applies to a
+        flattened version of the array.
+
+    Returns
+    -------
+    a : MaskedArray
+        A modified version of the input array, masked depending on the value
+        of the `axis` parameter.
+
+    Raises
+    ------
+    NotImplementedError
+        If input array `a` is not 2D.
+
+    See Also
+    --------
+    mask_rows : Mask rows of a 2D array that contain masked values.
+    mask_cols : Mask cols of a 2D array that contain masked values.
+    masked_where : Mask where a condition is met.
+
+    Notes
+    -----
+    The input array's mask is modified by this function.
+
+    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_rowcols(a)
+    masked_array(
+      data=[[0, --, 0],
+            [--, --, --],
+            [0, --, 0]],
+      mask=[[False,  True, False],
+            [ True,  True,  True],
+            [False,  True, False]],
+      fill_value=1)
+
+    """
+    a = array(a, subok=False)
+    if a.ndim != 2:
+        raise NotImplementedError("mask_rowcols works for 2D arrays only.")
+    m = getmask(a)
+    # Nothing is masked: return a
+    if m is nomask or not m.any():
+        return a
+    maskedval = m.nonzero()
+    a._mask = a._mask.copy()
+    if not axis:
+        a[np.unique(maskedval[0])] = masked
+    if axis in [None, 1, -1]:
+        a[:, np.unique(maskedval[1])] = masked
+    return a
+
+
+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__)