[Numpy-svn] r6088 - in branches/1.2.x/numpy/ma: . tests

numpy-svn@scip... numpy-svn@scip...
Fri Nov 21 15:50:19 CST 2008


Author: pierregm
Date: 2008-11-21 15:50:17 -0600 (Fri, 21 Nov 2008)
New Revision: 6088

Modified:
   branches/1.2.x/numpy/ma/core.py
   branches/1.2.x/numpy/ma/tests/test_core.py
Log:
Backporting bug fixes (6055, 6083, 6086, 6087)

Modified: branches/1.2.x/numpy/ma/core.py
===================================================================
--- branches/1.2.x/numpy/ma/core.py	2008-11-21 20:49:33 UTC (rev 6087)
+++ branches/1.2.x/numpy/ma/core.py	2008-11-21 21:50:17 UTC (rev 6088)
@@ -19,7 +19,7 @@
 __docformat__ = "restructuredtext en"
 
 __all__ = ['MAError', 'MaskType', 'MaskedArray',
-           'bool_', 'complex_', 'float_', 'int_', 'object_',
+           'bool_',
            'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue',
            'amax', 'amin', 'anom', 'anomalies', 'any', 'arange',
            'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2',
@@ -38,8 +38,8 @@
            'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray',
            'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10',
            'logical_and', 'logical_not', 'logical_or', 'logical_xor',
-           'make_mask', 'make_mask_descr', 'make_mask_none', 'mask_or', 'masked',
-           'masked_array', 'masked_equal', 'masked_greater',
+           'make_mask', 'make_mask_descr', 'make_mask_none', 'mask_or',
+           'masked', 'masked_array', 'masked_equal', 'masked_greater',
            'masked_greater_equal', 'masked_inside', 'masked_invalid',
            'masked_less','masked_less_equal', 'masked_not_equal',
            'masked_object','masked_outside', 'masked_print_option',
@@ -61,8 +61,7 @@
 import operator
 
 import numpy as np
-from numpy import ndarray, amax, amin, iscomplexobj, bool_, complex_, float_,\
-                  int_, object_
+from numpy import ndarray, amax, amin, iscomplexobj, bool_
 from numpy import array as narray
 
 import numpy.core.umath as umath
@@ -79,8 +78,13 @@
 
 
 def doc_note(initialdoc, note):
+    """
+    Adds a Notes section to an existing docstring.
+    """
     if initialdoc is None:
         return
+    if note is None:
+        return initialdoc
     newdoc = """
     %s
 
@@ -95,15 +99,9 @@
 #####--------------------------------------------------------------------------
 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__
+    pass
 
+
 #####--------------------------------------------------------------------------
 #---- --- Filling options ---
 #####--------------------------------------------------------------------------
@@ -127,7 +125,8 @@
 
 
 def default_fill_value(obj):
-    """Calculate the default fill value for the argument object.
+    """
+    Calculate the default fill value for the argument object.
 
     """
     if hasattr(obj,'dtype'):
@@ -150,15 +149,16 @@
     return defval
 
 def minimum_fill_value(obj):
-    """Calculate the default fill value suitable for taking the
-    minimum of ``obj``.
+    """
+    Calculate the default fill value suitable for taking the minimum of ``obj``.
 
     """
+    errmsg = "Unsuitable type for calculating minimum."
     if hasattr(obj, 'dtype'):
         objtype = obj.dtype
         filler = min_filler[objtype]
         if filler is None:
-            raise TypeError, 'Unsuitable type for calculating minimum.'
+            raise TypeError(errmsg)
         return filler
     elif isinstance(obj, float):
         return min_filler[ntypes.typeDict['float_']]
@@ -169,18 +169,19 @@
     elif isinstance(obj, np.dtype):
         return min_filler[obj]
     else:
-        raise TypeError, 'Unsuitable type for calculating minimum.'
+        raise TypeError(errmsg)
 
 def maximum_fill_value(obj):
-    """Calculate the default fill value suitable for taking the maximum
-    of ``obj``.
+    """
+    Calculate the default fill value suitable for taking the maximum of ``obj``.
 
     """
+    errmsg = "Unsuitable type for calculating maximum."
     if hasattr(obj, 'dtype'):
         objtype = obj.dtype
         filler = max_filler[objtype]
         if filler is None:
-            raise TypeError, 'Unsuitable type for calculating minimum.'
+            raise TypeError(errmsg)
         return filler
     elif isinstance(obj, float):
         return max_filler[ntypes.typeDict['float_']]
@@ -191,10 +192,21 @@
     elif isinstance(obj, np.dtype):
         return max_filler[obj]
     else:
-        raise TypeError, 'Unsuitable type for calculating minimum.'
+        raise TypeError(errmsg)
 
 
 def _check_fill_value(fill_value, ndtype):
+    """
+    Private function validating the given `fill_value` for the given dtype.
+
+    If fill_value is None, it is set to the default corresponding to the dtype
+    if this latter is standard (no fields). If the datatype is flexible (named
+    fields), fill_value is set to a tuple whose elements are the default fill
+    values corresponding to each field.
+
+    If fill_value is not None, its value is forced to the given dtype.
+
+    """
     ndtype = np.dtype(ndtype)
     fields = ndtype.fields
     if fill_value is None:
@@ -212,7 +224,7 @@
                 fill_value = np.array(fill_value, copy=False, dtype=fdtype)
             except ValueError:
                 err_msg = "Unable to transform %s to dtype %s"
-                raise ValueError(err_msg % (fill_value,fdtype))
+                raise ValueError(err_msg % (fill_value, fdtype))
         else:
             fval = np.resize(fill_value, len(ndtype.descr))
             fill_value = [np.asarray(f).astype(desc[1]).item()
@@ -231,9 +243,18 @@
 
 
 def set_fill_value(a, fill_value):
-    """Set the filling value of a, if a is a masked array.  Otherwise,
+    """
+    Set the filling value of a, if a is a masked array.  Otherwise,
     do nothing.
 
+    Parameters
+    ----------
+    a : ndarray
+        Input array
+    fill_value : var
+        Filling value. A consistency test is performed to make sure
+        the value is compatible with the dtype of a.
+
     Returns
     -------
     None
@@ -244,7 +265,8 @@
     return
 
 def get_fill_value(a):
-    """Return the filling value of a, if any.  Otherwise, returns the
+    """
+    Return the filling value of a, if any.  Otherwise, returns the
     default filling value for that type.
 
     """
@@ -255,7 +277,8 @@
     return result
 
 def common_fill_value(a, b):
-    """Return the common filling value of a and b, if any.
+    """
+    Return the common filling value of a and b, if any.
     If a and b have different filling values, returns None.
 
     """
@@ -267,7 +290,7 @@
 
 
 #####--------------------------------------------------------------------------
-def filled(a, value = None):
+def filled(a, fill_value = None):
     """Return a as an array with masked data replaced by value.  If
     value is None, get_fill_value(a) is used instead.  If a is already
     a ndarray, a itself is returned.
@@ -276,9 +299,9 @@
     ----------
     a : maskedarray or array_like
         An input object.
-    value : {var}, optional
-        Filling value. If not given, the output of get_fill_value(a)
-        is used instead.
+    fill_value : {var}, optional
+        Filling value. If None, the output of :func:`get_fill_value(a)` is used
+        instead.
 
     Returns
     -------
@@ -286,7 +309,7 @@
 
     """
     if hasattr(a, 'filled'):
-        return a.filled(value)
+        return a.filled(fill_value)
     elif isinstance(a, ndarray):
         # Should we check for contiguity ? and a.flags['CONTIGUOUS']:
         return a
@@ -297,8 +320,9 @@
 
 #####--------------------------------------------------------------------------
 def get_masked_subclass(*arrays):
-    """Return the youngest subclass of MaskedArray from a list of
-    (masked) arrays.  In case of siblings, the first takes over.
+    """
+    Return the youngest subclass of MaskedArray from a list of (masked) arrays.
+    In case of siblings, the first listed takes over.
 
     """
     if len(arrays) == 1:
@@ -319,13 +343,14 @@
 
 #####--------------------------------------------------------------------------
 def get_data(a, subok=True):
-    """Return the _data part of a (if any), or a as a ndarray.
+    """
+    Return the `_data` part of `a` if `a` is a MaskedArray, or `a` itself.
 
     Parameters
     ----------
     a : array_like
         A ndarray or a subclass of.
-    subok : bool
+    subok : {True, False}, optional
         Whether to force the output to a 'pure' ndarray (False) or to
         return a subclass of ndarray if approriate (True).
 
@@ -338,7 +363,8 @@
 getdata = get_data
 
 def fix_invalid(a, mask=nomask, copy=True, fill_value=None):
-    """Return (a copy of) a where invalid data (nan/inf) are masked
+    """
+    Return (a copy of) a where invalid data (nan/inf) are masked
     and replaced by fill_value.
 
     Note that a copy is performed by default (just in case...).
@@ -348,7 +374,7 @@
     a : array_like
         A (subclass of) ndarray.
     copy : bool
-        Whether to use a copy of a (True) or to fix a in place (False).
+        Whether to use a copy of `a` (True) or to fix `a` in place (False).
     fill_value : {var}, optional
         Value used for fixing invalid data.  If not given, the output
         of get_fill_value(a) is used instead.
@@ -535,7 +561,7 @@
         m = mask_or(getmask(a), getmask(b))
         (d1, d2) = (get_data(a), get_data(b))
         result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a, b))
-        if result.size > 1:
+        if len(result.shape):
             if m is not nomask:
                 result._mask = make_mask_none(result.shape)
                 result._mask.flat = m
@@ -649,9 +675,9 @@
             mb = mask_or(mb, t)
             # The following line controls the domain filling
             if t.size == d2.size:
-                d2 = np.where(t,self.filly,d2)
+                d2 = np.where(t, self.filly, d2)
             else:
-                d2 = np.where(np.resize(t, d2.shape),self.filly, d2)
+                d2 = np.where(np.resize(t, d2.shape), self.filly, d2)
         m = mask_or(ma, mb)
         if (not m.ndim) and m:
             return masked
@@ -787,9 +813,13 @@
     return mask
 
 def is_mask(m):
-    """Return True if m is a legal mask.
+    """
+    Return True if m is a valid, standard mask.
 
-    Does not check contents, only type.
+    Notes
+    -----
+    This function does not check contents, only the type. In particular,
+    this function returns False if the mask has a flexible dtype.
 
     """
     try:
@@ -797,8 +827,9 @@
     except AttributeError:
         return False
 
-def make_mask(m, copy=False, shrink=True, flag=None):
-    """Return m as a mask, creating a copy if necessary or requested.
+def make_mask(m, copy=False, shrink=True, flag=None, dtype=MaskType):
+    """
+    Return m as a mask, creating a copy if necessary or requested.
 
     The function can accept any sequence of integers or nomask.  Does
     not check that contents must be 0s and 1s.
@@ -811,6 +842,10 @@
         Whether to return a copy of m (True) or m itself (False).
     shrink : bool
         Whether to shrink m to nomask if all its values are False.
+    dtype : dtype
+        Data-type of the output mask. By default, the output mask has
+        a dtype of MaskType (bool). If the dtype is flexible, each field
+        has a boolean dtype.
 
     """
     if flag is not None:
@@ -820,24 +855,29 @@
     if m is nomask:
         return nomask
     elif isinstance(m, ndarray):
+        # We won't return after this point to make sure we can shrink the mask
+        # Fill the mask in case there are missing data
         m = filled(m, True)
-        if m.dtype.type is MaskType:
+        # Make sure the input dtype is valid
+        dtype = make_mask_descr(dtype)
+        if m.dtype == dtype:
             if copy:
-                result = narray(m, dtype=MaskType, copy=copy)
+                result = m.copy()
             else:
                 result = m
         else:
-            result = narray(m, dtype=MaskType)
+            result = np.array(m, dtype=dtype, copy=copy)
     else:
-        result = narray(filled(m, True), dtype=MaskType)
+        result = np.array(filled(m, True), dtype=MaskType)
     # Bas les masques !
-    if shrink and not result.any():
+    if shrink and (not result.dtype.names) and (not result.any()):
         return nomask
     else:
         return result
 
 def make_mask_none(newshape, dtype=None):
-    """Return a mask of shape s, filled with False.
+    """
+    Return a mask of shape s, filled with False.
 
     Parameters
     ----------
@@ -854,7 +894,8 @@
     return result
 
 def mask_or (m1, m2, copy=False, shrink=True):
-    """Return the combination of two masks m1 and m2.
+    """
+    Return the combination of two masks m1 and m2.
 
     The masks are combined with the *logical_or* operator, treating
     nomask as False.  The result may equal m1 or m2 if the other is
@@ -871,13 +912,28 @@
     shrink : {True, False}, optional
         Whether to shrink m to nomask if all its values are False.
 
+    Raises
+    ------
+    ValueError
+        If m1 and m2 have different flexible dtypes.
+
      """
-    if m1 is nomask:
-        return make_mask(m2, copy=copy, shrink=shrink)
-    if m2 is nomask:
-        return make_mask(m1, copy=copy, shrink=shrink)
+    if (m1 is nomask) or (m1 is False):
+        dtype = getattr(m2, 'dtype', MaskType)
+        return make_mask(m2, copy=copy, shrink=shrink, dtype=dtype)
+    if (m2 is nomask) or (m2 is False):
+        dtype = getattr(m1, 'dtype', MaskType)
+        return make_mask(m1, copy=copy, shrink=shrink, dtype=dtype)
     if m1 is m2 and is_mask(m1):
         return m1
+    (dtype1, dtype2) = (getattr(m1, 'dtype', None), getattr(m2, 'dtype', None))
+    if (dtype1 != dtype2):
+        raise ValueError("Incompatible dtypes '%s'<>'%s'" % (dtype1, dtype2))
+    if dtype1.names:
+        newmask = np.empty_like(m1)
+        for n in dtype1.names:
+            newmask[n] = umath.logical_or(m1[n], m2[n])
+        return newmask
     return make_mask(umath.logical_or(m1, m2), copy=copy, shrink=shrink)
 
 
@@ -900,6 +956,7 @@
         Whether to return a copy of ``a`` (True) or modify ``a`` in place (False).
 
     """
+    # Make sure that condition is a valid standard-type mask.
     cond = make_mask(condition)
     a = np.array(a, copy=copy, subok=True)
 
@@ -917,7 +974,11 @@
     return result
 
 def masked_greater(x, value, copy=True):
-    "Shortcut to masked_where, with condition = (x > value)."
+    """
+    Return the array `x` masked where (x > value).
+    Any value of mask already masked is kept masked.
+
+    """
     return masked_where(greater(x, value), x, copy=copy)
 
 def masked_greater_equal(x, value, copy=True):
@@ -1038,10 +1099,10 @@
         Whether to collapse a mask full of False to nomask
 
     """
-    abs = umath.absolute
+    mabs = umath.absolute
     xnew = filled(x, value)
     if issubclass(xnew.dtype.type, np.floating):
-        condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value))
+        condition = umath.less_equal(mabs(xnew-value), atol + rtol*mabs(value))
         mask = getattr(x, '_mask', nomask)
     else:
         condition = umath.equal(xnew, value)
