summaryrefslogtreecommitdiff
path: root/numpy/ma
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/ma')
-rw-r--r--numpy/ma/core.py92
-rw-r--r--numpy/ma/extras.py45
-rw-r--r--numpy/ma/tests/test_core.py21
-rw-r--r--numpy/ma/tests/test_extras.py3
-rw-r--r--numpy/ma/tests/test_old_ma.py3
5 files changed, 113 insertions, 51 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index 7dadb9d98..9ca9136dd 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -845,8 +845,7 @@ class _MaskedUnaryOperation:
d = getdata(a)
# Case 1.1. : Domained function
if self.domain is not None:
- with np.errstate():
- np.seterr(divide='ignore', invalid='ignore')
+ with np.errstate(divide='ignore', invalid='ignore'):
result = self.f(d, *args, **kwargs)
# Make a mask
m = ~umath.isfinite(result)
@@ -934,8 +933,7 @@ class _MaskedBinaryOperation:
else:
m = umath.logical_or(ma, mb)
# Get the result
- with np.errstate():
- np.seterr(divide='ignore', invalid='ignore')
+ with np.errstate(divide='ignore', invalid='ignore'):
result = self.f(da, db, *args, **kwargs)
# check it worked
if result is NotImplemented:
@@ -947,11 +945,8 @@ class _MaskedBinaryOperation:
return result
# Case 2. : array
# Revert result to da where masked
- if m.any():
- np.copyto(result, 0, casting='unsafe', where=m)
- # This only makes sense if the operation preserved the dtype
- if result.dtype == da.dtype:
- result += m * da
+ if m is not nomask:
+ np.copyto(result, da, casting='unsafe', where=m)
# Transforms to a (subclass of) MaskedArray
result = result.view(get_masked_subclass(a, b))
result._mask = m
@@ -1075,8 +1070,7 @@ class _DomainedBinaryOperation:
(da, db) = (getdata(a, subok=False), getdata(b, subok=False))
(ma, mb) = (getmask(a), getmask(b))
# Get the result
- with np.errstate():
- np.seterr(divide='ignore', invalid='ignore')
+ with np.errstate(divide='ignore', invalid='ignore'):
result = self.f(da, db, *args, **kwargs)
# check it worked
if result is NotImplemented:
@@ -1096,8 +1090,7 @@ class _DomainedBinaryOperation:
else:
return result
# When the mask is True, put back da
- np.copyto(result, 0, casting='unsafe', where=m)
- result += m * da
+ np.copyto(result, da, casting='unsafe', where=m)
result = result.view(get_masked_subclass(a, b))
result._mask = m
if isinstance(b, MaskedArray):
@@ -2792,12 +2785,50 @@ class MaskedArray(ndarray):
"""
# Get main attributes .........
self._update_from(obj)
+ # We have to decide how to initialize self.mask, based on
+ # obj.mask. This is very difficult. There might be some
+ # correspondence between the elements in the array we are being
+ # created from (= obj) and us. Or... there might not. This method can
+ # be called in all kinds of places for all kinds of reasons -- could
+ # be empty_like, could be slicing, could be a ufunc, could be a view,
+ # ... The numpy subclassing interface simply doesn't give us any way
+ # to know, which means that at best this method will be based on
+ # guesswork and heuristics. To make things worse, there isn't even any
+ # clear consensus about what the desired behavior is. For instance,
+ # most users think that np.empty_like(marr) -- which goes via this
+ # method -- should return a masked array with an empty mask (see
+ # gh-3404 and linked discussions), but others disagree, and they have
+ # existing code which depends on empty_like returning an array that
+ # matches the input mask.
+ #
+ # Historically our algorithm was: if the template object mask had the
+ # same *number of elements* as us, then we used *it's mask object
+ # itself* as our mask, so that writes to us would also write to the
+ # original array. This is horribly broken in multiple ways.
+ #
+ # Now what we do instead is, if the template object mask has the same
+ # number of elements as us, and we do not have the same base pointer
+ # as the template object (b/c views like arr[...] should keep the same
+ # mask), then we make a copy of the template object mask and use
+ # that. This is also horribly broken but somewhat less so. Maybe.
if isinstance(obj, ndarray):
- odtype = obj.dtype
- if odtype.names:
- _mask = getattr(obj, '_mask', make_mask_none(obj.shape, odtype))
+ # XX: This looks like a bug -- shouldn't it check self.dtype
+ # instead?
+ if obj.dtype.names:
+ _mask = getattr(obj, '_mask',
+ make_mask_none(obj.shape, obj.dtype))
else:
_mask = getattr(obj, '_mask', nomask)
+ # If self and obj point to exactly the same data, then probably
+ # self is a simple view of obj (e.g., self = obj[...]), so they
+ # should share the same mask. (This isn't 100% reliable, e.g. self
+ # could be the first row of obj, or have strange strides, but as a
+ # heuristic it's not bad.) In all other cases, we make a copy of
+ # the mask, so that future modifications to 'self' do not end up
+ # side-effecting 'obj' as well.
+ if (obj.__array_interface__["data"][0]
+ != self.__array_interface__["data"][0]):
+ _mask = _mask.copy()
else:
_mask = nomask
self._mask = _mask
@@ -3842,8 +3873,7 @@ class MaskedArray(ndarray):
"Raise self to the power other, in place."
other_data = getdata(other)
other_mask = getmask(other)
- with np.errstate():
- np.seterr(divide='ignore', invalid='ignore')
+ with np.errstate(divide='ignore', invalid='ignore'):
ndarray.__ipow__(self._data, np.where(self._mask, 1, other_data))
invalid = np.logical_not(np.isfinite(self._data))
if invalid.any():
@@ -4948,7 +4978,7 @@ class MaskedArray(ndarray):
Returns
-------
- {ndarray, scalar}
+ ndarray or scalar
If multi-dimension input, returns a new ndarray of indices to the
minimum values along the given axis. Otherwise, returns a scalar
of index to the minimum values along the given axis.
@@ -5031,6 +5061,10 @@ class MaskedArray(ndarray):
endwith : {True, False}, optional
Whether missing values (if any) should be forced in the upper indices
(at the end of the array) (True) or lower indices (at the beginning).
+ When the array contains unmasked values of the largest (or smallest if
+ False) representable value of the datatype the ordering of these values
+ and the masked values is undefined. To enforce the masked values are
+ at the end (beginning) in this case one must sort the mask.
fill_value : {var}, optional
Value used internally for the masked values.
If ``fill_value`` is not None, it supersedes ``endwith``.
@@ -5079,7 +5113,11 @@ class MaskedArray(ndarray):
return self
if fill_value is None:
if endwith:
- filler = minimum_fill_value(self)
+ # nan > inf
+ if np.issubdtype(self.dtype, np.floating):
+ filler = np.nan
+ else:
+ filler = minimum_fill_value(self)
else:
filler = maximum_fill_value(self)
else:
@@ -5596,9 +5634,8 @@ class mvoid(MaskedArray):
"""
#
def __new__(self, data, mask=nomask, dtype=None, fill_value=None,
- hardmask=False):
- dtype = dtype or data.dtype
- _data = np.array(data, dtype=dtype)
+ hardmask=False, copy=False, subok=True):
+ _data = np.array(data, copy=copy, subok=subok, dtype=dtype)
_data = _data.view(self)
_data._hardmask = hardmask
if mask is not nomask:
@@ -6118,8 +6155,7 @@ def power(a, b, third=None):
else:
basetype = MaskedArray
# Get the result and view it as a (subclass of) MaskedArray
- with np.errstate():
- np.seterr(divide='ignore', invalid='ignore')
+ with np.errstate(divide='ignore', invalid='ignore'):
result = np.where(m, fa, umath.power(fa, fb)).view(basetype)
result._update_from(a)
# Find where we're in trouble w/ NaNs and Infs
@@ -6190,7 +6226,11 @@ def sort(a, axis= -1, kind='quicksort', order=None, endwith=True, fill_value=Non
axis = 0
if fill_value is None:
if endwith:
- filler = minimum_fill_value(a)
+ # nan > inf
+ if np.issubdtype(a.dtype, np.floating):
+ filler = np.nan
+ else:
+ filler = minimum_fill_value(a)
else:
filler = maximum_fill_value(a)
else:
diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py
index a993fd05d..726be6cb6 100644
--- a/numpy/ma/extras.py
+++ b/numpy/ma/extras.py
@@ -658,21 +658,21 @@ def median(a, axis=None, out=None, overwrite_input=False):
Examples
--------
>>> x = np.ma.array(np.arange(8), mask=[0]*4 + [1]*4)
- >>> np.ma.extras.median(x)
+ >>> np.ma.median(x)
1.5
>>> x = np.ma.array(np.arange(10).reshape(2, 5), mask=[0]*6 + [1]*4)
- >>> np.ma.extras.median(x)
+ >>> np.ma.median(x)
2.5
- >>> np.ma.extras.median(x, axis=-1, overwrite_input=True)
+ >>> np.ma.median(x, axis=-1, overwrite_input=True)
masked_array(data = [ 2. 5.],
mask = False,
fill_value = 1e+20)
"""
if not hasattr(a, 'mask') or np.count_nonzero(a.mask) == 0:
- return masked_array(np.median(a, axis=axis, out=out,
- overwrite_input=overwrite_input), copy=False)
+ return masked_array(np.median(getattr(a, 'data', a), axis=axis,
+ out=out, overwrite_input=overwrite_input), copy=False)
if overwrite_input:
if axis is None:
asorted = a.ravel()
@@ -705,7 +705,14 @@ def median(a, axis=None, out=None, overwrite_input=False):
low = high
else:
low[odd] = high[odd]
- return np.ma.mean([low, high], axis=0, out=out)
+
+ if np.issubdtype(asorted.dtype, np.inexact):
+ # avoid inf / x = masked
+ s = np.ma.sum([low, high], axis=0, out=out)
+ np.true_divide(s.data, 2., casting='unsafe', out=s.data)
+ else:
+ s = np.ma.mean([low, high], axis=0, out=out)
+ return s
#..............................................................................
@@ -746,11 +753,11 @@ def compress_rowcols(x, axis=None):
[False False False]],
fill_value = 999999)
- >>> np.ma.extras.compress_rowcols(x)
+ >>> np.ma.compress_rowcols(x)
array([[7, 8]])
- >>> np.ma.extras.compress_rowcols(x, 0)
+ >>> np.ma.compress_rowcols(x, 0)
array([[6, 7, 8]])
- >>> np.ma.extras.compress_rowcols(x, 1)
+ >>> np.ma.compress_rowcols(x, 1)
array([[1, 2],
[4, 5],
[7, 8]])
@@ -781,7 +788,7 @@ def compress_rows(a):
"""
Suppress whole rows of a 2-D array that contain masked values.
- This is equivalent to ``np.ma.extras.compress_rowcols(a, 0)``, see
+ This is equivalent to ``np.ma.compress_rowcols(a, 0)``, see
`extras.compress_rowcols` for details.
See Also
@@ -795,7 +802,7 @@ def compress_cols(a):
"""
Suppress whole columns of a 2-D array that contain masked values.
- This is equivalent to ``np.ma.extras.compress_rowcols(a, 1)``, see
+ This is equivalent to ``np.ma.compress_rowcols(a, 1)``, see
`extras.compress_rowcols` for details.
See Also
@@ -1226,7 +1233,7 @@ def setdiff1d(ar1, ar2, assume_unique=False):
Examples
--------
>>> x = np.ma.array([1, 2, 3, 4], mask=[0, 1, 0, 1])
- >>> np.ma.extras.setdiff1d(x, [1, 2])
+ >>> np.ma.setdiff1d(x, [1, 2])
masked_array(data = [3 --],
mask = [False True],
fill_value = 999999)
@@ -1638,7 +1645,7 @@ def notmasked_edges(a, axis=None):
>>> np.array(am[~am.mask])
array([0, 1, 2, 3, 6])
- >>> np.ma.extras.notmasked_edges(ma)
+ >>> np.ma.notmasked_edges(ma)
array([0, 6])
"""
@@ -1677,7 +1684,7 @@ def flatnotmasked_contiguous(a):
Examples
--------
>>> a = np.ma.arange(10)
- >>> np.ma.extras.flatnotmasked_contiguous(a)
+ >>> np.ma.flatnotmasked_contiguous(a)
slice(0, 10, None)
>>> mask = (a < 3) | (a > 8) | (a == 5)
@@ -1685,10 +1692,10 @@ def flatnotmasked_contiguous(a):
>>> np.array(a[~a.mask])
array([3, 4, 6, 7, 8])
- >>> np.ma.extras.flatnotmasked_contiguous(a)
+ >>> np.ma.flatnotmasked_contiguous(a)
[slice(3, 5, None), slice(6, 9, None)]
>>> a[:] = np.ma.masked
- >>> print np.ma.extras.flatnotmasked_edges(a)
+ >>> print np.ma.flatnotmasked_edges(a)
None
"""
@@ -1741,7 +1748,7 @@ def notmasked_contiguous(a, axis=None):
>>> np.array(ma[~ma.mask])
array([0, 1, 2, 3, 6])
- >>> np.ma.extras.notmasked_contiguous(ma)
+ >>> np.ma.notmasked_contiguous(ma)
[slice(0, 4, None), slice(6, 7, None)]
"""
@@ -1810,7 +1817,7 @@ def clump_unmasked(a):
--------
>>> a = np.ma.masked_array(np.arange(10))
>>> a[[0, 1, 2, 6, 8, 9]] = np.ma.masked
- >>> np.ma.extras.clump_unmasked(a)
+ >>> np.ma.clump_unmasked(a)
[slice(3, 6, None), slice(7, 8, None)]
"""
@@ -1854,7 +1861,7 @@ def clump_masked(a):
--------
>>> a = np.ma.masked_array(np.arange(10))
>>> a[[0, 1, 2, 6, 8, 9]] = np.ma.masked
- >>> np.ma.extras.clump_masked(a)
+ >>> np.ma.clump_masked(a)
[slice(0, 3, None), slice(6, 7, None), slice(8, 10, None)]
"""
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index e6f659041..4ac3465aa 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -194,8 +194,7 @@ class TestMaskedArray(TestCase):
def test_fix_invalid(self):
# Checks fix_invalid.
- with np.errstate():
- np.seterr(invalid='ignore')
+ with np.errstate(invalid='ignore'):
data = masked_array([np.nan, 0., 1.], mask=[0, 0, 1])
data_fixed = fix_invalid(data)
assert_equal(data_fixed._data, [data.fill_value, 0., 1.])
@@ -815,7 +814,7 @@ class TestMaskedArrayArithmetic(TestCase):
res = count(ott)
self.assertTrue(res.dtype.type is np.intp)
assert_equal(3, res)
-
+
ott = ott.reshape((2, 2))
res = count(ott)
assert_(res.dtype.type is np.intp)
@@ -2227,6 +2226,13 @@ class TestMaskedArrayMethods(TestCase):
assert_equal(b.shape, a.shape)
assert_equal(b.fill_value, a.fill_value)
+ # check empty_like mask handling
+ a = masked_array([1, 2, 3], mask=[False, True, False])
+ b = empty_like(a)
+ assert_(not np.may_share_memory(a.mask, b.mask))
+ b = a.view(masked_array)
+ assert_(np.may_share_memory(a.mask, b.mask))
+
def test_put(self):
# Tests put.
d = arange(5)
@@ -3523,8 +3529,15 @@ class TestMaskedFields(TestCase):
assert_equal_records(a[-2]._mask, a._mask[-2])
def test_setitem(self):
- # Issue 2403
+ # Issue 4866: check that one can set individual items in [record][col]
+ # and [col][record] order
ndtype = np.dtype([('a', float), ('b', int)])
+ ma = np.ma.MaskedArray([(1.0, 1), (2.0, 2)], dtype=ndtype)
+ ma['a'][1] = 3.0
+ assert_equal(ma['a'], np.array([1.0, 3.0]))
+ ma[1]['a'] = 4.0
+ assert_equal(ma['a'], np.array([1.0, 4.0]))
+ # Issue 2403
mdtype = np.dtype([('a', bool), ('b', bool)])
# soft mask
control = np.array([(False, True), (True, True)], dtype=mdtype)
diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py
index 6ce1dc346..d7bc765a9 100644
--- a/numpy/ma/tests/test_extras.py
+++ b/numpy/ma/tests/test_extras.py
@@ -504,6 +504,9 @@ class TestApplyOverAxes(TestCase):
class TestMedian(TestCase):
+ def test_pytype(self):
+ r = np.ma.median([[np.inf, np.inf], [np.inf, np.inf]], axis=-1)
+ assert_equal(r, np.inf)
def test_2d(self):
# Tests median w/ 2D
diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py
index 87c2133d7..047f91c77 100644
--- a/numpy/ma/tests/test_old_ma.py
+++ b/numpy/ma/tests/test_old_ma.py
@@ -607,8 +607,7 @@ class TestMa(TestCase):
def test_testScalarArithmetic(self):
xm = array(0, mask=1)
#TODO FIXME: Find out what the following raises a warning in r8247
- with np.errstate():
- np.seterr(divide='ignore')
+ with np.errstate(divide='ignore'):
self.assertTrue((1 / array(0)).mask)
self.assertTrue((1 + xm).mask)
self.assertTrue((-xm).mask)