diff options
author | pierregm <pierregm@localhost> | 2008-11-19 15:00:37 +0000 |
---|---|---|
committer | pierregm <pierregm@localhost> | 2008-11-19 15:00:37 +0000 |
commit | 77807c098a3e3cc977589fa70c3a0c9d859d3df8 (patch) | |
tree | b929ccb40bab2d85ee525313be7baf508273b92e | |
parent | 0e3904026f9e2428a38631fd2168c3ed77a59ebc (diff) | |
download | numpy-77807c098a3e3cc977589fa70c3a0c9d859d3df8.tar.gz |
make_mask : Added a dtype keyword to support flexible-dtype
mask_or : Added support to flexible-dtype.
-rw-r--r-- | numpy/ma/core.py | 290 |
1 files changed, 187 insertions, 103 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 884914acc..66958f553 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -13,13 +13,14 @@ pgmdevlist_AT_gmail_DOT_com Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com) :author: Pierre Gerard-Marchant -:contact: pierregm_at_uga_dot_edu + + """ __author__ = "Pierre GF Gerard-Marchant" __docformat__ = "restructuredtext en" __all__ = ['MAError', 'MaskType', 'MaskedArray', - 'bool_', 'complex_', 'float_', 'int_', 'object_', + 'bool_', 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', @@ -38,8 +39,8 @@ __all__ = ['MAError', 'MaskType', 'MaskedArray', 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', - 'make_mask', 'make_mask_descr', 'make_mask_none', 'mask_or', 'masked', - 'masked_array', 'masked_equal', 'masked_greater', + 'make_mask', 'make_mask_descr', 'make_mask_none', 'mask_or', + 'masked', 'masked_array', 'masked_equal', 'masked_greater', 'masked_greater_equal', 'masked_inside', 'masked_invalid', 'masked_less','masked_less_equal', 'masked_not_equal', 'masked_object','masked_outside', 'masked_print_option', @@ -61,8 +62,7 @@ import cPickle import operator import numpy as np -from numpy import ndarray, amax, amin, iscomplexobj, bool_, complex_, float_,\ - int_, object_ +from numpy import ndarray, amax, amin, iscomplexobj, bool_ from numpy import array as narray import numpy.core.umath as umath @@ -79,8 +79,13 @@ np.seterr(all='ignore') def doc_note(initialdoc, note): + """ + Adds a Notes section to an existing docstring. + """ if initialdoc is None: return + if note is None: + return initialdoc newdoc = """ %s @@ -121,7 +126,8 @@ if 'float128' in ntypes.typeDict: def default_fill_value(obj): - """Calculate the default fill value for the argument object. + """ + Calculate the default fill value for the argument object. """ if hasattr(obj,'dtype'): @@ -144,15 +150,16 @@ def default_fill_value(obj): return defval def minimum_fill_value(obj): - """Calculate the default fill value suitable for taking the - minimum of ``obj``. + """ + Calculate the default fill value suitable for taking the minimum of ``obj``. """ + errmsg = "Unsuitable type for calculating minimum." if hasattr(obj, 'dtype'): objtype = obj.dtype filler = min_filler[objtype] if filler is None: - raise TypeError, 'Unsuitable type for calculating minimum.' + raise TypeError(errmsg) return filler elif isinstance(obj, float): return min_filler[ntypes.typeDict['float_']] @@ -163,18 +170,19 @@ def minimum_fill_value(obj): elif isinstance(obj, np.dtype): return min_filler[obj] else: - raise TypeError, 'Unsuitable type for calculating minimum.' + raise TypeError(errmsg) def maximum_fill_value(obj): - """Calculate the default fill value suitable for taking the maximum - of ``obj``. + """ + Calculate the default fill value suitable for taking the maximum of ``obj``. """ + errmsg = "Unsuitable type for calculating maximum." if hasattr(obj, 'dtype'): objtype = obj.dtype filler = max_filler[objtype] if filler is None: - raise TypeError, 'Unsuitable type for calculating minimum.' + raise TypeError(errmsg) return filler elif isinstance(obj, float): return max_filler[ntypes.typeDict['float_']] @@ -185,10 +193,21 @@ def maximum_fill_value(obj): elif isinstance(obj, np.dtype): return max_filler[obj] else: - raise TypeError, 'Unsuitable type for calculating minimum.' + raise TypeError(errmsg) def _check_fill_value(fill_value, ndtype): + """ + Private function validating the given `fill_value` for the given dtype. + + If fill_value is None, it is set to the default corresponding to the dtype + if this latter is standard (no fields). If the datatype is flexible (named + fields), fill_value is set to a tuple whose elements are the default fill + values corresponding to each field. + + If fill_value is not None, its value is forced to the given dtype. + + """ ndtype = np.dtype(ndtype) fields = ndtype.fields if fill_value is None: @@ -206,7 +225,7 @@ def _check_fill_value(fill_value, ndtype): fill_value = np.array(fill_value, copy=False, dtype=fdtype) except ValueError: err_msg = "Unable to transform %s to dtype %s" - raise ValueError(err_msg % (fill_value,fdtype)) + raise ValueError(err_msg % (fill_value, fdtype)) else: fval = np.resize(fill_value, len(ndtype.descr)) fill_value = [np.asarray(f).astype(desc[1]).item() @@ -225,9 +244,18 @@ def _check_fill_value(fill_value, ndtype): def set_fill_value(a, fill_value): - """Set the filling value of a, if a is a masked array. Otherwise, + """ + Set the filling value of a, if a is a masked array. Otherwise, do nothing. + Parameters + ---------- + a : ndarray + Input array + fill_value : var + Filling value. A consistency test is performed to make sure + the value is compatible with the dtype of a. + Returns ------- None @@ -238,7 +266,8 @@ def set_fill_value(a, fill_value): return def get_fill_value(a): - """Return the filling value of a, if any. Otherwise, returns the + """ + Return the filling value of a, if any. Otherwise, returns the default filling value for that type. """ @@ -249,7 +278,8 @@ def get_fill_value(a): return result def common_fill_value(a, b): - """Return the common filling value of a and b, if any. + """ + Return the common filling value of a and b, if any. If a and b have different filling values, returns None. """ @@ -261,18 +291,21 @@ def common_fill_value(a, b): #####-------------------------------------------------------------------------- -def filled(a, value = None): - """Return a as an array with masked data replaced by value. If - value is None, get_fill_value(a) is used instead. If a is already - a ndarray, a itself is returned. +def filled(a, fill_value = None): + """ + Return `a` as an array where masked data have been replaced by `value`. + + If `a` is not a MaskedArray, `a` itself is returned. + If `a` is a MaskedArray and `fill_value` is None, `fill_value` is set to + `a.fill_value`. Parameters ---------- a : maskedarray or array_like An input object. - value : {var}, optional - Filling value. If not given, the output of get_fill_value(a) - is used instead. + fill_value : {var}, optional + Filling value. If None, the output of :func:`get_fill_value(a)` is used + instead. Returns ------- @@ -280,7 +313,7 @@ def filled(a, value = None): """ if hasattr(a, 'filled'): - return a.filled(value) + return a.filled(fill_value) elif isinstance(a, ndarray): # Should we check for contiguity ? and a.flags['CONTIGUOUS']: return a @@ -291,8 +324,9 @@ def filled(a, value = None): #####-------------------------------------------------------------------------- def get_masked_subclass(*arrays): - """Return the youngest subclass of MaskedArray from a list of - (masked) arrays. In case of siblings, the first takes over. + """ + Return the youngest subclass of MaskedArray from a list of (masked) arrays. + In case of siblings, the first listed takes over. """ if len(arrays) == 1: @@ -313,13 +347,14 @@ def get_masked_subclass(*arrays): #####-------------------------------------------------------------------------- def get_data(a, subok=True): - """Return the _data part of a (if any), or a as a ndarray. + """ + Return the `_data` part of `a` if `a` is a MaskedArray, or `a` itself. Parameters ---------- a : array_like A ndarray or a subclass of. - subok : bool + subok : {True, False}, optional Whether to force the output to a 'pure' ndarray (False) or to return a subclass of ndarray if approriate (True). @@ -332,7 +367,8 @@ def get_data(a, subok=True): getdata = get_data def fix_invalid(a, mask=nomask, copy=True, fill_value=None): - """Return (a copy of) a where invalid data (nan/inf) are masked + """ + Return (a copy of) a where invalid data (nan/inf) are masked and replaced by fill_value. Note that a copy is performed by default (just in case...). @@ -342,7 +378,7 @@ def fix_invalid(a, mask=nomask, copy=True, fill_value=None): a : array_like A (subclass of) ndarray. copy : bool - Whether to use a copy of a (True) or to fix a in place (False). + Whether to use a copy of `a` (True) or to fix `a` in place (False). fill_value : {var}, optional Value used for fixing invalid data. If not given, the output of get_fill_value(a) is used instead. @@ -643,9 +679,9 @@ class _DomainedBinaryOperation: mb = mask_or(mb, t) # The following line controls the domain filling if t.size == d2.size: - d2 = np.where(t,self.filly,d2) + d2 = np.where(t, self.filly, d2) else: - d2 = np.where(np.resize(t, d2.shape),self.filly, d2) + d2 = np.where(np.resize(t, d2.shape), self.filly, d2) m = mask_or(ma, mb) if (not m.ndim) and m: return masked @@ -781,9 +817,13 @@ def getmaskarray(arr): return mask def is_mask(m): - """Return True if m is a legal mask. + """ + Return True if m is a valid, standard mask. - Does not check contents, only type. + Notes + ----- + This function does not check contents, only the type. In particular, + this function returns False if the mask has a flexible dtype. """ try: @@ -791,8 +831,9 @@ def is_mask(m): except AttributeError: return False -def make_mask(m, copy=False, shrink=True, flag=None): - """Return m as a mask, creating a copy if necessary or requested. +def make_mask(m, copy=False, shrink=True, flag=None, dtype=MaskType): + """ + Return m as a mask, creating a copy if necessary or requested. The function can accept any sequence of integers or nomask. Does not check that contents must be 0s and 1s. @@ -805,6 +846,10 @@ def make_mask(m, copy=False, shrink=True, flag=None): Whether to return a copy of m (True) or m itself (False). shrink : bool Whether to shrink m to nomask if all its values are False. + dtype : dtype + Data-type of the output mask. By default, the output mask has + a dtype of MaskType (bool). If the dtype is flexible, each field + has a boolean dtype. """ if flag is not None: @@ -814,31 +859,38 @@ def make_mask(m, copy=False, shrink=True, flag=None): if m is nomask: return nomask elif isinstance(m, ndarray): + # We won't return after this point to make sure we can shrink the mask + # Fill the mask in case there are missing data m = filled(m, True) - if m.dtype.type is MaskType: + # Make sure the input dtype is valid + dtype = make_mask_descr(dtype) + if m.dtype == dtype: if copy: - result = narray(m, dtype=MaskType, copy=copy) + result = m.copy() else: result = m else: - result = narray(m, dtype=MaskType) + result = np.array(m, dtype=dtype, copy=copy) else: - result = narray(filled(m, True), dtype=MaskType) + result = np.array(filled(m, True), dtype=MaskType) # Bas les masques ! if shrink and not result.any(): return nomask else: return result + def make_mask_none(newshape, dtype=None): - """Return a mask of shape s, filled with False. + """ + Return a mask of shape s, filled with False. Parameters ---------- news : tuple A tuple indicating the shape of the final mask. dtype: {None, dtype}, optional - A dtype. + If None, use MaskType. Otherwise, use a new datatype with the same fields + as `dtype` with boolean type. """ if dtype is None: @@ -848,7 +900,8 @@ def make_mask_none(newshape, dtype=None): return result def mask_or (m1, m2, copy=False, shrink=True): - """Return the combination of two masks m1 and m2. + """ + Return the combination of two masks m1 and m2. The masks are combined with the *logical_or* operator, treating nomask as False. The result may equal m1 or m2 if the other is @@ -865,13 +918,28 @@ def mask_or (m1, m2, copy=False, shrink=True): shrink : {True, False}, optional Whether to shrink m to nomask if all its values are False. + Raises + ------ + ValueError + If m1 and m2 have different flexible dtypes. + """ - if m1 is nomask: - return make_mask(m2, copy=copy, shrink=shrink) - if m2 is nomask: - return make_mask(m1, copy=copy, shrink=shrink) + if (m1 is nomask) or (m1 is False): + dtype = getattr(m2, 'dtype', MaskType) + return make_mask(m2, copy=copy, shrink=shrink, dtype=dtype) + if (m2 is nomask) or (m2 is False): + dtype = getattr(m1, 'dtype', MaskType) + return make_mask(m1, copy=copy, shrink=shrink, dtype=dtype) if m1 is m2 and is_mask(m1): return m1 + (dtype1, dtype2) = (getattr(m1, 'dtype', None), getattr(m2, 'dtype', None)) + if (dtype1 != dtype2): + raise ValueError("Incompatible dtypes '%s'<>'%s'" % (dtype1, dtype2)) + if dtype1.names: + newmask = np.empty_like(m1) + for n in dtype1.names: + newmask[n] = umath.logical_or(m1[n], m2[n]) + return newmask return make_mask(umath.logical_or(m1, m2), copy=copy, shrink=shrink) @@ -894,6 +962,7 @@ def masked_where(condition, a, copy=True): Whether to return a copy of ``a`` (True) or modify ``a`` in place (False). """ + # Make sure that condition is a valid standard-type mask. cond = make_mask(condition) a = np.array(a, copy=copy, subok=True) @@ -911,7 +980,11 @@ def masked_where(condition, a, copy=True): return result def masked_greater(x, value, copy=True): - "Shortcut to masked_where, with condition = (x > value)." + """ + Return the array `x` masked where (x > value). + Any value of mask already masked is kept masked. + + """ return masked_where(greater(x, value), x, copy=copy) def masked_greater_equal(x, value, copy=True): @@ -1181,6 +1254,7 @@ class FlatIter(object): a[index] = value def next(self): + "Returns the next element of the iterator." d = self.ma_iter.next() if self.maskiter is not None and self.maskiter.next(): d = masked @@ -1253,7 +1327,7 @@ class MaskedArray(ndarray): _data = np.array(data, dtype=dtype, copy=copy, subok=True, ndmin=ndmin) _baseclass = getattr(data, '_baseclass', type(_data)) # Check that we'ew not erasing the mask.......... - if isinstance(data,MaskedArray) and (data.shape != _data.shape): + if isinstance(data, MaskedArray) and (data.shape != _data.shape): copy = True # Careful, cls might not always be MaskedArray... if not isinstance(data, cls) or not subok: @@ -1358,7 +1432,7 @@ class MaskedArray(ndarray): # We need to copy the _basedict to avoid backward propagation _optinfo = {} _optinfo.update(getattr(obj, '_optinfo', {})) - _optinfo.update(getattr(obj, '_basedict',{})) + _optinfo.update(getattr(obj, '_basedict', {})) if not isinstance(obj, MaskedArray): _optinfo.update(getattr(obj, '__dict__', {})) _dict = dict(_fill_value=getattr(obj, '_fill_value', None), @@ -1454,7 +1528,7 @@ class MaskedArray(ndarray): else: output = ndarray.view(self, dtype, type) # Should we update the mask ? - if (getattr(output,'_mask', nomask) is not nomask): + if (getattr(output, '_mask', nomask) is not nomask): if dtype is None: dtype = output.dtype mdtype = make_mask_descr(dtype) @@ -1633,13 +1707,13 @@ class MaskedArray(ndarray): indices is not supported. """ - return self.__getitem__(slice(i,j)) + return self.__getitem__(slice(i, j)) #........................ def __setslice__(self, i, j, value): """x.__setslice__(i, j, value) <==> x[i:j]=value - Set the slice (i,j) of a to value. If value is masked, mask - those locations. + Set the slice (i,j) of a to value. If value is masked, mask + those locations. """ self.__setitem__(slice(i,j), value) @@ -2022,7 +2096,7 @@ masked_%(name)s(data = %(data)s, parameters = dict(name=name, data=str(self), mask=str(self._mask), fill=str(self.fill_value), dtype=str(self.dtype)) if self.dtype.names: - if n<= 1: + if n <= 1: return with_mask1_flx % parameters return with_mask_flx % parameters elif n <= 1: @@ -2149,13 +2223,13 @@ masked_%(name)s(data = %(data)s, result = self._data.imag.view(type(self)) result.__setmask__(self._mask) return result - imag = property(fget=get_imag,doc="Imaginary part") + imag = property(fget=get_imag, doc="Imaginary part.") def get_real(self): result = self._data.real.view(type(self)) result.__setmask__(self._mask) return result - real = property(fget=get_real,doc="Real part") + real = property(fget=get_real, doc="Real part") #............................................ @@ -2310,7 +2384,7 @@ masked_%(name)s(data = %(data)s, Parameters ---------- - indicies : 1-D array_like + indices : 1-D array_like Target indices, interpreted as integers. values : array_like Values to place in self._data copy at target indices. @@ -2380,34 +2454,34 @@ masked_%(name)s(data = %(data)s, #............................................ def all(self, axis=None, out=None): """ - Check if all of the elements of `a` are true. + Check if all of the elements of `a` are true. - Performs a :func:`logical_and` over the given axis and returns the result. - Masked values are considered as True during computation. - For convenience, the output array is masked where ALL the values along the - current axis are masked: if the output would have been a scalar and that - all the values are masked, then the output is `masked`. + Performs a :func:`logical_and` over the given axis and returns the result. + Masked values are considered as True during computation. + For convenience, the output array is masked where ALL the values along the + current axis are masked: if the output would have been a scalar and that + all the values are masked, then the output is `masked`. - Parameters - ---------- - axis : {None, integer} - Axis to perform the operation over. - If None, perform over flattened array. - out : {None, array}, optional - Array into which the result can be placed. Its type is preserved - and it must be of the right shape to hold the output. + Parameters + ---------- + axis : {None, integer} + Axis to perform the operation over. + If None, perform over flattened array. + out : {None, array}, optional + Array into which the result can be placed. Its type is preserved + and it must be of the right shape to hold the output. - See Also - -------- - all : equivalent function + See Also + -------- + all : equivalent function - Examples - -------- - >>> np.ma.array([1,2,3]).all() - True - >>> a = np.ma.array([1,2,3], mask=True) - >>> (a.all() is np.ma.masked) - True + Examples + -------- + >>> np.ma.array([1,2,3]).all() + True + >>> a = np.ma.array([1,2,3], mask=True) + >>> (a.all() is np.ma.masked) + True """ mask = self._mask.all(axis) @@ -2462,19 +2536,20 @@ masked_%(name)s(data = %(data)s, def nonzero(self): - """Return the indices of the elements of a that are not zero - nor masked, as a tuple of arrays. + """ + Return the indices of the elements of a that are not zero + nor masked, as a tuple of arrays. - There are as many tuples as dimensions of a, each tuple - contains the indices of the non-zero elements in that - dimension. The corresponding non-zero values can be obtained - with ``a[a.nonzero()]``. + There are as many tuples as dimensions of a, each tuple + contains the indices of the non-zero elements in that + dimension. The corresponding non-zero values can be obtained + with ``a[a.nonzero()]``. - To group the indices by element, rather than dimension, use - instead: ``transpose(a.nonzero())``. + To group the indices by element, rather than dimension, use + instead: ``transpose(a.nonzero())``. - The result of this is always a 2d array, with a row for each - non-zero element. + The result of this is always a 2d array, with a row for each + non-zero element. """ return narray(self.filled(0), copy=False).nonzero() @@ -2684,7 +2759,7 @@ masked_%(name)s(data = %(data)s, return result # Explicit output result = self.filled(1).prod(axis, dtype=dtype, out=out) - if isinstance(out,MaskedArray): + if isinstance(out, MaskedArray): outmask = getattr(out, '_mask', nomask) if (outmask is nomask): outmask = out._mask = make_mask_none(out.shape) @@ -2824,7 +2899,7 @@ masked_%(name)s(data = %(data)s, def std(self, axis=None, dtype=None, out=None, ddof=0): "" - dvar = self.var(axis=axis,dtype=dtype,out=out, ddof=ddof) + dvar = self.var(axis=axis, dtype=dtype, out=out, ddof=ddof) if dvar is not masked: dvar = sqrt(dvar) if out is not None: @@ -3039,7 +3114,7 @@ masked_%(name)s(data = %(data)s, """ if self._mask is nomask: - ndarray.sort(self,axis=axis, kind=kind, order=order) + ndarray.sort(self, axis=axis, kind=kind, order=order) else: if fill_value is None: if endwith: @@ -3700,7 +3775,15 @@ sort.__doc__ = MaskedArray.sort.__doc__ def compressed(x): - """Return a 1-D array of all the non-masked data.""" + """ + Return a 1-D array of all the non-masked data. + + See Also + -------- + MaskedArray.compressed + equivalent method + + """ if getmask(x) is nomask: return np.asanyarray(x) else: @@ -3737,11 +3820,12 @@ count.__doc__ = MaskedArray.count.__doc__ def expand_dims(x,axis): - """Expand the shape of the array by including a new axis before + """ + Expand the shape of the array by including a new axis before the given one. """ - result = n_expand_dims(x,axis) + result = n_expand_dims(x, axis) if isinstance(x, MaskedArray): new_shape = result.shape result = x.view() @@ -4148,8 +4232,8 @@ def dump(a,F): """ if not hasattr(F,'readline'): - F = open(F,'w') - return cPickle.dump(a,F) + F = open(F, 'w') + return cPickle.dump(a, F) def dumps(a): """ |