@@ -1187,6 +1248,7 @@
         a[index] = value
 
     def next(self):
+        "Returns the next element of the iterator."
         d = self.ma_iter.next()
         if self.maskiter is not None and self.maskiter.next():
             d = masked
@@ -1259,7 +1321,7 @@
         _data = np.array(data, dtype=dtype, copy=copy, subok=True, ndmin=ndmin)
         _baseclass = getattr(data, '_baseclass', type(_data))
         # Check that we'ew not erasing the mask..........
-        if isinstance(data,MaskedArray) and (data.shape != _data.shape):
+        if isinstance(data, MaskedArray) and (data.shape != _data.shape):
             copy = True
         # Careful, cls might not always be MaskedArray...
         if not isinstance(data, cls) or not subok:
@@ -1364,7 +1426,7 @@
         # We need to copy the _basedict to avoid backward propagation
         _optinfo = {}
         _optinfo.update(getattr(obj, '_optinfo', {}))
-        _optinfo.update(getattr(obj, '_basedict',{}))
+        _optinfo.update(getattr(obj, '_basedict', {}))
         if not isinstance(obj, MaskedArray):
             _optinfo.update(getattr(obj, '__dict__', {}))
         _dict = dict(_fill_value=getattr(obj, '_fill_value', None),
@@ -1378,7 +1440,7 @@
         self.__dict__.update(_optinfo)
         return
     #........................
-    def __array_finalize__(self,obj):
+    def __array_finalize__(self, obj):
         """Finalizes the masked array.
         """
         # Get main attributes .........
@@ -1460,7 +1522,7 @@
         else:
             output = ndarray.view(self, dtype, type)
         # Should we update the mask ?
-        if (getattr(output,'_mask', nomask) is not nomask):
+        if (getattr(output, '_mask', nomask) is not nomask):
             if dtype is None:
                 dtype = output.dtype
             mdtype = make_mask_descr(dtype)
@@ -1506,7 +1568,7 @@
             if self._mask is nomask:
                 output._mask = nomask
             else:
-                output._mask = self._mask.astype([(n,bool) for n in names])
+                output._mask = self._mask.astype([(n, bool) for n in names])
         # Don't check _fill_value if it's None, that'll speed things up
         if self._fill_value is not None:
             output._fill_value = _check_fill_value(self._fill_value, newtype)
@@ -1522,17 +1584,17 @@
 #        if getmask(indx) is not nomask:
 #            msg = "Masked arrays must be filled before they can be used as indices!"
 #            raise IndexError, msg
-        dout = ndarray.__getitem__(ndarray.view(self,ndarray), indx)
+        dout = ndarray.__getitem__(ndarray.view(self, ndarray), indx)
         # We could directly use ndarray.__getitem__ on self...
         # But then we would have to modify __array_finalize__ to prevent the
         # mask of being reshaped if it hasn't been set up properly yet...
         # So it's easier to stick to the current version
         _mask = self._mask
-        if not getattr(dout,'ndim', False):
+        if not getattr(dout, 'ndim', False):
             # A record ................
             if isinstance(dout, np.void):
                 mask = _mask[indx]
-                if mask.view((bool,len(mask.dtype))).any():
+                if mask.view((bool, len(mask.dtype))).any():
                     dout = masked_array(dout, mask=mask)
                 else:
                     return dout
@@ -1588,7 +1650,7 @@
                 _mask = self._mask = make_mask_none(self.shape, _dtype)
             # Now, set the mask to its value.
             if nbfields:
-                _mask[indx] = tuple([True,] * nbfields)
+                _mask[indx] = tuple([True] * nbfields)
             else:
                 _mask[indx] = True
             if not self._isfield:
@@ -1617,7 +1679,7 @@
             ndarray.__setitem__(_mask, indx, mval)
         elif hasattr(indx, 'dtype') and (indx.dtype==MaskType):
             indx = indx * umath.logical_not(_mask)
-            ndarray.__setitem__(_data,indx,dval)
+            ndarray.__setitem__(_data, indx, dval)
         else:
             if nbfields:
                 err_msg = "Flexible 'hard' masks are not yet supported..."
@@ -1639,16 +1701,16 @@
         indices is not supported.
 
         """
-        return self.__getitem__(slice(i,j))
+        return self.__getitem__(slice(i, j))
     #........................
     def __setslice__(self, i, j, value):
         """x.__setslice__(i, j, value) <==> x[i:j]=value
 
-        Set the slice (i,j) of a to value. If value is masked, mask
-        those locations.
+    Set the slice (i,j) of a to value. If value is masked, mask
+    those locations.
 
         """
-        self.__setitem__(slice(i,j), value)
+        self.__setitem__(slice(i, j), value)
     #............................................
     def __setmask__(self, mask, copy=False):
         """Set the mask.
@@ -1727,7 +1789,7 @@
         if _mask.size > 1:
             axis = 1
         else:
-            axis=None
+            axis = None
         #
         try:
             return _mask.view((bool_, len(self.dtype))).all(axis)
@@ -2028,7 +2090,7 @@
         parameters =  dict(name=name, data=str(self), mask=str(self._mask),
                            fill=str(self.fill_value), dtype=str(self.dtype))
         if self.dtype.names:
-            if n<= 1:
+            if n <= 1:
                 return with_mask1_flx % parameters
             return  with_mask_flx % parameters
         elif n <= 1:
@@ -2129,7 +2191,7 @@
         new_mask = mask_or(other_mask, invalid)
         self._mask = mask_or(self._mask, new_mask)
         # The following line is potentially problematic, as we change _data...
-        np.putmask(self._data,invalid,self.fill_value)
+        np.putmask(self._data, invalid, self.fill_value)
         return self
     #............................................
     def __float__(self):
@@ -2152,28 +2214,30 @@
         return int(self.item())
     #............................................
     def get_imag(self):
+        "Returns the imaginary part."
         result = self._data.imag.view(type(self))
         result.__setmask__(self._mask)
         return result
-    imag = property(fget=get_imag,doc="Imaginary part")
+    imag = property(fget=get_imag, doc="Imaginary part.")
 
     def get_real(self):
+        "Returns the real part."
         result = self._data.real.view(type(self))
         result.__setmask__(self._mask)
         return result
-    real = property(fget=get_real,doc="Real part")
+    real = property(fget=get_real, doc="Real part")
 
 
     #............................................
     def count(self, axis=None):
-        """Count the non-masked elements of the array along the given
-        axis.
+        """
+        Count the non-masked elements of the array along the given axis.
 
         Parameters
         ----------
         axis : int, optional
-            Axis along which to count the non-masked elements. If
-            not given, all the non masked elements are counted.
+            Axis along which to count the non-masked elements. If axis is None,
+            all the non masked elements are counted.
 
         Returns
         -------
@@ -2316,7 +2380,7 @@
 
         Parameters
         ----------
-        indicies : 1-D array_like
+        indices : 1-D array_like
             Target indices, interpreted as integers.
         values : array_like
             Values to place in self._data copy at target indices.
@@ -2386,34 +2450,34 @@
     #............................................
     def all(self, axis=None, out=None):
         """
-        Check if all of the elements of `a` are true.
+    Check if all of the elements of `a` are true.
 
-        Performs a :func:`logical_and` over the given axis and returns the result.
-        Masked values are considered as True during computation.
-        For convenience, the output array is masked where ALL the values along the
-        current axis are masked: if the output would have been a scalar and that
-        all the values are masked, then the output is `masked`.
+    Performs a :func:`logical_and` over the given axis and returns the result.
+    Masked values are considered as True during computation.
+    For convenience, the output array is masked where ALL the values along the
+    current axis are masked: if the output would have been a scalar and that
+    all the values are masked, then the output is `masked`.
 
-        Parameters
-        ----------
-        axis : {None, integer}
-            Axis to perform the operation over.
-            If None, perform over flattened array.
-        out : {None, array}, optional
-            Array into which the result can be placed. Its type is preserved
-            and it must be of the right shape to hold the output.
+    Parameters
+    ----------
+    axis : {None, integer}
+        Axis to perform the operation over.
+        If None, perform over flattened array.
+    out : {None, array}, optional
+        Array into which the result can be placed. Its type is preserved
+        and it must be of the right shape to hold the output.
 
-        See Also
-        --------
-        all : equivalent function
+    See Also
+    --------
+    all : equivalent function
 
-        Examples
-        --------
-        >>> np.ma.array([1,2,3]).all()
-        True
-        >>> a = np.ma.array([1,2,3], mask=True)
-        >>> (a.all() is np.ma.masked)
-        True
+    Examples
+    --------
+    >>> np.ma.array([1,2,3]).all()
+    True
+    >>> a = np.ma.array([1,2,3], mask=True)
+    >>> (a.all() is np.ma.masked)
+    True
 
         """
         mask = self._mask.all(axis)
@@ -2468,19 +2532,20 @@
 
 
     def nonzero(self):
-        """Return the indices of the elements of a that are not zero
-        nor masked, as a tuple of arrays.
+        """
+    Return the indices of the elements of a that are not zero
+    nor masked, as a tuple of arrays.
 
-        There are as many tuples as dimensions of a, each tuple
-        contains the indices of the non-zero elements in that
-        dimension.  The corresponding non-zero values can be obtained
-        with ``a[a.nonzero()]``.
+    There are as many tuples as dimensions of a, each tuple
+    contains the indices of the non-zero elements in that
+    dimension.  The corresponding non-zero values can be obtained
+    with ``a[a.nonzero()]``.
 
-        To group the indices by element, rather than dimension, use
-        instead: ``transpose(a.nonzero())``.
+    To group the indices by element, rather than dimension, use
+    instead: ``transpose(a.nonzero())``.
 
-        The result of this is always a 2d array, with a row for each
-        non-zero element.
+    The result of this is always a 2d array, with a row for each
+    non-zero element.
 
         """
         return narray(self.filled(0), copy=False).nonzero()
@@ -2690,7 +2755,7 @@
             return result
         # Explicit output
         result = self.filled(1).prod(axis, dtype=dtype, out=out)
-        if isinstance(out,MaskedArray):
+        if isinstance(out, MaskedArray):
             outmask = getattr(out, '_mask', nomask)
             if (outmask is nomask):
                 outmask = out._mask = make_mask_none(out.shape)
@@ -2788,7 +2853,7 @@
         if not axis:
             return (self - m)
         else:
-            return (self - expand_dims(m,axis))
+            return (self - expand_dims(m, axis))
 
     def var(self, axis=None, dtype=None, out=None, ddof=0):
         ""
@@ -2830,7 +2895,7 @@
 
     def std(self, axis=None, dtype=None, out=None, ddof=0):
         ""
-        dvar = self.var(axis=axis,dtype=dtype,out=out, ddof=ddof)
+        dvar = self.var(axis=axis, dtype=dtype, out=out, ddof=ddof)
         if dvar is not masked:
             dvar = sqrt(dvar)
             if out is not None:
@@ -3045,7 +3110,7 @@
 
         """
         if self._mask is nomask:
-            ndarray.sort(self,axis=axis, kind=kind, order=order)
+            ndarray.sort(self, axis=axis, kind=kind, order=order)
         else:
             if fill_value is None:
                 if endwith:
@@ -3055,7 +3120,8 @@
             else:
                 filler = fill_value
             idx = np.indices(self.shape)
-            idx[axis] = self.filled(filler).argsort(axis=axis,kind=kind,order=order)
+            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
@@ -3246,11 +3312,11 @@
         nbdims = self.ndim
         dtypesize = len(self.dtype)
         if nbdims == 0:
-            return tuple([None]*dtypesize)
+            return tuple([None] * dtypesize)
         elif nbdims == 1:
             maskedidx = _mask.nonzero()[0].tolist()
             if dtypesize:
-                nodata = tuple([None]*dtypesize)
+                nodata = tuple([None] * dtypesize)
             else:
                 nodata = None
             [operator.setitem(result,i,nodata) for i in maskedidx]
@@ -3377,9 +3443,11 @@
                 (self.__class__, self._baseclass, (0,), 'b', ),
                 self.__getstate__())
     #
