[Numpy-svn] r4767 - in branches/maskedarray/numpy/ma: . tests

numpy-svn at scipy.org numpy-svn at scipy.org
Fri Feb 1 18:40:40 EST 2008


Author: pierregm
Date: 2008-02-01 17:40:35 -0600 (Fri, 01 Feb 2008)
New Revision: 4767

Modified:
   branches/maskedarray/numpy/ma/core.py
   branches/maskedarray/numpy/ma/mrecords.py
   branches/maskedarray/numpy/ma/tests/test_core.py
   branches/maskedarray/numpy/ma/tests/test_mrecords.py
Log:
maskedarray.core:
tolist : make sure that a masked record is output as a tuple of None

maskedarray.mrecords:
* introduced mrecarray as the equivalent of recarray w/ fieldmask
* simplified fromarrays/fromrecords

Modified: branches/maskedarray/numpy/ma/core.py
===================================================================
--- branches/maskedarray/numpy/ma/core.py	2008-02-01 11:17:39 UTC (rev 4766)
+++ branches/maskedarray/numpy/ma/core.py	2008-02-01 23:40:35 UTC (rev 4767)
@@ -191,8 +191,8 @@
         else:
             fill_value = default_fill_value(dtype)
     else:
-        fval = numpy.resize(narray(fill_value,copy=False,dtype=object_),
-                            len(descr))
+        fill_value = narray(fill_value).tolist() 
+        fval = numpy.resize(fill_value, len(descr))
         if len(descr) > 1:
             fill_value = [numpy.asarray(f).astype(d[1]).item()
                           for (f,d) in zip(fval, descr)]
@@ -2491,15 +2491,23 @@
         if fill_value is not None:
             return self.filled(fill_value).tolist()
         result = self.filled().tolist()
-        if self._mask is nomask:
+        # Set temps to save time when dealing w/ mrecarrays...
+        _mask = self._mask
+        if _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]
+        nbdims = self.ndim
+        dtypesize = len(self.dtype)
+        if nbdims == 0:
+            return tuple([None]*dtypesize)
+        elif nbdims == 1:
+            maskedidx = _mask.nonzero()[0].tolist()
+            if dtypesize:
+                nodata = tuple([None]*dtypesize)
+            else:
+                nodata = None
+            [operator.setitem(result,i,nodata) for i in maskedidx]
         else:
-            for idx in zip(*[i.tolist() for i in self._mask.nonzero()]):
+            for idx in zip(*[i.tolist() for i in _mask.nonzero()]):
                 tmp = result
                 for i in idx[:-1]:
                     tmp = tmp[i]

Modified: branches/maskedarray/numpy/ma/mrecords.py
===================================================================
--- branches/maskedarray/numpy/ma/mrecords.py	2008-02-01 11:17:39 UTC (rev 4766)
+++ branches/maskedarray/numpy/ma/mrecords.py	2008-02-01 23:40:35 UTC (rev 4767)
@@ -1,35 +1,49 @@
 """mrecords
-Defines a class of record arrays supporting masked arrays.
 
+Defines the equivalent of recarrays for maskedarray.
+Masked arrays already support named fields, but masking works only by records.
+By comparison, mrecarrays support masking individual fields.
+
 :author: Pierre Gerard-Marchant
 """
+#TODO: We should make sure that no field is called '_mask','mask','_fieldmask',
+#TODO: ...or whatever restricted keywords.
+#TODO: An idea would be to no bother in the first place, and then rename the
+#TODO: invalid fields with a trailing underscore...
+#TODO: Maybe we could just overload the parser function ?
+
+
 __author__ = "Pierre GF Gerard-Marchant"
 
 import sys
 import types
 
 import numpy
-from numpy import bool_, complex_, float_, int_, str_, object_
+from numpy import bool_, complex_, float_, int_, str_, object_, dtype
 from numpy import array as narray
 import numpy.core.numeric as numeric
 import numpy.core.numerictypes as ntypes
 from numpy.core.defchararray import chararray
-from numpy.core.records import find_duplicate
-
-from numpy.core.records import format_parser, record, recarray
+from numpy.core.records import find_duplicate, format_parser, record, recarray
 from numpy.core.records import fromarrays as recfromarrays
+from numpy.core.records import fromrecords as recfromrecords
 
 ndarray = numeric.ndarray
 _byteorderconv = numpy.core.records._byteorderconv
 _typestr = ntypes._typestr
 
 import numpy.ma
-from numpy.ma import MaskedArray, masked, nomask, masked_array,\
-    make_mask, mask_or, getmask, getmaskarray, filled
+from numpy.ma import MAError, MaskedArray, masked, nomask, masked_array,\
+    make_mask, mask_or, getdata, getmask, getmaskarray, filled
 from numpy.ma.core import default_fill_value, masked_print_option
+_check_fill_value = numpy.ma.core._check_fill_value
 
 import warnings
 
+__all__ = ['MaskedRecords','mrecarray',
+           'fromarrays','fromrecords','fromtextfile','addfield',
+           ]
+
 reserved_fields = ['_data','_mask','_fieldmask', 'dtype']
 
 def _getformats(data):
@@ -80,7 +94,13 @@
     return numeric.dtype(ndescr)
 
 
+def _get_fieldmask(self):
+    mdescr = [(n,'|b1') for n in self.dtype.names]
+    fdmask = numpy.empty(self.shape, dtype=mdescr)
+    fdmask.flat = tuple([False]*len(mdescr))
+    return fdmask
 
