Spaces:
Running
Running
""" | |
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_', | |
'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 | |
import numpy.core.umath as umath | |
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 : tuple | |
Shape of the required MaskedArray. | |
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): | |
""" | |
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. | |
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) | |
""" | |
a = asarray(a) | |
m = getmask(a) | |
# inspired by 'average' in numpy/lib/function_base.py | |
if weights is None: | |
avg = a.mean(axis) | |
scl = avg.dtype.type(a.count(axis)) | |
else: | |
wgt = np.asanyarray(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) | |
scl = wgt.sum(axis=axis, dtype=result_dtype) | |
avg = np.multiply(a, wgt, dtype=result_dtype).sum(axis)/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 | |
r, k = _ureduce(a, func=_median, axis=axis, out=out, | |
overwrite_input=overwrite_input) | |
if keepdims: | |
return r.reshape(k) | |
else: | |
return r | |
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: | |
counts = count(asorted) | |
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, out) | |
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, out) | |
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. | |
""" | |
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) | |
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 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 along the given axis. | |
Parameters | |
---------- | |
a : narray | |
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 atmost 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__) | |