-    def __deepcopy__(self, memo={}):
+    def __deepcopy__(self, memo=None):
         from copy import deepcopy
         copied = MaskedArray.__new__(type(self), self, copy=True)
+        if memo is None:
+            memo = {}
         memo[id(self)] = copied
         for (k,v) in self.__dict__.iteritems():
             copied.__dict__[k] = deepcopy(v, memo)
@@ -3617,16 +3685,16 @@
     fa = getdata(a)
     fb = getdata(b)
     # Get the type of the result (so that we preserve subclasses)
-    if isinstance(a,MaskedArray):
+    if isinstance(a, MaskedArray):
         basetype = type(a)
     else:
         basetype = MaskedArray
     # Get the result and view it as a (subclass of) MaskedArray
-    result = umath.power(fa,fb).view(basetype)
+    result = umath.power(fa, fb).view(basetype)
     # Find where we're in trouble w/ NaNs and Infs
     invalid = np.logical_not(np.isfinite(result.view(ndarray)))
     # Retrieve some extra attributes if needed
-    if isinstance(result,MaskedArray):
+    if isinstance(result, MaskedArray):
         result._update_from(a)
     # Add the initial mask
     if m is not nomask:
@@ -3700,13 +3768,21 @@
         filler = fill_value
 #    return
     indx = np.indices(a.shape).tolist()