+
 class MaskedRecords(MaskedArray, object):
     """
 
@@ -96,89 +116,131 @@
     """
     _defaultfieldmask = nomask
     _defaulthardmask = False
-    def __new__(cls, data, mask=nomask, dtype=None,
-                hard_mask=False, fill_value=None,
-#                offset=0, strides=None,
+    #............................................
+    def __new__(cls, shape, dtype=None, buf=None, offset=0, strides=None,
                 formats=None, names=None, titles=None,
-                byteorder=None, aligned=False):
-        # Get the new descriptor ................
-        if dtype is not None:
-            descr = numeric.dtype(dtype)
+                byteorder=None, aligned=False,
+                mask=nomask, hard_mask=False, fill_value=None, keep_mask=True,
+                copy=False,
+                **options):
+        #
+        self = recarray.__new__(cls, shape, dtype=dtype, buf=buf, offset=offset,
+                                strides=strides, formats=formats,
+                                byteorder=byteorder, aligned=aligned,)
+#        self = self.view(cls)
+        #
+        mdtype = [(k,'|b1') for (k,_) in self.dtype.descr]
+        if mask is nomask or not numpy.size(mask):
+            if not keep_mask:
+                self._fieldmask = tuple([False]*len(mdtype))
         else:
-            if formats is None:
-                formats = _getformats(data)
-            parsed = format_parser(formats, names, titles, aligned, byteorder)
-            descr = parsed._descr
-        if names is not None:
-            descr = _checknames(descr,names)
-        _names = descr.names
-        mdescr = [(n,'|b1') for n in _names]
-        # get the shape .........................
-        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))
+            mask = narray(mask, copy=copy)
+            if mask.shape != self.shape:
+                (nd, nm) = (self.size, mask.size)
+                if nm == 1:
+                    mask = numpy.resize(mask, self.shape)
+                elif nm == nd:
+                    mask = numpy.reshape(mask, self.shape)
+                else:
+                    msg = "Mask and data not compatible: data size is %i, "+\
+                          "mask size is %i."
+                    raise MAError(msg % (nd, nm))
+                copy = True        
+            if not keep_mask:
+                self.__setmask__(mask)
+                self._sharedmask = True
             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
-
+                if mask.dtype == mdtype:
+                    _fieldmask = mask
+                else:
+                    _fieldmask = narray([tuple([m]*len(mdtype)) for m in mask],
+                                        dtype=mdtype)
+                self._fieldmask = _fieldmask
+        return self
+    #......................................................
     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
-                                )
+        # Make sure we have a _fieldmask by default ..
+        _fieldmask = getattr(obj, '_fieldmask', None)
+        if _fieldmask is None:
+            mdescr = [(n,'|b1') for (n,_) in self.dtype.descr]
+            _fieldmask = numpy.empty(self.shape, dtype=mdescr).view(recarray)
+            _fieldmask.flat = tuple([False]*len(mdescr))
+        # Update some of the attributes
+        attrdict = dict(_fieldmask=_fieldmask,
+                        _hardmask=getattr(obj,'_hardmask',False),
+                        _fill_value=getattr(obj,'_fill_value',None))
+        self.__dict__.update(attrdict)
+        # Finalize as a regular maskedarray .....
+        # Note: we can't call MaskedArray.__array_finalize__, it chokes pickling
+        self._update_from(obj)
+        # 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 _getdata(self):
         "Returns the data as a recarray."
         return self.view(recarray)
     _data = property(fget=_getdata)
+    #......................................................
+    def __setmask__(self, mask):
+        "Sets the mask and update the fieldmask."
+        names = self.dtype.names
+        fmask = self.__dict__['_fieldmask']
+        newmask = make_mask(mask, copy=False)
+        if names is not None:
+            if self._hardmask:
+                for n in names:
+                    fmask[n].__ior__(newmask)
+            else:
+                for n in names:
+                    fmask[n].flat = newmask
+        return
+    _setmask = __setmask__
+    #
+    def _getmask(self):
+        """Return 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)
+        else:
+            return self._fieldmask.view((bool_, len(self.dtype))).all()
+    _mask = property(fget=_getmask, fset=_setmask)
+    #......................................................
+    def get_fill_value(self):
+        """Return the filling value.
 
