diff options
Diffstat (limited to 'numpy/ma')
-rw-r--r-- | numpy/ma/core.py | 173 | ||||
-rw-r--r-- | numpy/ma/extras.py | 96 | ||||
-rw-r--r-- | numpy/ma/mrecords.py | 4 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 145 | ||||
-rw-r--r-- | numpy/ma/tests/test_extras.py | 26 | ||||
-rw-r--r-- | numpy/ma/tests/test_old_ma.py | 14 | ||||
-rw-r--r-- | numpy/ma/tests/test_subclassing.py | 43 |
7 files changed, 380 insertions, 121 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 8dc2ca86e..6c0a8f345 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -46,7 +46,7 @@ __docformat__ = "restructuredtext en" __all__ = ['MAError', 'MaskError', 'MaskType', 'MaskedArray', 'bool_', 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', - 'amax', 'amin', 'angle', 'anom', 'anomalies', 'any', 'arange', + 'amax', 'amin', 'angle', 'anom', 'anomalies', 'any', 'append', 'arange', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', 'arctanh', 'argmax', 'argmin', 'argsort', 'around', 'array', 'asarray', 'asanyarray', @@ -416,14 +416,19 @@ def _check_fill_value(fill_value, ndtype): fill_value = np.array(_recursive_set_fill_value(fill_value, descr), dtype=ndtype) else: - if isinstance(fill_value, basestring) and (ndtype.char not in 'SVU'): - fill_value = default_fill_value(ndtype) + if isinstance(fill_value, basestring) and (ndtype.char not in 'OSVU'): + err_msg = "Cannot set fill value of string with array of dtype %s" + raise TypeError(err_msg % ndtype) else: - # In case we want to convert 1e+20 to int... + # In case we want to convert 1e20 to int... try: - fill_value = np.array(fill_value, copy=False, dtype=ndtype)#.item() + fill_value = np.array(fill_value, copy=False, dtype=ndtype) except OverflowError: - fill_value = default_fill_value(ndtype) + # Raise TypeError instead of OverflowError. OverflowError + # is seldom used, and the real problem here is that the + # passed fill_value is not compatible with the ndtype. + err_msg = "Fill value %s overflows dtype %s" + raise TypeError(err_msg % (fill_value, ndtype)) return np.array(fill_value) @@ -838,8 +843,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) @@ -927,8 +931,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: @@ -940,11 +943,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 @@ -1068,8 +1068,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: @@ -1089,8 +1088,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): @@ -2465,7 +2463,6 @@ class _arraymethod(object): return result - class MaskedIterator(object): """ Flat iterator object to iterate over masked arrays. @@ -2529,8 +2526,14 @@ class MaskedIterator(object): result = self.dataiter.__getitem__(indx).view(type(self.ma)) if self.maskiter is not None: _mask = self.maskiter.__getitem__(indx) - _mask.shape = result.shape - result._mask = _mask + if isinstance(_mask, ndarray): + # set shape to match that of data; this is needed for matrices + _mask.shape = result.shape + result._mask = _mask + elif isinstance(_mask, np.void): + return mvoid(result, mask=_mask, hardmask=self.ma._hardmask) + elif _mask: # Just a scalar, masked + return masked return result ### This won't work is ravel makes a copy @@ -2562,8 +2565,12 @@ class MaskedIterator(object): """ d = next(self.dataiter) - if self.maskiter is not None and next(self.maskiter): - d = masked + if self.maskiter is not None: + m = next(self.maskiter) + if isinstance(m, np.void): + return mvoid(d, mask=m, hardmask=self.ma._hardmask) + elif m: # Just a scalar, masked + return masked return d next = __next__ @@ -3580,9 +3587,8 @@ class MaskedArray(ndarray): if m.dtype.names: m = m.view((bool, len(m.dtype))) if m.any(): - r = np.array(self._data.tolist(), dtype=object) - np.copyto(r, f, where=m) - return str(tuple(r)) + return str(tuple((f if _m else _d) for _d, _m in + zip(self._data.tolist(), m))) else: return str(self._data) elif m: @@ -3593,7 +3599,7 @@ class MaskedArray(ndarray): names = self.dtype.names if names is None: res = self._data.astype("O") - res[m] = f + res.view(ndarray)[m] = f else: rdtype = _recursive_make_descr(self.dtype, "O") res = self._data.astype(rdtype) @@ -3607,19 +3613,22 @@ class MaskedArray(ndarray): """ n = len(self.shape) - name = repr(self._data).split('(')[0] + if self._baseclass is np.ndarray: + name = 'array' + else: + name = self._baseclass.__name__ + parameters = dict(name=name, nlen=" " * len(name), - data=str(self), mask=str(self._mask), - fill=str(self.fill_value), dtype=str(self.dtype)) + data=str(self), mask=str(self._mask), + fill=str(self.fill_value), dtype=str(self.dtype)) if self.dtype.names: if n <= 1: return _print_templates['short_flx'] % parameters - return _print_templates['long_flx'] % parameters + return _print_templates['long_flx'] % parameters elif n <= 1: return _print_templates['short_std'] % parameters return _print_templates['long_std'] % parameters - def __eq__(self, other): "Check whether other equals self elementwise" if self is masked: @@ -3824,8 +3833,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(): @@ -3976,21 +3984,16 @@ class MaskedArray(ndarray): """ m = self._mask s = self.shape - ls = len(s) if m is nomask: - if ls == 0: - return 1 - if ls == 1: - return s[0] if axis is None: return self.size else: n = s[axis] t = list(s) del t[axis] - return np.ones(t) * n + return np.full(t, n, dtype=np.intp) n1 = np.size(m, axis) - n2 = m.astype(int).sum(axis) + n2 = np.sum(m, axis=axis, dtype=np.intp) if axis is None: return (n1 - n2) else: @@ -5071,12 +5074,12 @@ class MaskedArray(ndarray): filler = maximum_fill_value(self) else: filler = fill_value - idx = np.indices(self.shape) + idx = np.meshgrid(*[np.arange(x) for x in self.shape], sparse=True, + indexing='ij') idx[axis] = self.filled(filler).argsort(axis=axis, kind=kind, order=order) - idx_l = idx.tolist() - tmp_mask = self._mask[idx_l].flat - tmp_data = self._data[idx_l].flat + tmp_mask = self._mask[idx].flat + tmp_data = self._data[idx].flat self._data.flat = tmp_data self._mask.flat = tmp_mask return @@ -5387,10 +5390,20 @@ class MaskedArray(ndarray): #........................ def tostring(self, fill_value=None, order='C'): """ + This function is a compatibility alias for tobytes. Despite its name it + returns bytes not strings. + """ + + return self.tobytes(fill_value, order='C') + #........................ + def tobytes(self, fill_value=None, order='C'): + """ Return the array data as a string containing the raw bytes in the array. The array is filled with a fill value before the string conversion. + .. versionadded:: 1.9.0 + Parameters ---------- fill_value : scalar, optional @@ -5406,22 +5419,22 @@ class MaskedArray(ndarray): See Also -------- - ndarray.tostring + ndarray.tobytes tolist, tofile Notes ----- - As for `ndarray.tostring`, information about the shape, dtype, etc., + As for `ndarray.tobytes`, information about the shape, dtype, etc., but also about `fill_value`, will be lost. Examples -------- >>> x = np.ma.array(np.array([[1, 2], [3, 4]]), mask=[[0, 1], [1, 0]]) - >>> x.tostring() + >>> x.tobytes() '\\x01\\x00\\x00\\x00?B\\x0f\\x00?B\\x0f\\x00\\x04\\x00\\x00\\x00' """ - return self.filled(fill_value).tostring(order=order) + return self.filled(fill_value).tobytes(order=order) #........................ def tofile(self, fid, sep="", format="%s"): """ @@ -5503,9 +5516,9 @@ class MaskedArray(ndarray): self.shape, self.dtype, self.flags.fnc, - self._data.tostring(cf), + self._data.tobytes(cf), #self._data.tolist(), - getmaskarray(self).tostring(cf), + getmaskarray(self).tobytes(cf), #getmaskarray(self).tolist(), self._fill_value, ) @@ -6089,8 +6102,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 @@ -6151,7 +6163,7 @@ def argmax(a, axis=None, fill_value=None): pass d = filled(a, fill_value) return d.argmax(axis=axis) -argmin.__doc__ = MaskedArray.argmax.__doc__ +argmax.__doc__ = MaskedArray.argmax.__doc__ def sort(a, axis= -1, kind='quicksort', order=None, endwith=True, fill_value=None): "Function version of the eponymous method." @@ -6167,7 +6179,8 @@ def sort(a, axis= -1, kind='quicksort', order=None, endwith=True, fill_value=Non else: filler = fill_value # return - indx = np.indices(a.shape).tolist() + indx = np.meshgrid(*[np.arange(x) for x in a.shape], sparse=True, + indexing='ij') indx[axis] = filled(a, filler).argsort(axis=axis, kind=kind, order=order) return a[indx] sort.__doc__ = MaskedArray.sort.__doc__ @@ -6921,6 +6934,13 @@ def allclose (a, b, masked_equal=True, rtol=1e-5, atol=1e-8): """ x = masked_array(a, copy=False) y = masked_array(b, copy=False) + + # make sure y is an inexact type to avoid abs(MIN_INT); will cause + # casting of x later. + dtype = np.result_type(y, 1.) + if y.dtype != dtype: + y = masked_array(y, dtype=dtype, copy=False) + m = mask_or(getmask(x), getmask(y)) xinf = np.isinf(masked_array(x, copy=False, mask=m)).filled(False) # If we have some infs, they should fall at the same place. @@ -6932,13 +6952,16 @@ def allclose (a, b, masked_equal=True, rtol=1e-5, atol=1e-8): atol + rtol * umath.absolute(y)), masked_equal) return np.all(d) + if not np.all(filled(x[xinf] == y[xinf], masked_equal)): return False x = x[~xinf] y = y[~xinf] + d = filled(umath.less_equal(umath.absolute(x - y), atol + rtol * umath.absolute(y)), masked_equal) + return np.all(d) #.............................................................................. @@ -7243,3 +7266,41 @@ zeros = _convert2ma('zeros', params=dict(fill_value=None, hardmask=False)) zeros_like = np.zeros_like ############################################################################### +def append(a, b, axis=None): + """Append values to the end of an array. + + .. versionadded:: 1.9.0 + + Parameters + ---------- + arr : array_like + Values are appended to a copy of this array. + values : array_like + These values are appended to a copy of `arr`. It must be of the + correct shape (the same shape as `arr`, excluding `axis`). If `axis` + is not specified, `values` can be any shape and will be flattened + before use. + axis : int, optional + The axis along which `values` are appended. If `axis` is not given, + both `arr` and `values` are flattened before use. + + Returns + ------- + append : MaskedArray + A copy of `arr` with `values` appended to `axis`. Note that `append` + does not occur in-place: a new array is allocated and filled. If + `axis` is None, the result is a flattened array. + + See Also + -------- + numpy.append : Equivalent function in the top-level NumPy module. + + Examples + -------- + >>> import numpy.ma as ma + >>> a = ma.masked_values([1, 2, 3], 2) + >>> b = ma.masked_values([[4, 5, 6], [7, 8, 9]], 7) + >>> print(ma.append(a, b)) + [1 -- 3 4 5 6 -- 8 9] + """ + return concatenate([a, b], axis) diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py index d14812093..82a61a67c 100644 --- a/numpy/ma/extras.py +++ b/numpy/ma/extras.py @@ -416,26 +416,53 @@ def apply_over_axes(func, a, axes): """ (This docstring will be overwritten) """ - val = np.asarray(a) - msk = getmaskarray(a) + 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 = ma.array(func(*(val, axis)), mask=func(*(msk, axis))) + res = func(*args) if res.ndim == val.ndim: - (val, msk) = (res._data, res._mask) + val = res else: res = ma.expand_dims(res, axis) if res.ndim == val.ndim: - (val, msk) = (res._data, res._mask) + val = res else: - raise ValueError("Function is not returning"\ - " an array of correct shape") + raise ValueError("function is not returning " + "an array of the correct shape") return val -apply_over_axes.__doc__ = np.apply_over_axes.__doc__ +apply_over_axes.__doc__ = np.apply_over_axes.__doc__[ + :np.apply_over_axes.__doc__.find('Notes')].rstrip() + \ + """ + + Examples + -------- + >>> a = ma.arange(24).reshape(2,3,4) + >>> a[:,0,1] = ma.masked + >>> a[:,1,:] = ma.masked + >>> print a + [[[0 -- 2 3] + [-- -- -- --] + [8 9 10 11]] + + [[12 -- 14 15] + [-- -- -- --] + [20 21 22 23]]] + >>> print ma.apply_over_axes(ma.sum, a, [0,2]) + [[[46] + [--] + [124]]] + + Tuple axis arguments to ufuncs are equivalent: + + >>> print ma.sum(a, axis=(0,2)).reshape((1,-1,1)) + [[[46] + [--] + [124]]] +""" def average(a, axis=None, weights=None, returned=False): @@ -448,8 +475,8 @@ def average(a, axis=None, weights=None, returned=False): Data to be averaged. Masked entries are not taken into account in the computation. axis : int, optional - Axis along which the variance is computed. The default is to compute - the variance of the flattened array. + Axis along which the average is computed. The default is to compute + the average of 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 @@ -540,7 +567,7 @@ def average(a, axis=None, weights=None, returned=False): else: if weights is None: n = add.reduce(a, axis) - d = umath.add.reduce((-mask), axis=axis, dtype=float) + d = umath.add.reduce((~mask), axis=axis, dtype=float) else: w = filled(weights, 0.0) wsh = w.shape @@ -641,15 +668,9 @@ def median(a, axis=None, out=None, overwrite_input=False): fill_value = 1e+20) """ - def _median1D(data): - counts = filled(count(data), 0) - (idx, rmd) = divmod(counts, 2) - if rmd: - choice = slice(idx, idx + 1) - else: - choice = slice(idx - 1, idx + 1) - return data[choice].mean(0) - # + 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) if overwrite_input: if axis is None: asorted = a.ravel() @@ -660,14 +681,29 @@ def median(a, axis=None, out=None, overwrite_input=False): else: asorted = sort(a, axis=axis) if axis is None: - result = _median1D(asorted) + axis = 0 + elif axis < 0: + axis += a.ndim + + counts = asorted.shape[axis] - (asorted.mask).sum(axis=axis) + h = counts // 2 + # create indexing mesh grid for all but reduced axis + axes_grid = [np.arange(x) for i, x in enumerate(asorted.shape) + if i != axis] + ind = np.meshgrid(*axes_grid, sparse=True, indexing='ij') + # insert indices of low and high median + ind.insert(axis, h - 1) + low = asorted[ind] + ind[axis] = h + high = asorted[ind] + # duplicate high if odd number of elements so mean does nothing + odd = counts % 2 == 1 + if asorted.ndim == 1: + if odd: + low = high else: - result = apply_along_axis(_median1D, axis, asorted) - if out is not None: - out = result - return result - - + low[odd] = high[odd] + return np.ma.mean([low, high], axis=0, out=out) #.............................................................................. @@ -842,9 +878,9 @@ def mask_rowcols(a, axis=None): fill_value=999999) """ - a = asarray(a) + a = array(a, subok=False) if a.ndim != 2: - raise NotImplementedError("compress2d works for 2D arrays only.") + 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(): @@ -1735,7 +1771,7 @@ def _ezclump(mask): #def clump_masked(a): if mask.ndim > 1: mask = mask.ravel() - idx = (mask[1:] - mask[:-1]).nonzero() + idx = (mask[1:] ^ mask[:-1]).nonzero() idx = idx[0] + 1 slices = [slice(left, right) for (left, right) in zip(itertools.chain([0], idx), diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index a2380d813..e66596509 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -426,8 +426,8 @@ The fieldname base is either `_data` or `_mask`.""" self.shape, self.dtype, self.flags.fnc, - self._data.tostring(), - self._mask.tostring(), + self._data.tobytes(), + self._mask.tobytes(), self._fill_value, ) return state diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 764915236..8172335a8 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -13,6 +13,8 @@ import sys import pickle from functools import reduce +from nose.tools import assert_raises + import numpy as np import numpy.ma.core import numpy.core.fromnumeric as fromnumeric @@ -192,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.]) @@ -354,6 +355,13 @@ class TestMaskedArray(TestCase): assert_equal(copied.mask, [0, 0, 0]) assert_equal(a.mask, [0, 1, 0]) + def test_str_repr(self): + a = array([0, 1, 2], mask=[False, True, False]) + assert_equal(str(a), '[0 -- 2]') + assert_equal(repr(a), 'masked_array(data = [0 -- 2],\n' + ' mask = [False True False],\n' + ' fill_value = 999999)\n') + def test_pickling(self): # Tests pickling a = arange(10) @@ -474,6 +482,10 @@ class TestMaskedArray(TestCase): atest[idx] = btest[idx] assert_equal(atest, [20]) + def test_filled_w_object_dtype(self): + a = np.ma.masked_all(1, dtype='O') + assert_equal(a.filled('x')[0], 'x') + def test_filled_w_flexible_dtype(self): # Test filled w/ flexible dtype flexi = array([(1, 1, 1)], @@ -795,23 +807,29 @@ class TestMaskedArrayArithmetic(TestCase): def test_count_func(self): # Tests count - ott = array([0., 1., 2., 3.], mask=[1, 0, 0, 0]) - if sys.version_info[0] >= 3: - self.assertTrue(isinstance(count(ott), np.integer)) - else: - self.assertTrue(isinstance(count(ott), int)) - assert_equal(3, count(ott)) assert_equal(1, count(1)) assert_equal(0, array(1, mask=[1])) + + ott = array([0., 1., 2., 3.], mask=[1, 0, 0, 0]) + res = count(ott) + self.assertTrue(res.dtype.type is np.intp) + assert_equal(3, res) + ott = ott.reshape((2, 2)) - assert_(isinstance(count(ott, 0), ndarray)) - if sys.version_info[0] >= 3: - assert_(isinstance(count(ott), np.integer)) - else: - assert_(isinstance(count(ott), int)) - assert_equal(3, count(ott)) - assert_(getmask(count(ott, 0)) is nomask) - assert_equal([1, 2], count(ott, 0)) + res = count(ott) + assert_(res.dtype.type is np.intp) + assert_equal(3, res) + res = count(ott, 0) + assert_(isinstance(res, ndarray)) + assert_equal([1, 2], res) + assert_(getmask(res) is nomask) + + ott= array([0., 1., 2., 3.]) + res = count(ott, 0) + assert_(isinstance(res, ndarray)) + assert_(res.dtype.type is np.intp) + + assert_raises(IndexError, ott.count, 1) def test_minmax_func(self): # Tests minimum and maximum. @@ -1275,23 +1293,64 @@ class TestMaskedArrayAttributes(TestCase): assert_equal(a.mask, nomask) def test_flat(self): + # Test that flat can return all types of items [#4585, #4615] + # test simple access + test = masked_array(np.matrix([[1, 2, 3]]), mask=[0, 0, 1]) + assert_equal(test.flat[1], 2) + assert_equal(test.flat[2], masked) + self.assertTrue(np.all(test.flat[0:2] == test[0, 0:2])) # Test flat on masked_matrices test = masked_array(np.matrix([[1, 2, 3]]), mask=[0, 0, 1]) test.flat = masked_array([3, 2, 1], mask=[1, 0, 0]) control = masked_array(np.matrix([[3, 2, 1]]), mask=[1, 0, 0]) assert_equal(test, control) - # + # Test setting test = masked_array(np.matrix([[1, 2, 3]]), mask=[0, 0, 1]) testflat = test.flat testflat[:] = testflat[[2, 1, 0]] assert_equal(test, control) + testflat[0] = 9 + assert_equal(test[0, 0], 9) + # test 2-D record array + # ... on structured array w/ masked records + x = array([[(1, 1.1, 'one'), (2, 2.2, 'two'), (3, 3.3, 'thr')], + [(4, 4.4, 'fou'), (5, 5.5, 'fiv'), (6, 6.6, 'six')]], + dtype=[('a', int), ('b', float), ('c', '|S8')]) + x['a'][0, 1] = masked + x['b'][1, 0] = masked + x['c'][0, 2] = masked + x[-1, -1] = masked + xflat = x.flat + assert_equal(xflat[0], x[0, 0]) + assert_equal(xflat[1], x[0, 1]) + assert_equal(xflat[2], x[0, 2]) + assert_equal(xflat[:3], x[0]) + assert_equal(xflat[3], x[1, 0]) + assert_equal(xflat[4], x[1, 1]) + assert_equal(xflat[5], x[1, 2]) + assert_equal(xflat[3:], x[1]) + assert_equal(xflat[-1], x[-1, -1]) + i = 0 + j = 0 + for xf in xflat: + assert_equal(xf, x[j, i]) + i += 1 + if i >= x.shape[-1]: + i = 0 + j += 1 + # test that matrices keep the correct shape (#4615) + a = masked_array(np.matrix(np.eye(2)), mask=0) + b = a.flat + b01 = b[:2] + assert_equal(b01.data, array([[1., 0.]])) + assert_equal(b01.mask, array([[False, False]])) #------------------------------------------------------------------------------ class TestFillingValues(TestCase): - # + def test_check_on_scalar(self): - # Test _check_fill_value + # Test _check_fill_value set to valid and invalid values _check_fill_value = np.ma.core._check_fill_value # fval = _check_fill_value(0, int) @@ -1303,9 +1362,8 @@ class TestFillingValues(TestCase): assert_equal(fval, asbytes("0")) fval = _check_fill_value(None, "|S3") assert_equal(fval, default_fill_value("|S3")) - # - fval = _check_fill_value(1e+20, int) - assert_equal(fval, default_fill_value(0)) + self.assertRaises(TypeError, _check_fill_value, 1e+20, int) + self.assertRaises(TypeError, _check_fill_value, 'stuff', int) def test_check_on_fields(self): # Tests _check_fill_value with records @@ -1991,6 +2049,10 @@ class TestMaskedArrayMethods(TestCase): a[0] = 0 self.assertTrue(allclose(a, 0, masked_equal=True)) + # Test that the function works for MIN_INT integer typed arrays + a = masked_array([np.iinfo(np.int_).min], dtype=np.int_) + self.assertTrue(allclose(a, a)) + def test_allany(self): # Checks the any/all methods/functions. x = np.array([[0.13, 0.26, 0.90], @@ -3571,6 +3633,45 @@ def test_masked_array(): a = np.ma.array([0, 1, 2, 3], mask=[0, 0, 1, 0]) assert_equal(np.argwhere(a), [[1], [3]]) +def test_append_masked_array(): + a = np.ma.masked_equal([1,2,3], value=2) + b = np.ma.masked_equal([4,3,2], value=2) + + result = np.ma.append(a, b) + expected_data = [1, 2, 3, 4, 3, 2] + expected_mask = [False, True, False, False, False, True] + assert_array_equal(result.data, expected_data) + assert_array_equal(result.mask, expected_mask) + + a = np.ma.masked_all((2,2)) + b = np.ma.ones((3,1)) + + result = np.ma.append(a, b) + expected_data = [1] * 3 + expected_mask = [True] * 4 + [False] * 3 + assert_array_equal(result.data[-3], expected_data) + assert_array_equal(result.mask, expected_mask) + + result = np.ma.append(a, b, axis=None) + assert_array_equal(result.data[-3], expected_data) + assert_array_equal(result.mask, expected_mask) + + +def test_append_masked_array_along_axis(): + a = np.ma.masked_equal([1,2,3], value=2) + b = np.ma.masked_values([[4, 5, 6], [7, 8, 9]], 7) + + # When `axis` is specified, `values` must have the correct shape. + assert_raises(ValueError, np.ma.append, a, b, axis=0) + + result = np.ma.append(a[np.newaxis,:], b, axis=0) + expected = np.ma.arange(1, 10) + expected[[1, 6]] = np.ma.masked + expected = expected.reshape((3,3)) + assert_array_equal(result.data, expected.data) + assert_array_equal(result.mask, expected.mask) + + ############################################################################### if __name__ == "__main__": run_module_suite() diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py index dc0f87b92..6ce1dc346 100644 --- a/numpy/ma/tests/test_extras.py +++ b/numpy/ma/tests/test_extras.py @@ -479,6 +479,16 @@ class TestApplyAlongAxis(TestCase): xa = apply_along_axis(myfunc, 2, a) assert_equal(xa, [[1, 4], [7, 10]]) + # Tests kwargs functions + def test_3d_kwargs(self): + a = arange(12).reshape(2, 2, 3) + + def myfunc(b, offset=0): + return b[1+offset] + + xa = apply_along_axis(myfunc, 2, a, offset=1) + assert_equal(xa, [[2, 5], [8, 11]]) + class TestApplyOverAxes(TestCase): # Tests apply_over_axes @@ -489,7 +499,8 @@ class TestApplyOverAxes(TestCase): assert_equal(test, ctrl) a[(a % 2).astype(np.bool)] = masked test = apply_over_axes(np.sum, a, [0, 2]) - ctrl = np.array([[[30], [44], [60]]]) + ctrl = np.array([[[28], [44], [60]]]) + assert_equal(test, ctrl) class TestMedian(TestCase): @@ -530,6 +541,19 @@ class TestMedian(TestCase): x[x % 5 == 0] = masked assert_equal(median(x, 0), [[12, 10], [8, 9], [16, 17]]) + def test_neg_axis(self): + x = masked_array(np.arange(30).reshape(10, 3)) + x[:3] = x[-3:] = masked + assert_equal(median(x, axis=-1), median(x, axis=1)) + + def test_out(self): + x = masked_array(np.arange(30).reshape(10, 3)) + x[:3] = x[-3:] = masked + out = masked_array(np.ones(10)) + r = median(x, axis=1, out=out) + assert_equal(r, out) + assert_(type(r) == MaskedArray) + class TestCov(TestCase): diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index 27d699385..047f91c77 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -153,19 +153,14 @@ class TestMa(TestCase): def test_xtestCount(self): # Test count ott = array([0., 1., 2., 3.], mask=[1, 0, 0, 0]) - if sys.version_info[0] >= 3: - self.assertTrue(isinstance(count(ott), np.integer)) - else: - self.assertTrue(isinstance(count(ott), int)) + self.assertTrue(count(ott).dtype.type is np.intp) self.assertEqual(3, count(ott)) self.assertEqual(1, count(1)) self.assertTrue(eq(0, array(1, mask=[1]))) ott = ott.reshape((2, 2)) + self.assertTrue(count(ott).dtype.type is np.intp) assert_(isinstance(count(ott, 0), np.ndarray)) - if sys.version_info[0] >= 3: - assert_(isinstance(count(ott), np.integer)) - else: - assert_(isinstance(count(ott), int)) + self.assertTrue(count(ott).dtype.type is np.intp) self.assertTrue(eq(3, count(ott))) assert_(getmask(count(ott, 0)) is nomask) self.assertTrue(eq([1, 2], count(ott, 0))) @@ -612,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) diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py index c2c9b8ec9..ade5c59da 100644 --- a/numpy/ma/tests/test_subclassing.py +++ b/numpy/ma/tests/test_subclassing.py @@ -82,6 +82,24 @@ class MMatrix(MaskedArray, np.matrix,): mmatrix = MMatrix +# also a subclass that overrides __str__, __repr__ and __setitem__, disallowing +# setting to non-class values (and thus np.ma.core.masked_print_option) +class ComplicatedSubArray(SubArray): + def __str__(self): + return 'myprefix {0} mypostfix'.format( + super(ComplicatedSubArray, self).__str__()) + + def __repr__(self): + # Return a repr that does not start with 'name(' + return '<{0} {1}>'.format(self.__class__.__name__, self) + + def __setitem__(self, item, value): + # this ensures direct assignment to masked_print_option will fail + if not isinstance(value, ComplicatedSubArray): + raise ValueError("Can only set to MySubArray values") + super(ComplicatedSubArray, self).__setitem__(item, value) + + class TestSubclassing(TestCase): # Test suite for masked subclasses of ndarray. @@ -187,6 +205,31 @@ class TestSubclassing(TestCase): assert_equal(mxsub.info, xsub.info) assert_equal(mxsub._mask, m) + def test_subclass_repr(self): + """test that repr uses the name of the subclass + and 'array' for np.ndarray""" + x = np.arange(5) + mx = masked_array(x, mask=[True, False, True, False, False]) + self.assertTrue(repr(mx).startswith('masked_array')) + xsub = SubArray(x) + mxsub = masked_array(xsub, mask=[True, False, True, False, False]) + self.assertTrue(repr(mxsub).startswith( + 'masked_{0}(data = [-- 1 -- 3 4]'.format(SubArray.__name__))) + + def test_subclass_str(self): + """test str with subclass that has overridden str, setitem""" + # first without override + x = np.arange(5) + xsub = SubArray(x) + mxsub = masked_array(xsub, mask=[True, False, True, False, False]) + self.assertTrue(str(mxsub) == '[-- 1 -- 3 4]') + + xcsub = ComplicatedSubArray(x) + assert_raises(ValueError, xcsub.__setitem__, 0, + np.ma.core.masked_print_option) + mxcsub = masked_array(xcsub, mask=[True, False, True, False, False]) + self.assertTrue(str(mxcsub) == 'myprefix [-- 1 -- 3 4] mypostfix') + ############################################################################### if __name__ == '__main__': |