-    indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order)
+    indx[axis] = filled(a, filler).argsort(axis=axis, kind=kind, order=order)
     return a[indx]
 sort.__doc__ = MaskedArray.sort.__doc__
 
 
 def compressed(x):
-    """Return a 1-D array of all the non-masked data."""
+    """
+    Return a 1-D array of all the non-masked data.
+    
+    See Also
+    --------
+    MaskedArray.compressed
+        equivalent method
+    
+    """
     if getmask(x) is nomask:
         return np.asanyarray(x)
     else:
@@ -3742,12 +3818,13 @@
 count.__doc__ = MaskedArray.count.__doc__
 
 
-def expand_dims(x,axis):
-    """Expand the shape of the array by including a new axis before
+def expand_dims(x, axis):
+    """
+    Expand the shape of the array by including a new axis before
     the given one.
 
     """
-    result = n_expand_dims(x,axis)
+    result = n_expand_dims(x, axis)
     if isinstance(x, MaskedArray):
         new_shape = result.shape
         result = x.view()
@@ -4081,29 +4158,100 @@
     else:
         return False
 
-def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8):
-    """ Return True if all elements of a and b are equal subject to
+def allclose (a, b, masked_equal=True, rtol=1.e-5, atol=1.e-8, fill_value=None):
+    """
+        Returns True if two arrays are element-wise equal within a tolerance.
+
+    The tolerance values are positive, typically very small numbers.  The
+    relative difference (`rtol` * `b`) and the absolute difference (`atol`)
+    are added together to compare against the absolute difference between `a`
+    and `b`.
+
+    Parameters
+    ----------
+    a, b : array_like
+        Input arrays to compare.
+    fill_value : boolean, optional
+        Whether masked values in a or b are considered equal (True) or not
+        (False).
+        
+    rtol : Relative tolerance
+        The relative difference is equal to `rtol` * `b`.
+    atol : Absolute tolerance
+        The absolute difference is equal to `atol`.
+
+    Returns
+    -------
+    y : bool
+        Returns True if the two arrays are equal within the given
+        tolerance; False otherwise. If either array contains NaN, then
+        False is returned.
+
+    See Also
+    --------
+    all, any, alltrue, sometrue
+
+    Notes
+    -----
+    If the following equation is element-wise True, then allclose returns
+    True.
+
+     absolute(`a` - `b`) <= (`atol` + `rtol` * absolute(`b`))
+    
+    Return 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 = getdata(a)
-    d2 = getdata(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 np.alltrue(np.ravel(d))
+    if fill_value is not None:
+        warnings.warn("The use of fill_value is deprecated."\
+                      " Please use masked_equal instead.")
+        masked_equal = fill_value
+    #
+    x = masked_array(a, copy=False)
+    y = masked_array(b, copy=False)
+    m = mask_or(getmask(x), getmask(y))
+    xinf = np.isinf(masked_array(x, copy=False, mask=m)).filled(False)
+    # If we have some infs, they should fall at the same place.
+    if not np.all(xinf == filled(np.isinf(y), False)):
+        return False
+    # No infs at all
+    if not np.any(xinf):
+        d = filled(umath.less_equal(umath.absolute(x-y),
+                                    atol + rtol * umath.absolute(y)),
+                   masked_equal)
+        return np.all(d)
+    if not np.all(filled(x[xinf] == y[xinf], masked_equal)):
+        return False
+    x = x[~xinf]
+    y = y[~xinf]
+    d = filled(umath.less_equal(umath.absolute(x-y),
+                                atol + rtol * umath.absolute(y)),
+               masked_equal)
+    return np.all(d)
 
 #..............................................................................
 def asarray(a, dtype=None):
-    """asarray(data, dtype) = array(data, dtype, copy=0, subok=0)
-
+    """
+    Convert the input to a masked array.
+    
+    Parameters
+    ----------
+    a : array_like
+        Input data, in any form that can be converted to an array.  This
+        includes lists, lists of tuples, tuples, tuples of tuples, tuples
+        of lists and ndarrays.
+    dtype : data-type, optional
+        By default, the data-type is inferred from the input data.
+    order : {'C', 'F'}, optional
+        Whether to use row-major ('C') or column-major ('FORTRAN') memory
+        representation.  Defaults to 'C'.
+    
+    Returns
+    -------
+    out : ndarray
+        MaskedArray interpretation of `a`.  No copy is performed if the input
+        is already an ndarray.  If `a` is a subclass of ndarray, a base
+        class ndarray is returned.
     Return a as a MaskedArray object of the given dtype.
     If dtype is not given or None, is is set to the dtype of a.
     No copy is performed if a is already an array.
@@ -4127,7 +4275,7 @@
 #####--------------------------------------------------------------------------
 #---- --- Pickling ---
 #####--------------------------------------------------------------------------
-def dump(a,F):
+def dump(a, F):
     """
     Pickle the MaskedArray `a` to the file `F`.  `F` can either be
     the handle of an exiting file, or a string representing a file