+        """
+        if self._fill_value is None:
+            ddtype = self.dtype
+            fillval = _check_fill_value(None, ddtype)
+            self._fill_value = narray(tuple(fillval), dtype=ddtype)
+        return self._fill_value
+
+    def set_fill_value(self, value=None):
+        """Set the filling value to value.
+
+        If value is None, use a default based on the data type.
+
+        """
+        ddtype = self.dtype
+        fillval = _check_fill_value(value, ddtype)
+        self._fill_value = narray(tuple(fillval), dtype=ddtype)
+
+    fill_value = property(fget=get_fill_value, fset=set_fill_value,
+                          doc="Filling value.")
     #......................................................
+    def __len__(self):
+        "Returns the length"
+        # We have more than one record
+        if self.ndim:
+            return len(self._data)
+        # We have only one record: return the nb of fields
+        return len(self.dtype)
+    #......................................................
     def __getattribute__(self, attr):
         "Returns the given attribute."
         try:
@@ -215,7 +277,15 @@
                 exctype, value = sys.exc_info()[:2]
                 raise exctype, value
         else:
-            if attr not in list(self.dtype.names) + ['_mask','mask']:
+            # Get the list of names ......
+            _names = self.dtype.names
+            if _names is None:
+                _names = []
+            else:
+                _names = list(_names)
+            _names += ['_mask','mask']
+            # Check the attribute
+            if attr not in _names:
                 return ret
             if newattr:         # We just added this one
                 try:            #  or this setattr worked on an internal
@@ -227,14 +297,26 @@
         base_fmask = self._fieldmask
         _names = self.dtype.names
         if attr in _names:
-            fval = filled(val)
-            mval = getmaskarray(val)
+            if val is masked:
+                fval = self.fill_value[attr]
+                mval = True
+            else:
+                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':
+            #FIXME: We should check for self._harmask over there.
+#            if self._hardmask:
+#                val = make_mask(val)
+#                if val is not nomask:
+##                    mval = getmaskarray(val)
+#                    for k in _names:
+#                        m = mask_or(val, base_fmask.__getattr__(k))
+#                        base_fmask.__setattr__(k, m)
             self.__setmask__(val)
             return
     #............................................
@@ -242,30 +324,29 @@
         """Returns all the fields sharing the same fieldname base.
 The fieldname base is either `_data` or `_mask`."""
         _localdict = self.__dict__
+        _fieldmask = _localdict['_fieldmask']
         _data = self._data
         # We want a field ........
         if isinstance(indx, basestring):
             obj = _data[indx].view(MaskedArray)
-            obj._set_mask(_localdict['_fieldmask'][indx])
+            obj._set_mask(_fieldmask[indx])
             # Force to nomask if the mask is empty
             if not obj._mask.any():
                 obj._mask = nomask
+            # Force to masked if the mask is True
+            if not obj.ndim and obj._mask:
+                return masked
             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)
+        obj = narray(_data[indx], copy=False).view(mrecarray)
+        obj._fieldmask = narray(_fieldmask[indx], copy=False).view(recarray)
         return obj
-    #............................................
+    #....
     def __setitem__(self, indx, value):
         "Sets the given record to value."
         MaskedArray.__setitem__(self, indx, value)
-
-
+    #............................................
     def __setslice__(self, i, j, value):
         "Sets the slice described by [i,j] to `value`."
         _localdict = self.__dict__
@@ -295,31 +376,6 @@
                     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):
-        "Sets the 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
-        return
-
-    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):
         "Calculates the string representation."
@@ -331,14 +387,14 @@
             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)
-
+    #
     def __repr__(self):
         "Calculates the repr representation."
         _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),
+        reprstr.extend([fmt % ('    fill_value', self.fill_value),
                          '              )'])
         return str("\n".join(reprstr))
     #......................................................
@@ -355,11 +411,11 @@
         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``. If ``fill_value`` is None, ``self.fill_value`` is
-used instead.
+        """Returns an array of the same class as the _data part, where masked 
+    values are filled with fill_value.
+    If fill_value is None, self.fill_value is used instead.
 
-Subclassing is preserved.
+    Subclassing is preserved.
 
         """
         _localdict = self.__dict__
@@ -369,7 +425,7 @@
             return d
         #
         if fill_value is None:
-            value = _localdict['_fill_value']
+            value = _check_fill_value(_localdict['_fill_value'],self.dtype)
         else:
             value = fill_value
             if numeric.size(value) == 1:
@@ -383,168 +439,213 @@
                 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)
-    #.............................................
+        copied = self._data.copy().view(type(self))
+        copied._fieldmask = self._fieldmask.copy()
+        return copied
+    #......................................................
+    def tolist(self, fill_value=None):
+        """Copy 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 = narray(self.filled().tolist(), dtype=object)
+        mask = narray(self._fieldmask.tolist())
+        result[mask] = None
+        return [tuple(r) for r in result]
+    #--------------------------------------------
+    # Pickling
+    def __getstate__(self):
+        """Return the internal state of the masked array, for pickling purposes.
+
+        """
+        state = (1,
+                 self.shape,
+                 self.dtype,
+                 self.flags.fnc,
+                 self._data.tostring(),
+                 self._fieldmask.tostring(),
+                 self._fill_value,
+                 )
+        return state  
+    #
+    def __setstate__(self, state):
+        """Restore 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))
+        mdtype = dtype([(k,bool_) for (k,_) in self.dtype.descr])
+        self._fieldmask.__setstate__((shp, mdtype, isf, msk))
+        self.fill_value = flv
+    #
+    def __reduce__(self):
+        """Return a 3-tuple for pickling a MaskedArray.
+
+        """
+        return (_mrreconstruct,
+                (self.__class__, self._baseclass, (0,), 'b', ),
+                self.__getstate__())
+
+def _mrreconstruct(subtype, baseclass, baseshape, basetype,):
+    """Internal function that builds a new MaskedArray from the
+    information stored in a pickle.
+
+    """
+    _data = ndarray.__new__(baseclass, baseshape, basetype).view(subtype)
+#    _data._mask = ndarray.__new__(ndarray, baseshape, 'b1')
+#    return _data
+    _mask = ndarray.__new__(ndarray, baseshape, 'b1')
+    return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype,)
+
+
+mrecarray = MaskedRecords
+
 #####---------------------------------------------------------------------------
 #---- --- Constructors ---
 #####---------------------------------------------------------------------------
 
 def fromarrays(arraylist, dtype=None, shape=None, formats=None,
-               names=None, titles=None, aligned=False, byteorder=None):
+               names=None, titles=None, aligned=False, byteorder=None,
+               fill_value=None):
     """Creates a mrecarray from a (flat) list of masked arrays.
 
