diff options
author | pierregm <pierregm@localhost> | 2008-10-18 02:14:51 +0000 |
---|---|---|
committer | pierregm <pierregm@localhost> | 2008-10-18 02:14:51 +0000 |
commit | a4f1e507669fcce584f54a859bc48780a170bce3 (patch) | |
tree | bc24379704652d61916b9aab49ee6eb7a7e5ec5d /numpy | |
parent | 15d9bd5f01f896cec23bf90fcf8db5a9a385202b (diff) | |
download | numpy-a4f1e507669fcce584f54a859bc48780a170bce3.tar.gz |
core : * fixed make_mask_descr to recognize subdtypes
: * fixed some corner cases of view
mrecords : * fixed view
: * got rid of _fieldmask for _mask
: * WARNING: setting ._mask no longer calls __setmask__ : BE CAREFUL.
: * prevent setting ._mask directly, as it screws up things: use .mask instead
test_subclassing : * clean up
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/ma/core.py | 40 | ||||
-rw-r--r-- | numpy/ma/mrecords.py | 76 | ||||
-rw-r--r-- | numpy/ma/tests/test_core.py | 107 | ||||
-rw-r--r-- | numpy/ma/tests/test_mrecords.py | 79 | ||||
-rw-r--r-- | numpy/ma/tests/test_subclassing.py | 22 |
5 files changed, 237 insertions, 87 deletions
diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 4cb1d83a7..c71fccfa9 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -749,11 +749,20 @@ def make_mask_descr(ndtype): Each field is set to a bool. """ + # Make sure we do have a dtype + if not isinstance(ndtype, np.dtype): + ndtype = np.dtype(ndtype) + # Do we have some name fields ? if ndtype.names: mdescr = [list(_) for _ in ndtype.descr] for m in mdescr: m[1] = '|b1' - return [tuple(_) for _ in mdescr] + return np.dtype([tuple(_) for _ in mdescr]) + # Is this some kind of composite a la (np.float,2) + elif ndtype.subdtype: + mdescr = list(ndtype.subdtype) + mdescr[0] = np.dtype(bool) + return np.dtype(tuple(mdescr)) else: return MaskType @@ -1434,20 +1443,33 @@ class MaskedArray(ndarray): return result #............................................. def view(self, dtype=None, type=None): - if dtype is not None: + if dtype is None: if type is None: - args = (dtype,) + output = ndarray.view(self) else: - args = (dtype, type) + output = ndarray.view(self, type) elif type is None: - args = () + try: + if issubclass(dtype, ndarray): + output = ndarray.view(self, dtype) + dtype = None + else: + output = ndarray.view(self, dtype) + except TypeError: + output = ndarray.view(self, dtype) else: - args = (type,) - output = ndarray.view(self, *args) + output = ndarray.view(self, dtype, type) + # Should we update the mask ? if (getattr(output,'_mask', nomask) is not nomask): - mdtype = make_mask_descr(output.dtype) + if dtype is None: + dtype = output.dtype + mdtype = make_mask_descr(dtype) + output._mask = self._mask.view(mdtype, ndarray) output._mask.shape = output.shape + # Make sure to reset the _fill_value if needed + if getattr(output, '_fill_value', None): + output._fill_value = None return output view.__doc__ = ndarray.view.__doc__ #............................................. @@ -1996,7 +2018,7 @@ masked_%(name)s(data = %(data)s, # def __rmul__(self, other): "Multiply other by self, and return a new masked array." - return multiply(self, other) + return multiply(other, self) # def __div__(self, other): "Divide other into self, and return a new masked array." diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index 24c62d81b..b85c8281a 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -106,8 +106,6 @@ class MaskedRecords(MaskedArray, object): _fill_value : {record} Filling values for each field. """ - _defaultfieldmask = nomask - _defaulthardmask = False #............................................ def __new__(cls, shape, dtype=None, buf=None, offset=0, strides=None, formats=None, names=None, titles=None, @@ -164,7 +162,7 @@ class MaskedRecords(MaskedArray, object): dtype=mdescr).view(recarray) # Update some of the attributes _dict = self.__dict__ - _dict.update(_mask=_mask, _fieldmask=_mask) + _dict.update(_mask=_mask) self._update_from(obj) if _dict['_baseclass'] == ndarray: _dict['_baseclass'] = recarray @@ -226,7 +224,7 @@ class MaskedRecords(MaskedArray, object): def __setattr__(self, attr, val): "Sets the attribute attr to the value val." # Should we call __setmask__ first ? - if attr in ['_mask','mask','_fieldmask','fieldmask']: + if attr in ['mask', 'fieldmask']: self.__setmask__(val) return # Create a shortcut (so that we don't have to call getattr all the time) @@ -331,18 +329,44 @@ The fieldname base is either `_data` or `_mask`.""" reprstr.extend([fmt % (' fill_value', self.fill_value), ' )']) return str("\n".join(reprstr)) - #...................................................... - def view(self, obj): +# #...................................................... + def view(self, dtype=None, type=None): """Returns a view of the mrecarray.""" - try: - if issubclass(obj, ndarray): - return ndarray.view(self, obj) - except TypeError: - pass - dtype_ = np.dtype(obj) - if dtype_.fields is None: - return self.__array__().view(dtype_) - return ndarray.view(self, obj) + # OK, basic copy-paste from MaskedArray.view... + if dtype is None: + if type is None: + output = ndarray.view(self) + else: + output = ndarray.view(self, type) + # Here again... + elif type is None: + try: + if issubclass(dtype, ndarray): + output = ndarray.view(self, dtype) + dtype = None + else: + output = ndarray.view(self, dtype) + # OK, there's the change + except TypeError: + dtype = np.dtype(dtype) + # we need to revert to MaskedArray, but keeping the possibility + # ...of subclasses (eg, TimeSeriesRecords), so we'll force a type + # ...set to the first parent + if dtype.fields is None: + basetype = self.__class__.__bases__[0] + output = self.__array__().view(dtype, basetype) + output._update_from(self) + else: + output = ndarray.view(self, dtype) + output._fill_value = None + else: + output = ndarray.view(self, dtype, type) + # Update the mask, just like in MaskedArray.view + if (getattr(output,'_mask', nomask) is not nomask): + mdtype = ma.make_mask_descr(output.dtype) + output._mask = self._mask.view(mdtype, ndarray) + output._mask.shape = output.shape + return output def harden_mask(self): "Forces the mask to hard" @@ -355,7 +379,7 @@ The fieldname base is either `_data` or `_mask`.""" """Returns a copy of the masked record.""" _localdict = self.__dict__ copied = self._data.copy().view(type(self)) - copied._fieldmask = self._fieldmask.copy() + copied._mask = self._mask.copy() return copied def tolist(self, fill_value=None): @@ -371,7 +395,7 @@ The fieldname base is either `_data` or `_mask`.""" if fill_value is not None: return self.filled(fill_value).tolist() result = narray(self.filled().tolist(), dtype=object) - mask = narray(self._fieldmask.tolist()) + mask = narray(self._mask.tolist()) result[mask] = None return result.tolist() #-------------------------------------------- @@ -385,7 +409,7 @@ The fieldname base is either `_data` or `_mask`.""" self.dtype, self.flags.fnc, self._data.tostring(), - self._fieldmask.tostring(), + self._mask.tostring(), self._fill_value, ) return state @@ -405,7 +429,7 @@ The fieldname base is either `_data` or `_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.__dict__['_fieldmask'].__setstate__((shp, mdtype, isf, msk)) + self.__dict__['_mask'].__setstate__((shp, mdtype, isf, msk)) self.fill_value = flv # def __reduce__(self): @@ -508,7 +532,7 @@ def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None, Lists of tuples should be preferred over lists of lists for faster processing. """ # Grab the initial _fieldmask, if needed: - _fieldmask = getattr(reclist, '_fieldmask', None) + _mask = getattr(reclist, '_mask', None) # Get the list of records..... try: nfields = len(reclist[0]) @@ -533,13 +557,13 @@ def fromrecords(reclist, dtype=None, shape=None, formats=None, names=None, mask = np.array(mask, copy=False) maskrecordlength = len(mask.dtype) if maskrecordlength: - mrec._fieldmask.flat = mask + mrec._mask.flat = mask elif len(mask.shape) == 2: - mrec._fieldmask.flat = [tuple(m) for m in mask] + mrec._mask.flat = [tuple(m) for m in mask] else: - mrec._mask = mask - if _fieldmask is not None: - mrec._fieldmask[:] = _fieldmask + mrec.__setmask__(mask) + if _mask is not None: + mrec._mask[:] = _mask return mrec def _guessvartypes(arr): @@ -680,5 +704,5 @@ set to 'fi', where `i` is the number of existing fields. # Add the mask of the new field newmask.setfield(getmaskarray(newfield), *newmask.dtype.fields[newfieldname]) - newdata._fieldmask = newmask + newdata._mask = newmask return newdata diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py index 0a3970a28..6fe9d62a2 100644 --- a/numpy/ma/tests/test_core.py +++ b/numpy/ma/tests/test_core.py @@ -2270,6 +2270,21 @@ class TestMaskedArrayFunctions(TestCase): self.failUnless(c[0,0] is masked) self.failUnless(c.flags['C']) + + def test_make_mask_descr(self): + "Test make_mask_descr" + ntype = [('a',np.float), ('b',np.float)] + test = make_mask_descr(ntype) + assert_equal(test, [('a',np.bool),('b',np.bool)]) + # + ntype = (np.float, 2) + test = make_mask_descr(ntype) + assert_equal(test, (np.bool,2)) + # + ntype = np.float + test = make_mask_descr(ntype) + assert_equal(test, np.dtype(np.bool)) + #------------------------------------------------------------------------------ class TestMaskedFields(TestCase): @@ -2361,23 +2376,15 @@ class TestMaskedFields(TestCase): a = array(iterator, dtype=[('a',float),('b',float)]) a.mask[0] = (1,0) controlmask = np.array([1]+19*[0], dtype=bool) - # + # Transform globally to simple dtype test = a.view(float) assert_equal(test, data.ravel()) assert_equal(test.mask, controlmask) - # + # Transform globally to dty test = a.view((float,2)) assert_equal(test, data) assert_equal(test.mask, controlmask.reshape(-1,2)) # - test = a.view([('A',float),('B',float)]) - assert_equal(test.mask.dtype.names, ('A', 'B')) - assert_equal(test['A'], a['a']) - assert_equal(test['B'], a['b']) - # - test = a.view(np.ndarray) - assert_equal(test, a._data) - # test = a.view((float,2), np.matrix) assert_equal(test, data) self.failUnless(isinstance(test, np.matrix)) @@ -2399,6 +2406,86 @@ class TestMaskedFields(TestCase): assert_equal_records(a[-2]._data, a._data[-2]) assert_equal_records(a[-2]._mask, a._mask[-2]) + +class TestMaskedView(TestCase): + # + def setUp(self): + iterator = zip(np.arange(10), np.random.rand(10)) + data = np.array(iterator) + a = array(iterator, dtype=[('a',float),('b',float)]) + a.mask[0] = (1,0) + controlmask = np.array([1]+19*[0], dtype=bool) + self.data = (data, a, controlmask) + # + def test_view_to_nothing(self): + (data, a, controlmask) = self.data + test = a.view() + self.failUnless(isinstance(test, MaskedArray)) + assert_equal(test._data, a._data) + assert_equal(test._mask, a._mask) + + # + def test_view_to_type(self): + (data, a, controlmask) = self.data + test = a.view(np.ndarray) + self.failUnless(not isinstance(test, MaskedArray)) + assert_equal(test, a._data) + assert_equal_records(test, data.view(a.dtype).squeeze()) + # + def test_view_to_simple_dtype(self): + (data, a, controlmask) = self.data + # View globally + test = a.view(float) + self.failUnless(isinstance(test, MaskedArray)) + assert_equal(test, data.ravel()) + assert_equal(test.mask, controlmask) + # + def test_view_to_flexible_dtype(self): + (data, a, controlmask) = self.data + # + test = a.view([('A',float),('B',float)]) + assert_equal(test.mask.dtype.names, ('A', 'B')) + assert_equal(test['A'], a['a']) + assert_equal(test['B'], a['b']) + # + test = a[0].view([('A',float),('B',float)]) + self.failUnless(isinstance(test, MaskedArray)) + assert_equal(test.mask.dtype.names, ('A', 'B')) + assert_equal(test['A'], a['a'][0]) + assert_equal(test['B'], a['b'][0]) + # + test = a[-1].view([('A',float),('B',float)]) + self.failUnless(not isinstance(test, MaskedArray)) + assert_equal(test.dtype.names, ('A', 'B')) + assert_equal(test['A'], a['a'][-1]) + assert_equal(test['B'], a['b'][-1]) + + # + def test_view_to_subdtype(self): + (data, a, controlmask) = self.data + # View globally + test = a.view((float,2)) + self.failUnless(isinstance(test, MaskedArray)) + assert_equal(test, data) + assert_equal(test.mask, controlmask.reshape(-1,2)) + # View on 1 masked element + test = a[0].view((float,2)) + self.failUnless(isinstance(test, MaskedArray)) + assert_equal(test, data[0]) + assert_equal(test.mask, (1,0)) + # View on 1 unmasked element + test = a[-1].view((float,2)) + self.failUnless(not isinstance(test, MaskedArray)) + assert_equal(test, data[-1]) + # + def test_view_to_dtype_and_type(self): + (data, a, controlmask) = self.data + # + test = a.view((float,2), np.matrix) + assert_equal(test, data) + self.failUnless(isinstance(test, np.matrix)) + self.failUnless(not isinstance(test, MaskedArray)) + ############################################################################### #------------------------------------------------------------------------------ if __name__ == "__main__": diff --git a/numpy/ma/tests/test_mrecords.py b/numpy/ma/tests/test_mrecords.py index c30018092..db526c7fc 100644 --- a/numpy/ma/tests/test_mrecords.py +++ b/numpy/ma/tests/test_mrecords.py @@ -65,8 +65,6 @@ class TestMRecords(TestCase): assert_equal(mbase_first.tolist(), (1,1.1,'one')) # Used to be mask, now it's recordmask assert_equal(mbase_first.recordmask, nomask) - # _fieldmask and _mask should be the same thing - assert_equal(mbase_first._fieldmask.item(), (False, False, False)) assert_equal(mbase_first._mask.item(), (False, False, False)) assert_equal(mbase_first['a'], mbase['a'][0]) mbase_last = mbase[-1] @@ -75,7 +73,7 @@ class TestMRecords(TestCase): assert_equal(mbase_last.tolist(), (None,None,None)) # Used to be mask, now it's recordmask assert_equal(mbase_last.recordmask, True) - assert_equal(mbase_last._fieldmask.item(), (True, True, True)) + assert_equal(mbase_last._mask.item(), (True, True, True)) assert_equal(mbase_last['a'], mbase['a'][-1]) assert (mbase_last['a'] is masked) # as slice .......... @@ -107,7 +105,7 @@ class TestMRecords(TestCase): assert_equal(ma.getmaskarray(mbase['a']), [0]*5) # Use to be _mask, now it's recordmask assert_equal(mbase.recordmask, [False]*5) - assert_equal(mbase._fieldmask.tolist(), + assert_equal(mbase._mask.tolist(), np.array([(0,0,0),(0,1,1),(0,0,0),(0,0,0),(0,1,1)], dtype=bool)) # Set a field to mask ........................ @@ -117,7 +115,7 @@ class TestMRecords(TestCase): assert_equal(mbase.c.recordmask, [1]*5) assert_equal(ma.getmaskarray(mbase['c']), [1]*5) assert_equal(ma.getdata(mbase['c']), ['N/A']*5) - assert_equal(mbase._fieldmask.tolist(), + assert_equal(mbase._mask.tolist(), np.array([(0,0,1),(0,1,1),(0,0,1),(0,0,1),(0,1,1)], dtype=bool)) # Set fields by slices ....................... @@ -159,23 +157,23 @@ class TestMRecords(TestCase): base = self.base.copy() mbase = base.view(mrecarray) # Set the mask to True ....................... - mbase._mask = masked + mbase.mask = masked assert_equal(ma.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(), + assert_equal(mbase._mask.tolist(), np.array([(1,1,1)]*5, dtype=bool)) # Delete the mask ............................ - mbase._mask = nomask + mbase.mask = nomask assert_equal(ma.getmaskarray(mbase['c']), [0]*5) - assert_equal(mbase._fieldmask.tolist(), + assert_equal(mbase._mask.tolist(), np.array([(0,0,0)]*5, dtype=bool)) # def test_set_mask_fromarray(self): base = self.base.copy() mbase = base.view(mrecarray) # Sets the mask w/ an array - mbase._mask = [1,0,0,0,1] + mbase.mask = [1,0,0,0,1] assert_equal(mbase.a.mask, [1,0,0,0,1]) assert_equal(mbase.b.mask, [1,0,0,0,1]) assert_equal(mbase.c.mask, [1,0,0,0,1]) @@ -206,7 +204,7 @@ class TestMRecords(TestCase): # Set an element to mask ..................... mbase = base.view(mrecarray).copy() mbase[-2] = masked - assert_equal(mbase._fieldmask.tolist(), + assert_equal(mbase._mask.tolist(), np.array([(0,0,0),(1,1,1),(0,0,0),(1,1,1),(1,1,1)], dtype=bool)) # Used to be mask, now it's recordmask! @@ -265,11 +263,11 @@ class TestMRecords(TestCase): mbase = base.view(mrecarray) mbase.harden_mask() self.failUnless(mbase._hardmask) - mbase._mask = nomask + mbase.mask = nomask assert_equal_records(mbase._mask, base._mask) mbase.soften_mask() self.failUnless(not mbase._hardmask) - mbase._mask = nomask + mbase.mask = nomask # So, the mask of a field is no longer set to nomask... assert_equal_records(mbase._mask, ma.make_mask_none(base.shape,base.dtype)) @@ -286,7 +284,7 @@ class TestMRecords(TestCase): 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) + assert_equal_records(mrec_._mask, mrec._mask) # def test_filled(self): "Test filling the array" @@ -340,6 +338,45 @@ class TestMRecords(TestCase): np.array([(0,0,0),(1,1,1)], dtype=mult.dtype)) +class TestView(TestCase): + # + def setUp(self): + (a, b) = (np.arange(10), np.random.rand(10)) + ndtype = [('a',np.float), ('b',np.float)] + arr = np.array(zip(a,b), dtype=ndtype) + rec = arr.view(np.recarray) + # + marr = ma.array(zip(a,b), dtype=ndtype, fill_value=(-9., -99.)) + mrec = fromarrays([a,b], dtype=ndtype, fill_value=(-9., -99.)) + mrec.mask[3] = (False, True) + self.data = (mrec, a, b, arr) + # + def test_view_by_itself(self): + (mrec, a, b, arr) = self.data + test = mrec.view() + self.failUnless(isinstance(test, MaskedRecords)) + assert_equal_records(test, mrec) + assert_equal_records(test._mask, mrec._mask) + # + def test_view_simple_dtype(self): + (mrec, a, b, arr) = self.data + ntype = (np.float, 2) + test = mrec.view(ntype) + assert(isinstance(test, ma.MaskedArray)) + assert_equal(test, np.array(zip(a,b), dtype=np.float)) + assert(test[3,1] is ma.masked) + # + def test_view_flexible_type(self): + (mrec, a, b, arr) = self.data + alttype = [('A',np.float), ('B',np.float)] + test = mrec.view(alttype) + assert(isinstance(test, MaskedRecords)) + assert_equal_records(test, arr.view(alttype)) + assert(test['B'][3] is masked) + assert_equal(test.dtype, np.dtype(alttype)) + assert(test._fill_value is None) + + ################################################################################ class TestMRecordsImport(TestCase): "Base test class for MaskedArrays." @@ -395,7 +432,7 @@ class TestMRecordsImport(TestCase): _mrec = fromrecords(mrec) assert_equal(_mrec.dtype, mrec.dtype) assert_equal_records(_mrec._data, mrec.filled()) - assert_equal_records(_mrec._fieldmask, mrec._fieldmask) + assert_equal_records(_mrec._mask, mrec._mask) def test_fromrecords_wmask(self): "Tests construction from records w/ mask." @@ -403,20 +440,20 @@ class TestMRecordsImport(TestCase): # _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)]) + assert_equal(_mrec._mask.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)]) + assert_equal(_mrec._mask.tolist(), [(1,1,1),(1,1,1),(1,1,1)]) # - _mrec = fromrecords(nrec.tolist(), dtype=ddtype, mask=mrec._fieldmask) + _mrec = fromrecords(nrec.tolist(), dtype=ddtype, mask=mrec._mask) assert_equal_records(_mrec._data, mrec._data) - assert_equal(_mrec._fieldmask.tolist(), mrec._fieldmask.tolist()) + assert_equal(_mrec._mask.tolist(), mrec._mask.tolist()) # _mrec = fromrecords(nrec.tolist(), dtype=ddtype, - mask=mrec._fieldmask.tolist()) + mask=mrec._mask.tolist()) assert_equal_records(_mrec._data, mrec._data) - assert_equal(_mrec._fieldmask.tolist(), mrec._fieldmask.tolist()) + assert_equal(_mrec._mask.tolist(), mrec._mask.tolist()) def test_fromtextfile(self): "Tests reading from a text file." diff --git a/numpy/ma/tests/test_subclassing.py b/numpy/ma/tests/test_subclassing.py index 20a5dd0c4..939b8ee56 100644 --- a/numpy/ma/tests/test_subclassing.py +++ b/numpy/ma/tests/test_subclassing.py @@ -154,24 +154,4 @@ class TestSubclassing(TestCase): if __name__ == '__main__': run_module_suite() - if 0: - x = array(arange(5), mask=[0]+[1]*4) - my = masked_array(subarray(x)) - ym = msubarray(x) - # - z = (my+1) - self.failUnless(isinstance(z,MaskedArray)) - self.failUnless(not isinstance(z, MSubArray)) - self.failUnless(isinstance(z._data, SubArray)) - assert_equal(z._data.info, {}) - # - z = (ym+1) - self.failUnless(isinstance(z, MaskedArray)) - self.failUnless(isinstance(z, MSubArray)) - self.failUnless(isinstance(z._data, SubArray)) - self.failUnless(z._data.info['added'] > 0) - # - ym._set_mask([1,0,0,0,1]) - assert_equal(ym._mask, [1,0,0,0,1]) - ym._series._set_mask([0,0,0,0,1]) - assert_equal(ym._mask, [0,0,0,0,1]) + |