@@ -4135,8 +4283,8 @@
 
     """
     if not hasattr(F,'readline'):
-        F = open(F,'w')
-    return cPickle.dump(a,F)
+        F = open(F, 'w')
+    return cPickle.dump(a, F)
 
 def dumps(a):
     """

Modified: branches/1.2.x/numpy/ma/tests/test_core.py
===================================================================
--- branches/1.2.x/numpy/ma/tests/test_core.py	2008-11-21 20:49:33 UTC (rev 6087)
+++ branches/1.2.x/numpy/ma/tests/test_core.py	2008-11-21 21:50:17 UTC (rev 6088)
@@ -69,7 +69,7 @@
         self.failUnless(not isMaskedArray(x))
         self.failUnless(isMaskedArray(xm))
         self.failUnless((xm-ym).filled(0).any())
-        fail_if_equal(xm.mask.astype(int_), ym.mask.astype(int_))
+        fail_if_equal(xm.mask.astype(int), ym.mask.astype(int))
         s = x.shape
         assert_equal(np.shape(xm), s)
         assert_equal(xm.shape, s)
@@ -593,8 +593,13 @@
         assert_equal(y.shape, x.shape)
         assert_equal(y._mask, [True, True])
 
+    def test_arithmetic_with_masked_singleton_on_1d_singleton(self):
+        "Check that we're not losing the shape of a singleton"
+        x = masked_array([1, ])
+        y = x + masked
+        assert_equal(y.shape, x.shape)
+        assert_equal(y.mask, [True, ])
 