-*Parameters*:
-    arraylist : {sequence}
+    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}
+    dtype : numeric.dtype
         Data type descriptor.
-    {shape} : {integer}
-        Number of records. If None, ``shape`` is defined from the shape of the
+    shape : integer
+        Number of records. If None, shape is defined from the shape of the
         first array in the list.
-    formats : {sequence}
+    formats : sequence
         Sequence of formats for each individual field. If None, the formats will
         be autodetected by inspecting the fields and selecting the highest dtype
         possible.
-    names : {sequence}
+    names : sequence
         Sequence of the names of each field.
-    -titles : {sequence}
+    titles : sequence
       (Description to write)
-    aligned : {boolean}
+    aligned : boolean
       (Description to write, not used anyway)
-    byteorder: {boolean}
+    byteorder: boolean
       (Description to write, not used anyway)
+    fill_value : sequence
+        Sequence of data to be used as filling values.
 
-*Notes*:
+    Notes
+    -----
     Lists of tuples should be preferred over lists of lists for faster processing.
     """
-    arraylist = [masked_array(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)
+    datalist = [getdata(x) for x in arraylist]
+    masklist = [getmaskarray(x) for x in arraylist]
+    _array = recfromarrays(datalist, 
+                           dtype=dtype, shape=shape, formats=formats, 
+                           names=names, titles=titles, aligned=aligned,
+                           byteorder=byteorder).view(mrecarray)
+    _array._fieldmask[:] = zip(*masklist)
+    if fill_value is not None:
+        _array.fill_value = fill_value
+    return _array
+
+
 #..............................................................................
 def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None,
-                titles=None, aligned=False, byteorder=None):
+                titles=None, aligned=False, byteorder=None,
+                fill_value=None, mask=nomask):
     """Creates a MaskedRecords from a list of records.
 
-*Parameters*:
-    arraylist : {sequence}
+    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}
+    dtype : numeric.dtype
         Data type descriptor.
-    {shape} : {integer}
+    shape : integer
         Number of records. If None, ``shape`` is defined from the shape of the
         first array in the list.
-    formats : {sequence}
+    formats : sequence
         Sequence of formats for each individual field. If None, the formats will
         be autodetected by inspecting the fields and selecting the highest dtype
         possible.
-    names : {sequence}
+    names : sequence
         Sequence of the names of each field.
-    -titles : {sequence}
+    titles : sequence
       (Description to write)
-    aligned : {boolean}
+    aligned : boolean
       (Description to write, not used anyway)
-    byteorder: {boolean}
+    byteorder: boolean
       (Description to write, not used anyway)
+    fill_value : sequence
+        Sequence of data to be used as filling values.
+    mask : sequence or boolean.
+        External mask to apply on the data.
 
 *Notes*:
     Lists of tuples should be preferred over lists 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 .....
+    # Grab the initial _fieldmask, if needed:
+    _fieldmask = getattr(reclist, '_fieldmask', None)
+    # Get the list of records.....
     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
+    if isinstance(reclist, ndarray):
+        # Make sure we don't have some hidden mask
+        if isinstance(reclist,MaskedArray):
+            reclist = reclist.filled().view(ndarray)
+        # Grab the initial dtype, just in case
+        if dtype is None:
+            dtype = reclist.dtype
+        reclist = reclist.tolist()
+    mrec = recfromrecords(reclist, dtype=dtype, shape=shape, formats=formats, 
+                          names=names, titles=titles, 
+                          aligned=aligned, byteorder=byteorder).view(mrecarray)
+    # Set the fill_value if needed
+    if fill_value is not None:
+        mrec.fill_value = fill_value
+    # Now, let's deal w/ the mask
+    if mask is not nomask:
+        mask = narray(mask, copy=False)
+        maskrecordlength = len(mask.dtype)
+        if maskrecordlength:
+            mrec._fieldmask.flat = mask
+        elif len(mask.shape) == 2:
+            mrec._fieldmask.flat = [tuple(m) for m in mask]
         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
+            mrec._mask = mask
+    if _fieldmask is not None:
+        mrec._fieldmask[:] = _fieldmask
+    return mrec
 
-    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.
@@ -648,7 +749,7 @@
     _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)
+    return fromarrays(_datalist, dtype=mdescr)
 
 #....................................................................
 def addfield(mrecord, newfield, newfieldname=None):
@@ -685,28 +786,6 @@
     newdata._fieldmask = newmask
     return newdata
 
-################################################################################
-if __name__ == '__main__':
-    import numpy as N
-    from numpy.ma.testutils import assert_equal
-    if 1:
-        d = N.arange(5)
-        m = numpy.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 = masked_array(base_d, mask=base_m).T
-        mrecord = fromarrays(base,dtype=[('a',N.float_),('b',N.float_)])
-        mrec = MaskedRecords(mrecord.copy())
-        #
-    if 1:
-        mrec = mrec.copy()
-        mrec.harden_mask()
-        assert(mrec._hardmask)
-        mrec._mask = nomask
-        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
-        mrec.soften_mask()
-        assert(not mrec._hardmask)
-        mrec.mask = nomask
-        tmp = mrec['b']._mask
-        assert(mrec['b']._mask is nomask)
-        assert_equal(mrec['a']._mask,mrec['b']._mask)
+###############################################################################
+
+        

