[Scipy-svn] r3245 - in trunk/Lib/sandbox/maskedarray: . tests
scipy-svn at scipy.org
scipy-svn at scipy.org
Wed Aug 15 09:38:23 EDT 2007
Author: pierregm
Date: 2007-08-15 08:38:19 -0500 (Wed, 15 Aug 2007)
New Revision: 3245
Modified:
trunk/Lib/sandbox/maskedarray/core.py
trunk/Lib/sandbox/maskedarray/mrecords.py
trunk/Lib/sandbox/maskedarray/tests/test_core.py
Log:
mrecords : * fixed a pb w/ numpy.void
* returns 'masked' when accessing a masked attribute from a unique record.
core : * modified .tolist() so that fill_value=None now outputs None for masked values
Modified: trunk/Lib/sandbox/maskedarray/core.py
===================================================================
--- trunk/Lib/sandbox/maskedarray/core.py 2007-08-15 06:04:30 UTC (rev 3244)
+++ trunk/Lib/sandbox/maskedarray/core.py 2007-08-15 13:38:19 UTC (rev 3245)
@@ -0,0 +1,2698 @@
+# pylint: disable-msg=E1002
+"""MA: a facility for dealing with missing observations
+MA is generally used as a numpy.array look-alike.
+by Paul F. Dubois.
+
+Copyright 1999, 2000, 2001 Regents of the University of California.
+Released for unlimited redistribution.
+Adapted for numpy_core 2005 by Travis Oliphant and
+(mainly) Paul Dubois.
+
+Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant.
+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
+:version: $Id$
+"""
+__author__ = "Pierre GF Gerard-Marchant ($Author$)"
+__version__ = '1.0'
+__revision__ = "$Revision$"
+__date__ = '$Date$'
+
+__all__ = ['MAError', 'MaskType', 'MaskedArray',
+ 'bool_', 'complex_', 'float_', 'int_', 'object_',
+ 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
+ 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
+ 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
+ 'arctanh', 'argmax', 'argmin', 'argsort', 'around',
+ 'array', 'asarray',
+ 'bitwise_and', 'bitwise_or', 'bitwise_xor',
+ 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate',
+ 'cos', 'cosh', 'count',
+ 'diagonal', 'divide', 'dump', 'dumps',
+ 'empty', 'empty_like', 'equal', 'exp',
+ 'fabs', 'fmod', 'filled', 'floor', 'floor_divide',
+ 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot',
+ 'ids', 'inner', 'innerproduct',
+ '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_none', 'mask_or', 'masked',
+ 'masked_array', 'masked_equal', 'masked_greater',
+ 'masked_greater_equal', 'masked_inside', 'masked_less',
+ 'masked_less_equal', 'masked_not_equal', 'masked_object',
+ 'masked_outside', 'masked_print_option', 'masked_singleton',
+ 'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min',
+ 'minimum', 'multiply',
+ 'negative', 'nomask', 'nonzero', 'not_equal',
+ 'ones', 'outer', 'outerproduct',
+ 'power', 'product', 'ptp', 'put', 'putmask',
+ 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize',
+ 'right_shift', 'round_',
+ 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std',
+ 'subtract', 'sum', 'swapaxes',
+ 'take', 'tan', 'tanh', 'transpose', 'true_divide',
+ 'var', 'where',
+ 'zeros']
+
+import sys
+import types
+import cPickle
+import operator
+#
+import numpy
+from numpy import bool_, complex_, float_, int_, object_, str_
+
+import numpy.core.umath as umath
+import numpy.core.fromnumeric as fromnumeric
+import numpy.core.numeric as numeric
+import numpy.core.numerictypes as ntypes
+from numpy import bool_, dtype, typecodes, amax, amin, ndarray
+from numpy import expand_dims as n_expand_dims
+import warnings
+
+
+MaskType = bool_
+nomask = MaskType(0)
+
+divide_tolerance = 1.e-35
+numpy.seterr(all='ignore')
+
+# TODO: There's still a problem with N.add.reduce not working...
+# TODO: ...neither does N.add.accumulate
+
+#####--------------------------------------------------------------------------
+#---- --- Exceptions ---
+#####--------------------------------------------------------------------------
+class MAError(Exception):
+ "Class for MA related errors."
+ def __init__ (self, args=None):
+ "Creates an exception."
+ Exception.__init__(self,args)
+ self.args = args
+ def __str__(self):
+ "Calculates the string representation."
+ return str(self.args)
+ __repr__ = __str__
+
+#####--------------------------------------------------------------------------
+#---- --- Filling options ---
+#####--------------------------------------------------------------------------
+# b: boolean - c: complex - f: floats - i: integer - O: object - S: string
+default_filler = {'b': True,
+ 'c' : 1.e20 + 0.0j,
+ 'f' : 1.e20,
+ 'i' : 999999,
+ 'O' : '?',
+ 'S' : 'N/A',
+ 'u' : 999999,
+ 'V' : '???',
+ }
+max_filler = ntypes._minvals
+max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]])
+min_filler = ntypes._maxvals
+min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]])
+if 'float128' in ntypes.typeDict:
+ max_filler.update([(numpy.float128,-numeric.inf)])
+ min_filler.update([(numpy.float128, numeric.inf)])
+
+
+def default_fill_value(obj):
+ "Calculates the default fill value for an object `obj`."
+ if hasattr(obj,'dtype'):
+ defval = default_filler[obj.dtype.kind]
+ elif isinstance(obj, numeric.dtype):
+ defval = default_filler[obj.kind]
+ elif isinstance(obj, float):
+ defval = default_filler['f']
+ elif isinstance(obj, int) or isinstance(obj, long):
+ defval = default_filler['i']
+ elif isinstance(obj, str):
+ defval = default_filler['S']
+ elif isinstance(obj, complex):
+ defval = default_filler['c']
+ else:
+ defval = default_filler['O']
+ return defval
+
+def minimum_fill_value(obj):
+ "Calculates the default fill value suitable for taking the minimum of `obj`."
+ if hasattr(obj, 'dtype'):
+ objtype = obj.dtype
+ filler = min_filler[objtype]
+ if filler is None:
+ raise TypeError, 'Unsuitable type for calculating minimum.'
+ return filler
+ elif isinstance(obj, float):
+ return min_filler[ntypes.typeDict['float_']]
+ elif isinstance(obj, int):
+ return min_filler[ntypes.typeDict['int_']]
+ elif isinstance(obj, long):
+ return min_filler[ntypes.typeDict['uint']]
+ elif isinstance(obj, numeric.dtype):
+ return min_filler[obj]
+ else:
+ raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def maximum_fill_value(obj):
+ "Calculates the default fill value suitable for taking the maximum of `obj`."
+ if hasattr(obj, 'dtype'):
+ objtype = obj.dtype
+ filler = max_filler[objtype]
+ if filler is None:
+ raise TypeError, 'Unsuitable type for calculating minimum.'
+ return filler
+ elif isinstance(obj, float):
+ return max_filler[ntypes.typeDict['float_']]
+ elif isinstance(obj, int):
+ return max_filler[ntypes.typeDict['int_']]
+ elif isinstance(obj, long):
+ return max_filler[ntypes.typeDict['uint']]
+ elif isinstance(obj, numeric.dtype):
+ return max_filler[obj]
+ else:
+ raise TypeError, 'Unsuitable type for calculating minimum.'
+
+def set_fill_value(a, fill_value):
+ "Sets the fill value of `a` if it is a masked array."
+ if isinstance(a, MaskedArray):
+ a.set_fill_value(fill_value)
+
+def get_fill_value(a):
+ """Returns the fill value of `a`, if any.
+ Otherwise, returns the default fill value for that type.
+ """
+ if isinstance(a, MaskedArray):
+ result = a.fill_value
+ else:
+ result = default_fill_value(a)
+ return result
+
+def common_fill_value(a, b):
+ "Returns the common fill_value of `a` and `b`, if any, or `None`."
+ t1 = get_fill_value(a)
+ t2 = get_fill_value(b)
+ if t1 == t2:
+ return t1
+ return None
+
+#................................................
+def filled(a, value = None):
+ """Returns `a` as an array with masked data replaced by `value`.
+If `value` is `None` or the special element `masked`, `get_fill_value(a)`
+is used instead.
+
+If `a` is already a contiguous numeric array, `a` itself is returned.
+
+`filled(a)` can be used to be sure that the result is numeric when passing
+an object a to other software ignorant of MA, in particular to numpy itself.
+ """
+ if hasattr(a, 'filled'):
+ return a.filled(value)
+ elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']:
+ return a
+ elif isinstance(a, dict):
+ return numeric.array(a, 'O')
+ else:
+ return numeric.array(a)
+
+def get_masked_subclass(*arrays):
+ """Returns the youngest subclass of MaskedArray from a list of arrays,
+ or MaskedArray. In case of siblings, the first takes over."""
+ if len(arrays) == 1:
+ arr = arrays[0]
+ if isinstance(arr, MaskedArray):
+ rcls = type(arr)
+ else:
+ rcls = MaskedArray
+ else:
+ arrcls = [type(a) for a in arrays]
+ rcls = arrcls[0]
+ if not issubclass(rcls, MaskedArray):
+ rcls = MaskedArray
+ for cls in arrcls[1:]:
+ if issubclass(cls, rcls):
+ rcls = cls
+ return rcls
+
+#####--------------------------------------------------------------------------
+#---- --- Ufuncs ---
+#####--------------------------------------------------------------------------
+ufunc_domain = {}
+ufunc_fills = {}
+
+class domain_check_interval:
+ """Defines a valid interval,
+so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`."""
+ def __init__(self, a, b):
+ "domain_check_interval(a,b)(x) = true where x < a or y > b"
+ if (a > b):
+ (a, b) = (b, a)
+ self.a = a
+ self.b = b
+
+ def __call__ (self, x):
+ "Execute the call behavior."
+ return umath.logical_or(umath.greater (x, self.b),
+ umath.less(x, self.a))
+#............................
+class domain_tan:
+ """Defines a valid interval for the `tan` function,
+so that `domain_tan(eps) = True where `abs(cos(x)) < eps`"""
+ def __init__(self, eps):
+ "domain_tan(eps) = true where abs(cos(x)) < eps)"
+ self.eps = eps
+ def __call__ (self, x):
+ "Execute the call behavior."
+ return umath.less(umath.absolute(umath.cos(x)), self.eps)
+#............................
+class domain_safe_divide:
+ """defines a domain for safe division."""
+ def __init__ (self, tolerance=divide_tolerance):
+ self.tolerance = tolerance
+ def __call__ (self, a, b):
+ return umath.absolute(a) * self.tolerance >= umath.absolute(b)
+#............................
+class domain_greater:
+ "domain_greater(v)(x) = true where x <= v"
+ def __init__(self, critical_value):
+ "domain_greater(v)(x) = true where x <= v"
+ self.critical_value = critical_value
+
+ def __call__ (self, x):
+ "Execute the call behavior."
+ return umath.less_equal(x, self.critical_value)
+#............................
+class domain_greater_equal:
+ "domain_greater_equal(v)(x) = true where x < v"
+ def __init__(self, critical_value):
+ "domain_greater_equal(v)(x) = true where x < v"
+ self.critical_value = critical_value
+
+ def __call__ (self, x):
+ "Execute the call behavior."
+ return umath.less(x, self.critical_value)
+#..............................................................................
+class masked_unary_operation:
+ """Defines masked version of unary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+ - `f` : function.
+ - `fill` : Default filling value *[0]*.
+ - `domain` : Default domain *[None]*.
+ """
+ def __init__ (self, mufunc, fill=0, domain=None):
+ """ masked_unary_operation(aufunc, fill=0, domain=None)
+ aufunc(fill) must be defined
+ self(x) returns aufunc(x)
+ with masked values where domain(x) is true or getmask(x) is true.
+ """
+ self.f = mufunc
+ self.fill = fill
+ self.domain = domain
+ self.__doc__ = getattr(mufunc, "__doc__", str(mufunc))
+ self.__name__ = getattr(mufunc, "__name__", str(mufunc))
+ ufunc_domain[mufunc] = domain
+ ufunc_fills[mufunc] = fill
+ #
+ def __call__ (self, a, *args, **kwargs):
+ "Execute the call behavior."
+# numeric tries to return scalars rather than arrays when given scalars.
+ m = getmask(a)
+ d1 = filled(a, self.fill)
+ if self.domain is not None:
+ m = mask_or(m, numeric.asarray(self.domain(d1)))
+ # Take care of the masked singletong first ...
+ if m.ndim == 0 and m:
+ return masked
+ # Get the result....
+ if isinstance(a, MaskedArray):
+ result = self.f(d1, *args, **kwargs).view(type(a))
+ else:
+ result = self.f(d1, *args, **kwargs).view(MaskedArray)
+ # Fix the mask if we don't have a scalar
+ if result.ndim > 0:
+ result._mask = m
+ return result
+ #
+ def __str__ (self):
+ return "Masked version of %s. [Invalid values are masked]" % str(self.f)
+#..............................................................................
+class masked_binary_operation:
+ """Defines masked version of binary operations,
+where invalid values are pre-masked.
+
+:IVariables:
+ - `f` : function.
+ - `fillx` : Default filling value for first array*[0]*.
+ - `filly` : Default filling value for second array*[0]*.
+ - `domain` : Default domain *[None]*.
+ """
+ def __init__ (self, mbfunc, fillx=0, filly=0):
+ """abfunc(fillx, filly) must be defined.
+ abfunc(x, filly) = x for all x to enable reduce.
+ """
+ self.f = mbfunc
+ self.fillx = fillx
+ self.filly = filly
+ self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc))
+ self.__name__ = getattr(mbfunc, "__name__", str(mbfunc))
+ ufunc_domain[mbfunc] = None
+ ufunc_fills[mbfunc] = (fillx, filly)
+ #
+ def __call__ (self, a, b, *args, **kwargs):
+ "Execute the call behavior."
+ m = mask_or(getmask(a), getmask(b))
+ if (not m.ndim) and m:
+ return masked
+ d1 = filled(a, self.fillx)
+ d2 = filled(b, self.filly)
+# CHECK : Do we really need to fill the arguments ? Pro'ly not
+# result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b))
+ result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b))
+ if result.ndim > 0:
+ result._mask = m
+ return result
+ #
+ def reduce (self, target, axis=0, dtype=None):
+ """Reduces `target` along the given `axis`."""
+ if isinstance(target, MaskedArray):
+ tclass = type(target)
+ else:
+ tclass = MaskedArray
+ m = getmask(target)
+ t = filled(target, self.filly)
+ if t.shape == ():
+ t = t.reshape(1)
+ if m is not nomask:
+ m = make_mask(m, copy=1)
+ m.shape = (1,)
+ if m is nomask:
+ return self.f.reduce(t, axis).view(tclass)
+ t = t.view(tclass)
+ t._mask = m
+ # XXX: "or t.dtype" below is a workaround for what appears
+ # XXX: to be a bug in reduce.
+ tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype)
+ mr = umath.logical_and.reduce(m, axis)
+ tr = tr.view(tclass)
+ if mr.ndim > 0:
+ tr._mask = mr
+ return tr
+ elif mr:
+ return masked
+ return tr
+
+ def outer (self, a, b):
+ "Returns the function applied to the outer product of a and b."
+ ma = getmask(a)
+ mb = getmask(b)
+ if ma is nomask and mb is nomask:
+ m = nomask
+ else:
+ ma = getmaskarray(a)
+ mb = getmaskarray(b)
+ m = umath.logical_or.outer(ma, mb)
+ if (not m.ndim) and m:
+ return masked
+ rcls = get_masked_subclass(a,b)
+ d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls)
+ if d.ndim > 0:
+ d._mask = m
+ return d
+
+ def accumulate (self, target, axis=0):
+ """Accumulates `target` along `axis` after filling with y fill value."""
+ if isinstance(target, MaskedArray):
+ tclass = type(target)
+ else:
+ tclass = masked_array
+ t = filled(target, self.filly)
+ return self.f.accumulate(t, axis).view(tclass)
+
+ def __str__ (self):
+ return "Masked version of " + str(self.f)
+#..............................................................................
+class domained_binary_operation:
+ """Defines binary operations that have a domain, like divide.
+
+These are complicated so they are a separate class.
+They have no reduce, outer or accumulate.
+
+:IVariables:
+ - `f` : function.
+ - `fillx` : Default filling value for first array*[0]*.
+ - `filly` : Default filling value for second array*[0]*.
+ - `domain` : Default domain *[None]*.
+ """
+ def __init__ (self, dbfunc, domain, fillx=0, filly=0):
+ """abfunc(fillx, filly) must be defined.
+ abfunc(x, filly) = x for all x to enable reduce.
+ """
+ self.f = dbfunc
+ self.domain = domain
+ self.fillx = fillx
+ self.filly = filly
+ self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc))
+ self.__name__ = getattr(dbfunc, "__name__", str(dbfunc))
+ ufunc_domain[dbfunc] = domain
+ ufunc_fills[dbfunc] = (fillx, filly)
+
+ def __call__(self, a, b):
+ "Execute the call behavior."
+ ma = getmask(a)
+ mb = getmask(b)
+ d1 = filled(a, self.fillx)
+ d2 = filled(b, self.filly)
+ t = numeric.asarray(self.domain(d1, d2))
+
+ if fromnumeric.sometrue(t, None):
+ d2 = numeric.where(t, self.filly, d2)
+ mb = mask_or(mb, t)
+ m = mask_or(ma, mb)
+ if (not m.ndim) and m:
+ return masked
+ result = self.f(d1, d2).view(get_masked_subclass(a,b))
+ if result.ndim > 0:
+ result._mask = m
+ return result
+
+ def __str__ (self):
+ return "Masked version of " + str(self.f)
+
+#..............................................................................
+# Unary ufuncs
+exp = masked_unary_operation(umath.exp)
+conjugate = masked_unary_operation(umath.conjugate)
+sin = masked_unary_operation(umath.sin)
+cos = masked_unary_operation(umath.cos)
+tan = masked_unary_operation(umath.tan)
+arctan = masked_unary_operation(umath.arctan)
+arcsinh = masked_unary_operation(umath.arcsinh)
+sinh = masked_unary_operation(umath.sinh)
+cosh = masked_unary_operation(umath.cosh)
+tanh = masked_unary_operation(umath.tanh)
+abs = absolute = masked_unary_operation(umath.absolute)
+fabs = masked_unary_operation(umath.fabs)
+negative = masked_unary_operation(umath.negative)
+floor = masked_unary_operation(umath.floor)
+ceil = masked_unary_operation(umath.ceil)
+around = masked_unary_operation(fromnumeric.round_)
+logical_not = masked_unary_operation(umath.logical_not)
+# Domained unary ufuncs
+sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0))
+log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0))
+log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0))
+tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35))
+arcsin = masked_unary_operation(umath.arcsin, 0.0,
+ domain_check_interval(-1.0, 1.0))
+arccos = masked_unary_operation(umath.arccos, 0.0,
+ domain_check_interval(-1.0, 1.0))
+arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0))
+arctanh = masked_unary_operation(umath.arctanh, 0.0,
+ domain_check_interval(-1.0+1e-15, 1.0-1e-15))
+# Binary ufuncs
+add = masked_binary_operation(umath.add)
+subtract = masked_binary_operation(umath.subtract)
+multiply = masked_binary_operation(umath.multiply, 1, 1)
+arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0)
+equal = masked_binary_operation(umath.equal)
+equal.reduce = None
+not_equal = masked_binary_operation(umath.not_equal)
+not_equal.reduce = None
+less_equal = masked_binary_operation(umath.less_equal)
+less_equal.reduce = None
+greater_equal = masked_binary_operation(umath.greater_equal)
+greater_equal.reduce = None
+less = masked_binary_operation(umath.less)
+less.reduce = None
+greater = masked_binary_operation(umath.greater)
+greater.reduce = None
+logical_and = masked_binary_operation(umath.logical_and)
+alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce
+logical_or = masked_binary_operation(umath.logical_or)
+sometrue = logical_or.reduce
+logical_xor = masked_binary_operation(umath.logical_xor)
+bitwise_and = masked_binary_operation(umath.bitwise_and)
+bitwise_or = masked_binary_operation(umath.bitwise_or)
+bitwise_xor = masked_binary_operation(umath.bitwise_xor)
+hypot = masked_binary_operation(umath.hypot)
+# Domained binary ufuncs
+divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1)
+true_divide = domained_binary_operation(umath.true_divide,
+ domain_safe_divide(), 0, 1)
+floor_divide = domained_binary_operation(umath.floor_divide,
+ domain_safe_divide(), 0, 1)
+remainder = domained_binary_operation(umath.remainder,
+ domain_safe_divide(), 0, 1)
+fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1)
+
+
+#####--------------------------------------------------------------------------
+#---- --- Mask creation functions ---
+#####--------------------------------------------------------------------------
+def getmask(a):
+ """Returns the mask of `a`, if any, or `nomask`.
+Returns `nomask` if `a` is not a masked array.
+To get an array for sure use getmaskarray."""
+ if hasattr(a, "_mask"):
+ return a._mask
+ else:
+ return nomask
+
+def getmaskarray(a):
+ """Returns the mask of `a`, if any.
+Otherwise, returns an array of `False`, with the same shape as `a`.
+ """
+ m = getmask(a)
+ if m is nomask:
+ return make_mask_none(fromnumeric.shape(a))
+ else:
+ return m
+
+def is_mask(m):
+ """Returns `True` if `m` is a legal mask.
+Does not check contents, only type.
+ """
+ try:
+ return m.dtype.type is MaskType
+ except AttributeError:
+ return False
+#
+def make_mask(m, copy=False, small_mask=True, flag=None):
+ """make_mask(m, copy=0, small_mask=0)
+Returns `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.
+If `small_mask=True`, returns `nomask` if `m` contains no true elements.
+
+:Parameters:
+ - `m` (ndarray) : Mask.
+ - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+ - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+ """
+ if flag is not None:
+ warnings.warn("The flag 'flag' is now called 'small_mask'!",
+ DeprecationWarning)
+ small_mask = flag
+ if m is nomask:
+ return nomask
+ elif isinstance(m, ndarray):
+ m = filled(m, True)
+ if m.dtype.type is MaskType:
+ if copy:
+ result = numeric.array(m, dtype=MaskType, copy=copy)
+ else:
+ result = m
+ else:
+ result = numeric.array(m, dtype=MaskType)
+ else:
+ result = numeric.array(filled(m, True), dtype=MaskType)
+ # Bas les masques !
+ if small_mask and not result.any():
+ return nomask
+ else:
+ return result
+
+def make_mask_none(s):
+ "Returns a mask of shape `s`, filled with `False`."
+ result = numeric.zeros(s, dtype=MaskType)
+ return result
+
+def mask_or (m1, m2, copy=False, small_mask=True):
+ """Returns 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 nomask.
+
+:Parameters:
+ - `m` (ndarray) : Mask.
+ - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+ - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false.
+ """
+ if m1 is nomask:
+ return make_mask(m2, copy=copy, small_mask=small_mask)
+ if m2 is nomask:
+ return make_mask(m1, copy=copy, small_mask=small_mask)
+ if m1 is m2 and is_mask(m1):
+ return m1
+ return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask)
+
+#####--------------------------------------------------------------------------
+#--- --- Masking functions ---
+#####--------------------------------------------------------------------------
+def masked_where(condition, a, copy=True):
+ """Returns `x` as an array masked where `condition` is true.
+Masked values of `x` or `condition` are kept.
+
+:Parameters:
+ - `condition` (ndarray) : Masking condition.
+ - `x` (ndarray) : Array to mask.
+ - `copy` (boolean, *[False]*) : Returns a copy of `m` if true.
+ """
+ cond = filled(condition,1)
+ a = numeric.array(a, copy=copy, subok=True)
+ if hasattr(a, '_mask'):
+ cond = mask_or(cond, a._mask)
+ cls = type(a)
+ else:
+ cls = MaskedArray
+ result = a.view(cls)
+ result._mask = cond
+ return result
+
+def masked_greater(x, value, copy=1):
+ "Shortcut to `masked_where`, with ``condition = (x > value)``."
+ return masked_where(greater(x, value), x, copy=copy)
+
+def masked_greater_equal(x, value, copy=1):
+ "Shortcut to `masked_where`, with ``condition = (x >= value)``."
+ return masked_where(greater_equal(x, value), x, copy=copy)
+
+def masked_less(x, value, copy=True):
+ "Shortcut to `masked_where`, with ``condition = (x < value)``."
+ return masked_where(less(x, value), x, copy=copy)
+
+def masked_less_equal(x, value, copy=True):
+ "Shortcut to `masked_where`, with ``condition = (x <= value)``."
+ return masked_where(less_equal(x, value), x, copy=copy)
+
+def masked_not_equal(x, value, copy=True):
+ "Shortcut to `masked_where`, with ``condition = (x != value)``."
+ return masked_where((x != value), x, copy=copy)
+
+#
+def masked_equal(x, value, copy=True):
+ """Shortcut to `masked_where`, with ``condition = (x == value)``.
+For floating point, consider `masked_values(x, value)` instead.
+ """
+ return masked_where((x == value), x, copy=copy)
+# d = filled(x, 0)
+# c = umath.equal(d, value)
+# m = mask_or(c, getmask(x))
+# return array(d, mask=m, copy=copy)
+
+def masked_inside(x, v1, v2, copy=True):
+ """Shortcut to `masked_where`, where `condition` is True for x inside
+the interval `[v1,v2]` ``(v1 <= x <= v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+ """
+ if v2 < v1:
+ (v1, v2) = (v2, v1)
+ xf = filled(x)
+ condition = (xf >= v1) & (xf <= v2)
+ return masked_where(condition, x, copy=copy)
+
+def masked_outside(x, v1, v2, copy=True):
+ """Shortcut to `masked_where`, where `condition` is True for x outside
+the interval `[v1,v2]` ``(x < v1)|(x > v2)``.
+The boundaries `v1` and `v2` can be given in either order.
+ """
+ if v2 < v1:
+ (v1, v2) = (v2, v1)
+ xf = filled(x)
+ condition = (xf < v1) | (xf > v2)
+ return masked_where(condition, x, copy=copy)
+
+#
+def masked_object(x, value, copy=True):
+ """Masks the array `x` where the data are exactly equal to `value`.
+This function is suitable only for `object` arrays: for floating point,
+please use `masked_values` instead.
+The mask is set to `nomask` if posible.
+
+:parameter copy (Boolean, *[True]*): Returns a copy of `x` if true. """
+ if isMaskedArray(x):
+ condition = umath.equal(x._data, value)
+ mask = x._mask
+ else:
+ condition = umath.equal(fromnumeric.asarray(x), value)
+ mask = nomask
+ mask = mask_or(mask, make_mask(condition, small_mask=True))
+ return masked_array(x, mask=mask, copy=copy, fill_value=value)
+
+def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True):
+ """Masks the array `x` where the data are approximately equal to `value`
+(that is, ``abs(x - value) <= atol+rtol*abs(value)``).
+Suitable only for floating points. For integers, please use `masked_equal`.
+The mask is set to `nomask` if posible.
+
+:Parameters:
+ - `rtol` (Float, *[1e-5]*): Tolerance parameter.
+ - `atol` (Float, *[1e-8]*): Tolerance parameter.
+ - `copy` (boolean, *[False]*) : Returns a copy of `x` if True.
+ """
+ abs = umath.absolute
+ xnew = filled(x, value)
+ if issubclass(xnew.dtype.type, numeric.floating):
+ condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+ try:
+ mask = x._mask
+ except AttributeError:
+ mask = nomask
+ else:
+ condition = umath.equal(xnew, value)
+ mask = nomask
+ mask = mask_or(mask, make_mask(condition, small_mask=True))
+ return masked_array(xnew, mask=mask, copy=copy, fill_value=value)
+
+#####--------------------------------------------------------------------------
+#---- --- Printing options ---
+#####--------------------------------------------------------------------------
+class _MaskedPrintOption:
+ """Handles the string used to represent missing data in a masked array."""
+ def __init__ (self, display):
+ "Creates the masked_print_option object."
+ self._display = display
+ self._enabled = True
+
+ def display(self):
+ "Displays the string to print for masked values."
+ return self._display
+
+ def set_display (self, s):
+ "Sets the string to print for masked values."
+ self._display = s
+
+ def enabled(self):
+ "Is the use of the display value enabled?"
+ return self._enabled
+
+ def enable(self, small_mask=1):
+ "Set the enabling small_mask to `small_mask`."
+ self._enabled = small_mask
+
+ def __str__ (self):
+ return str(self._display)
+
+ __repr__ = __str__
+
+#if you single index into a masked location you get this object.
+masked_print_option = _MaskedPrintOption('--')
+
+#####--------------------------------------------------------------------------
+#---- --- MaskedArray class ---
+#####--------------------------------------------------------------------------
+##def _getoptions(a_out, a_in):
+## "Copies standards options of a_in to a_out."
+## for att in [']
+#class _mathmethod(object):
+# """Defines a wrapper for arithmetic methods.
+#Instead of directly calling a ufunc, the corresponding method of the `array._data`
+#object is called instead.
+# """
+# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None):
+# """
+#:Parameters:
+# - `methodname` (String) : Method name.
+# - `fill_self` (Float *[0]*) : Fill value for the instance.
+# - `fill_other` (Float *[0]*) : Fill value for the target.
+# - `domain` (Domain object *[None]*) : Domain of non-validity.
+# """
+# self.methodname = methodname
+# self.fill_self = fill_self
+# self.fill_other = fill_other
+# self.domain = domain
+# self.obj = None
+# self.__doc__ = self.getdoc()
+# #
+# def getdoc(self):
+# "Returns the doc of the function (from the doc of the method)."
+# try:
+# return getattr(MaskedArray, self.methodname).__doc__
+# except:
+# return getattr(ndarray, self.methodname).__doc__
+# #
+# def __get__(self, obj, objtype=None):
+# self.obj = obj
+# return self
+# #
+# def __call__ (self, other, *args):
+# "Execute the call behavior."
+# instance = self.obj
+# m_self = instance._mask
+# m_other = getmask(other)
+# base = instance.filled(self.fill_self)
+# target = filled(other, self.fill_other)
+# if self.domain is not None:
+# # We need to force the domain to a ndarray only.
+# if self.fill_other > self.fill_self:
+# domain = self.domain(base, target)
+# else:
+# domain = self.domain(target, base)
+# if domain.any():
+# #If `other` is a subclass of ndarray, `filled` must have the
+# # same subclass, else we'll lose some info.
+# #The easiest then is to fill `target` instead of creating
+# # a pure ndarray.
+# #Oh, and we better make a copy!
+# if isinstance(other, ndarray):
+# # We don't want to modify other: let's copy target, then
+# target = target.copy()
+# target[fromnumeric.asarray(domain)] = self.fill_other
+# else:
+# target = numeric.where(fromnumeric.asarray(domain),
+# self.fill_other, target)
+# m_other = mask_or(m_other, domain)
+# m = mask_or(m_self, m_other)
+# method = getattr(base, self.methodname)
+# result = method(target, *args).view(type(instance))
+# try:
+# result._mask = m
+# except AttributeError:
+# if m:
+# result = masked
+# return result
+#...............................................................................
+class _arraymethod(object):
+ """Defines a wrapper for basic array methods.
+Upon call, returns a masked array, where the new `_data` array is the output
+of the corresponding method called on the original `_data`.
+
+If `onmask` is True, the new mask is the output of the method calld on the initial mask.
+If `onmask` is False, the new mask is just a reference to the initial mask.
+
+:Parameters:
+ `funcname` : String
+ Name of the function to apply on data.
+ `onmask` : Boolean *[True]*
+ Whether the mask must be processed also (True) or left alone (False).
+ """
+ def __init__(self, funcname, onmask=True):
+ self._name = funcname
+ self._onmask = onmask
+ self.obj = None
+ self.__doc__ = self.getdoc()
+ #
+ def getdoc(self):
+ "Returns the doc of the function (from the doc of the method)."
+ methdoc = getattr(ndarray, self._name, None)
+ methdoc = getattr(numpy, self._name, methdoc)
+# methdoc = getattr(MaskedArray, self._name, methdoc)
+ if methdoc is not None:
+ return methdoc.__doc__
+# try:
+# return getattr(MaskedArray, self._name).__doc__
+# except:
+# try:
+# return getattr(numpy, self._name).__doc__
+# except:
+# return getattr(ndarray, self._name).__doc
+ #
+ def __get__(self, obj, objtype=None):
+ self.obj = obj
+ return self
+ #
+ def __call__(self, *args, **params):
+ methodname = self._name
+ data = self.obj._data
+ mask = self.obj._mask
+ cls = type(self.obj)
+ result = getattr(data, methodname)(*args, **params).view(cls)
+ result._smallmask = self.obj._smallmask
+ if result.ndim:
+ if not self._onmask:
+ result._mask = mask
+ elif mask is not nomask:
+ result.__setmask__(getattr(mask, methodname)(*args, **params))
+ return result
+#..........................................................
+
+class flatiter(object):
+ "Defines an interator."
+ def __init__(self, ma):
+ self.ma = ma
+ self.ma_iter = numpy.asarray(ma).flat
+
+ if ma._mask is nomask:
+ self.maskiter = None
+ else:
+ self.maskiter = ma._mask.flat
+
+ def __iter__(self):
+ return self
+
+ ### This won't work is ravel makes a copy
+ def __setitem__(self, index, value):
+ a = self.ma.ravel()
+ a[index] = value
+
+ def next(self):
+ d = self.ma_iter.next()
+ if self.maskiter is not None and self.maskiter.next():
+ d = masked
+ return d
+
+
+class MaskedArray(numeric.ndarray):
+ """Arrays with possibly masked values.
+Masked values of True exclude the corresponding element from any computation.
+
+Construction:
+ x = array(data, dtype=None, copy=True, order=False,
+ mask = nomask, fill_value=None, small_mask=True)
+
+If copy=False, every effort is made not to copy the data:
+If `data` is a MaskedArray, and argument mask=nomask, then the candidate data
+is `data._data` and the mask used is `data._mask`.
+If `data` is a numeric array, it is used as the candidate raw data.
+If `dtype` is not None and is different from data.dtype.char then a data copy is required.
+Otherwise, the candidate is used.
+
+If a data copy is required, the raw (unmasked) data stored is the result of:
+numeric.array(data, dtype=dtype.char, copy=copy)
+
+If `mask` is `nomask` there are no masked values.
+Otherwise mask must be convertible to an array of booleans with the same shape as x.
+If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`.
+Otherwise, the mask is not compressed.
+
+fill_value is used to fill in masked values when necessary, such as when
+printing and in method/function filled().
+The fill_value is not used for computation within this module.
+ """
+ __array_priority__ = 10.1
+ _defaultmask = nomask
+ _defaulthardmask = False
+ _baseclass = numeric.ndarray
+ def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None,
+ keep_mask=True, small_mask=True, hard_mask=False, flag=None,
+ subok=True, **options):
+ """array(data, dtype=None, copy=True, mask=nomask, fill_value=None)
+
+If `data` is already a ndarray, its dtype becomes the default value of dtype.
+ """
+ if flag is not None:
+ warnings.warn("The flag 'flag' is now called 'small_mask'!",
+ DeprecationWarning)
+ small_mask = flag
+ # Process data............
+ _data = numeric.array(data, dtype=dtype, copy=copy, subok=subok)
+ _baseclass = getattr(data, '_baseclass', type(_data))
+ _basedict = getattr(data, '_basedict', getattr(data, '__dict__', None))
+ if not isinstance(data, MaskedArray):
+ _data = _data.view(cls)
+ elif not subok:
+ _data = data.view(cls)
+ else:
+ _data = _data.view(type(data))
+ # Backwards compat .......
+ if hasattr(data,'_mask') and not isinstance(data, ndarray):
+ _data._mask = data._mask
+ _sharedmask = True
+ # Process mask ...........
+ if mask is nomask:
+ if not keep_mask:
+ _data._mask = nomask
+ if copy:
+ _data._mask = _data._mask.copy()
+ else:
+ mask = numeric.array(mask, dtype=MaskType, copy=copy)
+ if mask.shape != _data.shape:
+ (nd, nm) = (_data.size, mask.size)
+ if nm == 1:
+ mask = numeric.resize(mask, _data.shape)
+ elif nm == nd:
+ mask = fromnumeric.reshape(mask, _data.shape)
+ else:
+ msg = "Mask and data not compatible: data size is %i, "+\
+ "mask size is %i."
+ raise MAError, msg % (nd, nm)
+ if _data._mask is nomask:
+ _data._mask = mask
+ _data._sharedmask = True
+ else:
+ # Make a copy of the mask to avoid propagation
+ _data._sharedmask = False
+ if not keep_mask:
+ _data._mask = mask
+ else:
+ _data._mask = umath.logical_or(mask, _data._mask)
+
+
+ # Update fill_value.......
+ _data._fill_value = getattr(data, '_fill_value', fill_value)
+ if _data._fill_value is None:
+ _data._fill_value = default_fill_value(_data)
+ # Process extra options ..
+ _data._hardmask = hard_mask
+ _data._smallmask = small_mask
+ _data._baseclass = _baseclass
+ _data._basedict = _basedict
+ return _data
+ #........................
+ def __array_finalize__(self,obj):
+ """Finalizes the masked array.
+ """
+ # Finalize mask ...............
+ self._mask = getattr(obj, '_mask', nomask)
+ if self._mask is not nomask:
+ self._mask.shape = self.shape
+ # Get the remaining options ...
+ self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask)
+ self._smallmask = getattr(obj, '_smallmask', True)
+ self._sharedmask = True
+ self._baseclass = getattr(obj, '_baseclass', type(obj))
+ self._fill_value = getattr(obj, '_fill_value', None)
+ # Update special attributes ...
+ self._basedict = getattr(obj, '_basedict', getattr(obj, '__dict__', None))
+ if self._basedict is not None:
+ self.__dict__.update(self._basedict)
+ return
+ #..................................
+ def __array_wrap__(self, obj, context=None):
+ """Special hook for ufuncs.
+Wraps the numpy array and sets the mask according to context.
+ """
+ #TODO : Should we check for type result
+ result = obj.view(type(self))
+ #..........
+ if context is not None:
+ result._mask = result._mask.copy()
+ (func, args, _) = context
+ m = reduce(mask_or, [getmask(arg) for arg in args])
+ # Get domain mask
+ domain = ufunc_domain.get(func, None)
+ if domain is not None:
+ if len(args) > 2:
+ d = reduce(domain, args)
+ else:
+ d = domain(*args)
+ if m is nomask:
+ if d is not nomask:
+ m = d
+ else:
+ m |= d
+ if not m.ndim and m:
+ if m:
+ if result.shape == ():
+ return masked
+ result._mask = numeric.ones(result.shape, bool_)
+ else:
+ result._mask = m
+ #....
+# result._mask = m
+ result._fill_value = self._fill_value
+ result._hardmask = self._hardmask
+ result._smallmask = self._smallmask
+ result._baseclass = self._baseclass
+ return result
+ #.............................................
+ def __getitem__(self, indx):
+ """x.__getitem__(y) <==> x[y]
+Returns the item described by i. Not a copy as in previous versions.
+ """
+ # This test is useful, but we should keep things light...
+# if getmask(indx) is not nomask:
+# msg = "Masked arrays must be filled before they can be used as indices!"
+# raise IndexError, msg
+ # super() can't work here if the underlying data is a matrix...
+ dout = (self._data).__getitem__(indx)
+ m = self._mask
+ if hasattr(dout, 'shape') and len(dout.shape) > 0:
+ # Not a scalar: make sure that dout is a MA
+ dout = dout.view(type(self))
+ dout._smallmask = self._smallmask
+ if m is not nomask:
+ # use _set_mask to take care of the shape
+ dout.__setmask__(m[indx])
+ elif m is not nomask and m[indx]:
+ return masked
+ return dout
+ #........................
+ def __setitem__(self, indx, value):
+ """x.__setitem__(i, y) <==> x[i]=y
+Sets item described by index. If value is masked, masks those locations.
+ """
+ if self is masked:
+ raise MAError, 'Cannot alter the masked element.'
+# if getmask(indx) is not nomask:
+# msg = "Masked arrays must be filled before they can be used as indices!"
+# raise IndexError, msg
+ #....
+ if value is masked:
+ m = self._mask
+ if m is nomask:
+ m = make_mask_none(self.shape)
+# else:
+# m = m.copy()
+ m[indx] = True
+ self.__setmask__(m)
+ return
+ #....
+ dval = numeric.asarray(value).astype(self.dtype)
+ valmask = getmask(value)
+ if self._mask is nomask:
+ if valmask is not nomask:
+ self._mask = make_mask_none(self.shape)
+ self._mask[indx] = valmask
+ elif not self._hardmask:
+ _mask = self._mask.copy()
+ if valmask is nomask:
+ _mask[indx] = False
+ else:
+ _mask[indx] = valmask
+ self._set_mask(_mask)
+ elif hasattr(indx, 'dtype') and (indx.dtype==bool_):
+ indx = indx * umath.logical_not(self._mask)
+ else:
+ mindx = mask_or(self._mask[indx], valmask, copy=True)
+ dindx = self._data[indx]
+ if dindx.size > 1:
+ dindx[~mindx] = dval
+ elif mindx is nomask:
+ dindx = dval
+ dval = dindx
+ self._mask[indx] = mindx
+ # Set data ..........
+ #dval = filled(value).astype(self.dtype)
+ ndarray.__setitem__(self._data,indx,dval)
+ #............................................
+ def __getslice__(self, i, j):
+ """x.__getslice__(i, j) <==> x[i:j]
+Returns the slice described by i, j.
+The use of negative indices is not supported."""
+ return self.__getitem__(slice(i,j))
+ #........................
+ def __setslice__(self, i, j, value):
+ """x.__setslice__(i, j, value) <==> x[i:j]=value
+Sets a slice i:j to `value`.
+If `value` is masked, masks those locations."""
+ self.__setitem__(slice(i,j), value)
+ #............................................
+ def __setmask__(self, mask, copy=False):
+ newmask = make_mask(mask, copy=copy, small_mask=self._smallmask)
+# self.unshare_mask()
+ if self._mask is nomask:
+ self._mask = newmask
+ elif self._hardmask:
+ if newmask is not nomask:
+ self._mask.__ior__(newmask)
+ else:
+ # This one is tricky: if we set the mask that way, we may break the
+ # propagation. But if we don't, we end up with a mask full of False
+ # and a test on nomask fails...
+ if newmask is nomask:
+ self._mask = nomask
+ else:
+ self._mask.flat = newmask
+ if self._mask.shape:
+ self._mask = numeric.reshape(self._mask, self.shape)
+ _set_mask = __setmask__
+
+ def _get_mask(self):
+ """Returns the current mask."""
+ return self._mask
+
+ mask = property(fget=_get_mask, fset=__setmask__, doc="Mask")
+ #............................................
+ def harden_mask(self):
+ "Forces the mask to hard."
+ self._hardmask = True
+
+ def soften_mask(self):
+ "Forces the mask to soft."
+ self._hardmask = False
+
+ def unshare_mask(self):
+ "Copies the mask and set the sharedmask flag to False."
+ if self._sharedmask:
+ self._mask = self._mask.copy()
+ self._sharedmask = False
+
+ #............................................
+ def _get_data(self):
+ "Returns the current data (as a view of the original underlying data)>"
+ return self.view(self._baseclass)
+ _data = property(fget=_get_data)
+ #............................................
+ def _get_flat(self):
+ """Calculates the flat value.
+ """
+ return flatiter(self)
+ #
+ def _set_flat (self, value):
+ "x.flat = value"
+ y = self.ravel()
+ y[:] = value
+ #
+ flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version")
+ #............................................
+ def get_fill_value(self):
+ "Returns the filling value."
+ if self._fill_value is None:
+ self._fill_value = default_fill_value(self)
+ return self._fill_value
+
+ def set_fill_value(self, value=None):
+ """Sets the filling value to `value`.
+If None, uses the default, based on the data type."""
+ if value is None:
+ value = default_fill_value(self)
+ self._fill_value = value
+
+ fill_value = property(fget=get_fill_value, fset=set_fill_value,
+ doc="Filling value")
+
+ def filled(self, fill_value=None):
+ """Returns an array of the same class as `_data`,
+ with masked values filled with `fill_value`.
+Subclassing is preserved.
+
+If `fill_value` is None, uses self.fill_value.
+ """
+ m = self._mask
+ if m is nomask or not m.any():
+ return self._data
+ #
+ if fill_value is None:
+ fill_value = self.fill_value
+ #
+ if self is masked_singleton:
+ result = numeric.asanyarray(fill_value)
+ else:
+ result = self._data.copy()
+ try:
+ result[m] = fill_value
+ except (TypeError, AttributeError):
+ fill_value = numeric.array(fill_value, dtype=object)
+ d = result.astype(object)
+ result = fromnumeric.choose(m, (d, fill_value))
+ except IndexError:
+ #ok, if scalar
+ if self._data.shape:
+ raise
+ elif m:
+ result = numeric.array(fill_value, dtype=self.dtype)
+ else:
+ result = self._data
+ return result
+
+ def compressed(self):
+ "A 1-D array of all the non-masked data."
+ d = self.ravel()
+ if self._mask is nomask:
+ return d
+ elif not self._smallmask and not self._mask.any():
+ return d
+ else:
+ return d[numeric.logical_not(d._mask)]
+ #............................................
+ def __str__(self):
+ """x.__str__() <==> str(x)
+Calculates the string representation, using masked for fill if it is enabled.
+Otherwise, fills with fill value.
+ """
+ if masked_print_option.enabled():
+ f = masked_print_option
+ if self is masked:
+ return str(f)
+ m = self._mask
+ if m is nomask:
+ res = self._data
+ else:
+ if m.shape == ():
+ if m:
+ return str(f)
+ else:
+ return str(self._data)
+ # convert to object array to make filled work
+#CHECK: the two lines below seem more robust than the self._data.astype
+# res = numeric.empty(self._data.shape, object_)
+# numeric.putmask(res,~m,self._data)
+ res = self._data.astype("|O8")
+ res[m] = f
+ else:
+ res = self.filled(self.fill_value)
+ return str(res)
+
+ def __repr__(self):
+ """x.__repr__() <==> repr(x)
+Calculates the repr representation, using masked for fill if it is enabled.
+Otherwise fill with fill value.
+ """
+ with_mask = """\
+masked_%(name)s(data =
+ %(data)s,
+ mask =
+ %(mask)s,
+ fill_value=%(fill)s)
+"""
+ with_mask1 = """\
+masked_%(name)s(data = %(data)s,
+ mask = %(mask)s,
+ fill_value=%(fill)s)
+"""
+ n = len(self.shape)
+ name = repr(self._data).split('(')[0]
+ if n <= 1:
+ return with_mask1 % {
+ 'name': name,
+ 'data': str(self),
+ 'mask': str(self._mask),
+ 'fill': str(self.fill_value),
+ }
+ return with_mask % {
+ 'name': name,
+ 'data': str(self),
+ 'mask': str(self._mask),
+ 'fill': str(self.fill_value),
+ }
+ #............................................
+ def __iadd__(self, other):
+ "Adds other to self in place."
+ ndarray.__iadd__(self._data,other)
+ m = getmask(other)
+ if self._mask is nomask:
+ self._mask = m
+ elif m is not nomask:
+ self._mask += m
+ return self
+ #....
+ def __isub__(self, other):
+ "Subtracts other from self in place."
+ ndarray.__isub__(self._data,other)
+ m = getmask(other)
+ if self._mask is nomask:
+ self._mask = m
+ elif m is not nomask:
+ self._mask += m
+ return self
+ #....
+ def __imul__(self, other):
+ "Multiplies self by other in place."
+ ndarray.__imul__(self._data,other)
+ m = getmask(other)
+ if self._mask is nomask:
+ self._mask = m
+ elif m is not nomask:
+ self._mask += m
+ return self
+ #....
+ def __idiv__(self, other):
+ "Divides self by other in place."
+ dom_mask = domain_safe_divide().__call__(self, filled(other,1))
+ other_mask = getmask(other)
+ new_mask = mask_or(other_mask, dom_mask)
+ ndarray.__idiv__(self._data, other)
+ self._mask = mask_or(self._mask, new_mask)
+ return self
+ #............................................
+ def __float__(self):
+ "Converts self to float."
+ if self._mask is not nomask:
+ warnings.warn("Warning: converting a masked element to nan.")
+ return numpy.nan
+ #raise MAError, 'Cannot convert masked element to a Python float.'
+ return float(self.item())
+
+ def __int__(self):
+ "Converts self to int."
+ if self._mask is not nomask:
+ raise MAError, 'Cannot convert masked element to a Python int.'
+ return int(self.item())
+ #............................................
+ def count(self, axis=None):
+ """Counts the non-masked elements of the array along a given axis,
+and returns a masked array where the mask is True where all data are masked.
+If `axis` is None, counts all the non-masked elements, and returns either a
+scalar or the masked singleton."""
+ 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 numeric.ones(t) * n
+ n1 = fromnumeric.size(m, axis)
+ n2 = m.astype(int_).sum(axis)
+ if axis is None:
+ return (n1-n2)
+ else:
+ return masked_array(n1 - n2)
+ #............................................
+ def reshape (self, *s):
+ """Reshapes the array to shape s.
+Returns a new masked array.
+If you want to modify the shape in place, please use `a.shape = s`"""
+ result = self._data.reshape(*s).view(type(self))
+ result.__dict__.update(self.__dict__)
+ if result._mask is not nomask:
+ result._mask = self._mask.copy()
+ result._mask.shape = result.shape
+ return result
+ #
+ repeat = _arraymethod('repeat')
+ #
+ def resize(self, newshape, refcheck=True, order=False):
+ """Attempts to modify size and shape of self inplace.
+ The array must own its own memory and not be referenced by other arrays.
+ Returns None.
+ """
+ try:
+ self._data.resize(newshape, refcheck, order)
+ if self.mask is not nomask:
+ self._mask.resize(newshape, refcheck, order)
+ except ValueError:
+ raise ValueError("Cannot resize an array that has been referenced "
+ "or is referencing another array in this way.\n"
+ "Use the resize function.")
+ return None
+ #
+ flatten = _arraymethod('flatten')
+ #
+ def put(self, indices, values, mode='raise'):
+ """Sets storage-indexed locations to corresponding values.
+a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices.
+`values` can be scalar or an array shorter than indices, and it will be repeated,
+if necessary.
+If `values` has some masked values, the initial mask is updated in consequence,
+else the corresponding values are unmasked.
+ """
+ m = self._mask
+ # Hard mask: Get rid of the values/indices that fall on masked data
+ if self._hardmask and self._mask is not nomask:
+ mask = self._mask[indices]
+ indices = numeric.asarray(indices)
+ values = numeric.asanyarray(values)
+ values.resize(indices.shape)
+ indices = indices[~mask]
+ values = values[~mask]
+ #....
+ self._data.put(indices, values, mode=mode)
+ #....
+ if m is nomask:
+ m = getmask(values)
+ else:
+ m = m.copy()
+ if getmask(values) is nomask:
+ m.put(indices, False, mode=mode)
+ else:
+ m.put(indices, values._mask, mode=mode)
+ m = make_mask(m, copy=False, small_mask=True)
+ self._mask = m
+ #............................................
+ def ids (self):
+ """Return the address of the data and mask areas."""
+ return (self.ctypes.data, self._mask.ctypes.data)
+ #............................................
+ def all(self, axis=None, out=None):
+ """a.all(axis) returns True if all entries along the axis are True.
+ Returns False otherwise. If axis is None, uses the flatten array.
+ Masked data are considered as True during computation.
+ Outputs a masked array, where the mask is True if all data are masked along the axis.
+ Note: the out argument is not really operational...
+ """
+ d = self.filled(True).all(axis=axis, out=out).view(type(self))
+ if d.ndim > 0:
+ d.__setmask__(self._mask.all(axis))
+ return d
+
+ def any(self, axis=None, out=None):
+ """a.any(axis) returns True if some or all entries along the axis are True.
+ Returns False otherwise. If axis is None, uses the flatten array.
+ Masked data are considered as False during computation.
+ Outputs a masked array, where the mask is True if all data are masked along the axis.
+ Note: the out argument is not really operational...
+ """
+ d = self.filled(False).any(axis=axis, out=out).view(type(self))
+ if d.ndim > 0:
+ d.__setmask__(self._mask.all(axis))
+ return d
+
+ def nonzero(self):
+ """a.nonzero() returns a tuple of arrays
+
+ Returns a tuple of arrays, one for each dimension of a,
+ containing 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
+ transpose(a.nonzero())
+ instead. The result of this is always a 2d array, with a row for
+ each non-zero element."""
+ return numeric.asarray(self.filled(0)).nonzero()
+ #............................................
+ def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None):
+ """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None)
+Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`.
+ """
+ # TODO: What are we doing with `out`?
+ m = self._mask
+ if m is nomask:
+ result = super(MaskedArray, self).trace(offset=offset, axis1=axis1,
+ axis2=axis2, out=out)
+ return result.astype(dtype)
+ else:
+ D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2)
+ return D.astype(dtype).sum(axis=None)
+ #............................................
+ def sum(self, axis=None, dtype=None):
+ """a.sum(axis=None, dtype=None)
+Sums the array `a` over the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+ """
+ if self._mask is nomask:
+ mask = nomask
+ else:
+ mask = self._mask.all(axis)
+ if (not mask.ndim) and mask:
+ return masked
+ result = self.filled(0).sum(axis, dtype=dtype).view(type(self))
+ if result.ndim > 0:
+ result.__setmask__(mask)
+ return result
+
+ def cumsum(self, axis=None, dtype=None):
+ """a.cumprod(axis=None, dtype=None)
+Returns the cumulative sum of the elements of array `a` along the given axis `axis`.
+Masked values are set to 0.
+If `axis` is None, applies to a flattened version of the array.
+ """
+ result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self))
+ result.__setmask__(self.mask)
+ return result
+
+ def prod(self, axis=None, dtype=None):
+ """a.prod(axis=None, dtype=None)
+Returns the product of the elements of array `a` along the given axis `axis`.
+Masked elements are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+ """
+ if self._mask is nomask:
+ mask = nomask
+ else:
+ mask = self._mask.all(axis)
+ if (not mask.ndim) and mask:
+ return masked
+ result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self))
+ if result.ndim:
+ result.__setmask__(mask)
+ return result
+ product = prod
+
+ def cumprod(self, axis=None, dtype=None):
+ """a.cumprod(axis=None, dtype=None)
+Returns the cumulative product of ethe lements of array `a` along the given axis `axis`.
+Masked values are set to 1.
+If `axis` is None, applies to a flattened version of the array.
+ """
+ result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self))
+ result.__setmask__(self.mask)
+ return result
+
+ def mean(self, axis=None, dtype=None):
+ """a.mean(axis=None, dtype=None)
+
+ Averages the array over the given axis. If the axis is None,
+ averages over all dimensions of the array. Equivalent to
+
+ a.sum(axis, dtype) / size(a, axis).
+
+ The optional dtype argument is the data type for intermediate
+ calculations in the sum.
+
+ Returns a masked array, of the same class as a.
+ """
+ if self._mask is nomask:
+ return super(MaskedArray, self).mean(axis=axis, dtype=dtype)
+ else:
+ dsum = self.sum(axis=axis, dtype=dtype)
+ cnt = self.count(axis=axis)
+ return dsum*1./cnt
+
+ def anom(self, axis=None, dtype=None):
+ """a.anom(axis=None, dtype=None)
+ Returns the anomalies, or deviation from the average.
+ """
+ m = self.mean(axis, dtype)
+ if not axis:
+ return (self - m)
+ else:
+ return (self - expand_dims(m,axis))
+
+ def var(self, axis=None, dtype=None):
+ """a.var(axis=None, dtype=None)
+Returns the variance, a measure of the spread of a distribution.
+
+The variance is the average of the squared deviations from the mean,
+i.e. var = mean((x - x.mean())**2).
+ """
+ if self._mask is nomask:
+ # TODO: Do we keep super, or var _data and take a view ?
+ return super(MaskedArray, self).var(axis=axis, dtype=dtype)
+ else:
+ cnt = self.count(axis=axis)
+ danom = self.anom(axis=axis, dtype=dtype)
+ danom *= danom
+ dvar = numeric.array(danom.sum(axis) / cnt).view(type(self))
+ if axis is not None:
+ dvar._mask = mask_or(self._mask.all(axis), (cnt==1))
+ return dvar
+
+ def std(self, axis=None, dtype=None):
+ """a.std(axis=None, dtype=None)
+Returns the standard deviation, a measure of the spread of a distribution.
+
+The standard deviation is the square root of the average of the squared
+deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)).
+ """
+ dvar = self.var(axis,dtype)
+ if axis is not None or dvar is not masked:
+ dvar = sqrt(dvar)
+ return dvar
+ #............................................
+ def argsort(self, axis=None, fill_value=None, kind='quicksort',
+ order=None):
+ """Returns an array of indices that sort 'a' along the specified axis.
+ Masked values are filled beforehand to `fill_value`.
+ If `fill_value` is None, uses the default for the data type.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `kind` : String *['quicksort']*
+ Sorting algorithm (default 'quicksort')
+ Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+ Returns: array of indices that sort 'a' along the specified axis.
+
+ This method executes an indirect sort along the given axis using the
+ algorithm specified by the kind keyword. It returns an array of indices of
+ the same shape as 'a' that index data along the given axis in sorted order.
+
+ The various sorts are characterized by average speed, worst case
+ performance, need for work space, and whether they are stable. A stable
+ sort keeps items with the same key in the same relative order. The three
+ available algorithms have the following properties:
+
+ |------------------------------------------------------|
+ | kind | speed | worst case | work space | stable|
+ |------------------------------------------------------|
+ |'quicksort'| 1 | O(n^2) | 0 | no |
+ |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes |
+ |'heapsort' | 3 | O(n*log(n)) | 0 | no |
+ |------------------------------------------------------|
+
+ All the sort algorithms make temporary copies of the data when the sort is not
+ along the last axis. Consequently, sorts along the last axis are faster and use
+ less space than sorts along other axis.
+ """
+ if fill_value is None:
+ fill_value = default_fill_value(self)
+ d = self.filled(fill_value).view(ndarray)
+ return d.argsort(axis=axis, kind=kind, order=order)
+ #........................
+ def argmin(self, axis=None, fill_value=None):
+ """Returns a ndarray of indices for the minimum values of `a` along the
+ specified axis.
+ Masked values are treated as if they had the value `fill_value`.
+ If `fill_value` is None, the default for the data type is used.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `fill_value` : var *[None]*
+ Default filling value. If None, uses the minimum default for the data type.
+ """
+ if fill_value is None:
+ fill_value = minimum_fill_value(self)
+ d = self.filled(fill_value).view(ndarray)
+ return d.argmin(axis)
+ #........................
+ def argmax(self, axis=None, fill_value=None):
+ """Returns the array of indices for the maximum values of `a` along the
+ specified axis.
+ Masked values are treated as if they had the value `fill_value`.
+ If `fill_value` is None, the maximum default for the data type is used.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `fill_value` : var *[None]*
+ Default filling value. If None, uses the data type default.
+ """
+ if fill_value is None:
+ fill_value = maximum_fill_value(self._data)
+ d = self.filled(fill_value).view(ndarray)
+ return d.argmax(axis)
+
+ def sort(self, axis=-1, kind='quicksort', order=None,
+ endwith=True, fill_value=None):
+ """
+ Sort a along the given axis.
+
+ Keyword arguments:
+
+ axis -- axis to be sorted (default -1)
+ kind -- sorting algorithm (default 'quicksort')
+ Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+ order -- If a has fields defined, then the order keyword can be the
+ field name to sort on or a list (or tuple) of field names
+ to indicate the order that fields should be used to define
+ the sort.
+ endwith--Boolean flag indicating whether missing values (if any) should
+ be forced in the upper indices (at the end of the array) or
+ lower indices (at the beginning).
+
+ Returns: None.
+
+ This method sorts 'a' in place along the given axis using the algorithm
+ specified by the kind keyword.
+
+ The various sorts may characterized by average speed, worst case
+ performance, need for work space, and whether they are stable. A stable
+ sort keeps items with the same key in the same relative order and is most
+ useful when used with argsort where the key might differ from the items
+ being sorted. The three available algorithms have the following properties:
+
+ |------------------------------------------------------|
+ | kind | speed | worst case | work space | stable|
+ |------------------------------------------------------|
+ |'quicksort'| 1 | O(n^2) | 0 | no |
+ |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes |
+ |'heapsort' | 3 | O(n*log(n)) | 0 | no |
+ |------------------------------------------------------|
+
+ """
+ if self._mask is nomask:
+ ndarray.sort(self,axis=axis, kind=kind, order=order)
+ else:
+ if fill_value is None:
+ if endwith:
+ filler = minimum_fill_value(self)
+ else:
+ filler = maximum_fill_value(self)
+ else:
+ filler = fill_value
+ idx = numpy.indices(self.shape)
+ 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
+ self.flat = tmp_data
+ self._mask.flat = tmp_mask
+ return
+ #............................................
+ def min(self, axis=None, fill_value=None):
+ """Returns the minimum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+ mask = self._mask
+ # Check all/nothing case ......
+ if mask is nomask:
+ return super(MaskedArray, self).min(axis=axis)
+ elif (not mask.ndim) and mask:
+ return masked
+ # Get the mask ................
+ if axis is None:
+ mask = umath.logical_and.reduce(mask.flat)
+ else:
+ mask = umath.logical_and.reduce(mask, axis=axis)
+ # Get the fil value ...........
+ if fill_value is None:
+ fill_value = minimum_fill_value(self)
+ # Get the data ................
+ result = self.filled(fill_value).min(axis=axis).view(type(self))
+ if result.ndim > 0:
+ result._mask = mask
+ return result
+ #........................
+ def max(self, axis=None, fill_value=None):
+ """Returns the maximum/a along the given axis.
+If `axis` is None, applies to the flattened array. Masked values are filled
+with `fill_value` during processing. If `fill_value is None, it is set to the
+maximum_fill_value corresponding to the data type."""
+ mask = self._mask
+ # Check all/nothing case ......
+ if mask is nomask:
+ return super(MaskedArray, self).max(axis=axis)
+ elif (not mask.ndim) and mask:
+ return masked
+ # Check the mask ..............
+ if axis is None:
+ mask = umath.logical_and.reduce(mask.flat)
+ else:
+ mask = umath.logical_and.reduce(mask, axis=axis)
+ # Get the fill value ..........
+ if fill_value is None:
+ fill_value = maximum_fill_value(self)
+ # Get the data ................
+ result = self.filled(fill_value).max(axis=axis).view(type(self))
+ if result.ndim > 0:
+ result._mask = mask
+ return result
+ #........................
+ def ptp(self, axis=None, fill_value=None):
+ """Returns the visible data range (max-min) along the given axis.
+If the axis is `None`, applies on a flattened array. Masked values are filled
+with `fill_value` for processing. If `fill_value` is None, the maximum is uses
+the maximum default, the minimum uses the minimum default."""
+ return self.max(axis, fill_value) - self.min(axis, fill_value)
+
+ # Array methods ---------------------------------------
+ conj = conjugate = _arraymethod('conjugate')
+ copy = _arraymethod('copy')
+ diagonal = _arraymethod('diagonal')
+ take = _arraymethod('take')
+ ravel = _arraymethod('ravel')
+ transpose = _arraymethod('transpose')
+ T = property(fget=lambda self:self.transpose())
+ swapaxes = _arraymethod('swapaxes')
+ clip = _arraymethod('clip', onmask=False)
+ compress = _arraymethod('compress')
+ copy = _arraymethod('copy')
+ squeeze = _arraymethod('squeeze')
+ #--------------------------------------------
+ def tolist(self, fill_value=None):
+ """Copies the data portion of the array to a hierarchical python list and
+ returns that list. Data items are converted to the nearest compatible Python
+ type.
+ Masked values are converted to `fill_value`. If `fill_value` is None, the
+ corresponding entries in the output list will be None.
+ """
+ if fill_value is not None:
+ return self.filled(fill_value).tolist()
+ result = self.filled().tolist()
+ if self._mask is nomask:
+ return result
+ if self.ndim == 0:
+ return [None]
+ elif self.ndim == 1:
+ maskedidx = self._mask.nonzero()[0].tolist()
+ [operator.setitem(result,i,None) for i in maskedidx]
+ else:
+ for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
+ tmp = result
+ for i in idx[:-1]:
+ tmp = tmp[i]
+ tmp[idx[-1]] = None
+ return result
+
+
+ #........................
+ def tostring(self, fill_value=None):
+ """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string.
+
+ Keyword arguments:
+ order : order of the data item in the copy {"C","F","A"} (default "C")
+ fill_value : value used in lieu of missing data
+
+ Construct a Python string containing the raw bytes in the array. The order
+ of the data in arrays with ndim > 1 is specified by the 'order' keyword and
+ this keyword overrides the order of the array. The
+ choices are:
+
+ "C" -- C order (row major)
+ "Fortran" -- Fortran order (column major)
+ "Any" -- Current order of array.
+ None -- Same as "Any"
+
+ Masked data are filled with fill_value. If fill_value is None, the data-type-
+ dependent default is used."""
+ return self.filled(fill_value).tostring()
+ #--------------------------------------------
+ # Backwards Compatibility. Heck...
+ @property
+ def data(self):
+ """Returns the `_data` part of the MaskedArray."""
+ return self._data
+ def raw_data(self):
+ """Returns the `_data` part of the MaskedArray.
+You should really use `data` instead..."""
+ return self._data
+ #--------------------------------------------
+ # Pickling
+ def __getstate__(self):
+ "Returns the internal state of the masked array, for pickling purposes."
+ state = (1,
+ self.shape,
+ self.dtype,
+ self.flags.fnc,
+ self._data.tostring(),
+ getmaskarray(self).tostring(),
+ self._fill_value,
+ )
+ return state
+ #
+ def __setstate__(self, state):
+ """Restores the internal state of the masked array, for pickling purposes.
+ `state` is typically the output of the ``__getstate__`` output, and is a 5-tuple:
+
+ - class name
+ - a tuple giving the shape of the data
+ - a typecode for the data
+ - a binary string for the data
+ - a binary string for the mask.
+ """
+ (ver, shp, typ, isf, raw, msk, flv) = state
+ ndarray.__setstate__(self, (shp, typ, isf, raw))
+ self._mask.__setstate__((shp, dtype(bool), isf, msk))
+ self.fill_value = flv
+ #
+ def __reduce__(self):
+ """Returns a 3-tuple for pickling a MaskedArray."""
+ return (_mareconstruct,
+ (self.__class__, self._baseclass, (0,), 'b', ),
+ self.__getstate__())
+
+
+def _mareconstruct(subtype, baseclass, baseshape, basetype,):
+ """Internal function that builds a new MaskedArray from the information stored
+in a pickle."""
+ _data = ndarray.__new__(baseclass, baseshape, basetype)
+ _mask = ndarray.__new__(ndarray, baseshape, 'b1')
+ return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False)
+#MaskedArray.__dump__ = dump
+#MaskedArray.__dumps__ = dumps
+
+
+
+#####--------------------------------------------------------------------------
+#---- --- Shortcuts ---
+#####---------------------------------------------------------------------------
+def isMaskedArray(x):
+ "Is x a masked array, that is, an instance of MaskedArray?"
+ return isinstance(x, MaskedArray)
+isarray = isMaskedArray
+isMA = isMaskedArray #backward compatibility
+#masked = MaskedArray(0, int, mask=1)
+masked_singleton = MaskedArray(0, dtype=int_, mask=True)
+masked = masked_singleton
+
+masked_array = MaskedArray
+def array(data, dtype=None, copy=False, order=False, mask=nomask, subok=True,
+ keep_mask=True, small_mask=True, hard_mask=None, fill_value=None):
+ """array(data, dtype=None, copy=True, order=False, mask=nomask,
+ keep_mask=True, small_mask=True, fill_value=None)
+Acts as shortcut to MaskedArray, with options in a different order for convenience.
+And backwards compatibility...
+ """
+ #TODO: we should try to put 'order' somwehere
+ return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, subok=subok,
+ keep_mask=keep_mask, small_mask=small_mask,
+ hard_mask=hard_mask, fill_value=fill_value)
+
+def is_masked(x):
+ """Returns whether x has some masked values."""
+ m = getmask(x)
+ if m is nomask:
+ return False
+ elif m.any():
+ return True
+ return False
+
+
+#####---------------------------------------------------------------------------
+#---- --- Extrema functions ---
+#####---------------------------------------------------------------------------
+class _extrema_operation(object):
+ "Generic class for maximum/minimum functions."
+ def __call__(self, a, b=None):
+ "Executes the call behavior."
+ if b is None:
+ return self.reduce(a)
+ return where(self.compare(a, b), a, b)
+ #.........
+ def reduce(self, target, axis=None):
+ """Reduces target along the given axis."""
+ m = getmask(target)
+ if axis is not None:
+ kargs = { 'axis' : axis }
+ else:
+ kargs = {}
+ target = target.ravel()
+ if not (m is nomask):
+ m = m.ravel()
+ if m is nomask:
+ t = self.ufunc.reduce(target, **kargs)
+ else:
+ target = target.filled(self.fill_value_func(target)).view(type(target))
+ t = self.ufunc.reduce(target, **kargs)
+ m = umath.logical_and.reduce(m, **kargs)
+ if hasattr(t, '_mask'):
+ t._mask = m
+ elif m:
+ t = masked
+ return t
+ #.........
+ def outer (self, a, b):
+ "Returns the function applied to the outer product of a and b."
+ ma = getmask(a)
+ mb = getmask(b)
+ if ma is nomask and mb is nomask:
+ m = nomask
+ else:
+ ma = getmaskarray(a)
+ mb = getmaskarray(b)
+ m = logical_or.outer(ma, mb)
+ result = self.ufunc.outer(filled(a), filled(b))
+ result._mask = m
+ return result
+#............................
+class _minimum_operation(_extrema_operation):
+ "Object to calculate minima"
+ def __init__ (self):
+ """minimum(a, b) or minimum(a)
+In one argument case, returns the scalar minimum.
+ """
+ self.ufunc = umath.minimum
+ self.afunc = amin
+ self.compare = less
+ self.fill_value_func = minimum_fill_value
+#............................
+class _maximum_operation(_extrema_operation):
+ "Object to calculate maxima"
+ def __init__ (self):
+ """maximum(a, b) or maximum(a)
+ In one argument case returns the scalar maximum.
+ """
+ self.ufunc = umath.maximum
+ self.afunc = amax
+ self.compare = greater
+ self.fill_value_func = maximum_fill_value
+#..........................................................
+def min(array, axis=None, out=None):
+ """Returns the minima along the given axis.
+If `axis` is None, applies to the flattened array."""
+ if out is not None:
+ raise TypeError("Output arrays Unsupported for masked arrays")
+ if axis is None:
+ return minimum(array)
+ else:
+ return minimum.reduce(array, axis)
+#............................
+def max(obj, axis=None, out=None):
+ """Returns the maxima along the given axis.
+If `axis` is None, applies to the flattened array."""
+ if out is not None:
+ raise TypeError("Output arrays Unsupported for masked arrays")
+ if axis is None:
+ return maximum(obj)
+ else:
+ return maximum.reduce(obj, axis)
+#.............................
+def ptp(obj, axis=None):
+ """a.ptp(axis=None) = a.max(axis)-a.min(axis)"""
+ try:
+ return obj.max(axis)-obj.min(axis)
+ except AttributeError:
+ return max(obj, axis=axis) - min(obj, axis=axis)
+
+
+#####---------------------------------------------------------------------------
+#---- --- Definition of functions from the corresponding methods ---
+#####---------------------------------------------------------------------------
+class _frommethod:
+ """Defines functions from existing MaskedArray methods.
+:ivar _methodname (String): Name of the method to transform.
+ """
+ def __init__(self, methodname):
+ self._methodname = methodname
+ self.__doc__ = self.getdoc()
+ def getdoc(self):
+ "Returns the doc of the function (from the doc of the method)."
+ try:
+ return getattr(MaskedArray, self._methodname).__doc__
+ except:
+ return getattr(numpy, self._methodname).__doc__
+ def __call__(self, a, *args, **params):
+ if isinstance(a, MaskedArray):
+ return getattr(a, self._methodname).__call__(*args, **params)
+ #FIXME ----
+ #As x is not a MaskedArray, we transform it to a ndarray with asarray
+ #... and call the corresponding method.
+ #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2)))
+ #we end up with a "SystemError: NULL result without error in PyObject_Call"
+ #A dirty trick is then to call the initial numpy function...
+ method = getattr(fromnumeric.asarray(a), self._methodname)
+ try:
+ return method(*args, **params)
+ except SystemError:
+ return getattr(numpy,self._methodname).__call__(a, *args, **params)
+
+all = _frommethod('all')
+anomalies = anom = _frommethod('anom')
+any = _frommethod('any')
+conjugate = _frommethod('conjugate')
+ids = _frommethod('ids')
+nonzero = _frommethod('nonzero')
+diagonal = _frommethod('diagonal')
+maximum = _maximum_operation()
+mean = _frommethod('mean')
+minimum = _minimum_operation ()
+product = _frommethod('prod')
+ptp = _frommethod('ptp')
+ravel = _frommethod('ravel')
+repeat = _frommethod('repeat')
+std = _frommethod('std')
+sum = _frommethod('sum')
+swapaxes = _frommethod('swapaxes')
+take = _frommethod('take')
+var = _frommethod('var')
+
+#..............................................................................
+def power(a, b, third=None):
+ """Computes a**b elementwise.
+ Masked values are set to 1."""
+ if third is not None:
+ raise MAError, "3-argument power not supported."
+ ma = getmask(a)
+ mb = getmask(b)
+ m = mask_or(ma, mb)
+ fa = filled(a, 1)
+ fb = filled(b, 1)
+ if fb.dtype.char in typecodes["Integer"]:
+ return masked_array(umath.power(fa, fb), m)
+ md = make_mask((fa < 0), small_mask=1)
+ m = mask_or(m, md)
+ if m is nomask:
+ return masked_array(umath.power(fa, fb))
+ else:
+ fa[m] = 1
+ return masked_array(umath.power(fa, fb), m)
+
+#..............................................................................
+def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None):
+ """Returns an array of indices that sort 'a' along the specified axis.
+ Masked values are filled beforehand to `fill_value`.
+ If `fill_value` is None, uses the default for the data type.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `kind` : String *['quicksort']*
+ Sorting algorithm (default 'quicksort')
+ Possible values: 'quicksort', 'mergesort', or 'heapsort'
+
+ Returns: array of indices that sort 'a' along the specified axis.
+
+ This method executes an indirect sort along the given axis using the
+ algorithm specified by the kind keyword. It returns an array of indices of
+ the same shape as 'a' that index data along the given axis in sorted order.
+
+ The various sorts are characterized by average speed, worst case
+ performance, need for work space, and whether they are stable. A stable
+ sort keeps items with the same key in the same relative order. The three
+ available algorithms have the following properties:
+
+ |------------------------------------------------------|
+ | kind | speed | worst case | work space | stable|
+ |------------------------------------------------------|
+ |'quicksort'| 1 | O(n^2) | 0 | no |
+ |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes |
+ |'heapsort' | 3 | O(n*log(n)) | 0 | no |
+ |------------------------------------------------------|
+
+ All the sort algorithms make temporary copies of the data when the sort is not
+ along the last axis. Consequently, sorts along the last axis are faster and use
+ less space than sorts along other axis.
+ """
+ if fill_value is None:
+ fill_value = default_fill_value(a)
+ d = filled(a, fill_value)
+ if axis is None:
+ return d.argsort(kind=kind, order=order)
+ return d.argsort(axis, kind=kind, order=order)
+
+def argmin(a, axis=None, fill_value=None):
+ """Returns the array of indices for the minimum values of `a` along the
+ specified axis.
+ Masked values are treated as if they had the value `fill_value`.
+ If `fill_value` is None, the default for the data type is used.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `fill_value` : var *[None]*
+ Default filling value. If None, uses the data type default.
+ """
+ if fill_value is None:
+ fill_value = default_fill_value(a)
+ d = filled(a, fill_value)
+ return d.argmin(axis=axis)
+
+def argmax(a, axis=None, fill_value=None):
+ """Returns the array of indices for the maximum values of `a` along the
+ specified axis.
+ Masked values are treated as if they had the value `fill_value`.
+ If `fill_value` is None, the default for the data type is used.
+ Returns a numpy array.
+
+:Keywords:
+ `axis` : Integer *[None]*
+ Axis to be indirectly sorted (default -1)
+ `fill_value` : var *[None]*
+ Default filling value. If None, uses the data type default.
+ """
+ if fill_value is None:
+ fill_value = default_fill_value(a)
+ try:
+ fill_value = - fill_value
+ except:
+ pass
+ d = filled(a, fill_value)
+ return d.argmax(axis=axis)
+
+def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None):
+ """
+ Sort a along the given axis.
+
+Keyword arguments:
+
+axis -- axis to be sorted (default -1)
+kind -- sorting algorithm (default 'quicksort')
+ Possible values: 'quicksort', 'mergesort', or 'heapsort'.
+order -- If a has fields defined, then the order keyword can be the
+ field name to sort on or a list (or tuple) of field names
+ to indicate the order that fields should be used to define
+ the sort.
+endwith--Boolean flag indicating whether missing values (if any) should
+ be forced in the upper indices (at the end of the array) or
+ lower indices (at the beginning).
+
+Returns: None.
+
+This method sorts 'a' in place along the given axis using the algorithm
+specified by the kind keyword.
+
+The various sorts may characterized by average speed, worst case
+performance, need for work space, and whether they are stable. A stable
+sort keeps items with the same key in the same relative order and is most
+useful when used with argsort where the key might differ from the items
+being sorted. The three available algorithms have the following properties:
+
+|------------------------------------------------------|
+| kind | speed | worst case | work space | stable|
+|------------------------------------------------------|
+|'quicksort'| 1 | O(n^2) | 0 | no |
+|'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes |
+|'heapsort' | 3 | O(n*log(n)) | 0 | no |
+|------------------------------------------------------|
+
+All the sort algorithms make temporary copies of the data when the sort is
+not along the last axis. Consequently, sorts along the last axis are faster
+and use less space than sorts along other axis.
+
+"""
+ a = numeric.asanyarray(a)
+ if fill_value is None:
+ if endwith:
+ filler = minimum_fill_value(a)
+ else:
+ filler = maximum_fill_value(a)
+ else:
+ filler = fill_value
+# return
+ indx = numpy.indices(a.shape).tolist()
+ indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+ return a[indx]
+
+def compressed(x):
+ """Returns a compressed version of a masked array (or just the array if it
+ wasn't masked first)."""
+ if getmask(x) is None:
+ return x
+ else:
+ return x.compressed()
+
+def count(a, axis = None):
+ "Count of the non-masked elements in a, or along a certain axis."
+ a = masked_array(a)
+ return a.count(axis)
+
+def concatenate(arrays, axis=0):
+ "Concatenates the arrays along the given axis"
+ d = numeric.concatenate([filled(a) for a in arrays], axis)
+ rcls = get_masked_subclass(*arrays)
+ data = d.view(rcls)
+ for x in arrays:
+ if getmask(x) is not nomask:
+ break
+ else:
+ return data
+ dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis)
+ dm = make_mask(dm, copy=False, small_mask=True)
+ data._mask = dm
+ return data
+
+def expand_dims(x,axis):
+ """Expand the shape of a by including newaxis before given axis."""
+ result = n_expand_dims(x,axis)
+ if isinstance(x, MaskedArray):
+ new_shape = result.shape
+ result = x.view()
+ result.shape = new_shape
+ if result._mask is not nomask:
+ result._mask.shape = new_shape
+ return result
+
+#......................................
+def left_shift (a, n):
+ "Left shift n bits"
+ m = getmask(a)
+ if m is nomask:
+ d = umath.left_shift(filled(a), n)
+ return masked_array(d)
+ else:
+ d = umath.left_shift(filled(a, 0), n)
+ return masked_array(d, mask=m)
+
+def right_shift (a, n):
+ "Right shift n bits"
+ m = getmask(a)
+ if m is nomask:
+ d = umath.right_shift(filled(a), n)
+ return masked_array(d)
+ else:
+ d = umath.right_shift(filled(a, 0), n)
+ return masked_array(d, mask=m)
+#......................................
+def put(a, indices, values, mode='raise'):
+ """Sets storage-indexed locations to corresponding values.
+ Values and indices are filled if necessary."""
+ # We can't use 'frommethod', the order of arguments is different
+ try:
+ return a.put(indices, values, mode=mode)
+ except AttributeError:
+ return fromnumeric.asarray(a).put(indices, values, mode=mode)
+
+def putmask(a, mask, values): #, mode='raise'):
+ """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true.
+If `v` is shorter than `mask`, it will be repeated as necessary.
+In particular `v` can be a scalar or length 1 array."""
+ # We can't use 'frommethod', the order of arguments is different
+ try:
+ return a.putmask(values, mask)
+ except AttributeError:
+ return fromnumeric.asarray(a).putmask(values, mask)
+
+def transpose(a,axes=None):
+ """Returns a view of the array with dimensions permuted according to axes.
+If `axes` is None (default), returns array with dimensions reversed.
+ """
+ #We can't use 'frommethod', as 'transpose' doesn't take keywords
+ try:
+ return a.transpose(axes)
+ except AttributeError:
+ return fromnumeric.asarray(a).transpose(axes)
+
+def reshape(a, new_shape):
+ """Changes the shape of the array `a` to `new_shape`."""
+ #We can't use 'frommethod', it whine about some parameters. Dmmit.
+ try:
+ return a.reshape(new_shape)
+ except AttributeError:
+ return fromnumeric.asarray(a).reshape(new_shape)
+
+def resize(x, new_shape):
+ """resize(a,new_shape) returns a new array with the specified shape.
+ The total size of the original array can be any size.
+ The new array is filled with repeated copies of a. If a was masked, the new
+ array will be masked, and the new mask will be a repetition of the old one.
+ """
+ # We can't use _frommethods here, as N.resize is notoriously whiny.
+ m = getmask(x)
+ if m is not nomask:
+ m = fromnumeric.resize(m, new_shape)
+ result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x))
+ if result.ndim:
+ result._mask = m
+ return result
+
+
+#................................................
+def rank(obj):
+ """Gets the rank of sequence a (the number of dimensions, not a matrix rank)
+The rank of a scalar is zero."""
+ return fromnumeric.rank(filled(obj))
+#
+def shape(obj):
+ """Returns the shape of `a` (as a function call which also works on nested sequences).
+ """
+ return fromnumeric.shape(filled(obj))
+#
+def size(obj, axis=None):
+ """Returns the number of elements in the array along the given axis,
+or in the sequence if `axis` is None.
+ """
+ return fromnumeric.size(filled(obj), axis)
+#................................................
+
+#####--------------------------------------------------------------------------
+#---- --- Extra functions ---
+#####--------------------------------------------------------------------------
+def where (condition, x, y):
+ """where(condition, x, y) is x where condition is nonzero, y otherwise.
+ condition must be convertible to an integer array.
+ Answer is always the shape of condition.
+ The type depends on x and y. It is integer if both x and y are
+ the value masked.
+ """
+ fc = filled(not_equal(condition, 0), 0)
+ xv = filled(x)
+ xm = getmask(x)
+ yv = filled(y)
+ ym = getmask(y)
+ d = numeric.choose(fc, (yv, xv))
+ md = numeric.choose(fc, (ym, xm))
+ m = getmask(condition)
+ m = make_mask(mask_or(m, md), copy=False, small_mask=True)
+ return masked_array(d, mask=m)
+
+def choose (indices, t, out=None, mode='raise'):
+ "Returns array shaped like indices with elements chosen from t"
+ #TODO: implement options `out` and `mode`, if possible.
+ def fmask (x):
+ "Returns the filled array, or True if ``masked``."
+ if x is masked:
+ return 1
+ return filled(x)
+ def nmask (x):
+ "Returns the mask, True if ``masked``, False if ``nomask``."
+ if x is masked:
+ return 1
+ m = getmask(x)
+ if m is nomask:
+ return 0
+ return m
+ c = filled(indices, 0)
+ masks = [nmask(x) for x in t]
+ a = [fmask(x) for x in t]
+ d = numeric.choose(c, a)
+ m = numeric.choose(c, masks)
+ m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1)
+ return masked_array(d, mask=m)
+
+def round_(a, decimals=0, out=None):
+ """Returns reference to result. Copies a and rounds to 'decimals' places.
+
+ Keyword arguments:
+ decimals -- number of decimals to round to (default 0). May be negative.
+ out -- existing array to use for output (default copy of a).
+
+ Return:
+ Reference to out, where None specifies a copy of the original array a.
+
+ Round to the specified number of decimals. When 'decimals' is negative it
+ specifies the number of positions to the left of the decimal point. The
+ real and imaginary parts of complex numbers are rounded separately.
+ Nothing is done if the array is not of float type and 'decimals' is greater
+ than or equal to 0."""
+ result = fromnumeric.round_(filled(a), decimals, out)
+ if isinstance(a,MaskedArray):
+ result = result.view(type(a))
+ result._mask = a._mask
+ else:
+ result = result.view(MaskedArray)
+ return result
+
+def arange(start, stop=None, step=1, dtype=None):
+ """Just like range() except it returns a array whose type can be specified
+ by the keyword argument dtype.
+ """
+ return array(numeric.arange(start, stop, step, dtype),mask=nomask)
+
+def inner(a, b):
+ """inner(a,b) returns the dot product of two arrays, which has
+ shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the
+ product of the elements from the last dimensions of a and b.
+ Masked elements are replace by zeros.
+ """
+ fa = filled(a, 0)
+ fb = filled(b, 0)
+ if len(fa.shape) == 0:
+ fa.shape = (1,)
+ if len(fb.shape) == 0:
+ fb.shape = (1,)
+ return masked_array(numeric.inner(fa, fb))
+innerproduct = inner
+
+def outer(a, b):
+ """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))"""
+ fa = filled(a, 0).ravel()
+ fb = filled(b, 0).ravel()
+ d = numeric.outer(fa, fb)
+ ma = getmask(a)
+ mb = getmask(b)
+ if ma is nomask and mb is nomask:
+ return masked_array(d)
+ ma = getmaskarray(a)
+ mb = getmaskarray(b)
+ m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0)
+ return masked_array(d, mask=m)
+outerproduct = outer
+
+def allequal (a, b, fill_value=True):
+ """
+Returns `True` if all entries of a and b are equal, using
+fill_value as a truth value where either or both are masked.
+ """
+ m = mask_or(getmask(a), getmask(b))
+ if m is nomask:
+ x = filled(a)
+ y = filled(b)
+ d = umath.equal(x, y)
+ return d.all()
+ elif fill_value:
+ x = filled(a)
+ y = filled(b)
+ d = umath.equal(x, y)
+ dm = array(d, mask=m, copy=False)
+ return dm.filled(True).all(None)
+ else:
+ return False
+
+def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
+ """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances.
+If `fill_value` is True, masked values are considered equal.
+If `fill_value` is False, masked values considered unequal.
+The relative error rtol should be positive and << 1.0
+The absolute error `atol` comes into play for those elements of `b`
+ that are very small or zero; it says how small `a` must be also.
+ """
+ m = mask_or(getmask(a), getmask(b))
+ d1 = filled(a)
+ d2 = filled(b)
+ x = filled(array(d1, copy=0, mask=m), fill_value).astype(float)
+ y = filled(array(d2, copy=0, mask=m), 1).astype(float)
+ d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y))
+ return fromnumeric.alltrue(fromnumeric.ravel(d))
+
+#..............................................................................
+def asarray(a, dtype=None):
+ """asarray(data, dtype) = array(data, dtype, copy=0)
+Returns `a` as an masked array.
+No copy is performed if `a` is already an array.
+Subclasses are converted to base class MaskedArray.
+ """
+ return masked_array(a, dtype=dtype, copy=False, keep_mask=True)
+
+def empty(new_shape, dtype=float):
+ """empty((d1,...,dn),dtype=float,order='C')
+Returns a new array of shape (d1,...,dn) and given type with all its
+entries uninitialized. This can be faster than zeros."""
+ return numeric.empty(new_shape, dtype).view(MaskedArray)
+
+def empty_like(a):
+ """empty_like(a)
+Returns an empty (uninitialized) array of the shape and typecode of a.
+Note that this does NOT initialize the returned array.
+If you require your array to be initialized, you should use zeros_like()."""
+ return numeric.empty_like(a).view(MaskedArray)
+
+def ones(new_shape, dtype=float):
+ """ones(shape, dtype=None)
+Returns an array of the given dimensions, initialized to all ones."""
+ return numeric.ones(new_shape, dtype).view(MaskedArray)
+
+def zeros(new_shape, dtype=float):
+ """zeros(new_shape, dtype=None)
+Returns an array of the given dimensions, initialized to all zeros."""
+ return numeric.zeros(new_shape, dtype).view(MaskedArray)
+
+#####--------------------------------------------------------------------------
+#---- --- Pickling ---
+#####--------------------------------------------------------------------------
+def dump(a,F):
+ """Pickles the MaskedArray `a` to the file `F`.
+`F` can either be the handle of an exiting file, or a string representing a file name.
+ """
+ if not hasattr(F,'readline'):
+ F = open(F,'w')
+ return cPickle.dump(a,F)
+
+def dumps(a):
+ """Returns a string corresponding to the pickling of the MaskedArray."""
+ return cPickle.dumps(a)
+
+def load(F):
+ """Wrapper around ``cPickle.load`` which accepts either a file-like object or
+ a filename."""
+ if not hasattr(F, 'readline'):
+ F = open(F,'r')
+ return cPickle.load(F)
+
+def loads(strg):
+ "Loads a pickle from the current string."""
+ return cPickle.loads(strg)
+
+
+################################################################################
+
+if __name__ == '__main__':
+ from testutils import assert_equal, assert_almost_equal
+ if 1:
+ x = arange(10)
+ assert(x.ctypes.data == x.filled().ctypes.data)
+ if 0:
+ a = array([1,2,3,4],mask=[0,0,0,0],small_mask=True)
+ a[1] = masked
+ a[1] = 1
+ assert(a.ravel()._mask, [0,0,0,0])
+ assert(a.compressed(), a)
+ a[0] = masked
+ assert(a.compressed()._mask, [0,0,0])
+ if 0:
+ x = array(0, mask=0)
+ I = x.ctypes.data
+ J = x.filled().ctypes.data
+ print (I,J)
+ x = array([0,0], mask=0)
+ (I,J) = (x.ctypes.data, x.filled().ctypes.data)
+ print (I,J)
+ if 1:
+ x = array(numpy.arange(12))
+ x[[1,-2]] = masked
+ xlist = x.tolist()
+ assert(xlist[1] is None)
+ assert(xlist[-2] is None)
+ #
+ x.shape = (3,4)
+ xlist = x.tolist()
+ #
+ assert_equal(xlist[0],[0,None,2,3])
+ assert_equal(xlist[1],[4,5,6,7])
+ assert_equal(xlist[2],[8,9,None,11])
+
+
+
\ No newline at end of file
Modified: trunk/Lib/sandbox/maskedarray/mrecords.py
===================================================================
--- trunk/Lib/sandbox/maskedarray/mrecords.py 2007-08-15 06:04:30 UTC (rev 3244)
+++ trunk/Lib/sandbox/maskedarray/mrecords.py 2007-08-15 13:38:19 UTC (rev 3245)
@@ -117,4 +117,615 @@
_names = descr.names
mdescr = [(n,'|b1') for n in _names]
# get the shape .........................
-
\ No newline at end of file
+ try:
+ shape = numeric.asarray(data[0]).shape
+ except IndexError:
+ shape = len(data.dtype)
+ if isinstance(shape, int):
+ shape = (shape,)
+ # Construct the _data recarray ..........
+ if isinstance(data, record):
+ _data = numeric.asarray(data).view(recarray)
+ _fieldmask = mask
+ elif isinstance(data, MaskedRecords):
+ _data = data._data
+ _fieldmask = data._fieldmask
+ elif isinstance(data, recarray):
+ _data = data
+ if mask is nomask:
+ _fieldmask = data.astype(mdescr)
+ _fieldmask.flat = tuple([False]*len(mdescr))
+ else:
+ _fieldmask = mask
+ elif (isinstance(data, (tuple, numpy.void)) or\
+ hasattr(data,'__len__') and isinstance(data[0], (tuple, numpy.void))):
+ data = numeric.array(data, dtype=descr).view(recarray)
+ _data = data
+ if mask is nomask:
+ _fieldmask = data.astype(mdescr)
+ _fieldmask.flat = tuple([False]*len(mdescr))
+ else:
+ _fieldmask = mask
+ else:
+ _data = recarray(shape, dtype=descr)
+ _fieldmask = recarray(shape, dtype=mdescr)
+ for (n,v) in zip(_names, data):
+ _data[n] = numeric.asarray(v).view(ndarray)
+ _fieldmask[n] = getmaskarray(v)
+ #........................................
+ _data = _data.view(cls)
+ _data._fieldmask = _fieldmask
+ _data._hardmask = hard_mask
+ if fill_value is None:
+ _data._fill_value = [default_fill_value(numeric.dtype(d[1]))
+ for d in descr.descr]
+ else:
+ _data._fill_value = fill_value
+ return _data
+
+ def __array_finalize__(self,obj):
+ if isinstance(obj, MaskedRecords):
+ self.__dict__.update(_fieldmask=obj._fieldmask,
+ _hardmask=obj._hardmask,
+ _fill_value=obj._fill_value
+ )
+ else:
+ self.__dict__.update(_fieldmask = nomask,
+ _hardmask = False,
+ fill_value = None
+ )
+ return
+
+ def _getdata(self):
+ "Returns the data as a recarray."
+ return self.view(recarray)
+ _data = property(fget=_getdata)
+
+ #......................................................
+ def __getattribute__(self, attr):
+ try:
+ # Returns a generic attribute
+ return object.__getattribute__(self,attr)
+ except AttributeError:
+ # OK, so attr must be a field name
+ pass
+ # Get the list of fields ......
+ _names = self.dtype.names
+ if attr in _names:
+ _data = self._data
+ _mask = self._fieldmask
+ obj = numeric.asarray(_data.__getattribute__(attr)).view(MaskedArray)
+ obj.__setmask__(_mask.__getattribute__(attr))
+ if (obj.ndim == 0) and obj._mask:
+ return masked
+ return obj
+ raise AttributeError,"No attribute '%s' !" % attr
+
+ def __setattr__(self, attr, val):
+ newattr = attr not in self.__dict__
+ try:
+ # Is attr a generic attribute ?
+ ret = object.__setattr__(self, attr, val)
+ except:
+ # Not a generic attribute: exit if it's not a valid field
+ fielddict = self.dtype.names or {}
+ if attr not in fielddict:
+ exctype, value = sys.exc_info()[:2]
+ raise exctype, value
+ else:
+ if attr not in list(self.dtype.names) + ['_mask']:
+ return ret
+ if newattr: # We just added this one
+ try: # or this setattr worked on an internal
+ # attribute.
+ object.__delattr__(self, attr)
+ except:
+ return ret
+ # Case #1.: Basic field ............
+ base_fmask = self._fieldmask
+ _names = self.dtype.names
+ if attr in _names:
+ fval = filled(val)
+ mval = getmaskarray(val)
+ if self._hardmask:
+ mval = mask_or(mval, base_fmask.__getattr__(attr))
+ self._data.__setattr__(attr, fval)
+ base_fmask.__setattr__(attr, mval)
+ return
+ elif attr == '_mask':
+ self.__setmask__(val)
+ return
+ #............................................
+ def __getitem__(self, indx):
+ """Returns all the fields sharing the same fieldname base.
+ The fieldname base is either `_data` or `_mask`."""
+ _localdict = self.__dict__
+ _data = self._data
+ # We want a field ........
+ if isinstance(indx, str):
+ obj = _data[indx].view(MaskedArray)
+ obj._set_mask(_localdict['_fieldmask'][indx])
+ return obj
+ # We want some elements ..
+ # First, the data ........
+ obj = ndarray.__getitem__(self, indx)
+ if isinstance(obj, numpy.void):
+ obj = self.__class__(obj, dtype=self.dtype)
+ else:
+ obj = obj.view(type(self))
+ obj._fieldmask = numpy.asarray(_localdict['_fieldmask'][indx]).view(recarray)
+ return obj
+ #............................................
+ def __setitem__(self, indx, value):
+ """Sets the given record to value."""
+ MaskedArray.__setitem__(self, indx, value)
+
+# def __getslice__(self, i, j):
+# """Returns the slice described by [i,j]."""
+# _localdict = self.__dict__
+# return MaskedRecords(_localdict['_data'][i:j],
+# mask=_localdict['_fieldmask'][i:j],
+# dtype=self.dtype)
+#
+ def __setslice__(self, i, j, value):
+ """Sets the slice described by [i,j] to `value`."""
+ _localdict = self.__dict__
+ d = self._data
+ m = _localdict['_fieldmask']
+ names = self.dtype.names
+ if value is masked:
+ for n in names:
+ m[i:j][n] = True
+ elif not self._hardmask:
+ fval = filled(value)
+ mval = getmaskarray(value)
+ for n in names:
+ d[n][i:j] = fval
+ m[n][i:j] = mval
+ else:
+ mindx = getmaskarray(self)[i:j]
+ dval = numeric.asarray(value)
+ valmask = getmask(value)
+ if valmask is nomask:
+ for n in names:
+ mval = mask_or(m[n][i:j], valmask)
+ d[n][i:j][~mval] = value
+ elif valmask.size > 1:
+ for n in names:
+ mval = mask_or(m[n][i:j], valmask)
+ d[n][i:j][~mval] = dval[~mval]
+ m[n][i:j] = mask_or(m[n][i:j], mval)
+ self._fieldmask = m
+
+ #.....................................................
+ def __setmask__(self, mask):
+ names = self.dtype.names
+ fmask = self.__dict__['_fieldmask']
+ newmask = make_mask(mask, copy=False)
+# self.unshare_mask()
+ if self._hardmask:
+ for n in names:
+ fmask[n].__ior__(newmask)
+ else:
+ for n in names:
+ fmask[n].flat = newmask
+
+ def _getmask(self):
+ """Returns the mask of the mrecord: a record is masked when all the fields
+are masked."""
+ if self.size > 1:
+ return self._fieldmask.view((bool_, len(self.dtype))).all(1)
+
+ _setmask = __setmask__
+ _mask = property(fget=_getmask, fset=_setmask)
+
+ #......................................................
+ def __str__(self):
+ """x.__str__() <==> str(x)
+Calculates the string representation, using masked for fill if it is enabled.
+Otherwise, fills with fill value.
+ """
+ if self.size > 1:
+ mstr = ["(%s)" % ",".join([str(i) for i in s])
+ for s in zip(*[getattr(self,f) for f in self.dtype.names])]
+ return "[%s]" % ", ".join(mstr)
+ else:
+ mstr = numeric.asarray(self._data.item(), dtype=object_)
+ mstr[list(self._fieldmask)] = masked_print_option
+ return str(mstr)
+
+ def __repr__(self):
+ """x.__repr__() <==> repr(x)
+Calculates the repr representation, using masked for fill if it is enabled.
+Otherwise fill with fill value.
+ """
+ _names = self.dtype.names
+ fmt = "%%%is : %%s" % (max([len(n) for n in _names])+4,)
+ reprstr = [fmt % (f,getattr(self,f)) for f in self.dtype.names]
+ reprstr.insert(0,'masked_records(')
+ reprstr.extend([fmt % (' fill_value', self._fill_value),
+ ' )'])
+ return str("\n".join(reprstr))
+ #......................................................
+ def view(self, obj):
+ """Returns a view of the mrecarray."""
+ try:
+ if issubclass(obj, ndarray):
+ return ndarray.view(self, obj)
+ except TypeError:
+ pass
+ dtype = numeric.dtype(obj)
+ if dtype.fields is None:
+ return self.__array__().view(dtype)
+ return ndarray.view(self, obj)
+ #......................................................
+ def filled(self, fill_value=None):
+ """Returns an array of the same class as `_data`,
+ with masked values filled with `fill_value`.
+Subclassing is preserved.
+
+If `fill_value` is None, uses self.fill_value.
+ """
+ _localdict = self.__dict__
+ d = self._data
+ fm = _localdict['_fieldmask']
+ if not numeric.asarray(fm, dtype=bool_).any():
+ return d
+ #
+ if fill_value is None:
+ value = _localdict['_fill_value']
+ else:
+ value = fill_value
+ if numeric.size(value) == 1:
+ value = [value,] * len(self.dtype)
+ #
+ if self is masked:
+ result = numeric.asanyarray(value)
+ else:
+ result = d.copy()
+ for (n, v) in zip(d.dtype.names, value):
+ numpy.putmask(numeric.asarray(result[n]),
+ numeric.asarray(fm[n]), v)
+ return result
+ #............................................
+ def harden_mask(self):
+ "Forces the mask to hard"
+ self._hardmask = True
+ def soften_mask(self):
+ "Forces the mask to soft"
+ self._hardmask = False
+ #.............................................
+ def copy(self):
+ """Returns a copy of the masked record."""
+ _localdict = self.__dict__
+ return MaskedRecords(self._data.copy(),
+ mask=_localdict['_fieldmask'].copy(),
+ dtype=self.dtype)
+ #.............................................
+
+
+#####---------------------------------------------------------------------------
+#---- --- Constructors ---
+#####---------------------------------------------------------------------------
+
+def fromarrays(arraylist, dtype=None, shape=None, formats=None,
+ names=None, titles=None, aligned=False, byteorder=None):
+ """Creates a mrecarray from a (flat) list of masked arrays.
+
+:Parameters:
+ - `arraylist` : Sequence
+ A list of (masked) arrays. Each element of the sequence is first converted
+ to a masked array if needed. If a 2D array is passed as argument, it is
+ processed line by line
+ - `dtype` : numeric.dtype
+ Data type descriptor.
+ - `shape` : Integer *[None]*
+ Number of records. If None, `shape` is defined from the shape of the first
+ array in the list.
+ - `formats` :
+ (Description to write)
+ - `names` :
+ (description to write)
+ - `titles`:
+ (Description to write)
+ - `aligned`: Boolen *[False]*
+ (Description to write, not used anyway)
+ - `byteorder`: Boolen *[None]*
+ (Description to write, not used anyway)
+
+
+ """
+ arraylist = [MA.asarray(x) for x in arraylist]
+ # Define/check the shape.....................
+ if shape is None or shape == 0:
+ shape = arraylist[0].shape
+ if isinstance(shape, int):
+ shape = (shape,)
+ # Define formats from scratch ...............
+ if formats is None and dtype is None:
+ formats = _getformats(arraylist)
+ # Define the dtype ..........................
+ if dtype is not None:
+ descr = numeric.dtype(dtype)
+ _names = descr.names
+ else:
+ parsed = format_parser(formats, names, titles, aligned, byteorder)
+ _names = parsed._names
+ descr = parsed._descr
+ # Determine shape from data-type.............
+ if len(descr) != len(arraylist):
+ msg = "Mismatch between the number of fields (%i) and the number of "\
+ "arrays (%i)"
+ raise ValueError, msg % (len(descr), len(arraylist))
+ d0 = descr[0].shape
+ nn = len(d0)
+ if nn > 0:
+ shape = shape[:-nn]
+ # Make sure the shape is the correct one ....
+ for k, obj in enumerate(arraylist):
+ nn = len(descr[k].shape)
+ testshape = obj.shape[:len(obj.shape)-nn]
+ if testshape != shape:
+ raise ValueError, "Array-shape mismatch in array %d" % k
+ # Reconstruct the descriptor, by creating a _data and _mask version
+ return MaskedRecords(arraylist, dtype=descr)
+#..............................................................................
+def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None,
+ titles=None, aligned=False, byteorder=None):
+ """Creates a MaskedRecords from a list of records.
+
+ The data in the same field can be heterogeneous, they will be promoted
+ to the highest data type. This method is intended for creating
+ smaller record arrays. If used to create large array without formats
+ defined, it can be slow.
+
+ If formats is None, then this will auto-detect formats. Use a list of
+ tuples rather than a list of lists for faster processing.
+ """
+ # reclist is in fact a mrecarray .................
+ if isinstance(reclist, MaskedRecords):
+ mdescr = reclist.dtype
+ shape = reclist.shape
+ return MaskedRecords(reclist, dtype=mdescr)
+ # No format, no dtype: create from to arrays .....
+ nfields = len(reclist[0])
+ if formats is None and dtype is None: # slower
+ if isinstance(reclist, recarray):
+ arrlist = [reclist.field(i) for i in range(len(reclist.dtype))]
+ if names is None:
+ names = reclist.dtype.names
+ else:
+ obj = numeric.array(reclist,dtype=object)
+ arrlist = [numeric.array(obj[...,i].tolist())
+ for i in xrange(nfields)]
+ return MaskedRecords(arrlist, formats=formats, names=names,
+ titles=titles, aligned=aligned, byteorder=byteorder)
+ # Construct the descriptor .......................
+ if dtype is not None:
+ descr = numeric.dtype(dtype)
+ _names = descr.names
+ else:
+ parsed = format_parser(formats, names, titles, aligned, byteorder)
+ _names = parsed._names
+ descr = parsed._descr
+
+ try:
+ retval = numeric.array(reclist, dtype = descr).view(recarray)
+ except TypeError: # list of lists instead of list of tuples
+ if (shape is None or shape == 0):
+ shape = len(reclist)*2
+ if isinstance(shape, (int, long)):
+ shape = (shape*2,)
+ if len(shape) > 1:
+ raise ValueError, "Can only deal with 1-d array."
+ retval = recarray(shape, mdescr)
+ for k in xrange(retval.size):
+ retval[k] = tuple(reclist[k])
+ return MaskedRecords(retval, dtype=descr)
+ else:
+ if shape is not None and retval.shape != shape:
+ retval.shape = shape
+ #
+ return MaskedRecords(retval, dtype=descr)
+
+def _guessvartypes(arr):
+ """Tries to guess the dtypes of the str_ ndarray `arr`, by testing element-wise
+ conversion. Returns a list of dtypes.
+ The array is first converted to ndarray. If the array is 2D, the test is
+ performed on the first line. An exception is raised if the file is 3D or more.
+ """
+ vartypes = []
+ arr = numeric.asarray(arr)
+ if len(arr.shape) == 2 :
+ arr = arr[0]
+ elif len(arr.shape) > 2:
+ raise ValueError, "The array should be 2D at most!"
+ # Start the conversion loop .......
+ for f in arr:
+ try:
+ val = int(f)
+ except ValueError:
+ try:
+ val = float(f)
+ except ValueError:
+ try:
+ val = complex(f)
+ except ValueError:
+ vartypes.append(arr.dtype)
+ else:
+ vartypes.append(complex_)
+ else:
+ vartypes.append(float_)
+ else:
+ vartypes.append(int_)
+ return vartypes
+
+def openfile(fname):
+ "Opens the file handle of file `fname`"
+ # A file handle ...................
+ if hasattr(fname, 'readline'):
+ return fname
+ # Try to open the file and guess its type
+ try:
+ f = open(fname)
+ except IOError:
+ raise IOError, "No such file: '%s'" % fname
+ if f.readline()[:2] != "\\x":
+ f.seek(0,0)
+ return f
+ raise NotImplementedError, "Wow, binary file"
+
+
+def fromtextfile(fname, delimitor=None, commentchar='#', missingchar='',
+ varnames=None, vartypes=None):
+ """Creates a mrecarray from data stored in the file `filename`.
+
+:Parameters:
+ - `filename` : file name/handle
+ Handle of an opened file.
+ - `delimitor` : Character *None*
+ Alphanumeric character used to separate columns in the file.
+ If None, any (group of) white spacestring(s) will be used.
+ - `commentchar` : String *['#']*
+ Alphanumeric character used to mark the start of a comment.
+ - `missingchar` : String *['']*
+ String indicating missing data, and used to create the masks.
+ - `varnames` : Sequence *[None]*
+ Sequence of the variable names. If None, a list will be created from
+ the first non empty line of the file.
+ - `vartypes` : Sequence *[None]*
+ Sequence of the variables dtypes. If None, the sequence will be estimated
+ from the first non-commented line.
+
+
+ Ultra simple: the varnames are in the header, one line"""
+ # Try to open the file ......................
+ f = openfile(fname)
+ # Get the first non-empty line as the varnames
+ while True:
+ line = f.readline()
+ firstline = line[:line.find(commentchar)].strip()
+ _varnames = firstline.split(delimitor)
+ if len(_varnames) > 1:
+ break
+ if varnames is None:
+ varnames = _varnames
+ # Get the data ..............................
+ _variables = MA.asarray([line.strip().split(delimitor) for line in f
+ if line[0] != commentchar and len(line) > 1])
+ (_, nfields) = _variables.shape
+ # Try to guess the dtype ....................
+ if vartypes is None:
+ vartypes = _guessvartypes(_variables[0])
+ else:
+ vartypes = [numeric.dtype(v) for v in vartypes]
+ if len(vartypes) != nfields:
+ msg = "Attempting to %i dtypes for %i fields!"
+ msg += " Reverting to default."
+ warnings.warn(msg % (len(vartypes), nfields))
+ vartypes = _guessvartypes(_variables[0])
+ # Construct the descriptor ..................
+ mdescr = [(n,f) for (n,f) in zip(varnames, vartypes)]
+ # Get the data and the mask .................
+ # We just need a list of masked_arrays. It's easier to create it like that:
+ _mask = (_variables.T == missingchar)
+ _datalist = [masked_array(a,mask=m,dtype=t)
+ for (a,m,t) in zip(_variables.T, _mask, vartypes)]
+ return MaskedRecords(_datalist, dtype=mdescr)
+
+#....................................................................
+def addfield(mrecord, newfield, newfieldname=None):
+ """Adds a new field to the masked record array, using `newfield` as data
+and `newfieldname` as name. If `newfieldname` is None, the new field name is
+set to 'fi', where `i` is the number of existing fields.
+ """
+ _data = mrecord._data
+ _mask = mrecord._fieldmask
+ if newfieldname is None or newfieldname in reserved_fields:
+ newfieldname = 'f%i' % len(_data.dtype)
+ newfield = MA.asarray(newfield)
+ # Get the new data ............
+ # Create a new empty recarray
+ newdtype = numeric.dtype(_data.dtype.descr + \
+ [(newfieldname, newfield.dtype)])
+ newdata = recarray(_data.shape, newdtype)
+ # Add the exisintg field
+ [newdata.setfield(_data.getfield(*f),*f)
+ for f in _data.dtype.fields.values()]
+ # Add the new field
+ newdata.setfield(newfield._data, *newdata.dtype.fields[newfieldname])
+ newdata = newdata.view(MaskedRecords)
+ # Get the new mask .............
+ # Create a new empty recarray
+ newmdtype = numeric.dtype([(n,bool_) for n in newdtype.names])
+ newmask = recarray(_data.shape, newmdtype)
+ # Add the old masks
+ [newmask.setfield(_mask.getfield(*f),*f)
+ for f in _mask.dtype.fields.values()]
+ # Add the mask of the new field
+ newmask.setfield(getmaskarray(newfield),
+ *newmask.dtype.fields[newfieldname])
+ newdata._fieldmask = newmask
+ return newdata
+
+################################################################################
+if __name__ == '__main__':
+ import numpy as N
+ from maskedarray.testutils import assert_equal
+ if 1:
+ d = N.arange(5)
+ m = MA.make_mask([1,0,0,1,1])
+ base_d = N.r_[d,d[::-1]].reshape(2,-1).T
+ base_m = N.r_[[m, m[::-1]]].T
+ base = MA.array(base_d, mask=base_m).T
+ mrecord = fromarrays(base,dtype=[('a',N.float_),('b',N.float_)])
+ mrec = MaskedRecords(mrecord.copy())
+ #
+ mrec.a[3:] = 5
+ assert_equal(mrec.a, [0,1,2,5,5])
+ assert_equal(mrec.a._mask, [1,0,0,0,0])
+ #
+ mrec.b[3:] = masked
+ assert_equal(mrec.b, [4,3,2,1,0])
+ assert_equal(mrec.b._mask, [1,1,0,1,1])
+ #
+ mrec[:2] = masked
+ assert_equal(mrec._mask, [1,1,0,0,0])
+ mrec[-1] = masked
+ assert_equal(mrec._mask, [1,1,0,0,1])
+ if 1:
+ nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]],
+ dtype=[('a',N.float_),('b',N.float_)])
+ mrec = mrecord
+ #....................
+ mrecfr = fromrecords(nrec)
+ assert_equal(mrecfr.a, mrec.a)
+ assert_equal(mrecfr.dtype, mrec.dtype)
+ #....................
+ tmp = mrec[::-1] #.tolist()
+ mrecfr = fromrecords(tmp)
+ assert_equal(mrecfr.a, mrec.a[::-1])
+ #....................
+ mrecfr = fromrecords(nrec.tolist(), names=nrec.dtype.names)
+ assert_equal(mrecfr.a, mrec.a)
+ assert_equal(mrecfr.dtype, mrec.dtype)
+ if 1:
+ assert_equal(mrec.a, MA.array(d,mask=m))
+ assert_equal(mrec.b, MA.array(d[::-1],mask=m[::-1]))
+ assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]])).all())
+ assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
+ assert_equal(mrec.a[1], mrec[1].a)
+
+ if 1:
+ x = [(1.,10.,'a'),(2.,20,'b'),(3.14,30,'c'),(5.55,40,'d')]
+ desc = [('ffloat', N.float_), ('fint', N.int_), ('fstr', 'S10')]
+ mr = MaskedRecords(x,dtype=desc)
+ mr[0] = masked
+ mr.ffloat[-1] = masked
+ #
+ mrlast = mr[-1]
+ assert(isinstance(mrlast,MaskedRecords))
+ assert(hasattr(mrlast,'ffloat'))
+ assert_equal(mrlast.ffloat, masked)
+
+
\ No newline at end of file
Modified: trunk/Lib/sandbox/maskedarray/tests/test_core.py
===================================================================
--- trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-08-15 06:04:30 UTC (rev 3244)
+++ trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-08-15 13:38:19 UTC (rev 3245)
@@ -12,10 +12,11 @@
import types
-import numpy as N
+import numpy
import numpy.core.fromnumeric as fromnumeric
from numpy.testing import NumpyTest, NumpyTestCase
from numpy.testing.utils import build_err_msg
+from numpy import array as narray
import maskedarray.testutils
from maskedarray.testutils import *
@@ -23,7 +24,7 @@
import maskedarray.core as coremodule
from maskedarray.core import *
-pi = N.pi
+pi = numpy.pi
#..............................................................................
class test_ma(NumpyTestCase):
@@ -34,16 +35,16 @@
def setUp (self):
"Base data definition."
- x = N.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.])
- y = N.array([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.])
+ x = narray([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.])
+ y = narray([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.])
a10 = 10.
m1 = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
m2 = [0, 0, 1, 0, 0, 1, 1, 0, 0, 0 ,0, 1]
xm = masked_array(x, mask=m1)
ym = masked_array(y, mask=m2)
- z = N.array([-.5, 0., .5, .8])
+ z = narray([-.5, 0., .5, .8])
zm = masked_array(z, mask=[0,1,0,0])
- xf = N.where(m1, 1.e+20, x)
+ xf = numpy.where(m1, 1.e+20, x)
xm.set_fill_value(1.e+20)
self.d = (x, y, a10, m1, m2, xm, ym, z, zm, xf)
#........................
@@ -55,7 +56,7 @@
assert((xm-ym).filled(0).any())
fail_if_equal(xm.mask.astype(int_), ym.mask.astype(int_))
s = x.shape
- assert_equal(N.shape(xm), s)
+ assert_equal(numpy.shape(xm), s)
assert_equal(xm.shape, s)
assert_equal(xm.dtype, x.dtype)
assert_equal(zm.dtype, z.dtype)
@@ -115,14 +116,14 @@
assert_equal(x**2, xm**2)
assert_equal(abs(x)**2.5, abs(xm) **2.5)
assert_equal(x**y, xm**ym)
- assert_equal(N.add(x,y), add(xm, ym))
- assert_equal(N.subtract(x,y), subtract(xm, ym))
- assert_equal(N.multiply(x,y), multiply(xm, ym))
- assert_equal(N.divide(x,y), divide(xm, ym))
+ assert_equal(numpy.add(x,y), add(xm, ym))
+ assert_equal(numpy.subtract(x,y), subtract(xm, ym))
+ assert_equal(numpy.multiply(x,y), multiply(xm, ym))
+ assert_equal(numpy.divide(x,y), divide(xm, ym))
#........................
def check_mixed_arithmetic(self):
"Tests mixed arithmetics."
- na = N.array([1])
+ na = narray([1])
ma = array([1])
self.failUnless(isinstance(na + ma, MaskedArray))
self.failUnless(isinstance(ma + na, MaskedArray))
@@ -246,28 +247,28 @@
def check_basic_ufuncs (self):
"Test various functions such as sin, cos."
(x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
- assert_equal(N.cos(x), cos(xm))
- assert_equal(N.cosh(x), cosh(xm))
- assert_equal(N.sin(x), sin(xm))
- assert_equal(N.sinh(x), sinh(xm))
- assert_equal(N.tan(x), tan(xm))
- assert_equal(N.tanh(x), tanh(xm))
- assert_equal(N.sqrt(abs(x)), sqrt(xm))
- assert_equal(N.log(abs(x)), log(xm))
- assert_equal(N.log10(abs(x)), log10(xm))
- assert_equal(N.exp(x), exp(xm))
- assert_equal(N.arcsin(z), arcsin(zm))
- assert_equal(N.arccos(z), arccos(zm))
- assert_equal(N.arctan(z), arctan(zm))
- assert_equal(N.arctan2(x, y), arctan2(xm, ym))
- assert_equal(N.absolute(x), absolute(xm))
- assert_equal(N.equal(x,y), equal(xm, ym))
- assert_equal(N.not_equal(x,y), not_equal(xm, ym))
- assert_equal(N.less(x,y), less(xm, ym))
- assert_equal(N.greater(x,y), greater(xm, ym))
- assert_equal(N.less_equal(x,y), less_equal(xm, ym))
- assert_equal(N.greater_equal(x,y), greater_equal(xm, ym))
- assert_equal(N.conjugate(x), conjugate(xm))
+ assert_equal(numpy.cos(x), cos(xm))
+ assert_equal(numpy.cosh(x), cosh(xm))
+ assert_equal(numpy.sin(x), sin(xm))
+ assert_equal(numpy.sinh(x), sinh(xm))
+ assert_equal(numpy.tan(x), tan(xm))
+ assert_equal(numpy.tanh(x), tanh(xm))
+ assert_equal(numpy.sqrt(abs(x)), sqrt(xm))
+ assert_equal(numpy.log(abs(x)), log(xm))
+ assert_equal(numpy.log10(abs(x)), log10(xm))
+ assert_equal(numpy.exp(x), exp(xm))
+ assert_equal(numpy.arcsin(z), arcsin(zm))
+ assert_equal(numpy.arccos(z), arccos(zm))
+ assert_equal(numpy.arctan(z), arctan(zm))
+ assert_equal(numpy.arctan2(x, y), arctan2(xm, ym))
+ assert_equal(numpy.absolute(x), absolute(xm))
+ assert_equal(numpy.equal(x,y), equal(xm, ym))
+ assert_equal(numpy.not_equal(x,y), not_equal(xm, ym))
+ assert_equal(numpy.less(x,y), less(xm, ym))
+ assert_equal(numpy.greater(x,y), greater(xm, ym))
+ assert_equal(numpy.less_equal(x,y), less_equal(xm, ym))
+ assert_equal(numpy.greater_equal(x,y), greater_equal(xm, ym))
+ assert_equal(numpy.conjugate(x), conjugate(xm))
#........................
def check_count_func (self):
"Tests count"
@@ -286,7 +287,7 @@
def check_minmax_func (self):
"Tests minimum and maximum."
(x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
- xr = N.ravel(x) #max doesn't work if shaped
+ xr = numpy.ravel(x) #max doesn't work if shaped
xmr = ravel(xm)
assert_equal(max(xr), maximum(xmr)) #true because of careful selection of data
assert_equal(min(xr), minimum(xmr)) #true because of careful selection of data
@@ -326,50 +327,50 @@
def check_addsumprod (self):
"Tests add, sum, product."
(x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
- assert_equal(N.add.reduce(x), add.reduce(x))
- assert_equal(N.add.accumulate(x), add.accumulate(x))
+ assert_equal(numpy.add.reduce(x), add.reduce(x))
+ assert_equal(numpy.add.accumulate(x), add.accumulate(x))
assert_equal(4, sum(array(4),axis=0))
assert_equal(4, sum(array(4), axis=0))
- assert_equal(N.sum(x,axis=0), sum(x,axis=0))
- assert_equal(N.sum(filled(xm,0),axis=0), sum(xm,axis=0))
- assert_equal(N.sum(x,0), sum(x,0))
- assert_equal(N.product(x,axis=0), product(x,axis=0))
- assert_equal(N.product(x,0), product(x,0))
- assert_equal(N.product(filled(xm,1),axis=0), product(xm,axis=0))
+ assert_equal(numpy.sum(x,axis=0), sum(x,axis=0))
+ assert_equal(numpy.sum(filled(xm,0),axis=0), sum(xm,axis=0))
+ assert_equal(numpy.sum(x,0), sum(x,0))
+ assert_equal(numpy.product(x,axis=0), product(x,axis=0))
+ assert_equal(numpy.product(x,0), product(x,0))
+ assert_equal(numpy.product(filled(xm,1),axis=0), product(xm,axis=0))
s = (3,4)
x.shape = y.shape = xm.shape = ym.shape = s
if len(s) > 1:
- assert_equal(N.concatenate((x,y),1), concatenate((xm,ym),1))
- assert_equal(N.add.reduce(x,1), add.reduce(x,1))
- assert_equal(N.sum(x,1), sum(x,1))
- assert_equal(N.product(x,1), product(x,1))
+ assert_equal(numpy.concatenate((x,y),1), concatenate((xm,ym),1))
+ assert_equal(numpy.add.reduce(x,1), add.reduce(x,1))
+ assert_equal(numpy.sum(x,1), sum(x,1))
+ assert_equal(numpy.product(x,1), product(x,1))
#.........................
def check_concat(self):
"Tests concatenations."
(x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d
# basic concatenation
- assert_equal(N.concatenate((x,y)), concatenate((xm,ym)))
- assert_equal(N.concatenate((x,y)), concatenate((x,y)))
- assert_equal(N.concatenate((x,y)), concatenate((xm,y)))
- assert_equal(N.concatenate((x,y,x)), concatenate((x,ym,x)))
+ assert_equal(numpy.concatenate((x,y)), concatenate((xm,ym)))
+ assert_equal(numpy.concatenate((x,y)), concatenate((x,y)))
+ assert_equal(numpy.concatenate((x,y)), concatenate((xm,y)))
+ assert_equal(numpy.concatenate((x,y,x)), concatenate((x,ym,x)))
# Concatenation along an axis
s = (3,4)
x.shape = y.shape = xm.shape = ym.shape = s
- assert_equal(xm.mask, N.reshape(m1, s))
- assert_equal(ym.mask, N.reshape(m2, s))
+ assert_equal(xm.mask, numpy.reshape(m1, s))
+ assert_equal(ym.mask, numpy.reshape(m2, s))
xmym = concatenate((xm,ym),1)
- assert_equal(N.concatenate((x,y),1), xmym)
- assert_equal(N.concatenate((xm.mask,ym.mask),1), xmym._mask)
+ assert_equal(numpy.concatenate((x,y),1), xmym)
+ assert_equal(numpy.concatenate((xm.mask,ym.mask),1), xmym._mask)
#........................
def check_indexing(self):
"Tests conversions and indexing"
- x1 = N.array([1,2,4,3])
+ x1 = numpy.array([1,2,4,3])
x2 = array(x1, mask=[1,0,0,0])
x3 = array(x1, mask=[0,1,0,1])
x4 = array(x1)
# test conversion to strings
junk, garbage = str(x2), repr(x2)
- assert_equal(N.sort(x1),sort(x2,endwith=False))
+ assert_equal(numpy.sort(x1),sort(x2,endwith=False))
# tests of indexing
assert type(x2[1]) is type(x1[1])
assert x1[1] == x2[1]
@@ -396,14 +397,14 @@
x4[:] = masked_array([1,2,3,4],[0,1,1,0])
assert allequal(getmask(x4), array([0,1,1,0]))
assert allequal(x4, array([1,2,3,4]))
- x1 = N.arange(5)*1.0
+ x1 = numpy.arange(5)*1.0
x2 = masked_values(x1, 3.0)
assert_equal(x1,x2)
assert allequal(array([0,0,0,1,0],MaskType), x2.mask)
#FIXME: Well, eh, fill_value is now a property assert_equal(3.0, x2.fill_value())
assert_equal(3.0, x2.fill_value)
x1 = array([1,'hello',2,3],object)
- x2 = N.array([1,'hello',2,3],object)
+ x2 = numpy.array([1,'hello',2,3],object)
s1 = x1[1]
s2 = x2[1]
assert_equal(type(s2), str)
@@ -420,7 +421,7 @@
m3 = make_mask(m, copy=1)
assert(m is not m3)
- x1 = N.arange(5)
+ x1 = numpy.arange(5)
y1 = array(x1, mask=m)
#assert( y1._data is x1)
assert_equal(y1._data.__array_interface__, x1.__array_interface__)
@@ -585,15 +586,15 @@
def check_TakeTransposeInnerOuter(self):
"Test of take, transpose, inner, outer products"
x = arange(24)
- y = N.arange(24)
+ y = numpy.arange(24)
x[5:6] = masked
x = x.reshape(2,3,4)
y = y.reshape(2,3,4)
- assert_equal(N.transpose(y,(2,0,1)), transpose(x,(2,0,1)))
- assert_equal(N.take(y, (2,0,1), 1), take(x, (2,0,1), 1))
- assert_equal(N.inner(filled(x,0),filled(y,0)),
+ assert_equal(numpy.transpose(y,(2,0,1)), transpose(x,(2,0,1)))
+ assert_equal(numpy.take(y, (2,0,1), 1), take(x, (2,0,1), 1))
+ assert_equal(numpy.inner(filled(x,0),filled(y,0)),
inner(x, y))
- assert_equal(N.outer(filled(x,0),filled(y,0)),
+ assert_equal(numpy.outer(filled(x,0),filled(y,0)),
outer(x, y))
y = array(['abc', 1, 'def', 2, 3], object)
y[2] = masked
@@ -642,7 +643,7 @@
assert_equal(1, int(array([[[1]]])))
assert_equal(1.0, float(array([[1]])))
self.failUnlessRaises(ValueError, float, array([1,1]))
- assert N.isnan(float(array([1],mask=[1])))
+ assert numpy.isnan(float(array([1],mask=[1])))
#TODO: Check how bool works...
#TODO: self.failUnless(bool(array([0,1])))
#TODO: self.failUnless(bool(array([0,0],mask=[0,1])))
@@ -722,11 +723,11 @@
assert_equal(a_pickled._data, a._data)
assert_equal(a_pickled.fill_value, 999)
#
- a = array(N.matrix(range(10)), mask=[1,0,1,0,0]*2)
+ a = array(numpy.matrix(range(10)), mask=[1,0,1,0,0]*2)
a_pickled = cPickle.loads(a.dumps())
assert_equal(a_pickled._mask, a._mask)
assert_equal(a_pickled, a)
- assert(isinstance(a_pickled._data,N.matrix))
+ assert(isinstance(a_pickled._data,numpy.matrix))
#...............................................................................
@@ -795,7 +796,7 @@
"Test class for miscellaneous MaskedArrays methods."
def setUp(self):
"Base data definition."
- x = N.array([ 8.375, 7.545, 8.828, 8.5 , 1.757, 5.928,
+ x = numpy.array([ 8.375, 7.545, 8.828, 8.5 , 1.757, 5.928,
8.43 , 7.78 , 9.865, 5.878, 8.979, 4.732,
3.012, 6.022, 5.095, 3.116, 5.238, 3.957,
6.04 , 9.63 , 7.712, 3.382, 4.489, 6.479,
@@ -804,7 +805,7 @@
X = x.reshape(6,6)
XX = x.reshape(3,2,2,3)
- m = N.array([0, 1, 0, 1, 0, 0,
+ m = numpy.array([0, 1, 0, 1, 0, 0,
1, 0, 1, 1, 0, 1,
0, 0, 0, 1, 0, 1,
0, 0, 0, 1, 1, 1,
@@ -814,7 +815,7 @@
mX = array(data=X,mask=m.reshape(X.shape))
mXX = array(data=XX,mask=m.reshape(XX.shape))
- m2 = N.array([1, 1, 0, 1, 0, 0,
+ m2 = numpy.array([1, 1, 0, 1, 0, 0,
1, 1, 1, 1, 0, 1,
0, 0, 1, 1, 0, 1,
0, 0, 0, 1, 1, 1,
@@ -847,8 +848,8 @@
(x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
(n,m) = X.shape
assert_equal(mx.ptp(),mx.compressed().ptp())
- rows = N.zeros(n,N.float_)
- cols = N.zeros(m,N.float_)
+ rows = numpy.zeros(n,numpy.float_)
+ cols = numpy.zeros(m,numpy.float_)
for k in range(m):
cols[k] = mX[:,k].compressed().ptp()
for k in range(n):
@@ -888,7 +889,7 @@
for k in range(6):
assert_almost_equal(mXvar1[k],mX[k].compressed().var())
assert_almost_equal(mXvar0[k],mX[:,k].compressed().var())
- assert_almost_equal(N.sqrt(mXvar0[k]), mX[:,k].compressed().std())
+ assert_almost_equal(numpy.sqrt(mXvar0[k]), mX[:,k].compressed().std())
def check_argmin(self):
"Tests argmin & argmax on MaskedArrays."
@@ -969,16 +970,16 @@
#........................
def check_anyall(self):
"""Checks the any/all methods/functions."""
- x = N.array([[ 0.13, 0.26, 0.90],
+ x = numpy.array([[ 0.13, 0.26, 0.90],
[ 0.28, 0.33, 0.63],
[ 0.31, 0.87, 0.70]])
- m = N.array([[ True, False, False],
+ m = numpy.array([[ True, False, False],
[False, False, False],
- [True, True, False]], dtype=N.bool_)
+ [True, True, False]], dtype=numpy.bool_)
mx = masked_array(x, mask=m)
- xbig = N.array([[False, False, True],
+ xbig = numpy.array([[False, False, True],
[False, False, True],
- [False, True, True]], dtype=N.bool_)
+ [False, True, True]], dtype=numpy.bool_)
mxbig = (mx > 0.5)
mxsmall = (mx < 0.5)
#
@@ -996,24 +997,24 @@
assert_equal(mxsmall.any(0), [True, True, False])
assert_equal(mxsmall.any(1), [True, True, False])
#
- X = N.matrix(x)
+ X = numpy.matrix(x)
mX = masked_array(X, mask=m)
mXbig = (mX > 0.5)
mXsmall = (mX < 0.5)
#
assert (mXbig.all()==False)
assert (mXbig.any()==True)
- assert_equal(mXbig.all(0), N.matrix([False, False, True]))
- assert_equal(mXbig.all(1), N.matrix([False, False, True]).T)
- assert_equal(mXbig.any(0), N.matrix([False, False, True]))
- assert_equal(mXbig.any(1), N.matrix([ True, True, True]).T)
+ assert_equal(mXbig.all(0), numpy.matrix([False, False, True]))
+ assert_equal(mXbig.all(1), numpy.matrix([False, False, True]).T)
+ assert_equal(mXbig.any(0), numpy.matrix([False, False, True]))
+ assert_equal(mXbig.any(1), numpy.matrix([ True, True, True]).T)
#
assert (mXsmall.all()==False)
assert (mXsmall.any()==True)
- assert_equal(mXsmall.all(0), N.matrix([True, True, False]))
- assert_equal(mXsmall.all(1), N.matrix([False, False, False]).T)
- assert_equal(mXsmall.any(0), N.matrix([True, True, False]))
- assert_equal(mXsmall.any(1), N.matrix([True, True, False]).T)
+ assert_equal(mXsmall.all(0), numpy.matrix([True, True, False]))
+ assert_equal(mXsmall.all(1), numpy.matrix([False, False, False]).T)
+ assert_equal(mXsmall.any(0), numpy.matrix([True, True, False]))
+ assert_equal(mXsmall.any(1), numpy.matrix([True, True, False]).T)
def check_keepmask(self):
"Tests the keep mask flag"
@@ -1110,7 +1111,7 @@
def check_sort(self):
"Test sort"
- x = array([1,4,2,3],mask=[0,1,0,0],dtype=N.uint8)
+ x = array([1,4,2,3],mask=[0,1,0,0],dtype=numpy.uint8)
#
sortedx = sort(x)
assert_equal(sortedx._data,[1,2,3,4])
@@ -1124,7 +1125,7 @@
assert_equal(x._data,[1,2,3,4])
assert_equal(x._mask,[0,0,0,1])
#
- x = array([1,4,2,3],mask=[0,1,0,0],dtype=N.uint8)
+ x = array([1,4,2,3],mask=[0,1,0,0],dtype=numpy.uint8)
x.sort(endwith=False)
assert_equal(x._data, [4,1,2,3])
assert_equal(x._mask, [1,0,0,0])
@@ -1133,10 +1134,10 @@
sortedx = sort(x)
assert(not isinstance(sorted, MaskedArray))
#
- x = array([0,1,-1,-2,2], mask=nomask, dtype=N.int8)
+ x = array([0,1,-1,-2,2], mask=nomask, dtype=numpy.int8)
sortedx = sort(x, endwith=False)
assert_equal(sortedx._data, [-2,-1,0,1,2])
- x = array([0,1,-1,-2,2], mask=[0,1,0,0,1], dtype=N.int8)
+ x = array([0,1,-1,-2,2], mask=[0,1,0,0,1], dtype=numpy.int8)
sortedx = sort(x, endwith=False)
assert_equal(sortedx._data, [1,2,-2,-1,0])
assert_equal(sortedx._mask, [1,1,0,0,0])
@@ -1190,7 +1191,7 @@
a = array([0,0], mask=[1,1])
aravel = a.ravel()
assert_equal(a._mask.shape, a.shape)
- a = array(N.matrix([1,2,3,4,5]), mask=[[0,1,0,0,0]])
+ a = array(numpy.matrix([1,2,3,4,5]), mask=[[0,1,0,0,0]])
aravel = a.ravel()
assert_equal(a.shape,(1,5))
assert_equal(a._mask.shape, a.shape)
@@ -1223,6 +1224,21 @@
assert_equal(b._data, [2,3,4])
assert_equal(b._mask, nomask)
+ def check_tolist(self):
+ "Tests to list"
+ x = array(numpy.arange(12))
+ x[[1,-2]] = masked
+ xlist = x.tolist()
+ assert(xlist[1] is None)
+ assert(xlist[-2] is None)
+ #
+ x.shape = (3,4)
+ xlist = x.tolist()
+ #
+ assert_equal(xlist[0],[0,None,2,3])
+ assert_equal(xlist[1],[4,5,6,7])
+ assert_equal(xlist[2],[8,9,None,11])
+
#..............................................................................
###############################################################################
More information about the Scipy-svn
mailing list