-
     def test_scalar_arithmetic(self):
         x = array(0, mask=0)
         assert_equal(x.filled().ctypes.data, x.ctypes.data)
@@ -1293,6 +1298,27 @@
         assert_equal(m.transpose(), m._data.transpose())
 
 
+    def test_allclose(self):
+        "Tests allclose on arrays"
+        a = np.random.rand(10)
+        b = a + np.random.rand(10) * 1e-8
+        self.failUnless(allclose(a,b))
+        # Test allclose w/ infs
+        a[0] = np.inf
+        self.failUnless(not allclose(a,b))
+        b[0] = np.inf
+        self.failUnless(allclose(a,b))
+        # Test all close w/ masked
+        a = masked_array(a)
+        a[-1] = masked
+        self.failUnless(allclose(a,b, masked_equal=True))
+        self.failUnless(not allclose(a, b, masked_equal=False))
+        # Test comparison w/ scalar
+        a *= 1e-8
+        a[0] = 0
+        self.failUnless(allclose(a, 0, masked_equal=True))
+        
+
     def test_allany(self):
         """Checks the any/all methods/functions."""
         x = np.array([[ 0.13,  0.26,  0.90],
@@ -1462,7 +1488,7 @@
 
     def test_empty(self):
         "Tests empty/like"
-        datatype = [('a',int_),('b',float),('c','|S8')]
+        datatype = [('a',int),('b',float),('c','|S8')]
         a = masked_array([(1,1.1,'1.1'),(2,2.2,'2.2'),(3,3.3,'3.3')],
                          dtype=datatype)
         assert_equal(len(a.fill_value.item()), len(datatype))
@@ -1727,7 +1753,7 @@
         x = array(zip([1,2,3],
                       [1.1,2.2,3.3],
                       ['one','two','thr']),
-                  dtype=[('a',int_),('b',float),('c','|S8')])
+                  dtype=[('a',int),('b',float),('c','|S8')])
         x[-1] = masked
         assert_equal(x.tolist(), [(1,1.1,'one'),(2,2.2,'two'),(None,None,None)])
 
@@ -1952,7 +1978,7 @@
         m2XX = array(data=XX,mask=m2.reshape(XX.shape))
         self.d =  (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX)
 
-    #------------------------------------------------------
+
     def test_varstd(self):
         "Tests var & std on MaskedArrays."
         (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d
@@ -2016,6 +2042,7 @@
         assert_equal(masked_where(not_equal(x,2), x), masked_not_equal(x,2))
         assert_equal(masked_where([1,1,0,0,0], [1,2,3,4,5]), [99,99,3,4,5])
 
+
     def test_masked_where_oddities(self):
         """Tests some generic features."""
         atest = ones((10,10,10), dtype=float)
@@ -2023,6 +2050,7 @@
         ctest = masked_where(btest,atest)
         assert_equal(atest,ctest)
 
+
     def test_masked_where_shape_constraint(self):
         a = arange(10)
         try:
@@ -2140,11 +2168,12 @@
         tmp[(xm<=2).filled(True)] = True
         assert_equal(d._mask, tmp)
         #
-        ixm = xm.astype(int_)
+        ixm = xm.astype(int)
         d = where(ixm>2, ixm, masked)
         assert_equal(d, [-9,-9,-9,-9, -9, 4, -9, -9, 10, -9, -9, 3])
         assert_equal(d.dtype, ixm.dtype)
 
+
     def test_where_with_masked_choice(self):
         x = arange(10)
         x[3] = masked
@@ -2285,6 +2314,70 @@
         test = make_mask_descr(ntype)
         assert_equal(test, np.dtype(np.bool))
 
+
+    def test_make_mask(self):
+        "Test make_mask"
+        # w/ a list as an input
+        mask = [0,1]
+        test = make_mask(mask)
+        assert_equal(test.dtype, MaskType)
+        assert_equal(test, [0,1])
+        # w/ a ndarray as an input
+        mask = np.array([0,1], dtype=np.bool)
+        test = make_mask(mask)
+        assert_equal(test.dtype, MaskType)
+        assert_equal(test, [0,1])
+        # w/ a flexible-type ndarray as an input - use default
+        mdtype = [('a', np.bool), ('b', np.bool)]
+        mask = np.array([(0, 0), (0, 1)], dtype=mdtype)
+        test = make_mask(mask)
+        assert_equal(test.dtype, MaskType)
+        assert_equal(test, [1,1])
+        # w/ a flexible-type ndarray as an input - use input dtype
+        mdtype = [('a', np.bool), ('b', np.bool)]
+        mask = np.array([(0, 0), (0, 1)], dtype=mdtype)
+        test = make_mask(mask, dtype=mask.dtype)
+        assert_equal(test.dtype, mdtype)
+        assert_equal(test, mask)
+        # w/ a flexible-type ndarray as an input - use input dtype
+        mdtype = [('a', np.float), ('b', np.float)]
+        bdtype = [('a', np.bool), ('b', np.bool)]
+        mask = np.array([(0, 0), (0, 1)], dtype=mdtype)
+        test = make_mask(mask, dtype=mask.dtype)
+        assert_equal(test.dtype, bdtype)
+        assert_equal(test, np.array([(0, 0), (0, 1)], dtype=bdtype))
+
+
+    def test_mask_or(self):
+        # Initialize
+        mtype = [('a', np.bool), ('b', np.bool)]
+        mask = np.array([(0, 0), (0, 1), (1, 0), (0, 0)], dtype=mtype)
+        # Test using nomask as input
+        test = mask_or(mask, nomask)
+        assert_equal(test, mask)
+        test = mask_or(nomask, mask)
+        assert_equal(test, mask)
+        # Using False as input
+        test = mask_or(mask, False)
+        assert_equal(test, mask)
+        # Using True as input. Won't work, but keep it for the kicks
+        #test = ma.mask_or(mask, True)
+        #control = np.array([(1, 1), (1, 1), (1, 1), (1, 1)], dtype=mtype)
+        #assert_equal(test, control)
+        # Using another array w/ the same dtype
+        other = np.array([(0, 1), (0, 1), (0, 1), (0, 1)], dtype=mtype)
+        test = mask_or(mask, other)
+        control = np.array([(0, 1), (0, 1), (1, 1), (0, 1)], dtype=mtype)
+        assert_equal(test, control)
+        # Using another array w/ a different dtype
+        othertype = [('A', np.bool), ('B', np.bool)]
+        other = np.array([(0, 1), (0, 1), (0, 1), (0, 1)], dtype=othertype)
+        try:
+            test = mask_or(mask, other)
+        except ValueError:
+            pass
+
+
 #------------------------------------------------------------------------------
 
 class TestMaskedFields(TestCase):
@@ -2407,6 +2500,8 @@
         assert_equal_records(a[-2]._mask, a._mask[-2])
 
 
+#------------------------------------------------------------------------------
+
 class TestMaskedView(TestCase):
     #
     def setUp(self):



More information about the Numpy-svn mailing list