Modified: branches/maskedarray/numpy/ma/tests/test_core.py
===================================================================
--- branches/maskedarray/numpy/ma/tests/test_core.py	2008-02-01 11:17:39 UTC (rev 4766)
+++ branches/maskedarray/numpy/ma/tests/test_core.py	2008-02-01 23:40:35 UTC (rev 4767)
@@ -1347,7 +1347,16 @@
         assert_equal(xlist[0],[0,None,2,3])
         assert_equal(xlist[1],[4,5,6,7])
         assert_equal(xlist[2],[8,9,None,11])
+        # Make sure a masked record is output as a tuple of None
+        x = array(zip([1,2,3],
+                      [1.1,2.2,3.3],
+                      ['one','two','thr']),
+                  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)])
+        
 
+
     def test_squeeze(self):
         "Check squeeze"
         data = masked_array([[1,2,3]])

Modified: branches/maskedarray/numpy/ma/tests/test_mrecords.py
===================================================================
--- branches/maskedarray/numpy/ma/tests/test_mrecords.py	2008-02-01 11:17:39 UTC (rev 4766)
+++ branches/maskedarray/numpy/ma/tests/test_mrecords.py	2008-02-01 23:40:35 UTC (rev 4767)
@@ -1,33 +1,35 @@
 # pylint: disable-msg=W0611, W0612, W0511,R0201
-"""Tests suite for mrecarray.
+"""Tests suite for mrecords.
 
 :author: Pierre Gerard-Marchant
 :contact: pierregm_at_uga_dot_edu
-:version: $Id: test_mrecords.py 3473 2007-10-29 15:18:13Z jarrod.millman $
 """
 __author__ = "Pierre GF Gerard-Marchant ($Author: jarrod.millman $)"
-__version__ = '1.0'
 __revision__ = "$Revision: 3473 $"
 __date__     = '$Date: 2007-10-29 17:18:13 +0200 (Mon, 29 Oct 2007) $'
 
 import types
 
 import numpy as N
-import numpy.core.fromnumeric  as fromnumeric
+from numpy import recarray, bool_, int_, float_
+from numpy import array as narray
+from numpy.core.records import fromrecords as recfromrecords
+from numpy.core.records import fromarrays as recfromarrays
+import numpy.core.fromnumeric  as fromnumeric_
 from numpy.testing import NumpyTest, NumpyTestCase
 from numpy.testing.utils import build_err_msg
 
 import numpy.ma.testutils
-from numpy.ma.testutils import *
+from numpy.ma.testutils import assert_equal, assert_equal_records
 
 import numpy.ma
-from numpy.ma import masked_array, masked, nomask
+from numpy.ma import masked_array, masked, nomask, getdata, getmaskarray
 
 #import numpy.ma.mrecords
 #from numpy.ma.mrecords import mrecarray, fromarrays, fromtextfile, fromrecords
 
 import numpy.ma.mrecords
-from numpy.ma.mrecords import MaskedRecords, \
+from numpy.ma.mrecords import MaskedRecords, mrecarray,\
     fromarrays, fromtextfile, fromrecords, addfield
 
 #..............................................................................
@@ -39,111 +41,275 @@
 
     def setup(self):
         "Generic setup"
-        d = N.arange(5)
-        m = numpy.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 = masked_array(base_d, mask=base_m)
-        mrecord = fromarrays(base.T, dtype=[('a',N.float_),('b',N.float_)])
-        self.data = [d, m, mrecord]
+        ilist = [1,2,3,4,5]
+        flist = [1.1,2.2,3.3,4.4,5.5]
+        slist = ['one','two','three','four','five']
+        ddtype = [('a',int_),('b',float_),('c','|S8')]
+        mask = [0,1,0,0,1]
+        self.base = masked_array(zip(ilist,flist,slist), mask=mask, dtype=ddtype)
+        
+    def test_byview(self):
+        "Test creation by view"
+        base = self.base
+        mbase = base.view(mrecarray)
+        assert_equal(mbase._mask, base._mask)
+        assert isinstance(mbase._data, recarray)
+        assert_equal_records(mbase._data, base._data.view(recarray))
+        for field in ('a','b','c'):
+            assert_equal(base[field], mbase[field])
+        assert_equal_records(mbase.view(mrecarray), mbase)
 
     def test_get(self):
         "Tests fields retrieval"
-        [d, m, mrec] = self.data
-        mrec = mrec.copy()
-        assert_equal(mrec.a, masked_array(d,mask=m))
-        assert_equal(mrec.b, masked_array(d[::-1],mask=m[::-1]))
-        assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]], dtype=mrec._fieldmask.dtype)).all())
-        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
-        assert_equal(mrec.a[1], mrec[1].a)
-        #
-        assert(isinstance(mrec[:2], MaskedRecords))
-        assert_equal(mrec[:2]['a'], d[:2])
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        # As fields..........
+        for field in ('a','b','c'):
+            assert_equal(getattr(mbase,field), mbase[field])
+            assert_equal(base[field], mbase[field])
+        # as elements .......
+        mbase_first = mbase[0]
+        assert isinstance(mbase_first, mrecarray)
+        assert_equal(mbase_first.dtype, mbase.dtype)
+        assert_equal(mbase_first.tolist(), (1,1.1,'one'))
+        assert_equal(mbase_first.mask, nomask)
+        assert_equal(mbase_first._fieldmask.item(), (False, False, False))
+        assert_equal(mbase_first['a'], mbase['a'][0])
+        mbase_last = mbase[-1]        
+        assert isinstance(mbase_last, mrecarray)
+        assert_equal(mbase_last.dtype, mbase.dtype)
+        assert_equal(mbase_last.tolist(), (None,None,None))
+        assert_equal(mbase_last.mask, True)
+        assert_equal(mbase_last._fieldmask.item(), (True, True, True))
+        assert_equal(mbase_last['a'], mbase['a'][-1])
+        assert (mbase_last['a'] is masked)
+        # as slice ..........
+        mbase_sl = mbase[:2]
+        assert isinstance(mbase_sl, mrecarray)
+        assert_equal(mbase_sl.dtype, mbase.dtype)
+        assert_equal(mbase_sl._mask, [0,1])
+        assert_equal_records(mbase_sl, base[:2].view(mrecarray))
+        for field in ('a','b','c'):
+            assert_equal(getattr(mbase_sl,field), base[:2][field])
 
-    def test_set(self):
-        "Tests setting fields/attributes."
-        [d, m, mrecord] = self.data
-        mrecord.a._data[:] = 5
-        assert_equal(mrecord['a']._data, [5,5,5,5,5])
-        mrecord.a = 1
-        assert_equal(mrecord['a']._data, [1]*5)
-        assert_equal(getmaskarray(mrecord['a']), [0]*5)
-        mrecord.b = masked
-        assert_equal(mrecord.b.mask, [1]*5)
-        assert_equal(getmaskarray(mrecord['b']), [1]*5)
-        mrecord._mask = masked
-        assert_equal(getmaskarray(mrecord['b']), [1]*5)
-        assert_equal(mrecord['a']._mask, mrecord['b']._mask)
-        mrecord._mask = nomask
-        assert_equal(getmaskarray(mrecord['b']), [0]*5)
-        assert_equal(mrecord['a']._mask, mrecord['b']._mask)
+    def test_set_fields(self):
+        "Tests setting fields."        
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        mbase = mbase.copy()
+        mbase.fill_value = (999999,1e20,'N/A')
+        # Change the data, the mask should be conserved
+        mbase.a._data[:] = 5
+        assert_equal(mbase['a']._data, [5,5,5,5,5])
+        assert_equal(mbase['a']._mask, [0,1,0,0,1])
+        # Change the elements, and the mask will follow
+        mbase.a = 1
+        assert_equal(mbase['a']._data, [1]*5)
+        assert_equal(getmaskarray(mbase['a']), [0]*5)
+        assert_equal(mbase._mask, [False]*5)
+        assert_equal(mbase._fieldmask.tolist(), 
+                     narray([(0,0,0),(0,1,1),(0,0,0),(0,0,0),(0,1,1)],
+                            dtype=bool_))
+        # Set a field to mask ........................
+        mbase.c = masked
+        assert_equal(mbase.c.mask, [1]*5)
+        assert_equal(getmaskarray(mbase['c']), [1]*5)
+        assert_equal(getdata(mbase['c']), ['N/A']*5)
+        assert_equal(mbase._fieldmask.tolist(), 
+                     narray([(0,0,1),(0,1,1),(0,0,1),(0,0,1),(0,1,1)],
+                            dtype=bool_))
+        # Set fields by slices .......................
+        mbase = base.view(mrecarray).copy()
+        mbase.a[3:] = 5
+        assert_equal(mbase.a, [1,2,3,5,5])
+        assert_equal(mbase.a._mask, [0,1,0,0,0])
+        mbase.b[3:] = masked
+        assert_equal(mbase.b, base['b'])
+        assert_equal(mbase.b._mask, [0,1,0,1,1])
+    #
+    def test_set_mask(self):
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        # Set the mask to True .......................
+        mbase._mask = masked
+        assert_equal(getmaskarray(mbase['b']), [1]*5)
+        assert_equal(mbase['a']._mask, mbase['b']._mask)
+        assert_equal(mbase['a']._mask, mbase['c']._mask)
+        assert_equal(mbase._fieldmask.tolist(), 
+                     narray([(1,1,1)]*5,
+                            dtype=bool_))
+        # Delete the mask ............................
+        mbase._mask = nomask
+        assert_equal(getmaskarray(mbase['c']), [0]*5)
+        assert_equal(mbase._fieldmask.tolist(), 
+                     narray([(0,0,0)]*5,
+                            dtype=bool_))
+    #
+    def test_set_elements(self):
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        # Set an element to mask .....................
+        mbase[-2] = masked
+        assert_equal(mbase._fieldmask.tolist(),
+                     narray([(0,0,0),(1,1,1),(0,0,0),(1,1,1),(1,1,1)],
+                             dtype=bool_))
+        assert_equal(mbase._mask, [0,1,0,1,1])
+        # Set slices .................................
+        mbase = base.view(mrecarray).copy()
+        mbase[:2] = 5
+        assert_equal(mbase.a._data, [5,5,3,4,5])
+        assert_equal(mbase.a._mask, [0,0,0,0,1])
+        assert_equal(mbase.b._data, [5.,5.,3.3,4.4,5.5])
+        assert_equal(mbase.b._mask, [0,0,0,0,1])
+        assert_equal(mbase.c._data, ['5','5','three','four','five'])
+        assert_equal(mbase.b._mask, [0,0,0,0,1])
         #
-    def test_setfields(self):
-        "Tests setting fields."
-        [d, m, mrecord] = self.data
-        mrecord.a[3:] = 5
-        assert_equal(mrecord.a, [0,1,2,5,5])
-        assert_equal(mrecord.a._mask, [1,0,0,0,0])
-        #
-        mrecord.b[3:] = masked
-        assert_equal(mrecord.b, [4,3,2,1,0])
-        assert_equal(mrecord.b._mask, [1,1,0,1,1])
-
-    def test_setslices(self):
-        "Tests setting slices."
-        [d, m, mrec] = self.data
-        mrec[:2] = 5
-        assert_equal(mrec.a._data, [5,5,2,3,4])
-        assert_equal(mrec.b._data, [5,5,2,1,0])
-        assert_equal(mrec.a._mask, [0,0,0,1,1])
-        assert_equal(mrec.b._mask, [0,0,0,0,1])
-        #
-        mrec[:2] = masked
-        assert_equal(mrec._mask, [1,1,0,0,1])
-        mrec[-2] = masked
-        assert_equal(mrec._mask, [1,1,0,1,1])
-        #
+        mbase = base.view(mrecarray).copy()
+        mbase[:2] = masked
+        assert_equal(mbase.a._data, [1,2,3,4,5])
+        assert_equal(mbase.a._mask, [1,1,0,0,1])
+        assert_equal(mbase.b._data, [1.1,2.2,3.3,4.4,5.5])
+        assert_equal(mbase.b._mask, [1,1,0,0,1])
+        assert_equal(mbase.c._data, ['one','two','three','four','five'])
+        assert_equal(mbase.b._mask, [1,1,0,0,1])
+    #
     def test_setslices_hardmask(self):
         "Tests setting slices w/ hardmask."
-        [d, m, mrec] = self.data
-        mrec.harden_mask()
-        mrec[-2:] = 5
-        assert_equal(mrec.a._data, [0,1,2,3,4])
-        assert_equal(mrec.b._data, [4,3,2,5,0])
-        assert_equal(mrec.a._mask, [1,0,0,1,1])
-        assert_equal(mrec.b._mask, [1,1,0,0,1])
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        mbase.harden_mask()
+        mbase[-2:] = 5
+        assert_equal(mbase.a._data, [1,2,3,5,5])
+        assert_equal(mbase.b._data, [1.1,2.2,3.3,5,5.5])
+        assert_equal(mbase.c._data, ['one','two','three','5','five'])
+        assert_equal(mbase.a._mask, [0,1,0,0,1])
+        assert_equal(mbase.b._mask, mbase.a._mask)
+        assert_equal(mbase.b._mask, mbase.c._mask)
 
     def test_hardmask(self):
         "Test hardmask"
-        [d, m, mrec] = self.data
-        mrec = mrec.copy()
-        mrec.harden_mask()
-        assert(mrec._hardmask)
-        mrec._mask = nomask
-        assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0))
-        mrec.soften_mask()
-        assert(not mrec._hardmask)
-        mrec._mask = nomask
-        assert(mrec['b']._mask is nomask)
-        assert_equal(mrec['a']._mask,mrec['b']._mask)
+        base = self.base.copy()
+        mbase = base.view(mrecarray)
+        mbase.harden_mask()
+        assert(mbase._hardmask)
+        mbase._mask = nomask
+        assert_equal(mbase._mask, [0,1,0,0,1])
+        mbase.soften_mask()
+        assert(not mbase._hardmask)
+        mbase._mask = nomask
+        assert(mbase['b']._mask is nomask)
+        assert_equal(mbase['a']._mask,mbase['b']._mask)
+    #
+    def test_pickling(self):
+        "Test pickling"
+        import cPickle
+        base = self.base.copy()
+        mrec = base.view(mrecarray)
+        _ = cPickle.dumps(mrec)
+        mrec_ = cPickle.loads(_)
+        assert_equal(mrec_.dtype, mrec.dtype)
+        assert_equal_records(mrec_._data, mrec._data)
+        assert_equal(mrec_._mask, mrec._mask)
+        assert_equal_records(mrec_._fieldmask, mrec._fieldmask)
+    #
+    def test_filled(self):
+        "Test filling the array"
+        _a = masked_array([1,2,3],mask=[0,0,1],dtype=int_)
+        _b = masked_array([1.1,2.2,3.3],mask=[0,0,1],dtype=float_)
+        _c = masked_array(['one','two','three'],mask=[0,0,1],dtype='|S8')
+        ddtype = [('a',int_),('b',float_),('c','|S8')]
+        mrec = fromarrays([_a,_b,_c], dtype=ddtype, 
+                          fill_value=(99999,99999.,'N/A')) 
+        mrecfilled = mrec.filled()
+        assert_equal(mrecfilled['a'], narray((1,2,99999), dtype=int_))
+        assert_equal(mrecfilled['b'], narray((1.1,2.2,99999.), dtype=float_))
+        assert_equal(mrecfilled['c'], narray(('one','two','N/A'), dtype='|S8'))
+    #
+    def test_tolist(self):
+        "Test tolist."
+        _a = masked_array([1,2,3],mask=[0,0,1],dtype=int_)
+        _b = masked_array([1.1,2.2,3.3],mask=[0,0,1],dtype=float_)
+        _c = masked_array(['one','two','three'],mask=[1,0,0],dtype='|S8')
+        ddtype = [('a',int_),('b',float_),('c','|S8')]
+        mrec = fromarrays([_a,_b,_c], dtype=ddtype, 
+                          fill_value=(99999,99999.,'N/A')) 
+        #
+        assert_equal(mrec.tolist(),
+                     [(1,1.1,None),(2,2.2,'two'),(None,None,'three')])
 
+################################################################################
+class TestMRecordsImport(NumpyTestCase):
+    "Base test class for MaskedArrays."
+    def __init__(self, *args, **kwds):
+        NumpyTestCase.__init__(self, *args, **kwds)
+        self.setup()
+
+    def setup(self):
+        "Generic setup"
+        _a = masked_array([1,2,3],mask=[0,0,1],dtype=int_)
+        _b = masked_array([1.1,2.2,3.3],mask=[0,0,1],dtype=float_)
+        _c = masked_array(['one','two','three'],mask=[0,0,1],dtype='|S8')
+        ddtype = [('a',int_),('b',float_),('c','|S8')]
+        mrec = fromarrays([_a,_b,_c], dtype=ddtype, 
+                          fill_value=(99999,99999.,'N/A')) 
+        nrec = recfromarrays((_a.data,_b.data,_c.data), dtype=ddtype)
+        self.data = (mrec, nrec, ddtype)
+        
+    def test_fromarrays(self):
+        _a = masked_array([1,2,3],mask=[0,0,1],dtype=int_)
+        _b = masked_array([1.1,2.2,3.3],mask=[0,0,1],dtype=float_)
+        _c = masked_array(['one','two','three'],mask=[0,0,1],dtype='|S8')
+        (mrec, nrec, _) = self.data
+        for (f,l) in zip(('a','b','c'),(_a,_b,_c)):
+            assert_equal(getattr(mrec,f)._mask, l._mask)
+        
+        
     def test_fromrecords(self):
-        "Test from recarray."
-        [d, m, mrec] = self.data
-        nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]],
-                                         dtype=[('a',N.float_),('b',N.float_)])
-        #....................
-        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)
+        "Test construction from records."
+        (mrec, nrec, ddtype) = self.data
+        #......
+        palist = [(1, 'abc', 3.7000002861022949, 0),
+                  (2, 'xy', 6.6999998092651367, 1),
+                  (0, ' ', 0.40000000596046448, 0)]
+        pa = recfromrecords(palist, names='c1, c2, c3, c4')
+        mpa = fromrecords(palist, names='c1, c2, c3, c4')
+        assert_equal_records(pa,mpa)
+        #.....
+        _mrec = fromrecords(nrec)
+        assert_equal(_mrec.dtype, mrec.dtype)
+        for field in _mrec.dtype.names:
+            assert_equal(getattr(_mrec, field), getattr(mrec._data, field)) 
+        #
+        _mrec = fromrecords(nrec.tolist(), names='c1,c2,c3')
+        assert_equal(_mrec.dtype, [('c1',int_),('c2',float_),('c3','|S5')])
+        for (f,n) in zip(('c1','c2','c3'), ('a','b','c')):
+            assert_equal(getattr(_mrec,f), getattr(mrec._data, n))
+        #
+        _mrec = fromrecords(mrec)
+        assert_equal(_mrec.dtype, mrec.dtype)
+        assert_equal_records(_mrec._data, mrec.filled())
+        assert_equal_records(_mrec._fieldmask, mrec._fieldmask)
+            
+    def test_fromrecords_wmask(self):
+        "Tests construction from records w/ mask."
+        (mrec, nrec, ddtype) = self.data
+        #
+        _mrec = fromrecords(nrec.tolist(), dtype=ddtype, mask=[0,1,0,])
+        assert_equal_records(_mrec._data, mrec._data)
+        assert_equal(_mrec._fieldmask.tolist(), [(0,0,0),(1,1,1),(0,0,0)])
+        #
+        _mrec = fromrecords(nrec.tolist(), dtype=ddtype, mask=True)
+        assert_equal_records(_mrec._data, mrec._data)
+        assert_equal(_mrec._fieldmask.tolist(), [(1,1,1),(1,1,1),(1,1,1)])
+        #
+        _mrec = fromrecords(nrec.tolist(), dtype=ddtype, mask=mrec._fieldmask)
+        assert_equal_records(_mrec._data, mrec._data)
+        assert_equal(_mrec._fieldmask.tolist(), mrec._fieldmask.tolist())
+        #
+        _mrec = fromrecords(nrec.tolist(), dtype=ddtype, 
+                            mask=mrec._fieldmask.tolist())
+        assert_equal_records(_mrec._data, mrec._data)
+        assert_equal(_mrec._fieldmask.tolist(), mrec._fieldmask.tolist())
 
     def test_fromtextfile(self):
         "Tests reading from a text file."
@@ -170,10 +336,11 @@
 
     def test_addfield(self):
         "Tests addfield"
-        [d, m, mrec] = self.data
-        mrec = addfield(mrec, masked_array(d+10, mask=m[::-1]))
-        assert_equal(mrec.f2, d+10)
-        assert_equal(mrec.f2._mask, m[::-1])
+        (mrec, nrec, ddtype) = self.data
+        (d,m) = ([100,200,300], [1,0,0])
+        mrec = addfield(mrec, masked_array(d, mask=m))
+        assert_equal(mrec.f3, d)
+        assert_equal(mrec.f3._mask, m)
 
 ###############################################################################
 #------------------------------------------------------------------------------




More information about the Numpy-svn mailing list