From scipy-svn at scipy.org Thu Feb 1 00:34:24 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 31 Jan 2007 23:34:24 -0600 (CST) Subject: [Scipy-svn] r2667 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070201053424.331AB39C03E@new.scipy.org> Author: pierregm Date: 2007-01-31 23:34:20 -0600 (Wed, 31 Jan 2007) New Revision: 2667 Modified: trunk/Lib/sandbox/timeseries/tdates.py trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries:fixed getitem with nd series tdates: force a length=1 when end_date is None and length is None Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-01 04:26:05 UTC (rev 2666) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-01 05:34:20 UTC (rev 2667) @@ -908,7 +908,8 @@ # Check if we have an end_date if end_date is None: if length is None: - raise ValueError,"No length precised!" +# raise ValueError,"No length precised!" + length = 1 else: if not isDate(end_date): raise DateError, "Ending date should be a valid Date instance!" Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-01 04:26:05 UTC (rev 2666) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-01 05:34:20 UTC (rev 2667) @@ -27,7 +27,7 @@ from timeseries import tseries #reload(tseries) -from timeseries.tseries import Date, date_array_fromlist, date_array +from timeseries.tseries import Date, date_array_fromlist, date_array, thisday from timeseries.tseries import time_series, TimeSeries, adjust_endpoints, \ mask_period, align_series, fill_missing_dates @@ -244,6 +244,15 @@ dseries = series[series.dates[3]:series.dates[7]] assert_equal(dseries, series[3:7]) + def test_on2d(self): + "Tests getitem on a 2D series" + (a,b,d) = ([1,2,3],[3,2,1], date_array(thisday('M'),length=3)) + ser_x = time_series(N.column_stack((a,b)), dates=d) + assert_equal(ser_x[0,0], time_series(a[0],d[0])) + assert_equal(ser_x[0,:], time_series([(a[0],b[0])], d[0])) + assert_equal(ser_x[:,0], time_series(a, d)) + assert_equal(ser_x[:,:], ser_x) + class test_functions(NumpyTestCase): "Some getitem tests" def __init__(self, *args, **kwds): Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 04:26:05 UTC (rev 2666) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 05:34:20 UTC (rev 2667) @@ -36,7 +36,7 @@ from numpy.core.records import fromarrays as recfromarrays import maskedarray as MA -#reload(MA) +reload(MA) from maskedarray.core import MaskedArray, MAError, masked, nomask, \ filled, getmask, getmaskarray, make_mask_none, mask_or, make_mask, \ masked_array @@ -54,6 +54,10 @@ import cseries #reload(cseries) + + + + __all__ = [ 'TimeSeriesError','TimeSeriesCompatibilityError','TimeSeries','isTimeSeries', 'time_series', @@ -295,29 +299,38 @@ def __checkindex(self, indx): "Checks the validity of an index." if isinstance(indx, int): - return indx + return (indx, indx) if isinstance(indx, str): - return self._dates.date_to_index(Date(self._dates.freq, string=indx)) + indx = self._dates.date_to_index(Date(self._dates.freq, string=indx)) + return (indx, indx) elif isDate(indx) or isDateArray(indx): - return self._dates.date_to_index(indx) + indx = self._dates.date_to_index(indx) + return (indx, indx) elif isinstance(indx,slice): - slice_start = self.__checkindex(indx.start) - slice_stop = self.__checkindex(indx.stop) - return slice(slice_start, slice_stop, indx.step) + slice_start = self.__checkindex(indx.start)[0] + slice_stop = self.__checkindex(indx.stop)[0] + indx = slice(slice_start, slice_stop, indx.step) + return (indx,indx) + elif isinstance(indx, tuple): + if len(indx) > self.shape: + raise IndexError, "Too many indices" + elif len(indx)==2: + return (indx,indx[0]) + return (indx,indx[:-1]) elif isTimeSeries(indx): indx = indx._series if getmask(indx) is not nomask: msg = "Masked arrays must be filled before they can be used as indices!" raise IndexError, msg - return indx + return (indx,indx) def __getitem__(self, indx): """x.__getitem__(y) <==> x[y] Returns the item described by i. Not a copy as in previous versions. """ - indx = self.__checkindex(indx) - data = self._series[indx] - date = self._dates[indx] + (sindx, dindx) = self.__checkindex(indx) + data = self._series[sindx] + date = self._dates[dindx] m = self._mask singlepoint = (len(numeric.shape(date))==0) @@ -330,7 +343,7 @@ return TimeSeries(data, dates=date, mask=nomask, keep_mask=True, copy=False) - mi = m[indx] + mi = m[sindx] if mi.size == 1: if mi: return TimeSeries(data, dates=date, mask=True) @@ -345,35 +358,35 @@ """ if self is masked: raise MAError, 'Cannot alter the masked element.' - indx = self.__checkindex(indx) + (sindx, dindx) = self.__checkindex(indx) #.... if isinstance(value, TimeSeries): - assert(_timeseriescompat(self[indx], value)) - self._series[indx] = value._series + assert(_timeseriescompat(self[sindx], value)) + self._series[sindx] = value._series else: - self._series[indx] = value + self._series[sindx] = value # Don't forget to update the mask ! self._mask = self._series._mask #........................ def __getslice__(self, i, j): "Gets slice described by i, j" - i = self.__checkindex(i) - j = self.__checkindex(j) - (data, date) = (self._series[i:j], self._dates[i:j]) + (si,di) = self.__checkindex(i) + (sj,dj) = self.__checkindex(j) + (data, date) = (self._series[si:sj], self._dates[di:dj]) return TimeSeries(data, dates=date, copy=False) #.... def __setslice__(self, i, j, value): "Gets item described by i. Not a copy as in previous versions." - i = self.__checkindex(i) - j = self.__checkindex(j) + (si,di) = self.__checkindex(i) + (sj,dj) = self.__checkindex(j) #.... # data = self._series[i:j] if isinstance(value, TimeSeries): - assert(_timeseriescompat(self[i:j], value)) - self._series[i:j] = value._series + assert(_timeseriescompat(self[si:sj], value)) + self._series[si:sj] = value._series else: - self._series[i:j] = value + self._series[si:sj] = value # Don't forget to update the mask ! self._mask = self._series._mask #...................................................... @@ -937,7 +950,6 @@ length=length, include_last=include_last, freq=freq) else: dates = date_array([], freq=freq) - elif not isinstance(dates, DateArray): dates = date_array(dlist=dates, freq=freq) return TimeSeries(data=data, dates=dates, mask=mask, observed=observed, @@ -1354,6 +1366,8 @@ ################################################################################ if __name__ == '__main__': from maskedarray.testutils import assert_equal + import numpy as N + # if 0: # dlist = ['2007-01-%02i' % i for i in range(1,16)] # dates = date_array(dlist) @@ -1410,15 +1424,39 @@ dlist = ['2007-01-%02i' % i for i in range(1,16)] dates = date_array_fromlist(dlist) data = masked_array(numeric.arange(15), mask=[1,0,0,0,0]*3, dtype=float_) - self_d = (time_series(range(15), dlist), data, dates) - (ser, data, dates) = self_d - + self_d = (time_series(data, dlist), data, dates) + (series, data, dates) = self_d + + assert_equal(series[3:7]._series._data, data[3:7]._data) + assert_equal(series[3:7]._series._mask, data[3:7]._mask) + assert_equal(series[3:7]._dates, dates[3:7]) + # Ditto + assert_equal(series[:5]._series._data, data[:5]._data) + assert_equal(series[:5]._series._mask, data[:5]._mask) + assert_equal(series[:5]._dates, dates[:5]) + # With set + series[:5] = 0 + assert_equal(series[:5]._series, [0,0,0,0,0]) + dseries = N.log(series) + series[-5:] = dseries[-5:] + assert_equal(series[-5:], dseries[-5:]) + # Now, using dates ! + dseries = series[series.dates[3]:series.dates[7]] + assert_equal(dseries, series[3:7]) + + if 1: hodie = tdates.today('M') ser_0 = time_series([], [], freq='M') ser_2 = time_series([1,2], start_date=hodie) ser_1 = time_series([1], hodie, freq='M') - + (a,b,d) = ([1,2,3],[3,2,1], date_array(tdates.today('M'),length=3)) + ser_x = time_series(numpy.column_stack((a,b)), dates=d) + assert_equal(ser_x[0,0], time_series(a[0],d[0])) + assert_equal(ser_x[0,:], time_series([(a[0],b[0])], d[0])) + assert_equal(ser_x[:,0], time_series(a, d)) + assert_equal(ser_x[:,:], ser_x) + print "OK" # # Testing a basic condition on data # cond = (series<8).filled(False) # dseries = series[cond] From scipy-svn at scipy.org Thu Feb 1 13:16:02 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 1 Feb 2007 12:16:02 -0600 (CST) Subject: [Scipy-svn] r2668 - trunk/Lib/sandbox/timeseries Message-ID: <20070201181602.E044239C07A@new.scipy.org> Author: pierregm Date: 2007-02-01 12:15:59 -0600 (Thu, 01 Feb 2007) New Revision: 2668 Modified: trunk/Lib/sandbox/timeseries/tdates.py trunk/Lib/sandbox/timeseries/tmulti.py Log: tmulti:fixed tmulti.__getitem__ Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-01 05:34:20 UTC (rev 2667) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-01 18:15:59 UTC (rev 2668) @@ -539,14 +539,15 @@ except AttributeError: pass r = ndarray.__getitem__(self, indx) +# return r if not hasattr(r, "size"): if isinstance(r, int): return Date(self.freq, value=r) else: return r - elif r.size == 1: + elif r.size == 1: # Only one element, and it's not a scalar: we have a DateArray of size 1 - if len(numeric.shape(r)) > 0: + if len(r.shape) > 0: r = r.item() return Date(self.freq, value=r) else: @@ -981,4 +982,7 @@ if __name__ == '__main__': assert (Date('D','2007-01')==Date('D',string='2007-01')) assert (Date('D','2007-01')==Date('D', value=732677)) - assert (Date('D',732677)==Date('D', value=732677)) \ No newline at end of file + assert (Date('D',732677)==Date('D', value=732677)) + n = Date('D','2007-01') + tmp = date_array(n,n+3) + print tmp[0] \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tmulti.py =================================================================== --- trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-01 05:34:20 UTC (rev 2667) +++ trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-01 18:15:59 UTC (rev 2668) @@ -238,18 +238,20 @@ obj._mask = make_mask(_localdict['_fieldmask'][indx]) return obj # We want some elements .. - indx = super(MultiTimeSeries, self)._TimeSeries__checkindex(indx) - return MultiTimeSeries(_localdict['_series'][indx], - dates=_localdict['_dates'][indx], + (sindx, dindx) = super(MultiTimeSeries, self)._TimeSeries__checkindex(indx) + return MultiTimeSeries(_localdict['_series'][sindx], + dates=_localdict['_dates'][dindx], # mask=_localdict['_fieldmask'][indx], dtype=self.dtype) def __getslice__(self, i, j): """Returns the slice described by [i,j].""" _localdict = self.__dict__ - return MultiTimeSeries(_localdict['_data'][i:j], - mask=_localdict['_fieldmask'][i:j], - dates=_localdict['_dates'][i:j], + (si, di) = super(MultiTimeSeries, self)._TimeSeries__checkindex(i) + (sj, dj) = super(MultiTimeSeries, self)._TimeSeries__checkindex(j) + return MultiTimeSeries(_localdict['_data'][si:sj], + mask=_localdict['_fieldmask'][si:sj], + dates=_localdict['_dates'][di:dj], dtype=self.dtype) def __setslice__(self, i, j, value): @@ -564,48 +566,17 @@ ################################################################################ -#if 1: -# from tseries import aligned -# import maskedarray.testutils -# from maskedarray.testutils import * -# -# if 1: -# mlist = ['2005-%02i' % i for i in range(1,13)] -# mlist += ['2006-%02i' % i for i in range(1,13)] -# mdata = numpy.arange(24) -# mser1 = time_series(mdata, mlist, observed='SUMMED') -# # -# if 1: -# mlist2 = ['2004-%02i' % i for i in range(1,13)] -# mlist2 += ['2005-%02i' % i for i in range(1,13)] -# mser2 = time_series(mdata, mlist2, observed='SUMMED') -# # -# (malg1,malg2) = aligned(mser1, mser2) -# if 1: -# mrec = MR.fromarrays([mser1]) -# descr = [('A',N.float_),('B',N.float_)] -# -#if 1: -# import numpy as N -# if 1: -## def setup(self): -## "Generic setup" -# d = N.arange(5) -# m = MA.make_mask([1,0,0,1,1]) -# base_d = N.r_[d,d[::-1]].reshape(2,-1).T -# base_m = N.r_[[m, m[::-1]]].T -# base = MA.array(base_d, mask=base_m) -# mrec = MR.fromarrays(base.T,) -# dlist = ['2007-%02i' % (i+1) for i in d] -# dates = date_array(dlist) -# ts = time_series(mrec,dates) -# mts = MultiTimeSeries(mrec,dates) -# -# logmts = N.log(mts) -# self_data = [d, m, mrec, dlist, dates, ts, mts] -# # -# mts.addfield(masked_array(d+10, mask=m[::-1])) -# assert('f2' in mts.dtype.names) -# assert_equal(mts.f2, d+10) -# assert_equal(mts.f2._mask, m[::-1]) - +if __name__ == '__main__': + import numpy as N + if 1: + d = N.arange(5) + m = MA.make_mask([1,0,0,1,1]) + base_d = N.r_[d,d[::-1]].reshape(2,-1).T + base_m = N.r_[[m, m[::-1]]].T + base = MA.array(base_d, mask=base_m) + mrec = MR.fromarrays(base.T,) + dlist = ['2007-%02i' % (i+1) for i in d] + dates = date_array(dlist) + ts = time_series(mrec,dates) + mts = MultiTimeSeries(mrec,dates) + self_data = [d, m, mrec, dlist, dates, ts, mts] \ No newline at end of file From scipy-svn at scipy.org Thu Feb 1 15:47:01 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 1 Feb 2007 14:47:01 -0600 (CST) Subject: [Scipy-svn] r2669 - trunk/Lib/sandbox/maskedarray Message-ID: <20070201204701.B8C7639C10E@new.scipy.org> Author: pierregm Date: 2007-02-01 14:46:59 -0600 (Thu, 01 Feb 2007) New Revision: 2669 Modified: trunk/Lib/sandbox/maskedarray/core.py Log: core: make sure that masked_array(masked) is masked Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-01 18:15:59 UTC (rev 2668) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-01 20:46:59 UTC (rev 2669) @@ -855,6 +855,8 @@ # 1. Argument is MA ........... if isinstance(data, MaskedArray) or\ (hasattr(data,"_mask") and hasattr(data,"_data")) : + if data is masked: + return masked if keep_mask: if mask is nomask: if copy: @@ -3023,4 +3025,5 @@ a = arange(4) a[1:-1] = masked b = a[:-5] - + if 1: + assert(masked_array(masked) is masked) From scipy-svn at scipy.org Thu Feb 1 16:36:23 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 1 Feb 2007 15:36:23 -0600 (CST) Subject: [Scipy-svn] r2670 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070201213623.37FB639C069@new.scipy.org> Author: pierregm Date: 2007-02-01 15:36:21 -0600 (Thu, 01 Feb 2007) New Revision: 2670 Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries: define tsmasked and 'robustify' the tests on masked Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-01 20:46:59 UTC (rev 2669) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-01 21:36:21 UTC (rev 2670) @@ -29,7 +29,7 @@ #reload(tseries) from timeseries.tseries import Date, date_array_fromlist, date_array, thisday from timeseries.tseries import time_series, TimeSeries, adjust_endpoints, \ - mask_period, align_series, fill_missing_dates + mask_period, align_series, fill_missing_dates, tsmasked class test_creation(NumpyTestCase): "Base test class for MaskedArrays." @@ -184,6 +184,16 @@ series[2] = masked assert_equal(series._mask, [1,0,1]+[1,0,0]*4) assert_equal(series._series._mask, [1,0,1]+[1,0,0]*4) + # + def test_ismasked(self): + "Checks checks on masked" + (series, data) =self.d + assert(series[0] is tsmasked) + assert(tsmasked._series is masked) + assert(series._series[0] is masked) + assert(series[0]._series is masked) + + #............................................................................... class test_getitem(NumpyTestCase): Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 20:46:59 UTC (rev 2669) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 21:36:21 UTC (rev 2670) @@ -36,17 +36,17 @@ from numpy.core.records import fromarrays as recfromarrays import maskedarray as MA -reload(MA) +#reload(MA) from maskedarray.core import MaskedArray, MAError, masked, nomask, \ filled, getmask, getmaskarray, make_mask_none, mask_or, make_mask, \ masked_array import tcore as corelib -reload(corelib) +#reload(corelib) from tcore import * import tdates -reload(tdates) +#reload(tdates) from tdates import DateError, InsufficientDateError from tdates import Date, isDate, DateArray, isDateArray, \ date_array, date_array_fromlist, date_array_fromrange, thisday @@ -239,12 +239,17 @@ newdates = date_array(dlist=dates, freq=freq) else: newdates = dates + # Check data ......... _data = data if hasattr(data, '_mask') : mask = mask_or(data._mask, mask) + # Set default ........ cls._defaultdates = newdates cls._defaultobserved = corelib.fmtObserv(observed) + if _data is masked: + assert(numeric.size(newdates)==1) + return _data.view(cls) newdata = super(TimeSeries,cls).__new__(cls, _data, mask=mask, **options) assert(_datadatescompat(newdata._data,newdates)) @@ -268,15 +273,21 @@ else: self._dates = self._defaultdates self.observed = self._defaultobserved - self._series = MA.array(obj, mask=self._defaultmask, - copy=False, hard_mask=self._defaulthardmask) - self._mask = self._defaultmask self._data = obj + self._mask = self._defaultmask + if obj is masked: + self._series = masked + else: + self._series = MA.array(obj, mask=self._defaultmask, + copy=False, hard_mask=self._defaulthardmask) self._hardmask = self._defaulthardmask self.fill_value = self._fill_value self._mask = self._series._mask self._data = self._series._data self._hardmask = self._series._hardmask + # + TimeSeries._defaulthardmask = False + TimeSeries._defaultmask = nomask #tslog.info("__array_finalize__ sends %s" % type(self)) return #............................................ @@ -336,7 +347,8 @@ singlepoint = (len(numeric.shape(date))==0) if singlepoint: - data = data.reshape((list((1,)) + list(data.shape))) + if data is not masked: + data = data.reshape((list((1,)) + list(data.shape))) date = date_array(start_date=date, length=1, freq=date.freq) if m is nomask: @@ -346,7 +358,9 @@ mi = m[sindx] if mi.size == 1: if mi: - return TimeSeries(data, dates=date, mask=True) + output = tsmasked + output._dates = date + return output return TimeSeries(data, dates=date, mask=nomask) else: return TimeSeries(data, dates=date, mask=mi) @@ -962,6 +976,8 @@ "Returns whether the series is a valid TimeSeries object." return isinstance(series, TimeSeries) +tsmasked = TimeSeries(masked,dates=Date('D',0)) + ##### -------------------------------------------------------------------------- #---- ... Additional functions ... ##### -------------------------------------------------------------------------- @@ -1427,42 +1443,7 @@ self_d = (time_series(data, dlist), data, dates) (series, data, dates) = self_d - assert_equal(series[3:7]._series._data, data[3:7]._data) - assert_equal(series[3:7]._series._mask, data[3:7]._mask) - assert_equal(series[3:7]._dates, dates[3:7]) - # Ditto - assert_equal(series[:5]._series._data, data[:5]._data) - assert_equal(series[:5]._series._mask, data[:5]._mask) - assert_equal(series[:5]._dates, dates[:5]) - # With set - series[:5] = 0 - assert_equal(series[:5]._series, [0,0,0,0,0]) - dseries = N.log(series) - series[-5:] = dseries[-5:] - assert_equal(series[-5:], dseries[-5:]) - # Now, using dates ! - dseries = series[series.dates[3]:series.dates[7]] - assert_equal(dseries, series[3:7]) - - - if 1: - hodie = tdates.today('M') - ser_0 = time_series([], [], freq='M') - ser_2 = time_series([1,2], start_date=hodie) - ser_1 = time_series([1], hodie, freq='M') - (a,b,d) = ([1,2,3],[3,2,1], date_array(tdates.today('M'),length=3)) - ser_x = time_series(numpy.column_stack((a,b)), dates=d) - assert_equal(ser_x[0,0], time_series(a[0],d[0])) - assert_equal(ser_x[0,:], time_series([(a[0],b[0])], d[0])) - assert_equal(ser_x[:,0], time_series(a, d)) - assert_equal(ser_x[:,:], ser_x) - print "OK" -# # Testing a basic condition on data -# cond = (series<8).filled(False) -# dseries = series[cond] -# assert_equal(dseries._data, [1,2,3,4,6,7]) -# assert_equal(dseries._dates, series._dates[[1,2,3,4,6,7]]) -# assert_equal(dseries._mask, nomask) -# # Testing a basic condition on dates -# series[series._dates < Date('D',string='2007-01-06')] = masked -# assert_equal(series[:5]._series._mask, [1,1,1,1,1]) \ No newline at end of file + assert(series[0] is tsmasked) + assert(tsmasked._series is masked) + assert(series._series[0] is masked) + assert(series[0]._series is masked) From scipy-svn at scipy.org Thu Feb 1 16:41:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 1 Feb 2007 15:41:55 -0600 (CST) Subject: [Scipy-svn] r2671 - trunk/Lib/sandbox/timeseries Message-ID: <20070201214155.131AE39C069@new.scipy.org> Author: pierregm Date: 2007-02-01 15:41:54 -0600 (Thu, 01 Feb 2007) New Revision: 2671 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: tseries: make sure TimeSeries has a fake options attribute (needed by MaskedArray) Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 21:36:21 UTC (rev 2670) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 21:41:54 UTC (rev 2671) @@ -192,12 +192,13 @@ The combination of `series` and `dates` is the `data` part. """ + options = None def __new__(cls, data, dates=None, mask=nomask, freq=None, observed=None, start_date=None, dtype=None, copy=False, fill_value=None, keep_mask=True, small_mask=True, hard_mask=False): #tslog.info("__new__: received data types %s, %s" % (type(data), data)) - options = dict(copy=copy, dtype=dtype, fill_value=fill_value, + maparms = dict(copy=copy, dtype=dtype, fill_value=fill_value, keep_mask=keep_mask, small_mask=small_mask, hard_mask=hard_mask, ) if isinstance(data, TimeSeries): @@ -251,7 +252,7 @@ assert(numeric.size(newdates)==1) return _data.view(cls) newdata = super(TimeSeries,cls).__new__(cls, _data, mask=mask, - **options) + **maparms) assert(_datadatescompat(newdata._data,newdates)) return newdata From scipy-svn at scipy.org Fri Feb 2 09:14:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 2 Feb 2007 08:14:55 -0600 (CST) Subject: [Scipy-svn] r2672 - trunk/Lib/sandbox/timeseries Message-ID: <20070202141455.C5E9839C111@new.scipy.org> Author: mattknox_ca Date: 2007-02-02 08:14:52 -0600 (Fri, 02 Feb 2007) New Revision: 2672 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: added tsmasked to __all__ list Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-01 21:41:54 UTC (rev 2671) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-02 14:14:52 UTC (rev 2672) @@ -64,7 +64,7 @@ 'day_of_week','day_of_year','day','month','quarter','year','hour','minute','second', 'tofile','asrecords','flatten','adjust_endpoints','align_series','aligned', 'mask_period','mask_inside_period','mask_outside_period', -'convert','fill_missing_dates', 'stack' +'convert','fill_missing_dates', 'stack', 'tsmasked' ] #............................................................................... From scipy-svn at scipy.org Fri Feb 2 09:21:53 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 2 Feb 2007 08:21:53 -0600 (CST) Subject: [Scipy-svn] r2673 - trunk/Lib/sandbox/timeseries Message-ID: <20070202142153.ECB4A39C247@new.scipy.org> Author: mattknox_ca Date: 2007-02-02 08:21:45 -0600 (Fri, 02 Feb 2007) New Revision: 2673 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: made tsmasked behave the same as ma.masked when used as the value in __setitem__ Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-02 14:14:52 UTC (rev 2672) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-02 14:21:45 UTC (rev 2673) @@ -375,7 +375,9 @@ raise MAError, 'Cannot alter the masked element.' (sindx, dindx) = self.__checkindex(indx) #.... - if isinstance(value, TimeSeries): + if value is tsmasked: + self._series[sindx] = masked + elif isinstance(value, TimeSeries): assert(_timeseriescompat(self[sindx], value)) self._series[sindx] = value._series else: From scipy-svn at scipy.org Fri Feb 2 22:15:51 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 2 Feb 2007 21:15:51 -0600 (CST) Subject: [Scipy-svn] r2674 - trunk/Lib/sandbox/maskedarray Message-ID: <20070203031551.DD38F39C25C@new.scipy.org> Author: pierregm Date: 2007-02-02 21:15:48 -0600 (Fri, 02 Feb 2007) New Revision: 2674 Modified: trunk/Lib/sandbox/maskedarray/core.py Log: Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-02 14:21:45 UTC (rev 2673) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-03 03:15:48 UTC (rev 2674) @@ -856,7 +856,7 @@ if isinstance(data, MaskedArray) or\ (hasattr(data,"_mask") and hasattr(data,"_data")) : if data is masked: - return masked + return masked.view(cls) if keep_mask: if mask is nomask: if copy: From scipy-svn at scipy.org Fri Feb 2 22:21:32 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 2 Feb 2007 21:21:32 -0600 (CST) Subject: [Scipy-svn] r2675 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070203032132.A16D139C034@new.scipy.org> Author: pierregm Date: 2007-02-02 21:21:30 -0600 (Fri, 02 Feb 2007) New Revision: 2675 Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries: add concatenate_series and compressed Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-03 03:15:48 UTC (rev 2674) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-03 03:21:30 UTC (rev 2675) @@ -29,7 +29,7 @@ #reload(tseries) from timeseries.tseries import Date, date_array_fromlist, date_array, thisday from timeseries.tseries import time_series, TimeSeries, adjust_endpoints, \ - mask_period, align_series, fill_missing_dates, tsmasked + mask_period, align_series, fill_missing_dates, tsmasked, concatenate_series class test_creation(NumpyTestCase): "Base test class for MaskedArrays." @@ -453,6 +453,54 @@ except: exception = True assert(exception) + + def test_compressed(self): + "Tests compress" + dlist = ['2007-01-%02i' % i for i in range(1,16)] + dates = date_array_fromlist(dlist) + data = masked_array(numeric.arange(15), mask=[1,0,0,0,0]*3, dtype=float_) + series = time_series(data, dlist) + # + keeper = N.array([0,1,1,1,1]*3, dtype=bool_) + c_series = series.compressed() + assert_equal(c_series._data, [1,2,3,4,6,7,8,9,11,12,13,14]) + assert_equal(c_series._mask, nomask) + assert_equal(c_series._dates, dates[keeper]) + # + series_st = time_series(MA.column_stack((data,data[::-1])), + dates=dates) + c_series = series_st.compressed() + d = [1,2,3,6,7,8,11,12,13] + assert_equal(c_series._data, N.c_[(d,list(reversed(d)))]) + assert_equal(c_series._mask, nomask) + assert_equal(c_series._dates, dates[d]) + + def test_concatenate(self): + "Tests concatenate" + dlist = ['2007-%02i' % i for i in range(1,6)] + dates = date_array_fromlist(dlist) + data = masked_array(numeric.arange(5), mask=[1,0,0,0,0], dtype=float_) + # + ser_1 = time_series(data, dates) + ser_2 = time_series(data, dates=dates+10) + newseries = concatenate_series([ser_1, ser_2]) + assert_equal(newseries._data,[0,1,2,3,4,0,0,0,0,0,0,1,2,3,4]) + assert_equal(newseries._mask,[1,0,0,0,0]+[1]*5+[1,0,0,0,0]) + # + ser_1 = time_series(data, dates) + ser_2 = time_series(data, dates=dates+10) + newseries = concatenate_series([ser_1, ser_2], keep_gap=False) + assert_equal(newseries._data,[0,1,2,3,4,0,1,2,3,4]) + assert_equal(newseries._mask,[1,0,0,0,0]+[1,0,0,0,0]) + assert newseries.has_missing_dates() + # + ser_2 = time_series(data, dates=dates+3) + newseries = concatenate_series([ser_1, ser_2]) + assert_equal(newseries._data,[0,1,2,0,1,2,3,4]) + assert_equal(newseries._mask,[1,0,0,1,0,0,0,0]) + # + + ############################################################################### Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-03 03:15:48 UTC (rev 2674) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-03 03:21:30 UTC (rev 2675) @@ -64,7 +64,7 @@ 'day_of_week','day_of_year','day','month','quarter','year','hour','minute','second', 'tofile','asrecords','flatten','adjust_endpoints','align_series','aligned', 'mask_period','mask_inside_period','mask_outside_period', -'convert','fill_missing_dates', 'stack', 'tsmasked' +'convert','fill_missing_dates', 'stack' ] #............................................................................... @@ -148,7 +148,7 @@ if len(set(shapes)) > 1: errItems = tuple(set(shapes)) raise TimeSeriesCompatibilityError('size', "1: %s" % str(errItems[0].shape), - "2: %s" % str(errItems[1].shape)) + "2: %s" % str(errItems[1].shape)) return True @@ -175,6 +175,18 @@ return numeric.asarray(numeric.shape(data))[:-1].prod() else: return numeric.size(data) + +def _compare_frequencies(*series): + """Compares the frequencies of a sequence of series. + Returns the common frequency, or raises an exception if series have different + frequencies.""" + unique_freqs = numpy.unique([x.freqstr for x in series]) + try: + common_freq = unique_freqs.item() + except ValueError: + raise TimeSeriesError, \ + "All series must have same frequency!" + return common_freq ##### -------------------------------------------------------------------------- ##--- ... Time Series ... @@ -374,7 +386,7 @@ if self is masked: raise MAError, 'Cannot alter the masked element.' (sindx, dindx) = self.__checkindex(indx) - #.... + #.... if value is tsmasked: self._series[sindx] = masked elif isinstance(value, TimeSeries): @@ -1053,7 +1065,27 @@ """Masks values falling outside a given range of dates.""" return mask_period(data, start_date=start_date, end_date=end_date, inside=False, include_edges=include_edges, inplace=inplace) -#.......................................................... + +#............................................................................... +def compressed(series): + """Suppresses missing values from a time series.""" + if series._mask is nomask: + return series + if series.ndim == 1: + keeper = ~(series._mask) + elif series.ndim == 2: + # Both dates and data are 2D: ravel first + if series._dates.ndim == 2: + series = series.ravel() + keeper = ~(series._mask) + # a 2D series: suppress the rows (dates are in columns) + else: + keeper = ~(series._mask.any(-1)) + else: + raise NotImplementedError + return series[keeper] +TimeSeries.compressed = compressed +#............................................................................... def adjust_endpoints(a, start_date=None, end_date=None): """Returns a TimeSeries going from `start_date` to `end_date`. If `start_date` and `end_date` both fall into the initial range of dates, @@ -1139,14 +1171,7 @@ if len(series) < 2: return series unique_freqs = numpy.unique([x.freqstr for x in series]) - try: - common_freq = unique_freqs.item() - except ValueError: - raise TimeSeriesError, \ - "All series must have same frequency!" - if common_freq == 'U': - raise TimeSeriesError, \ - "Cannot adjust a series with 'Undefined' frequency." + common_freq = _compare_frequencies(*series) valid_states = [x.isvalid() for x in series] if not numpy.all(valid_states): raise TimeSeriesError, \ @@ -1272,11 +1297,7 @@ newseries = TimeSeries(newdata, series._dates, **options) return newseries TimeSeries.tshift = tshift -#.................................................................... - - - -#.................................................................... +#............................................................................... def fill_missing_dates(data, dates=None, freq=None,fill_value=None): """Finds and fills the missing dates in a time series. The data corresponding to the initially missing dates are masked, or filled to @@ -1317,7 +1338,6 @@ n = len(dflat) if not dflat.has_missing_dates(): return time_series(data, dflat) - # ...and now, fill it ! ...... (tstart, tend) = dflat[[0,-1]] newdates = date_array(start_date=tstart, end_date=tend, include_last=True) @@ -1367,8 +1387,7 @@ else: nshp = tuple([-1,] + list(data.shape[1:])) return time_series(newdata.reshape(nshp), newdates) - -#.................................................................... +#............................................................................... def stack(*series): """performs a column_stack on the data from each series, and the resulting series has the same dates as each individual series. All series @@ -1380,65 +1399,48 @@ _timeseriescompat_multiple(*series) return time_series(MA.column_stack(series), series[0]._dates, **_attrib_dict(series[0])) - +#............................................................................... +def concatenate_series(series, keep_gap=True): + """Concatenates a sequence of series, by chronological order. + Overlapping data are processed in a FIFO basis: the data from the first series + of the sequence will be overwritten by the data of the second series, and so forth. + If keep_gap is true, any gap between consecutive, non overlapping series are + kept: the corresponding data are masked. + """ + common_f = _compare_frequencies(*series) + start_date = min([s.start_date for s in series if s.start_date is not None]) + end_date = max([s.end_date for s in series if s.end_date is not None]) + newdtype = max([s.dtype for s in series]) + whichone = numeric.zeros((end_date-start_date+1), dtype=int_) + newseries = time_series(numeric.empty((end_date-start_date+1), dtype=newdtype), + dates=date_array(start_date, end_date, freq=common_f), + mask=True) + newdata = newseries._data + newmask = newseries._mask + for (k,s) in enumerate(series): + start = s.start_date - start_date + end = start + len(s) + whichone[start:end] = k+1 + newdata[start:end] = s._data + if s._mask is nomask: + newmask[start:end] = False + else: + newmask[start:end] = s._mask + keeper = whichone.astype(bool_) + if not keep_gap: + newseries = newseries[keeper] + else: + newdata[~keeper] = 0 + return newseries + ################################################################################ if __name__ == '__main__': from maskedarray.testutils import assert_equal import numpy as N -# if 0: -# dlist = ['2007-01-%02i' % i for i in range(1,16)] -# dates = date_array(dlist) -# data = masked_array(numeric.arange(15, dtype=float_), mask=[1,0,0,0,0]*3) -## btseries = BaseTimeSeries(data._data, dates) -# tseries = time_series(data, dlist) -# dseries = numpy.log(tseries) -# if 0: -# mlist = ['2005-%02i' % i for i in range(1,13)] -# mlist += ['2006-%02i' % i for i in range(1,13)] -# mdata = numpy.arange(24) -# mser1 = time_series(mdata, mlist, observed='SUMMED') -# # -# mlist2 = ['2004-%02i' % i for i in range(1,13)] -# mlist2 += ['2005-%02i' % i for i in range(1,13)] -# mser2 = time_series(mdata, mlist2, observed='SUMMED') -# # -# today = thisday('m') -# (malg1,malg2) = aligned(mser1, mser2) -# -# C = convert(mser2,'A') -# D = convert(mser2,'A',func=None) -# -# if 0: -# dlist = ['2007-01-%02i' % i for i in range(1,16)] -# dates = date_array(dlist) -# print "."*50+"\ndata" -# data = masked_array(numeric.arange(15)-6, mask=[1,0,0,0,0]*3) -# print "."*50+"\nseries" -# tseries = time_series(data, dlist) -# -# if 0: -# dlist_1 = ['2007-01-%02i' % i for i in range(1,8)] -# dlist_2 = ['2007-01-%02i' % i for i in numpy.arange(1,28)[::4]] -# data = masked_array(numeric.arange(7), mask=[1,0,0,0,0,0,0]) -# tseries_1 = time_series(data, dlist_1) -# tseries_2 = time_series(data, dlist_2) -# tseries_3 = time_series(data[::-1], dlist_2) -# -# try: -# tseries = tseries_1 + tseries_2 -# except TimeSeriesCompatibilityError: -# print "I knew it!" -# tseries = tseries_2 + tseries_3 -# assert_equal(tseries._dates, tseries_3._dates) -# assert_equal(tseries._mask, [1,0,0,0,0,0,1]) -# -# if 0: -# mser3 = time_series(MA.mr_[malg1._series, 100+malg2._series].reshape(2,-1).T, -# dates=malg1.dates) -# data = mser3._series._data + if 1: dlist = ['2007-01-%02i' % i for i in range(1,16)] dates = date_array_fromlist(dlist) From scipy-svn at scipy.org Fri Feb 2 23:19:08 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 2 Feb 2007 22:19:08 -0600 (CST) Subject: [Scipy-svn] r2676 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070203041908.75B2939C030@new.scipy.org> Author: pierregm Date: 2007-02-02 22:19:06 -0600 (Fri, 02 Feb 2007) New Revision: 2676 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Log: mpl_timeseries: add nonsingular from matplotlib SVN Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-03 03:21:30 UTC (rev 2675) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-03 04:19:06 UTC (rev 2676) @@ -27,7 +27,7 @@ from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ Locator, FixedLocator -from matplotlib.transforms import nonsingular +#from matplotlib.transforms import nonsingular import numpy as N import maskedarray as MA @@ -99,6 +99,33 @@ figure_instance._seen[key] = a return a + +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + ''' + Ensure the endpoints of a range are not too close together. + + "too close" means the interval is smaller than 'tiny' times + the maximum absolute value. + + If they are too close, each will be moved by the 'expander'. + If 'increasing' is True and vmin > vmax, they will be swapped. + ''' + #TODO: Remove that when matplotlib incorporate it by default + swapped = False + if vmax < vmin: + vmin, vmax = vmax, vmin + swapped = True + if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: + if vmin==0.0: + vmin = -expander + vmax = expander + else: + vmin -= expander*abs(vmin) + vmax += expander*abs(vmax) + if swapped and not increasing: + vmin, vmax = vmax, vmin + return vmin, vmax + ##### ------------------------------------------------------------------------- #---- --- Locators --- ##### ------------------------------------------------------------------------- From scipy-svn at scipy.org Mon Feb 5 10:51:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 5 Feb 2007 09:51:13 -0600 (CST) Subject: [Scipy-svn] r2677 - trunk/Lib/sandbox/timeseries Message-ID: <20070205155113.A486239C00E@new.scipy.org> Author: mattknox_ca Date: 2007-02-05 09:51:10 -0600 (Mon, 05 Feb 2007) New Revision: 2677 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: major overhaul Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-03 04:19:06 UTC (rev 2676) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-05 15:51:10 UTC (rev 2677) @@ -4,18 +4,27 @@ :author: Pierre GF Gerard-Marchant & Matt Knox :contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com :version: $Id: tdates.py 2641 2007-01-30 18:40:17Z mattknox_ca $ + +Ideas borrowed from: + +- George Sakkis + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/267662 + +- Mike Brown + http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 """ __author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" __version__ = '1.0' __revision__ = "$Revision: 2641 $" __date__ = '$Date: 2007-01-30 13:40:17 -0500 (Tue, 30 Jan 2007) $' -import cStringIO,operator, types +import sys +import cStringIO, operator, types, copy import tseries as ts import tdates as td __all__ = [ - 'report', 'wrap_onspace', 'wrap_onspace_strict', + 'Report', 'wrap_onspace', 'wrap_onspace_strict', 'wrap_always'] class fmtfunc_wrapper: @@ -26,7 +35,10 @@ - `mask_rep` : string to use for masked values """ def __init__ (self, fmtfunc, mask_rep): - self.f = fmtfunc + if fmtfunc is None: + self.f = str + else: + self.f = fmtfunc self.mr = mask_rep def __call__ (self, item): @@ -38,59 +50,109 @@ return self.f(item) -def report(*tseries, **kwargs): - """generate a table report of *tseries with dates in the left column. +_default_options = { + 'dates':None, + 'header_row':None, + 'header_char':'-', + 'row_char':None, + 'footer_label':None, + 'footer_char':'-', + 'footer_func':None, + 'delim':' | ', + 'justify':None, + 'prefix':'', + 'postfix':'', + 'mask_rep':'--', + 'datefmt':None, + 'fmtfunc':str, + 'wrapfunc':lambda x:x, + 'col_width':None, + 'nls':'\n', + 'output':sys.stdout +} -:Parameters: +class Report(object): + """Create a tabular TimeSeries report with dates in the left column. +All instance variables are optional and simply serve as the defaults when calling +the report. Parameters for calling the report are the exact same as for +initialization. When calling the report, new options specified will not be saved +to the instance. + +:IVariables: - `*tseries` : time series objects. Must all be at the same frequency, but do not need to be aligned. + - `dates` (DateArray, *[None]*) : dates at which values of all the series will be output. If not specified, data will be output from the minimum start_date to the maximum end_date of all the time series objects - - `header_row` (list, *[None]*) : optional list of column headers. Length - must be equal to len(tseries) (no date column header specified) or - len(tseries)+1 (first header is assumed to be date column header) + + - `header_row` (list, *[None]*) : List of column headers. Specifying + the header for the date column is optional. + - `header_char` (string, *['-']*): Character to be used for the row separator line between the header and first row of data. None for no separator. This is ignored if `header_row` is None. + - `row_char` (string, *[None]*): Character to be used for the row separator line between each row of data. None for no separator + - `footer_func` (List of functions or single function, *[None]*) : A function or list of functions for summarizing each data column in the report. For example, ma.sum to get the sum of the column. If a list of functions is provided - there must be exactly one function for each column. + there must be exactly one function for each column. Do not specify a function + for the Date column. + - `footer_char` (string, *['-']*): Character to be used for the row separator line between the last row of data and the footer. None for no separator. This is ignored if `footer_func` is None. - - `footer_label` (string, *[None]*) : label for the footer row. This is - ignored if footer_func is None. + + - `footer_label` (string, *[None]*) : label for the footer row. This goes at the + end of the date column. This is ignored if footer_func is None. + - `justify` (List of strings or single string, *[None]*) : Determines how are data justified in their column. If not specified, the date column and string columns are left justified, and everything else is right justified. If a string is specified, it must be one of 'left', 'right', or 'center' and all columns will be justified the same way. If a list is specified, each column will be justified according to the specification for that column in the list - (specifying the justification for the date column is optional). + Specifying the justification for the date column is optional. + - `prefix` (string, *['']*) : A string prepended to each printed row. + - `postfix` (string, *['']*) : A string appended to each printed row. + - `mask_rep` (string, *['--']*): String used to represent masked values in output + - `datefmt` (string, *[None]*) : Formatting string used for displaying the dates in the date column. If None, str() is simply called on the dates + - `fmtfunc` (List of functions or single function, *[None]*) : A function or list of functions for formatting each data column in the report. If not specified, str() is simply called on each item. If a list of functions is - provided, there must be exactly one function for each column - - `wrapfunc` (function, *[lambda x:x]*): A function f(text) for wrapping text; - each element in the table is first wrapped by this function. Useful functions - for this are wrap_onspace, wrap_onspace_strict, and wrap_always (which are - part of this module). Eg wrapfunc=lambda x: wrap_onspace(x, 10) + provided, there must be exactly one function for each column. Do not specify + a function for the Date column, that is handled by the datefmt argument + - `wrapfunc` (List of functions or single function, *[lambda x:x]*): A function + f(text) for wrapping text; each element in the column is first wrapped by this + function. Instances of wrap_onspace, wrap_onspace_strict, and wrap_always + (which are part of this module) work well for this. Eg. wrapfunc=wrap_onspace(10) + If a list is specified, each column will be wrapped according to the + specification for that column in the list. Specifying a function for the Date + column is optional + + - `col_width` (list of integers or single integer, *[None]*): use this to specify + a width for all columns (single integer), or each column individually (list + of integers). The column will be at least as wide as col_width, but may be + larger if cell contents exceed col_width. If specifying a list, you may + optionally specify the width for the Date column as the first entry + :Examples: import numpy as np import timeseries as ts - from timeseries import report, wrap_onspace + import maskedarray as ma + from timeseries import Report, wrap_onspace series1 = ts.time_series(np.random.uniform(-100,100,15), start_date=ts.thisday('b')-15) series2 = ts.time_series(np.random.uniform(-100,100,13), start_date=ts.thisday('b')-10) @@ -98,213 +160,345 @@ darray = ts.date_array(start_date=ts.thisday('b')-8, end_date=ts.thisday('b')-3) - # print all values of series1 and series2 and show 2 decimal places. - # show masked values as "N/A" - print report(series1, series2, fmtfunc=lambda x:'%.2f' % x, mask_rep='N/A') - - # same thing, but format one column one with 2 decimal places, and column two with 4 - print report(series1, series2, fmtfunc=[(lambda x:'%.2f' % x), (lambda x:'%.4f' % x)], mask_rep='N/A') - - # print an html table of the data over a specified range - print "" + \ - report(series1, series2, series3, dates=darray, - delim="") + \ - "
", prefix="
", postfix="
" + txt_o = open('myfile.txt', 'w') + html_o = open('myfile.html', 'w') - # print a table with columns 10 characters wide when possible, but don't break up a word - print r.report(series1, series3, series2, wrapfunc=lambda x: wrap_onspace(x, 10))""" + # report containing only numerical series, showing 2 decimal places + num_report = Report(series1, series2, fmtfunc=lambda x:'%.2f' % x) - dates = kwargs.pop('dates', None) - header_row = kwargs.pop('header_row', None) - header_char = kwargs.pop('header_char', '-') - row_char = kwargs.pop('row_char', None) - footer_label = kwargs.pop('footer_label', None) - footer_char = kwargs.pop('footer_char', '-') - footer_func = kwargs.pop('footer_func', None) - delim = kwargs.pop('delim', ' | ') - justify = kwargs.pop('justify', None) - prefix = kwargs.pop('prefix', '') - postfix = kwargs.pop('postfix', '') - mask_rep = kwargs.pop('mask_rep', '--') - datefmt = kwargs.pop('datefmt', None) - fmtfunc = kwargs.pop('fmtfunc', str) - - - if type(fmtfunc) != types.ListType: - fmtfunc = [fmtfunc_wrapper(fmtfunc, mask_rep)]*len(tseries) - else: - fmtfunc = [fmtfunc_wrapper(f, mask_rep) for f in fmtfunc] - - wrapfunc = kwargs.pop('wrapfunc', lambda x:x) + # report containing some string and numerical data + mixed_report = Report(series1, series2, series3) - if len(kwargs) > 0: - raise KeyError("Unrecognized keyword(s): %s" % (", ".join(kwargs.keys()))) + # output a csv report suitable for excel to sys.stdout, show masked values as "N/A" + num_report(delim=', ', mask_rep='N/A') - if header_row is not None: - has_header=True - if len(header_row) == len(tseries)+1: - # label for date column included - rows = [header_row] - elif len(header_row) == len(tseries): - # label for date column not included - rows = [['']+header_row] - else: - has_header=False - rows=[] + # format one column one with 2 decimal places, and column two with 4. + # Add a sum footer. Write the output to txt_o + num_report(fmtfunc=[(lambda x:'%.2f' % x), (lambda x:'%.4f' % x)], + footer_func=ma.sum, footer_label='sum', output=txt_o) + + # create an html table of the data over a specified range. + # Wrap text in cells to width 10. Output to html_o + html_o.write("") + mixed_report(series1, series2, series3, dates=darray, + delim="", + wrapfunc=wrap_onspace(10, nls='
'), output=html_o) + html_o.write("
", prefix="
", postfix="
")""" + + def __init__(self, *tseries, **kwargs): - if justify is not None: - if type(justify) == types.StringType: - # justify all columns the the same way - justify = [justify for x in range(len(tseries)+1)] - else: #assume it is a list or tuple, etc - if len(justify) == len(tseries): - # justification for date column not included, so set that - # to left by default - justify = ['left'] + justify - else: - # default column justification - justify = ['left'] - for ser in tseries: - if str(ser.dtype)[:2] == '|S': justify.append('left') - else: justify.append('right') + self.options = {} + self.tseries = None + if len(tseries) > 0: + self.tseries = tseries + self.options = self.__make_dict(**kwargs) + def __make_dict(self, **kwargs): + + option_dict = copy.copy(self.options) - if datefmt is None: - def datefmt_func(date): return str(date) - else: - def datefmt_func(date): return date.strfmt(datefmt) + option_list = list(_default_options) - if dates is None: - tseries = ts.align_series(*tseries) - dates = td.date_array(start_date=tseries[0].start_date, - end_date=tseries[0].end_date) - else: - tseries = ts.align_series(start_date=dates[0], end_date=dates[-1], *tseries) + for x in [kw for kw in option_list if kw in kwargs]: + option_dict[x] = kwargs.pop(x) + + if len(kwargs) > 0: + raise KeyError("Unrecognized keyword(s): %s" % (", ".join(kwargs.keys()))) + + return option_dict + + def set_series(self, *tseries): + """set new time series for the report + +:Paramaters: + - `*tseries` : the TimeSeries objects to be used in the report""" + self.tseries = tseries + + def set_options(self, **kwargs): + """set new options or modify options in the report + +:Paramaters: + - `**kwargs` : the options to be used in the report. See the __doc__ + string for the Report class for valid options""" + self.options = self.__make_dict(**kwargs) + - for d in dates: - rows.append([datefmt_func(d)]+[fmtfunc[i](ser[d]) for i, ser in enumerate(tseries)]) + def __call__(self, *tseries, **kwargs): + """generate a report + +:Paramaters: + - `*tseries` : the TimeSeries objects to be used in the report. If + omitted, the previously set TimeSeries objects will be used + - `**kwargs` : the options to be used in the report. See the __doc__ + string for the Report class for valid options. If omitted, the + previously set options will be used""" + + option_dict = self.__make_dict(**kwargs) + if len(tseries) == 0: + tseries = self.tseries - if footer_func is not None: - has_footer=True - if type(footer_func) != types.ListType: - footer_func = [footer_func]*len(tseries) + def option(kw): + return option_dict.get(kw, _default_options[kw]) + + dates = option('dates') + header_row = option('header_row') + header_char = option('header_char') + row_char = option('row_char') + footer_label = option('footer_label') + footer_char = option('footer_char') + footer_func = option('footer_func') + delim = option('delim') + justify = option('justify') + prefix = option('prefix') + postfix = option('postfix') + mask_rep = option('mask_rep') + datefmt = option('datefmt') + fmtfunc = option('fmtfunc') + wrapfunc = option('wrapfunc') + col_width = option('col_width') + nls=option('nls') + output=option('output') + + if header_row is not None: + has_header=True + if len(header_row) == len(tseries)+1: + # label for date column included + rows = [header_row] + elif len(header_row) == len(tseries): + # label for date column not included + rows = [['']+header_row] + else: + raise ValueError("mismatch with number of headers and series") + else: + has_header=False + rows=[] - if footer_label is None: footer_label = [''] - else: footer_label = [footer_label] - rows.append(footer_label + [fmtfunc[i](footer_func[i](ser)) for i, ser in enumerate(tseries)]) - else: - has_footer=False + if justify is not None: + _justify = kwargs.pop('justify') + if isinstance(justify, str): + # justify all columns the the same way + justify = [justify for x in range(len(tseries)+1)] + elif isinstance(justify, list): #assume it is a list or tuple, etc + if len(justify) == len(tseries): + # justification for date column not included, so set that + # to left by default + justify = ['left'] + justify + else: + raise ValueError("invalid `justify` specification") + else: + # default column justification + justify = ['left'] + for ser in tseries: + if str(ser.dtype)[:2] == '|S': justify.append('left') + else: justify.append('right') - return indent(rows, - has_header=has_header, header_char=header_char, - has_footer=has_footer, footer_char=footer_char, - separate_rows=separate_rows, row_char=row_char, - delim=delim, justify=justify, - prefix=prefix, postfix=postfix, wrapfunc=wrapfunc) - + if datefmt is None: + def datefmt_func(date): return str(date) + else: + def datefmt_func(date): return date.strfmt(datefmt) + if dates is None: + tseries = ts.align_series(*tseries) + dates = td.date_array(start_date=tseries[0].start_date, + end_date=tseries[0].end_date) + else: + tseries = ts.align_series(start_date=dates[0], end_date=dates[-1], *tseries) + if isinstance(fmtfunc, list): + fmtfunc = [fmtfunc_wrapper(f, mask_rep) for f in fmtfunc] + else: + fmtfunc = [fmtfunc_wrapper(fmtfunc, mask_rep)]*len(tseries) + + if isinstance(wrapfunc, list): + if len(wrapfunc) == len(tseries): + wrapfunc = [lambda x: x] + wrapfunc + else: + wrapfunc = [wrapfunc for x in range(len(tseries)+1)] + + if isinstance(col_width, list): + if len(col_width) == len(tseries): + col_width = [-1] + col_width + else: + col_width = [col_width for x in range(len(tseries)+1)] -# written by George Sakkis -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/267662 -def indent(rows, - has_header=False, header_char='-', - has_footer=False, footer_char='-', - separate_rows=False, row_char='_', - delim=' | ', justify=None, - prefix='', postfix='', wrapfunc=lambda x:x): - """Indents a table by column. - - rows: A sequence of sequences of items, one sequence per row. - - has_header: True if the first row consists of the columns' names. - - header_char: Character to be used for the row separator line - (if has_header==True or separate_rows==True). - - delim: The column delimiter. - - justify: Determines how are data justified in their column. - Valid values are 'left','right' and 'center'. - - separate_rows: True if rows are to be separated by a line - of 'header_char's. - - prefix: A string prepended to each printed row. - - postfix: A string appended to each printed row. - - wrapfunc: A function f(text) for wrapping text; each element in - the table is first wrapped by this function.""" + ############################################################ + # temporary hack to handle singletons for different types of + # behaviour until we finalize how they will be handled + ############################################################ + if ts.time_series([1,2], start_date=ts.thisday('b'))[0].ndim == 0: + def getval(series, date): return series[date] + else: + def getval(series, date): + temp = series[date] + if temp is ts.tsmasked: + return temp + else: + return temp.series[0] + ############################################################ + for d in dates: + rows.append([datefmt_func(d)]+[fmtfunc[i](getval(ser, d)) for i, ser in enumerate(tseries)]) + + if footer_func is not None: + has_footer=True + if not isinstance(footer_func, list): + footer_func = [footer_func]*len(tseries) + + if footer_label is None: footer_label = [''] + else: footer_label = [footer_label] + + footer_data = [] + for i, ser in enumerate(tseries): + if footer_func[i] is None: + footer_data.append('') + else: + footer_data.append(fmtfunc[i](footer_func[i](ser))) + + rows.append(footer_label + footer_data) + else: + has_footer=False + + + def rowWrapper(row): + newRows = [wrapfunc[i](item).split('\n') for i, item in enumerate(row)] + return [[(substr or '') for substr in item] for item in map(None,*newRows)] + # break each logical row into one or more physical ones + logicalRows = [rowWrapper(row) for row in rows] + numLogicalRows = len(logicalRows) + # columns of physical rows + columns = map(None,*reduce(operator.add,logicalRows)) + numCols = len(columns) + colNums = list(range(numCols)) + + # get the maximum of each column by the string length of its items + maxWidths = [max(col_width[i], *[len(str(item)) for item in column]) + for i, column in enumerate(columns)] + + def getSeparator(char, separate): + if char is not None and separate: + return char * (len(prefix) + len(postfix) + sum(maxWidths) + \ + len(delim)*(len(maxWidths)-1)) + else: + return None + + header_separator = getSeparator(header_char, has_header) + footer_separator = getSeparator(footer_char, has_footer) + row_separator = getSeparator(row_char, True) + + # select the appropriate justify method + justify_funcs = {'center':str.center, 'right':str.rjust, 'left':str.ljust} + + if has_header and has_footer: + data_start = 1 + data_end = numLogicalRows-3 + elif has_header: + data_start = 1 + data_end = numLogicalRows-2 + elif has_footer: + data_start = 0 + data_end = numLogicalRows-3 + else: + data_start = 0 + data_end = numLogicalRows-2 + + for rowNum, physicalRows in enumerate(logicalRows): + for row in physicalRows: + output.write(prefix \ + + delim.join([justify_funcs[justify[colNum].lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ + + postfix + nls) + + if row_separator and (data_start <= rowNum <= data_end): + output.write(row_separator + nls) + elif header_separator and rowNum < data_start: + output.write(header_separator + nls) + elif footer_separator and rowNum == data_end + 1: + output.write(footer_separator + nls) + + +class wrap_onspace(object): + """A callable word-wrap class that preserves existing line breaks +and most spaces in the text. - def rowWrapper(row): - newRows = [wrapfunc(item).split('\n') for item in row] - return [[substr or '' for substr in item] for item in map(None,*newRows)] - # break each logical row into one or more physical ones - logicalRows = [rowWrapper(row) for row in rows] - numLogicalRows = len(logicalRows) - # columns of physical rows - columns = map(None,*reduce(operator.add,logicalRows)) - numCols = len(columns) - colNums = list(range(numCols)) +:IVariables: + - `width` (int): width to wrap at. Won't split up words wider than `width` + - `nls` (str, *['\n']*): New line separator. Assumes existing line + breaks use this new line separator as well. + +:Parameters (for __call__ method): + - `text` (str): text to wrap""" + + def __init__(self, width, nls='\n'): + self.width = width + self.nls = nls + + def __call__(self, text): - if justify is None: - justify = ['left' for x in range(numCols)] + width = self.width + nls = self.nls - # get the maximum of each column by the string length of its items - maxWidths = [max([len(str(item)) for item in column]) for column in columns] - - def getSeparator(char, separate): - if char is not None and separate: - return char * (len(prefix) + len(postfix) + sum(maxWidths) + \ - len(delim)*(len(maxWidths)-1)) - else: - return None - - header_separator = getSeparator(header_char, has_header) - footer_separator = getSeparator(footer_char, has_footer) - row_separator = getSeparator(row_char, separate_rows) - - # select the appropriate justify method - justify_funcs = {'center':str.center, 'right':str.rjust, 'left':str.ljust} - - output=cStringIO.StringIO() + if nls[-1] == '\n': line_end = nls + else: line_end = nls + '\n' - for rowNum, physicalRows in enumerate(logicalRows): - for row in physicalRows: - print >> output, \ - prefix \ - + delim.join([justify_funcs[justify[colNum].lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ - + postfix + def break_or_space(line, word, width): + temp_idx = (len(line[line.rfind(nls)+1:]) + len(word.split(nls,1)[0]) >= width) + if temp_idx: + return line_end + else: + return ' ' - if row_separator and (0 < rowNum < numLogicalRows-2): - print >> output, row_separator - elif header_separator and rowNum == 0: - print >> output, header_separator - elif footer_separator and rowNum == numLogicalRows-2: - print >> output, footer_separator - - return output.getvalue() + return reduce(lambda line, word, width=width: '%s%s%s' % + (line, + break_or_space(line, word, width), + word), + text.split(' ') + ) -# written by Mike Brown -# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 -def wrap_onspace(text, width): - """ - A word-wrap function that preserves existing line breaks - and most spaces in the text. Expects that existing line - breaks are posix newlines (\n). - """ - return reduce(lambda line, word, width=width: '%s%s%s' % - (line, - ' \n'[(len(line[line.rfind('\n')+1:]) - + len(word.split('\n',1)[0] - ) >= width)], - word), - text.split(' ') - ) import re -def wrap_onspace_strict(text, width): - """Similar to wrap_onspace, but enforces the width constraint: - words longer than width are split.""" - wordRegex = re.compile(r'\S{'+str(width)+r',}') - return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width),text),width) +class wrap_onspace_strict(object): + """A callable word-wrap class similar to wrap_onspace, but +enforces the width constraint: words longer than width are split. + +:IVariables: + - `width` (int): width to wrap at. Will split up words wider than `width` + - `nls` (str, *['\n']*): New line separator. Assumes existing line + breaks use this new line separator as well. + +:Parameters (for __call__ method): + - `text` (str): text to wrap""" + def __init__(self, width, nls='\n'): + self.width = width + self.nls = nls + + def __call__(self, text): + + width = self.width + nls = self.nls + + wordRegex = re.compile(r'\S{'+str(width)+r',}') + return wrap_onspace(wordRegex.sub(lambda m: wrap_always(m.group(),width, nls=nls),text),width, nls=nls) + + import math -def wrap_always(text, width): - """A simple word-wrap function that wraps text on exactly width characters. - It doesn't split the text in words.""" - return '\n'.join([ text[width*i:width*(i+1)] \ - for i in xrange(int(math.ceil(1.*len(text)/width))) ]) +class wrap_always(object): + """A callable word-wrap class that wraps text on exactly width +characters. It doesn't split the text into words. + +:IVariables: + - `width` (int): width to wrap at. + - `nls` (str, *['\n']*): New line separator. + +:Parameters (for __call__ method): + - `text` (str): text to wrap""" + + def __init__(self, width, nls='\n'): + self.width = width + self.nls = nls + + def __call__(self, text): + + width = self.width + nls = self.nls + return nls.join([ text[width*i:width*(i+1)] \ + for i in xrange(int(math.ceil(1.*len(text)/width))) ]) From scipy-svn at scipy.org Mon Feb 5 15:49:41 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 5 Feb 2007 14:49:41 -0600 (CST) Subject: [Scipy-svn] r2678 - trunk/Lib/sandbox/timeseries Message-ID: <20070205204941.811E439C0F0@new.scipy.org> Author: mattknox_ca Date: 2007-02-05 14:49:37 -0600 (Mon, 05 Feb 2007) New Revision: 2678 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: modified date_to_index to handle DateArray's Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-05 15:51:10 UTC (rev 2677) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-05 20:49:37 UTC (rev 2678) @@ -673,16 +673,24 @@ def date_to_index(self, date): "Returns the index corresponding to one given date, as an integer." - if self.isvalid(): - index = date.value - self[0].value - if index < 0 or index > self.size: - raise ValueError, "Date out of bounds!" - return index + if isDate(date): + if self.isvalid(): + index = date.value - self[0].value + if index < 0 or index > self.size: + raise ValueError, "Date out of bounds!" + return index + else: + index_asarray = (self == date.value).nonzero() + if fromnumeric.size(index_asarray) == 0: + raise ValueError, "Date out of bounds!" + return index_asarray[0][0] else: - index_asarray = (self == date.value).nonzero() - if fromnumeric.size(index_asarray) == 0: - raise ValueError, "Date out of bounds!" - return index_asarray[0][0] + #date is a DateArray + def idx_check(val): return (date == val).any() + idx_check_v = numpy.vectorize(idx_check) + return numpy.where(idx_check_v(self.tovalue()))[0] + + #...................................................... def get_steps(self): """Returns the time steps between consecutive dates. From scipy-svn at scipy.org Mon Feb 5 16:26:24 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 5 Feb 2007 15:26:24 -0600 (CST) Subject: [Scipy-svn] r2679 - trunk/Lib/sandbox/timeseries Message-ID: <20070205212624.5FB5839C088@new.scipy.org> Author: mattknox_ca Date: 2007-02-05 15:26:17 -0600 (Mon, 05 Feb 2007) New Revision: 2679 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: added fixed_width and output parameters Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-05 20:49:37 UTC (rev 2678) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-05 21:26:17 UTC (rev 2679) @@ -68,7 +68,8 @@ 'wrapfunc':lambda x:x, 'col_width':None, 'nls':'\n', - 'output':sys.stdout + 'output':sys.stdout, + 'fixed_width':True } class Report(object): @@ -147,6 +148,13 @@ larger if cell contents exceed col_width. If specifying a list, you may optionally specify the width for the Date column as the first entry + - `output` (buffer, *[sys.stdout]*): `output` must have a write method. + + - `fixed_width` (boolean, *[True]*): If True, columns are fixed width (ie. + cells will be padded with spaces to ensure all cells in a given column are + the same width). If False, `col_width` will be ignored and cells will not + be padded. + :Examples: import numpy as np @@ -258,6 +266,7 @@ col_width = option('col_width') nls=option('nls') output=option('output') + fixed_width=option('fixed_width') if header_row is not None: has_header=True @@ -273,24 +282,27 @@ has_header=False rows=[] - if justify is not None: - _justify = kwargs.pop('justify') - if isinstance(justify, str): - # justify all columns the the same way - justify = [justify for x in range(len(tseries)+1)] - elif isinstance(justify, list): #assume it is a list or tuple, etc - if len(justify) == len(tseries): - # justification for date column not included, so set that - # to left by default - justify = ['left'] + justify + if fixed_width: + if justify is not None: + _justify = kwargs.pop('justify') + if isinstance(justify, str): + # justify all columns the the same way + justify = [justify for x in range(len(tseries)+1)] + elif isinstance(justify, list): #assume it is a list or tuple, etc + if len(justify) == len(tseries): + # justification for date column not included, so set that + # to left by default + justify = ['left'] + justify + else: + raise ValueError("invalid `justify` specification") else: - raise ValueError("invalid `justify` specification") + # default column justification + justify = ['left'] + for ser in tseries: + if str(ser.dtype)[:2] == '|S': justify.append('left') + else: justify.append('right') else: - # default column justification - justify = ['left'] - for ser in tseries: - if str(ser.dtype)[:2] == '|S': justify.append('left') - else: justify.append('right') + justify = [None for x in range(len(tseries)+1)] if datefmt is None: def datefmt_func(date): return str(date) @@ -308,16 +320,22 @@ fmtfunc = [fmtfunc_wrapper(f, mask_rep) for f in fmtfunc] else: fmtfunc = [fmtfunc_wrapper(fmtfunc, mask_rep)]*len(tseries) + + def wrapfunc_default(func): + if func is None: return lambda x:x + else: return func if isinstance(wrapfunc, list): if len(wrapfunc) == len(tseries): wrapfunc = [lambda x: x] + wrapfunc + wrapfunc = [wrapfunc_default(func) for func in wrapfunc] else: - wrapfunc = [wrapfunc for x in range(len(tseries)+1)] + wrapfunc = [wrapfunc_default(wrapfunc) for x in range(len(tseries)+1)] + if isinstance(col_width, list): if len(col_width) == len(tseries): - col_width = [-1] + col_width + col_width = [None] + col_width else: col_width = [col_width for x in range(len(tseries)+1)] @@ -352,7 +370,7 @@ if footer_func[i] is None: footer_data.append('') else: - footer_data.append(fmtfunc[i](footer_func[i](ser))) + footer_data.append(fmtfunc[i](footer_func[i](ser[dates]))) rows.append(footer_label + footer_data) else: @@ -386,7 +404,8 @@ row_separator = getSeparator(row_char, True) # select the appropriate justify method - justify_funcs = {'center':str.center, 'right':str.rjust, 'left':str.ljust} + justify_funcs = {'center':str.center, 'right':str.rjust, 'left':str.ljust, + 'none':(lambda text, width: text)} if has_header and has_footer: data_start = 1 @@ -404,7 +423,7 @@ for rowNum, physicalRows in enumerate(logicalRows): for row in physicalRows: output.write(prefix \ - + delim.join([justify_funcs[justify[colNum].lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ + + delim.join([justify_funcs[str(justify[colNum]).lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ + postfix + nls) if row_separator and (data_start <= rowNum <= data_end): From scipy-svn at scipy.org Mon Feb 5 17:33:35 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 5 Feb 2007 16:33:35 -0600 (CST) Subject: [Scipy-svn] r2680 - trunk/Lib/sandbox/timeseries Message-ID: <20070205223335.86AF139C020@new.scipy.org> Author: mattknox_ca Date: 2007-02-05 16:33:32 -0600 (Mon, 05 Feb 2007) New Revision: 2680 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: changed nls behaviour in wrap_onspace Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-05 21:26:17 UTC (rev 2679) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-05 22:33:32 UTC (rev 2680) @@ -454,14 +454,11 @@ width = self.width nls = self.nls - - if nls[-1] == '\n': line_end = nls - else: line_end = nls + '\n' def break_or_space(line, word, width): temp_idx = (len(line[line.rfind(nls)+1:]) + len(word.split(nls,1)[0]) >= width) if temp_idx: - return line_end + return nls else: return ' ' From scipy-svn at scipy.org Mon Feb 5 21:46:49 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 5 Feb 2007 20:46:49 -0600 (CST) Subject: [Scipy-svn] r2681 - trunk/Lib/sandbox/timeseries Message-ID: <20070206024649.CDF3339C017@new.scipy.org> Author: pierregm Date: 2007-02-05 20:46:47 -0600 (Mon, 05 Feb 2007) New Revision: 2681 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: tcore: fixed the famous singleton problem (nD data stay nD, but 1D data stay 1D) tcore: allowed DateArray to be indices Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-05 22:33:32 UTC (rev 2680) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-06 02:46:47 UTC (rev 2681) @@ -60,7 +60,7 @@ __all__ = [ 'TimeSeriesError','TimeSeriesCompatibilityError','TimeSeries','isTimeSeries', -'time_series', +'time_series', 'tsmasked', 'day_of_week','day_of_year','day','month','quarter','year','hour','minute','second', 'tofile','asrecords','flatten','adjust_endpoints','align_series','aligned', 'mask_period','mask_inside_period','mask_outside_period', @@ -165,7 +165,9 @@ elif data.ndim > 1: dsize = numeric.asarray(data.shape)[:-1].prod() if dsize == tsize: - return True + return True + elif data.ndim == 0 and tsize <= 1: + return True raise TimeSeriesCompatibilityError('size', "data: %s" % dsize, "dates: %s" % tsize) @@ -327,9 +329,17 @@ if isinstance(indx, str): indx = self._dates.date_to_index(Date(self._dates.freq, string=indx)) return (indx, indx) - elif isDate(indx) or isDateArray(indx): + elif isDate(indx): indx = self._dates.date_to_index(indx) return (indx, indx) + elif isDateArray(indx): + if indx.size == 1: + indx = self._dates.date_to_index(indx[0]) + return (indx,indx) + else: + d2i = self._dates.date_to_index + tmp = numpy.fromiter((d2i(i) for i in indx),int_) + return (tmp,tmp) elif isinstance(indx,slice): slice_start = self.__checkindex(indx.start)[0] slice_stop = self.__checkindex(indx.stop)[0] @@ -360,7 +370,7 @@ singlepoint = (len(numeric.shape(date))==0) if singlepoint: - if data is not masked: + if data is not masked and self.ndim > 1: data = data.reshape((list((1,)) + list(data.shape))) date = date_array(start_date=date, length=1, freq=date.freq) @@ -1439,16 +1449,18 @@ from maskedarray.testutils import assert_equal import numpy as N - - if 1: - dlist = ['2007-01-%02i' % i for i in range(1,16)] - dates = date_array_fromlist(dlist) - data = masked_array(numeric.arange(15), mask=[1,0,0,0,0]*3, dtype=float_) - self_d = (time_series(data, dlist), data, dates) - (series, data, dates) = self_d - - assert(series[0] is tsmasked) - assert(tsmasked._series is masked) - assert(series._series[0] is masked) - assert(series[0]._series is masked) + #TODO: CHECK THAT, AND PUT IT IN tests/test_timeseries IF IT WORKS + # Test 2 points of 3 variables + xx = time_series([[1,2,3],[4,5,6]], start_date=thisday('b')) + assert_equal(xx[0]._data, [[1,2,3]]) + assert_equal(xx[:,0]._data, [1,4]) + # Test a single point of 3 variables + xx = time_series([[1,2,3]], start_date=thisday('b')) + assert_equal(xx[0]._data, [[1,2,3]]) + assert_equal(xx[:,0]._data, [[1]]) + # Test 3 data points + x = time_series([1,2,3], start_date=thisday('b')) + assert_equal(x[0], 1) + # Test using a DateArray as items + assert_equal(x[x._dates[:]],x) From scipy-svn at scipy.org Tue Feb 6 11:08:06 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 10:08:06 -0600 (CST) Subject: [Scipy-svn] r2682 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070206160806.5372A39C220@new.scipy.org> Author: mattknox_ca Date: 2007-02-06 10:08:02 -0600 (Tue, 06 Feb 2007) New Revision: 2682 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Log: fixed import statements. cast to integer values passed to Date constructor to avoid warnings Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-06 02:46:47 UTC (rev 2681) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-06 16:08:02 UTC (rev 2682) @@ -32,10 +32,8 @@ import numpy as N import maskedarray as MA -import tdates -from tdates import date_array, Date -import tseries -from tseries import TimeSeries +import timeseries as TS +from timeseries import date_array, Date, TimeSeries import warnings @@ -192,8 +190,8 @@ def _initialize_dates(self, start_val, end_val): "Returns a DateArray for the current frequency." freq = self.freqstr - dates = date_array(start_date=Date(freq, value=start_val), - end_date=Date(freq, value=end_val), + dates = date_array(start_date=Date(freq, value=int(start_val)), + end_date=Date(freq, value=int(end_val)), freq=freq) return dates @@ -719,9 +717,9 @@ da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), length=51) - ser = tseries.time_series(MA.arange(len(da)), dates=da) + ser = TS.time_series(MA.arange(len(da)), dates=da) ser[4] = MA.masked - ser_2 = tseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) + ser_2 = TS.time_series(MA.arange(len(da)), dates=da.asfreq('M')) pylab.figure() pylab.gcf().add_tsplot(111) From scipy-svn at scipy.org Tue Feb 6 14:53:32 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 13:53:32 -0600 (CST) Subject: [Scipy-svn] r2683 - trunk/Lib/sandbox/maskedarray Message-ID: <20070206195332.4FBDD39C0BF@new.scipy.org> Author: pierregm Date: 2007-02-06 13:53:30 -0600 (Tue, 06 Feb 2007) New Revision: 2683 Modified: trunk/Lib/sandbox/maskedarray/core.py Log: core: fixed a pb with sort & dimensions larger than 1 Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-06 16:08:02 UTC (rev 2682) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-06 19:53:30 UTC (rev 2683) @@ -1796,7 +1796,8 @@ dtype = self.dtype, fill_value=self.fill_value, **self.options) #............................................ - def argsort(self, axis=None, fill_value=None, kind='quicksort'): + def argsort(self, axis=None, fill_value=None, kind='quicksort', + order=None): """Returns an array of indices that sort 'a' along the specified axis. Masked values are filled beforehand to `fill_value`. If `fill_value` is None, uses the default for the data type. @@ -1836,8 +1837,8 @@ fill_value = default_fill_value(self._data) d = self.filled(fill_value) if axis is None: - return d.argsort(kind=kind) - return d.argsort(axis, kind) + return d.argsort(kind=kind, order=order) + return d.argsort(axis, kind=kind, order=order) def argmin(self, axis=None, fill_value=None): """Returns the array of indices for the minimum values of `a` along the @@ -2626,7 +2627,9 @@ filler = minimum_fill_value(a) else: filler = maximum_fill_value(a) - indx = filled(a,filler).argsort(axis=axis,kind=kind,order=order) +# return + indx = numpy.indices(a.shape).tolist() + indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order) return a[indx] def compressed(x): From scipy-svn at scipy.org Tue Feb 6 20:20:39 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 19:20:39 -0600 (CST) Subject: [Scipy-svn] r2684 - in trunk/Lib/stsci: convolve convolve/lib image image/lib Message-ID: <20070207012039.A79DD39C05F@new.scipy.org> Author: chanley Date: 2007-02-06 19:20:26 -0600 (Tue, 06 Feb 2007) New Revision: 2684 Modified: trunk/Lib/stsci/convolve/lib/__init__.py trunk/Lib/stsci/convolve/setup.py trunk/Lib/stsci/image/lib/__init__.py trunk/Lib/stsci/image/setup.py Log: added version numbers to stsci software to support next STScI software release. Modified: trunk/Lib/stsci/convolve/lib/__init__.py =================================================================== --- trunk/Lib/stsci/convolve/lib/__init__.py 2007-02-06 19:53:30 UTC (rev 2683) +++ trunk/Lib/stsci/convolve/lib/__init__.py 2007-02-07 01:20:26 UTC (rev 2684) @@ -1,2 +1,3 @@ +__version__ = '2.0' from Convolve import * import iraf_frame Modified: trunk/Lib/stsci/convolve/setup.py =================================================================== --- trunk/Lib/stsci/convolve/setup.py 2007-02-06 19:53:30 UTC (rev 2683) +++ trunk/Lib/stsci/convolve/setup.py 2007-02-07 01:20:26 UTC (rev 2684) @@ -18,6 +18,6 @@ setup(author='Todd Miller', author_email = 'help at stsci.edu', description = 'image array convolution functions', - version = '0.1', + version = '2.0', **config) Modified: trunk/Lib/stsci/image/lib/__init__.py =================================================================== --- trunk/Lib/stsci/image/lib/__init__.py 2007-02-06 19:53:30 UTC (rev 2683) +++ trunk/Lib/stsci/image/lib/__init__.py 2007-02-07 01:20:26 UTC (rev 2684) @@ -2,7 +2,7 @@ from _image import * from combine import * - +__version__ = '2.0' if sys.version_info < (2,4): def test(): import doctest, _image, combine Modified: trunk/Lib/stsci/image/setup.py =================================================================== --- trunk/Lib/stsci/image/setup.py 2007-02-06 19:53:30 UTC (rev 2683) +++ trunk/Lib/stsci/image/setup.py 2007-02-07 01:20:26 UTC (rev 2684) @@ -17,6 +17,6 @@ setup(author='Todd Miller', author_email = 'help at stsci.edu', description = 'image array manipulation functions', - version = '0.1', + version = '2.0', **config) From scipy-svn at scipy.org Tue Feb 6 23:53:21 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 22:53:21 -0600 (CST) Subject: [Scipy-svn] r2685 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070207045321.7390039C1F5@new.scipy.org> Author: pierregm Date: 2007-02-06 22:53:19 -0600 (Tue, 06 Feb 2007) New Revision: 2685 Modified: trunk/Lib/sandbox/timeseries/plotlib/__init__.py Log: Modified: trunk/Lib/sandbox/timeseries/plotlib/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/__init__.py 2007-02-07 01:20:26 UTC (rev 2684) +++ trunk/Lib/sandbox/timeseries/plotlib/__init__.py 2007-02-07 04:53:19 UTC (rev 2685) @@ -1 +1 @@ -from plot import * \ No newline at end of file +from mpl_timeseries import * \ No newline at end of file From scipy-svn at scipy.org Tue Feb 6 23:53:30 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 22:53:30 -0600 (CST) Subject: [Scipy-svn] r2686 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070207045330.1165E39C212@new.scipy.org> Author: pierregm Date: 2007-02-06 22:53:28 -0600 (Tue, 06 Feb 2007) New Revision: 2686 Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py Log: Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-07 04:53:19 UTC (rev 2685) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-07 04:53:28 UTC (rev 2686) @@ -0,0 +1,775 @@ +""" +Classes to plot TimeSeries w/ matplotlib. + +:author: Pierre GF Gerard-Marchant +:contact: pierregm_at_uga_edu +:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ +:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" +__version__ = '1.0' +__revision__ = "$Revision: 2676 $" +__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' + + +import matplotlib +from matplotlib import pylab, rcParams +from matplotlib.artist import setp +from matplotlib.axes import Subplot, PolarSubplot +from matplotlib.cbook import flatten +from matplotlib.collections import LineCollection +from matplotlib.contour import ContourSet +from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ + DateFormatter +from matplotlib.figure import Figure +from matplotlib.legend import Legend +from matplotlib.mlab import meshgrid +from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ + Locator, FixedLocator + +#from matplotlib.transforms import nonsingular + +import numpy as N +import maskedarray as MA + +import timeseries +from timeseries import date_array, Date, DateArray, TimeSeries +#from tdates import date_array, Date +#import tseries +#from tseries import TimeSeries + +import warnings + +#####--------------------------------------------------------------------------- +#---- --- Matplotlib extensions --- +#####--------------------------------------------------------------------------- + +def add_generic_subplot(figure_instance, *args, **kwargs): + """Generalizes the `add_subplot` figure method to generic subplots. +The specific Subplot object class to add is given through the keywords +`SubplotClass` or `class`. + +:Parameters: + `figure_instance` : Figure object + Figure to which the generic subplot should be attached. + `args` : Misc + Miscellaneous arguments to the subplot. + `kwargs` : Dictionary + Keywords. Same keywords as `Subplot`, with the addition of + - `SubplotClass` : Type of subplot + - `subclass` : Shortcut to `SubplotClass`. + - any keyword required by the `SubplotClass` subclass. + """ + + key = figure_instance._make_key(*args, **kwargs) + #TODO: Find why, sometimes, key is not hashable (even if tuple) + # else, there's a fix below + try: + key.__hash__() + except TypeError: + key = str(key) + # + if figure_instance._seen.has_key(key): + ax = figure_instance._seen[key] + figure_instance.sca(ax) + return ax + # + if not len(args): + return +# if hasattr(args[0], '__array__'): +# fixedargs = args[1:] +# else: +# fixedargs = args + # + SubplotClass = kwargs.pop("SubplotClass", Subplot) + SubplotClass = kwargs.pop("subclass",SubplotClass) + if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): + a = args[0] + assert(a.get_figure() is figure_instance) +# a.set_figure(figure_instance) + else: + ispolar = kwargs.pop('polar', False) + if ispolar: + a = PolarSubplot(figure_instance, *args, **kwargs) + else: + a = SubplotClass(figure_instance, *args, **kwargs) + + figure_instance.axes.append(a) + figure_instance._axstack.push(a) + figure_instance.sca(a) + figure_instance._seen[key] = a + return a + + +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + ''' + Ensure the endpoints of a range are not too close together. + + "too close" means the interval is smaller than 'tiny' times + the maximum absolute value. + + If they are too close, each will be moved by the 'expander'. + If 'increasing' is True and vmin > vmax, they will be swapped. + ''' + #TODO: Remove that when matplotlib incorporate it by default + swapped = False + if vmax < vmin: + vmin, vmax = vmax, vmin + swapped = True + if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: + if vmin == 0.0: + vmin = -expander + vmax = expander + else: + vmin -= expander*abs(vmin) + vmax += expander*abs(vmax) + if swapped and not increasing: + vmin, vmax = vmax, vmin + return vmin, vmax + +##### ------------------------------------------------------------------------- +#---- --- Locators --- +##### ------------------------------------------------------------------------- + +def _get_default_annual_spacing(nyears): + """Returns a default spacing between consecutive ticks for annual data.""" + if nyears < 20: + (min_spacing, maj_spacing) = (1, 2) + elif nyears < 50: + (min_spacing, maj_spacing) = (1, 5) + elif nyears < 100: + (min_spacing, maj_spacing) = (5, 10) + elif nyears < 200: + (min_spacing, maj_spacing) = (5, 20) + elif nyears < 400: + (min_spacing, maj_spacing) = (5, 25) + elif nyears < 1000: + (min_spacing, maj_spacing) = (10, 50) + else: + (min_spacing, maj_spacing) = (20, 100) + return (min_spacing, maj_spacing) + +def _get_default_quarterly_spacing(nquarters): + """Returns a default spacing between consecutive ticks for quarterly data.""" + if nquarters <= 3*4: + (min_spacing, maj_spacing) = (1,4) + elif nquarters <= 11*4: + (min_spacing, maj_spacing) = (1,4) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) + min_spacing = min_anndef * 4 + maj_spacing = maj_anndef * 4 + return (min_spacing, maj_spacing) + +def _get_default_monthly_spacing(nmonths): + """Returns a default spacing between consecutive ticks for monthly data.""" + if nmonths <= 10: + (min_spacing, maj_spacing) = (1,3) + elif nmonths <= 2*12: + (min_spacing, maj_spacing) = (1,6) + elif nmonths <= 3*12: + (min_spacing, maj_spacing) = (1,12) + elif nmonths <= 11*12: + (min_spacing, maj_spacing) = (3,12) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) + min_spacing = min_anndef * 12 + maj_spacing = maj_anndef * 12 + return (min_spacing, maj_spacing) + +#............................................................................... +class TimeSeries_DateLocator(Locator): + "Locates the ticks along an axis controlled by a DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + self.freqstr = freq + self.base = base + (self.quarter, self.month, self.day) = (quarter, month, day) + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + + def _initialize_dates(self, start_val, end_val): + "Returns a DateArray for the current frequency." + freq = self.freqstr + dates = date_array(start_date=Date(freq, value=int(start_val)), + end_date=Date(freq, value=int(end_val)), + freq=freq) + return dates + + def _get_default_spacing(self, span): + "Returns the default ticks spacing." + raise NotImplementedError('Derived must override') + + def __call__(self): + 'Return the locations of the ticks.' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + base = self._get_default_spacing(vmax-vmin+1) + else: + base = self.base + d = vmin // base + vmin = (d+1) * base + self.offset + locs = range(vmin, vmax+1, base) + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + if self.isdynamic: + base = self._get_default_spacing(dmax-dmin+1) + else: + base = self.base + (d,m) = divmod(dmin, base) + if m < base/2: + vmin = d * base + else: + vmin = (d+1) * base + (d,m) = divmod(dmax, base) + vmax = (d+1) * base + if vmin == vmax: + vmin -= 1 + vmax += 1 + return nonsingular(vmin, vmax) + +#............................................................................... +class TimeSeries_AnnualLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by an annual DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, + base, quarter, month, day) + + def _get_default_spacing(self, span): + "Returns the default tick spacing for annual data." + (minor, major) = _get_default_annual_spacing(span) + if self.isminor: + return minor + return major +#............................................................................... +class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a quarterly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset=1 + + def _get_default_spacing(self, span): + "Returns the default tick spacing for quarterly data." + (minor, major) = _get_default_quarterly_spacing(span) + if self.isminor: + return minor + return major +#............................................................................... +class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a monthly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset = 1 + + def _get_default_spacing(self, span): + "Returns the default tick spacing for monthly data." + (minor, major) = _get_default_monthly_spacing(span) + if self.isminor: + return minor + return major + +#............................................................................... +class TimeSeries_DailyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a daily DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, + base, quarter, month, day) + if self.freqstr == 'B': + self.daysinyear = 261 + else: + self.daysinyear = 365 + self._cacheddates = None + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for daily data." + daysperyear = self.daysinyear + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + # + if span <= daysperyear//12: + minor = default + major = default[(dates.day_of_week == 1)] + elif span <= daysperyear//3: + minor = default[(dates.day_of_week == 1)] + major = default[(dates.day == 1)] + elif span <= 1.5 * daysperyear: + minor = default[(dates.day_of_week == 1)] + major = default[(dates.day == 1)] + elif span <= 3 * daysperyear: + minor = default[(dates.day == 1)] + major = default[(dates.day_of_year == 1)] + elif span <= 11 * daysperyear: + minor = default[(dates.quarter != (dates-1).quarter)] + major = default[(dates.day_of_year == 1)] + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) + annual = (dates.day_of_year == 1) + minor = default[annual & (dates.years % min_anndef == 0)] + major = default[annual & (dates.years % maj_anndef == 0)] + if self.isminor: + return minor + return major + + def __call__(self): + 'Return the locations of the ticks' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + locs = self._get_default_locs(vmin, vmax) + else: + base = self.base + (d,m) = divmod(vmin, base) + vmin = (d+1) * base + locs = range(vmin, vmax+1, base) + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + locs = self._get_default_locs(dmin, dmax) + (vmin, vmax) = locs[[0,-1]] + if vmin == vmax: + vmin -= 1 + vmax += 1 + return nonsingular(vmin, vmax) + +#............................................................................... +class TimeSeries_YearLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) year. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive annual ticks. + - `quarter` : Integer *[1]* + Tells on which quarter the ticks should be. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + if freq == 'Q': + locs = (dates.quarters == self.quarter) + elif freq == 'M': + locs = (dates.months == self.month) + elif freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return dates.tovalue()[locs] +#............................................... +class TimeSeries_QuarterLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) quarter. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'Q': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq == 'M': + locs = (dates.months % 4 == self.month) + elif freq in 'BDU': + locs = (dates.months % 4 == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] +#............................................................................... +class TimeSeries_MonthLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) month. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'AQ': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'M': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] + +#####--------------------------------------------------------------------------- +#---- --- Formatter --- +#####--------------------------------------------------------------------------- +class TimeSeries_DateFormatter(Formatter): + """Formats the ticks along a DateArray axis.""" + + def __init__(self, freq, fmt=None): + if fmt is None: + fmt = Date.default_fmtstr[freq] + self.fmt = fmt + self.freqstr = freq + + def __call__(self, x, pos=0): + return Date(self.freqstr, value=int(x)).strfmt(self.fmt) + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries plots --- +#####-------------------------------------------------------------------------- +class TimeSeriesPlot(Subplot, object): + """Defines a time series based subclass of Subplot.""" + def __init__(self, fig=None, *args, **kwargs): + """ +Accepts the same keywords as a standard subplot, plus a specific `series` keyword. + +:Parameters: + `fig` : Figure + Base figure. + +:Keywords: + `series` : TimeSeries + Data to plot + + """ + # Retrieve the series ................... + _series = kwargs.pop('series',None) + Subplot.__init__(self,fig,*args,**kwargs) +# # Force fig to be defined ..... +# if fig is None: +# fig = TSFigure(_series) + # Process options ....................... + if _series is not None: + assert hasattr(_series, "dates") + self._series = _series.ravel() + self.xdata = _series.dates + self.freqstr = _series.dates.freqstr + self.xaxis.set_major_locator + + else: + self._series = None + self.xdata = None + self.freqstr = None + self._austoscale = False + # Get the data to plot + self.legendsymbols = [] + self.legendlabels = [] + #............................................ + def set_ydata(self, series=None): + """Sets the base time series.""" + if self._series is not None: + print "WARNING ! Base series is being changed.""" + self._series = series.ravel() + if isinstance(series, TimeSeries): + self.xdata = self.series.dates + #.... + def get_ydata(self): + """Gets the base time series.""" + return self._series + ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') + #............................................ + def _check_plot_params(self,*args): + """Defines the plot coordinates (and basic plotting arguments).""" + remaining = list(args) + # No args ? Use defaults, if any + if len(args) == 0: + if self.xdata is None: + raise ValueError, "No date information available!" + return (self.xdata, self.ydata) + output = [] + while len(remaining) > 0: + a = remaining.pop(0) + # The argument is a format: use default dates/ + if isinstance(a,str): + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, self.ydata, a]) + # The argument is a TimeSeries: use its dates for x + elif isinstance(a, TimeSeries): + (x,y) = (a._dates, a._series) + if len(remaining) > 0 and isinstance(remaining[0], str): + b = remaining.pop(0) + output.extend([x,y,b]) + else: + output.extend([x,y]) + # The argument is a DateArray............ + elif isinstance(a, (Date, DateArray)): + # Force to current freq + if self.freqstr is not None: + if a.freqstr != self.freqstr: + a = a.asfreq(self.freqstr) + # There's an argument after + if len(remaining) > 0: + #...and it's a format string + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata, b]) + #... and it's another date: use the default + elif isinstance(remaining[0], DateArray): + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata]) + #... and it must be some data + else: + b = remaining.pop(0) + if len(remaining) > 0: + if isinstance(remaining[0], str): + c = remaining.pop(0) + output.extend([a,b,c]) + else: + output.extend([a,b]) + # continue + else: + if self.ydata is None: + raise ValueError, "No data information available!" + #else: + # break + # Otherwise.............................. + elif len(remaining) > 0: + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a, b]) + #continue + elif self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a]) + #continue + # Reinitialize the plot if needed ........... + if self.xdata is None: + self.xdata = output[0] + self.freqstr = self.xdata.freqstr + # Force the xdata to the current frequency + elif output[0].freqstr != self.freqstr: + output = list(output) + output[0] = output[0].asfreq(self.freqstr) + return output + #............................................ + def tsplot(self,*parms,**kwargs): + """Plots the data parsed in argument. +This command accepts the same keywords as `matplotlib.plot`.""" + #print "Parameters: %s - %i" % (parms, len(parms)) + parms = self._check_plot_params(*parms) + self.legendlabels.append(kwargs.get('label',None)) + Subplot.plot(self, *parms,**kwargs) + pylab.draw_if_interactive() +# #............................................ +# def ybaseline(self,ybase,**kwargs): +# """Plots an horizontal baseline on each subplot.""" +# self.axhline(ybase,**kwargs) + #............................................ + def format_dateaxis(self,maj_spacing=None, min_spacing=None, + strformat="%Y", rotate=True): + """Pretty-formats the date axis (x-axis). + +:Parameters: + `major` : Integer *[5]* + Major tick locator, in years (major tick every `major` years). + `minor` : Integer *[12]* + Minor tick locator, in months (minor ticks every `minor` months). + `strformat` : String *['%Y']* + String format for major ticks ("%Y"). + """ + # Get the locator class ................. + if self.freqstr in 'BDU': + locator = TimeSeries_DailyLocator + self.xaxis.set_major_locator(locator(self.freqstr, + minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_locator(locator(self.freqstr, + minor_locator=True, + dynamic_mode=True)) + else: + if self.freqstr == 'A': + locator = TimeSeries_AnnualLocator + elif self.freqstr == 'Q': + locator = TimeSeries_QuarterlyLocator + elif self.freqstr == 'M': + locator = TimeSeries_MonthlyLocator + self.xaxis.set_major_locator(locator(minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_locator(locator(minor_locator=True, + dynamic_mode=True)) + #........................................ + self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) + if rcParams['backend'] == 'PS': + rotate = False + warnings.warn("dateplot: PS backend detected, rotate disabled") + if self.is_last_row(): + if rotate: + setp(self.get_xticklabels(),rotation=45) +# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) +# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) +# else: +# self.set_xticklabels([]) +# self.set_xlabel('') +# #............................................ +# def plot_shifts(self,shifts,**kwargs): +# """Plots regime shifts. +#:param shifts: Shifts/trends to plot. +#:type shifts: `RegimeShift` +# """ +# self.tsplot(self.xdata,shifts.regimes,**kwargs) +# for x in shifts.xshifts[0]: +# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) + #............................................ +TSPlot = TimeSeriesPlot + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries Figures --- +#####-------------------------------------------------------------------------- +class TimeSeriesFigure(Figure): + """Time Series Figure: all subplots share the same time series. + """ + def __init__(self, series=None, **kwargs): + self._series = series + Figure.__init__(self,**kwargs) + fspnum = kwargs.pop('fspnum',None) + if fspnum is not None: + self.add_tsplot(fspnum, series=series) + #......... + def add_tsplot(self, *args, **kwargs): + """Adds a `TimeSeriesPlot` subplot to the figure.""" + kwargs.update(SubplotClass=TimeSeriesPlot, + series=self._series) + return add_generic_subplot(self, *args, **kwargs) + add_plot = add_tsplot +TSFigure = TimeSeriesFigure +#Figure.add_tsplot = +#................................................ +def tsfigure(series, **figargs): + """Creates a new `TimeSeriesFigure` object. + +:Parameters: + `series` : TimeSeries object + Input data. + `figargs` : Dictionary + Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. + """ + figargs.update(FigureClass=TSFigure) + figargs.update(series=series) +# print "figargs:",figargs +# num = figargs.pop('num',None) + fig = pylab.figure(**figargs) + return fig + +def add_tsplot(axes, *args, **kwargs): + kwargs.update(SubplotClass=TimeSeriesPlot) + if 'series' not in kwargs.keys(): + kwargs['series'] = None + return add_generic_subplot(axes, *args, **kwargs) +Figure.add_tsplot = add_tsplot + + +def tsplot(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = pylab.ishold() + h = kwargs.pop('hold', None) + if h is not None: + pylab.hold(h) + try: + ret = pylab.gca().add_tsplot(*args, **kwargs) + pylab.draw_if_interactive() + except: + pylab.hold(b) + raise + + pylab.hold(b) + return ret + +################################################################################ +if __name__ == '__main__': + + da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), + length=51) + ser = timeseries.time_series(MA.arange(len(da)), dates=da) + ser[4] = MA.masked + ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) + + pylab.figure() + pylab.gcf().add_tsplot(111) + pylab.gca().tsplot(ser, 'ko-') + pylab.gca().format_dateaxis() + pylab.gca().tsplot(ser_2, 'rs') + pylab.show() + \ No newline at end of file From scipy-svn at scipy.org Tue Feb 6 23:59:44 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 6 Feb 2007 22:59:44 -0600 (CST) Subject: [Scipy-svn] r2687 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070207045944.4181F39C071@new.scipy.org> Author: pierregm Date: 2007-02-06 22:59:42 -0600 (Tue, 06 Feb 2007) New Revision: 2687 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Log: plotlib: import mpl_timeseries directly mpl_timeseries: allow more than 3 args at the same time in tsplot Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-07 04:53:28 UTC (rev 2686) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-07 04:59:42 UTC (rev 2687) @@ -32,8 +32,11 @@ import numpy as N import maskedarray as MA -import timeseries as TS -from timeseries import date_array, Date, TimeSeries +import timeseries +from timeseries import date_array, Date, DateArray, TimeSeries +#from tdates import date_array, Date +#import tseries +#from tseries import TimeSeries import warnings @@ -114,7 +117,7 @@ vmin, vmax = vmax, vmin swapped = True if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin==0.0: + if vmin == 0.0: vmin = -expander vmax = expander else: @@ -538,46 +541,88 @@ #............................................ def _check_plot_params(self,*args): """Defines the plot coordinates (and basic plotting arguments).""" - # At least three arguments .... - if len(args) >= 3: - params = args[:3] - # Two arguments only .......... - elif len(args) == 2: - if isinstance(args[1], str): - # The last argument is a format string - arg = args[0] - if isinstance(arg, TimeSeries): - params = (arg._dates, arg._series, args[1]) - elif self.xdata is None: + remaining = list(args) + # No args ? Use defaults, if any + if len(args) == 0: + if self.xdata is None: + raise ValueError, "No date information available!" + return (self.xdata, self.ydata) + output = [] + while len(remaining) > 0: + a = remaining.pop(0) + # The argument is a format: use default dates/ + if isinstance(a,str): + if self.xdata is None: raise ValueError, "No date information available!" else: - params = (self.xdata, args[0], args[1]) - else: - params = args - # One argument only ........... - elif len(args) == 1: - if isinstance(args[0], str): - if self.xdata is None: + output.extend([self.xdata, self.ydata, a]) + # The argument is a TimeSeries: use its dates for x + elif isinstance(a, TimeSeries): + (x,y) = (a._dates, a._series) + if len(remaining) > 0 and isinstance(remaining[0], str): + b = remaining.pop(0) + output.extend([x,y,b]) + else: + output.extend([x,y]) + # The argument is a DateArray............ + elif isinstance(a, (Date, DateArray)): + # Force to current freq + if self.freqstr is not None: + if a.freqstr != self.freqstr: + a = a.asfreq(self.freqstr) + # There's an argument after + if len(remaining) > 0: + #...and it's a format string + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata, b]) + #... and it's another date: use the default + elif isinstance(remaining[0], DateArray): + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata]) + #... and it must be some data + else: + b = remaining.pop(0) + if len(remaining) > 0: + if isinstance(remaining[0], str): + c = remaining.pop(0) + output.extend([a,b,c]) + else: + output.extend([a,b]) + # continue + else: + if self.ydata is None: + raise ValueError, "No data information available!" + #else: + # break + # Otherwise.............................. + elif len(remaining) > 0: + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a, b]) + #continue + elif self.xdata is None: raise ValueError, "No date information available!" else: - params = (self.xdata, self.ydata, args[0]) - elif isinstance(args[0], TimeSeries): - if self.xdata is None: - arg = args[0] - params = (arg._dates, arg._series) - else: - params = (self.xdata, args[0]) - else: - params = (self.xdata, self.ydata) - # Reinitialize the plot if needed + output.extend([self.xdata, a]) + #continue + # Reinitialize the plot if needed ........... if self.xdata is None: - self.xdata = params[0] + self.xdata = output[0] self.freqstr = self.xdata.freqstr # Force the xdata to the current frequency - elif params[0].freqstr != self.freqstr: - params = list(params) - params[0] = params[0].asfreq(self.freqstr) - return params + elif output[0].freqstr != self.freqstr: + output = list(output) + output[0] = output[0].asfreq(self.freqstr) + return output #............................................ def tsplot(self,*parms,**kwargs): """Plots the data parsed in argument. @@ -626,12 +671,12 @@ dynamic_mode=True)) #........................................ self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) -# if rcParams['backend'] == 'PS': -# rotate = False -# warnings.warn("dateplot: PS backend detected, rotate disabled") -# if self.is_last_row(): -# if rotate: -# setp(self.get_xticklabels(),rotation=45) + if rcParams['backend'] == 'PS': + rotate = False + warnings.warn("dateplot: PS backend detected, rotate disabled") + if self.is_last_row(): + if rotate: + setp(self.get_xticklabels(),rotation=45) # self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) # self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) # else: @@ -717,9 +762,9 @@ da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), length=51) - ser = TS.time_series(MA.arange(len(da)), dates=da) + ser = timeseries.time_series(MA.arange(len(da)), dates=da) ser[4] = MA.masked - ser_2 = TS.time_series(MA.arange(len(da)), dates=da.asfreq('M')) + ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) pylab.figure() pylab.gcf().add_tsplot(111) From scipy-svn at scipy.org Wed Feb 7 07:18:45 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 7 Feb 2007 06:18:45 -0600 (CST) Subject: [Scipy-svn] r2688 - in trunk/Lib/io: . tests Message-ID: <20070207121845.DEE6439C0F6@new.scipy.org> Author: matthew.brett at gmail.com Date: 2007-02-07 06:18:41 -0600 (Wed, 07 Feb 2007) New Revision: 2688 Modified: trunk/Lib/io/npfile.py trunk/Lib/io/tests/test_npfile.py Log: Modified interface to npfile, cleaned up and expanded docstrings Modified: trunk/Lib/io/npfile.py =================================================================== --- trunk/Lib/io/npfile.py 2007-02-07 04:59:42 UTC (rev 2687) +++ trunk/Lib/io/npfile.py 2007-02-07 12:18:41 UTC (rev 2688) @@ -1,7 +1,7 @@ -# Author: Matthew Brett, Travis Oliphant +# Authors: Matthew Brett, Travis Oliphant """ -Class for reading and writing numpy arrays from / to files +Class for reading and writing numpy arrays from / to binary files """ import sys @@ -23,10 +23,38 @@ (['native', 'n'], ['ieee-le', 'l'], ['ieee-be', 'B']) for native, little-endian, or big-endian respectively. - Attributes + Attributes: endian -- default endian code for reading / writing order -- default order for reading writing ('C' or 'F') - file -- file object containing read / written data + file -- file object containing read / written data + + Methods: + seek, tell, close -- as for file objects + rewind -- set read position to beginning of file + read_raw -- read string data from file (read method of file) + write_raw -- write string data to file (write method of file) + read_array -- read numpy array from binary file data + write_array -- write numpy array contents to binary file + + Example use: + >>> from StringIO import StringIO + >>> import numpy as N + >>> from scipy.io import npfile + >>> arr = N.arange(10).reshape(5,2) + >>> # Make file-like object (could also be file name) + >>> my_file = StringIO() + >>> npf = npfile(my_file) + >>> npf.write_array(arr) + >>> npf.rewind() + >>> npf.read_array((5,2), arr.dtype) + >>> npf.close() + >>> # Or read write in Fortran order, Big endian + >>> # and read back in C, system endian + >>> my_file = StringIO() + >>> npf = npfile(my_file, order='F', endian='>') + >>> npf.write_array(arr) + >>> npf.rewind() + >>> npf.read_array((5,2), arr.dtype) ''' def __init__(self, file_name, @@ -44,7 +72,6 @@ if closed: raise TypeError, 'File object should be open' self.file = file_name - self.endian = endian self.order = order @@ -95,26 +122,11 @@ else: self.seek(-howmany,1) - def size(self): - """Return the size of the file. - - Cached once found - """ - try: - sz = self.thesize - except AttributeError: - curpos = self.tell() - self.seek(0,2) - sz = self.tell() - self.seek(curpos) - self.thesize = sz - return sz - - def raw_read(self, size=-1): + def read_raw(self, size=-1): """Read raw bytes from file as string.""" return self.file.read(size) - def raw_write(self, str): + def write_raw(self, str): """Write string to file as raw bytes.""" return self.file.write(str) @@ -134,16 +146,16 @@ dt_endian = sys_endian_code return dt_endian - def write(self, data, endian=None, order=None): + def write_array(self, data, endian=None, order=None): ''' Write to open file object the flattened numpy array data Inputs data - numpy array or object convertable to array endian - endianness of written data (can be None, 'dtype', '<', '>') - (default from self.endian) + (if None, get from self.endian) order - order of array to write (C, F) - (default from self.order) + (if None from self.order) ''' endian, order = self._endian_order(endian, order) data = N.asarray(data) @@ -153,20 +165,21 @@ data = data.byteswap() self.file.write(data.tostring(order=order)) - fwrite = write - - def read(self, shape, dt, endian=None, order=None): + def read_array(self, shape, dt, endian=None, order=None): '''Read data from file and return it in a numpy array. Inputs ------ shape - shape of output array, or number of elements dt - dtype of array to be read - endian - endianness of written data + endian - endianness of data in file (can be None, 'dtype', '<', '>') - (default from self.endian) - order - order of array to be read ('C' or 'F') - (default from self.order) + (if None, get from self.endian) + order - order of array in file (C, F) + (if None get from self.order) + + Outputs + arr - array from file with given dtype (dt) ''' endian, order = self._endian_order(endian, order) try: @@ -185,4 +198,3 @@ return arr.byteswap() return arr.copy() - fread = read Modified: trunk/Lib/io/tests/test_npfile.py =================================================================== --- trunk/Lib/io/tests/test_npfile.py 2007-02-07 04:59:42 UTC (rev 2687) +++ trunk/Lib/io/tests/test_npfile.py 2007-02-07 12:18:41 UTC (rev 2688) @@ -14,12 +14,12 @@ fd, fname = mkstemp() npf = npfile(fname) arr = N.reshape(N.arange(10), (5,2)) - self.assertRaises(IOError, npf.write, arr) + self.assertRaises(IOError, npf.write_array, arr) npf.close() npf = npfile(fname, 'w') - npf.write(arr) + npf.write_array(arr) npf.rewind() - self.assertRaises(IOError, npf.read, + self.assertRaises(IOError, npf.read_array, arr.shape, arr.dtype) npf.close() @@ -41,14 +41,14 @@ assert npf.parse_endian('dtype') == 'dtype' self.assertRaises(ValueError, npf.parse_endian, 'nonsense') - def test_raw_read_write(self): + def test_read_write_raw(self): npf = npfile(StringIO()) str = 'test me with this string' - npf.raw_write(str) + npf.write_raw(str) npf.rewind() - assert str == npf.raw_read(len(str)) + assert str == npf.read_raw(len(str)) - def test_read_write(self): + def test_read_write_array(self): npf = npfile(StringIO()) arr = N.reshape(N.arange(10), (5,2)) # Arr as read in fortran order @@ -65,27 +65,27 @@ bs_arr = arr.newbyteorder(nbo) adt = arr.dtype shp = arr.shape - npf.write(arr) + npf.write_array(arr) npf.rewind() - assert_array_equal(npf.read(shp, adt), arr) + assert_array_equal(npf.read_array(shp, adt), arr) npf.rewind() - assert_array_equal(npf.read(shp, adt, endian=swapped_code), + assert_array_equal(npf.read_array(shp, adt, endian=swapped_code), bs_arr) npf.rewind() - assert_array_equal(npf.read(shp, adt, order='F'), + assert_array_equal(npf.read_array(shp, adt, order='F'), f_arr) npf.rewind() - npf.write(arr, order='F') + npf.write_array(arr, order='F') npf.rewind() - assert_array_equal(npf.read(shp, adt), + assert_array_equal(npf.read_array(shp, adt), cf_arr) npf = npfile(StringIO(), endian='swapped', order='F') - npf.write(arr) + npf.write_array(arr) npf.rewind() - assert_array_equal(npf.read(shp, adt), arr) + assert_array_equal(npf.read_array(shp, adt), arr) npf.rewind() - assert_array_equal(npf.read(shp, adt, endian='dtype'), bs_arr) + assert_array_equal(npf.read_array(shp, adt, endian='dtype'), bs_arr) npf.rewind() - assert_array_equal(npf.read(shp, adt, order='C'), cf_arr) + assert_array_equal(npf.read_array(shp, adt, order='C'), cf_arr) From scipy-svn at scipy.org Wed Feb 7 09:33:09 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 7 Feb 2007 08:33:09 -0600 (CST) Subject: [Scipy-svn] r2689 - trunk/Lib/sandbox/spline Message-ID: <20070207143309.BCA4C39C018@new.scipy.org> Author: jtravs Date: 2007-02-07 08:32:55 -0600 (Wed, 07 Feb 2007) New Revision: 2689 Modified: trunk/Lib/sandbox/spline/fitpack.py Log: Fixed bug with parametric spline. Modified: trunk/Lib/sandbox/spline/fitpack.py =================================================================== --- trunk/Lib/sandbox/spline/fitpack.py 2007-02-07 12:18:41 UTC (rev 2688) +++ trunk/Lib/sandbox/spline/fitpack.py 2007-02-07 14:32:55 UTC (rev 2689) @@ -236,8 +236,9 @@ _parcur_cache['t']=t _parcur_cache['wrk']=wrk _parcur_cache['iwrk']=iwrk - c.shape=idim,nest - c = c[:][:n-k-1] + c = c[:n*idim] + c.shape=idim,n + c = c[:,:n-k-1] tcku = [t[:n],list(c),k],u if ier<=0 and not quiet: print _iermess[ier][0] From scipy-svn at scipy.org Thu Feb 8 07:05:31 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 8 Feb 2007 06:05:31 -0600 (CST) Subject: [Scipy-svn] r2690 - in trunk/Lib/sandbox: . rbf rbf/tests spline/tests Message-ID: <20070208120531.B72AF39C375@new.scipy.org> Author: jtravs Date: 2007-02-08 06:05:18 -0600 (Thu, 08 Feb 2007) New Revision: 2690 Added: trunk/Lib/sandbox/rbf/ trunk/Lib/sandbox/rbf/README.txt trunk/Lib/sandbox/rbf/__init__.py trunk/Lib/sandbox/rbf/info.py trunk/Lib/sandbox/rbf/rbf.py trunk/Lib/sandbox/rbf/setup.py trunk/Lib/sandbox/rbf/tests/ trunk/Lib/sandbox/rbf/tests/example.py trunk/Lib/sandbox/rbf/tests/test_rbf.py Modified: trunk/Lib/sandbox/setup.py trunk/Lib/sandbox/spline/tests/test_fitpack.py Log: Added new rbf package to sandbox Added: trunk/Lib/sandbox/rbf/README.txt =================================================================== --- trunk/Lib/sandbox/rbf/README.txt 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/README.txt 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,8 @@ +This package uses radial basis functions for n-dimensional +smoothing/interpolation of scattered data + +It is closely based on the MAtlab code by Alex Chirokov found at: + +http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=10056&objectType=FILE + +John Travers \ No newline at end of file Added: trunk/Lib/sandbox/rbf/__init__.py =================================================================== --- trunk/Lib/sandbox/rbf/__init__.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/__init__.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,11 @@ +# +# rbf - Radial Basis Functions +# + +from info import __doc__ + +from rbf import * + +__all__ = filter(lambda s:not s.startswith('_'),dir()) +from numpy.testing import NumpyTest +test = NumpyTest().test \ No newline at end of file Added: trunk/Lib/sandbox/rbf/info.py =================================================================== --- trunk/Lib/sandbox/rbf/info.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/info.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,9 @@ +""" +Radial Basis Functions +=================== + +rbf - Radial basis functions for interpolation/smoothing. + +""" + +postpone_import = 1 Added: trunk/Lib/sandbox/rbf/rbf.py =================================================================== --- trunk/Lib/sandbox/rbf/rbf.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/rbf.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,126 @@ +#!/usr/bin/env python +""" +rbf - Radial basis functions for interpolation/smoothing scattered Nd data. + +Written by John Travers , February 2007 +Based closely on Matlab code by Alex Chirokov + +Permission to use, modify, and distribute this software is given under the +terms of the SciPy (BSD style) license. See LICENSE.txt that came with +this distribution for specifics. + +NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +""" + +import scipy as s +import scipy.linalg + +class Rbf(object): + """ A class for radial basis function approximation/interpolation of + n-dimensional scattered data. + """ + def __init__(self,x,y, function='multiquadrics', constant=None, smooth=0): + """ Constructor for Rbf class. + + Inputs: + x (dim, n) array of coordinates for the nodes + y (n,) array of values at the nodes + function the radial basis function + 'linear', 'cubic' 'thinplate', 'multiquadrics' + or 'gaussian', default is 'multiquadrics' + constant adjustable constant for gaussian or multiquadrics + functions - defaults to approximate average distance + between nodes (which is a good start) + smooth values greater than zero increase the smoothness + of the approximation. + 0 is for interpolation (default), the function will + always go through the nodal points in this case. + + Outputs: None + """ + if len(x.shape) == 1: + nxdim = 1 + nx = x.shape[0] + else: + (nxdim, nx)=x.shape + if len(y.shape) == 1: + nydim = 1 + ny = y.shape[0] + else: + (nydim, ny)=y.shape + x.shape = (nxdim, nx) + y.shape = (nydim, ny) + if nx != ny: + raise ValueError, 'x and y should have the same number of points' + if nydim != 1: + raise ValueError, 'y should be a length n vector' + self.x = x + self.y = y + self.function = function + if (constant==None + and ((function == 'multiquadrics') or (function == 'gaussian'))): + # approx. average distance between the nodes + constant = (s.product(x.T.max(0)-x.T.min(0),axis=0)/nx)**(1/nxdim) + self.constant = constant + self.smooth = smooth + if self.function == 'linear': + self.phi = lambda r: r + elif self.function == 'cubic': + self.phi = lambda r: r*r*r + elif self.function == 'multiquadrics': + self.phi = lambda r: s.sqrt(1.0+r*r/(self.constant*self.constant)) + elif self.function == 'thinplate': + self.phi = lambda r: r*r*s.log(r+1) + elif self.function == 'gaussian': + self.phi = lambda r: s.exp(-0.5*r*r/(self.rbfconst*self.constant)) + else: + raise ValueError, 'unkown function' + A = self._rbf_assemble() + b=s.r_[y.T, s.zeros((nxdim+1, 1), float)] + self.coeff = s.linalg.solve(A,b) + + def __call__(self, xi): + """ Evaluate the radial basis function approximation at points xi. + + Inputs: + xi (dim, n) array of coordinates for the points to evaluate at + + Outputs: + y (n,) array of values at the points xi + """ + if len(xi.shape) == 1: + nxidim = 1 + nxi = xi.shape[0] + else: + (nxidim, nxi)=xi.shape + xi.shape = (nxidim, nxi) + (nxdim, nx) = self.x.shape + if nxdim != nxidim: + raise ValueError, 'xi should have the same number of rows as an' \ + ' array used to create RBF interpolation' + f = s.zeros(nxi, float) + r = s.zeros(nx, float) + for i in range(nxi): + st=0.0 + r = s.dot(xi[:,i,s.newaxis],s.ones((1,nx))) - self.x + r = s.sqrt(sum(r*r)) + st = self.coeff[nx,:] + s.sum(self.coeff[0:nx,:].flatten()*self.phi(r)) + for k in range(nxdim): + st=st+self.coeff[k+nx+1,:]*xi[k,i] + f[i] = st + return f + + def _rbf_assemble(self): + (nxdim, nx)=self.x.shape + A=s.zeros((nx,nx), float) + for i in range(nx): + for j in range(i+1): + r=s.linalg.norm(self.x[:,i]-self.x[:,j]) + temp=self.phi(r) + A[i,j]=temp + A[j,i]=temp + A[i,i] = A[i,i] - self.smooth + P = s.c_[s.ones((nx,1), float), self.x.T] + A = s.r_[s.c_[A, P], s.c_[P.T, s.zeros((nxdim+1,nxdim+1), float)]] + return A Added: trunk/Lib/sandbox/rbf/setup.py =================================================================== --- trunk/Lib/sandbox/rbf/setup.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/setup.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +import os + +def configuration(parent_package='',top_path=None): + from numpy.distutils.misc_util import Configuration + + config = Configuration('rbf', parent_package, top_path) + + config.add_data_dir('tests') + + return config + +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(**configuration(top_path='').todict()) \ No newline at end of file Added: trunk/Lib/sandbox/rbf/tests/example.py =================================================================== --- trunk/Lib/sandbox/rbf/tests/example.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/tests/example.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,52 @@ +import scipy as s +import scipy.interpolate + +from scipy.sandbox.rbf import Rbf + +import matplotlib +matplotlib.use('Agg') +import pylab as p + +# 1d tests - setup data +x = s.linspace(0,10,9) +y = s.sin(x) +xi = s.linspace(0,10,101) + +# use interpolate methods +ius = s.interpolate.InterpolatedUnivariateSpline(x,y) +yi = ius(xi) +p.subplot(2,1,1) +p.plot(x,y,'o',xi,yi, xi, s.sin(xi),'r') +p.title('Interpolation using current scipy fitpack2') + +# use RBF method +rbf = Rbf(x, y) +fi = rbf(xi) +p.subplot(2,1,2) +p.plot(x,y,'bo',xi.flatten(),fi.flatten(),'g',xi.flatten(), + s.sin(xi.flatten()),'r') +p.title('RBF interpolation - multiquadrics') +p.savefig('rbf1dtest.png') +p.close() + +# 2-d tests - setup scattered data +x = s.rand(50,1)*4-2 +y = s.rand(50,1)*4-2 +z = x*s.exp(-x**2-y**2) +ti = s.linspace(-2.0,2.0,81) +(XI,YI) = s.meshgrid(ti,ti) + +# use RBF +rbf = Rbf(s.c_[x.flatten(),y.flatten()].T,z.T,constant=2) +ZI = rbf(s.c_[XI.flatten(), YI.flatten()].T) +ZI.shape = XI.shape + +# plot the result +from enthought.tvtk.tools import mlab +f=mlab.figure(browser=False) +su=mlab.Surf(XI,YI,ZI,ZI,scalar_visibility=True) +f.add(su) +su.lut_type='blue-red' +f.objects[0].axis.z_label='value' +pp = mlab.Spheres(s.c_[x.flatten(), y.flatten(), z.flatten()],radius=0.03) +f.add(pp) \ No newline at end of file Property changes on: trunk/Lib/sandbox/rbf/tests/example.py ___________________________________________________________________ Name: svn:executable + * Added: trunk/Lib/sandbox/rbf/tests/test_rbf.py =================================================================== --- trunk/Lib/sandbox/rbf/tests/test_rbf.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/rbf/tests/test_rbf.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# Created by John Travers, February 2007 +""" Test functions for rbf module """ + +from numpy.testing import * +import numpy as n + +set_package_path() +from rbf.rbf import Rbf +restore_path() + +class test_Rbf1D(NumpyTestCase): + def check_multiquadrics(self): + x = n.linspace(0,10,9) + y = n.sin(x) + rbf = Rbf(x, y) + yi = rbf(x) + assert_array_almost_equal(y.flatten(), yi) + +class test_Rbf2D(NumpyTestCase): + def check_multiquadrics(self): + x = n.random.rand(50,1)*4-2 + y = n.random.rand(50,1)*4-2 + z = x*n.exp(-x**2-y**2) + rbf = Rbf(n.c_[x.flatten(),y.flatten()].T,z.T,constant=2) + zi = rbf(n.c_[x.flatten(), y.flatten()].T) + zi.shape = x.shape + assert_array_almost_equal(z, zi) + +if __name__ == "__main__": + NumpyTest().run() \ No newline at end of file Modified: trunk/Lib/sandbox/setup.py =================================================================== --- trunk/Lib/sandbox/setup.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/setup.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -81,6 +81,9 @@ # New spline package (based on scipy.interpolate) #config.add_subpackage('spline') + + # Radial basis functions package + #config.add_subpackage('rbf') return config Modified: trunk/Lib/sandbox/spline/tests/test_fitpack.py =================================================================== --- trunk/Lib/sandbox/spline/tests/test_fitpack.py 2007-02-07 14:32:55 UTC (rev 2689) +++ trunk/Lib/sandbox/spline/tests/test_fitpack.py 2007-02-08 12:05:18 UTC (rev 2690) @@ -142,3 +142,6 @@ decimal=1) assert_almost_equal(0.0, around(abs(splev(uv[0],tck)-f(uv[0])),2),decimal=1) + +if __name__ == "__main__": + NumpyTest().run() \ No newline at end of file From scipy-svn at scipy.org Thu Feb 8 07:05:54 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 8 Feb 2007 06:05:54 -0600 (CST) Subject: [Scipy-svn] r2691 - trunk/Lib/sandbox/rbf Message-ID: <20070208120554.1E58A39C375@new.scipy.org> Author: jtravs Date: 2007-02-08 06:05:50 -0600 (Thu, 08 Feb 2007) New Revision: 2691 Removed: trunk/Lib/sandbox/rbf/README.txt Log: Delete mistaken README upload Deleted: trunk/Lib/sandbox/rbf/README.txt =================================================================== --- trunk/Lib/sandbox/rbf/README.txt 2007-02-08 12:05:18 UTC (rev 2690) +++ trunk/Lib/sandbox/rbf/README.txt 2007-02-08 12:05:50 UTC (rev 2691) @@ -1,8 +0,0 @@ -This package uses radial basis functions for n-dimensional -smoothing/interpolation of scattered data - -It is closely based on the MAtlab code by Alex Chirokov found at: - -http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=10056&objectType=FILE - -John Travers \ No newline at end of file From scipy-svn at scipy.org Thu Feb 8 08:49:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 8 Feb 2007 07:49:04 -0600 (CST) Subject: [Scipy-svn] r2692 - in trunk/Lib: interpolate sandbox/spline Message-ID: <20070208134904.38EE839C3C0@new.scipy.org> Author: jtravs Date: 2007-02-08 07:48:57 -0600 (Thu, 08 Feb 2007) New Revision: 2692 Modified: trunk/Lib/interpolate/fitpack.py trunk/Lib/sandbox/spline/fitpack.py Log: Apply patch from Zach Pincus fixing parametric spline unrolling to both interpolate and sandbox.spline Modified: trunk/Lib/interpolate/fitpack.py =================================================================== --- trunk/Lib/interpolate/fitpack.py 2007-02-08 12:05:50 UTC (rev 2691) +++ trunk/Lib/interpolate/fitpack.py 2007-02-08 13:48:57 UTC (rev 2692) @@ -436,16 +436,20 @@ t,c,k=tck try: c[0][0] + parametric = True + except: + parametric = False + if parametric: return map(lambda c,x=x,t=t,k=k,der=der:splev(x,[t,c,k],der),c) - except: pass - if not (0<=der<=k): - raise ValueError,"0<=der=%d<=k=%d must hold"%(der,k) - x=myasarray(x) - y,ier=_fitpack._spl_(x,der,t,c,k) - if ier==10: raise ValueError,"Invalid input data" - if ier: raise TypeError,"An error occurred" - if len(y)>1: return y - return y[0] + else: + if not (0<=der<=k): + raise ValueError,"0<=der=%d<=k=%d must hold"%(der,k) + x=myasarray(x) + y,ier=_fitpack._spl_(x,der,t,c,k) + if ier==10: raise ValueError,"Invalid input data" + if ier: raise TypeError,"An error occurred" + if len(y)>1: return y + return y[0] def splint(a,b,tck,full_output=0): """Evaluate the definite integral of a B-spline. @@ -475,11 +479,17 @@ of the FITPACK functions """ t,c,k=tck - try: c[0][0];return _ntlist(map(lambda c,a=a,b=b,t=t,k=k:splint(a,b,[t,c,k]),c)) - except: pass - aint,wrk=_fitpack._splint(t,c,k,a,b) - if full_output: return aint,wrk - else: return aint + try: + c[0][0] + parametric = True + except: + parametric = False + if parametric: + return _ntlist(map(lambda c,a=a,b=b,t=t,k=k:splint(a,b,[t,c,k]),c)) + else: + aint,wrk=_fitpack._splint(t,c,k,a,b) + if full_output: return aint,wrk + else: return aint def sproot(tck,mest=10): """Find the roots of a cubic B-spline. @@ -509,18 +519,24 @@ t,c,k=tck if k==4: t=t[1:-1] if k==5: t=t[2:-2] - try: c[0][0];return _ntlist(map(lambda c,t=t,k=k,mest=mest:sproot([t,c,k],mest),c)) - except: pass - if len(t)<8: - raise TypeError,"The number of knots %d>=8"%(len(t)) - z,ier=_fitpack._sproot(t,c,k,mest) - if ier==10: - raise TypeError,"Invalid input data. t1<=..<=t4=8"%(len(t)) + z,ier=_fitpack._sproot(t,c,k,mest) + if ier==10: + raise TypeError,"Invalid input data. t1<=..<=t41: - return map(lambda x,tck=tck:spalde(x,tck),x) - d,ier=_fitpack._spalde(t,c,k,x[0]) - if ier==0: return d - if ier==10: - raise TypeError,"Invalid input data. t(k)<=x<=t(n-k+1) must hold." - raise TypeError,"Unknown error" + else: + try: x=x.tolist() + except: + try: x=list(x) + except: x=[x] + if len(x)>1: + return map(lambda x,tck=tck:spalde(x,tck),x) + d,ier=_fitpack._spalde(t,c,k,x[0]) + if ier==0: return d + if ier==10: + raise TypeError,"Invalid input data. t(k)<=x<=t(n-k+1) must hold." + raise TypeError,"Unknown error" #def _curfit(x,y,w=None,xb=None,xe=None,k=3,task=0,s=None,t=None, # full_output=0,nest=None,per=0,quiet=1): @@ -783,16 +803,20 @@ t,c,k=tck try: c[0][0] + parametric = True + except: + parametric = False + if parametric: cc = [] for c_vals in c: tt, cc_val, kk = insert(x, [t, c_vals, k], m) cc.append(cc_val) - return (tt, cc, kk) - except: pass - tt, cc, ier = _fitpack._insert(per, t, c, k, x, m) - if ier==10: raise ValueError,"Invalid input data" - if ier: raise TypeError,"An error occurred" - return (tt, cc, k) + return (tt, cc, kk) + else: + tt, cc, ier = _fitpack._insert(per, t, c, k, x, m) + if ier==10: raise ValueError,"Invalid input data" + if ier: raise TypeError,"An error occurred" + return (tt, cc, k) if __name__ == "__main__": Modified: trunk/Lib/sandbox/spline/fitpack.py =================================================================== --- trunk/Lib/sandbox/spline/fitpack.py 2007-02-08 12:05:50 UTC (rev 2691) +++ trunk/Lib/sandbox/spline/fitpack.py 2007-02-08 13:48:57 UTC (rev 2692) @@ -443,21 +443,24 @@ t,c,k=tck try: c[0][0] # check if c is >1-d + parametric = True # it is + except TypeError: parametric = False # c is 1-d + if parametric: return map(lambda c,x=x,t=t,k=k,der=der:splev(x,[t,c,k],der),c) - except TypeError: pass # c is 1-d - if not (0<=der<=k): - raise ValueError,"0<=der=%d<=k=%d must hold"%(der,k) - x=myasarray(x) - c = c.copy() - c.resize(len(t)) - if (der>0): - y,ier=dfitpack.splder(t,c,k,x,der) else: - y,ier=dfitpack.splev(t,c,k,x) - if ier==10: raise ValueError,"Invalid input data" - if ier: raise TypeError,"An unkown error occurred" - if len(y)>1: return y - return y[0] + if not (0<=der<=k): + raise ValueError,"0<=der=%d<=k=%d must hold"%(der,k) + x=myasarray(x) + c = c.copy() + c.resize(len(t)) + if (der>0): + y,ier=dfitpack.splder(t,c,k,x,der) + else: + y,ier=dfitpack.splev(t,c,k,x) + if ier==10: raise ValueError,"Invalid input data" + if ier: raise TypeError,"An unkown error occurred" + if len(y)>1: return y + return y[0] def splint(a,b,tck,full_output=False): """Evaluate the definite integral of a B-spline. @@ -485,13 +488,16 @@ t,c,k=tck try: c[0][0] # check if c is >1-d + parametric = True # it is + except TypeError: parametric = False # c is 1-d + if parametric: return map(lambda c,a=a,b=b,t=t,k=k:splint(a,b,[t,c,k]),c) - except TypeError: pass # c is 1-d - c = c.copy() - c.resize(len(t)) - aint,wrk = dfitpack.splint(t,c,k,a,b) - if full_output: return aint,wrk - else: return aint + else: + c = c.copy() + c.resize(len(t)) + aint,wrk = dfitpack.splint(t,c,k,a,b) + if full_output: return aint,wrk + else: return aint def sproot(tck,mest=10): """Find the roots of a cubic B-spline. @@ -520,21 +526,24 @@ raise ValueError, "Sproot only valid for cubic bsplines" try: c[0][0] # check if c is >1-d + parametric = True # it is + except TypeError: parametric = False # c is 1-d + if parametric: return map(lambda c,t=t,k=k,mest=mest:sproot([t,c,k],mest),c) - except TypeError: pass # c is 1-d - if len(t)<8: - raise TypeError,"The number of knots be >=8" - c = c.copy() - c.resize(len(t)) - z,m,ier=dfitpack.sproot(t,c,mest) - if ier==10: - raise TypeError,"Invalid input data. t1<=..<=t4=8" + c = c.copy() + c.resize(len(t)) + z,m,ier=dfitpack.sproot(t,c,mest) + if ier==10: + raise TypeError,"Invalid input data. t1<=..<=t41-d + parametric = True # it is + except TypeError: parametric = False # c is 1-d + if parametric: return map(lambda c,x=x,t=t,k=k:spalde(x,[t,c,k]),c) - except TypeError: pass # c is 1-d - try: x=x.tolist() - except: - try: x=list(x) - except: x=[x] - if len(x)>1: - return map(lambda x,tck=tck:spalde(x,tck),x) - c = c.copy() - c.resize(len(t)) - d,ier=dfitpack.spalde(t,c,k,x[0]) - if ier==0: return d - if ier==10: - raise TypeError,"Invalid input data. t(k)<=x<=t(n-k+1) must hold." - raise TypeError,"Unknown error" + else: + try: x=x.tolist() + except: + try: x=list(x) + except: x=[x] + if len(x)>1: + return map(lambda x,tck=tck:spalde(x,tck),x) + c = c.copy() + c.resize(len(t)) + d,ier=dfitpack.spalde(t,c,k,x[0]) + if ier==0: return d + if ier==10: + raise TypeError,"Invalid input data. t(k)<=x<=t(n-k+1) must hold." + raise TypeError,"Unknown error" _surfit_cache = {} From scipy-svn at scipy.org Thu Feb 8 12:07:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 8 Feb 2007 11:07:55 -0600 (CST) Subject: [Scipy-svn] r2693 - trunk/Lib/io Message-ID: <20070208170755.30C2839C0A8@new.scipy.org> Author: matthew.brett at gmail.com Date: 2007-02-08 11:07:46 -0600 (Thu, 08 Feb 2007) New Revision: 2693 Modified: trunk/Lib/io/mio.py trunk/Lib/io/mio4.py trunk/Lib/io/mio5.py trunk/Lib/io/miobase.py trunk/Lib/io/npfile.py Log: Clean up namespace imports; this removesfromfile/tofile from io namespace - they are from numpy Modified: trunk/Lib/io/mio.py =================================================================== --- trunk/Lib/io/mio.py 2007-02-08 13:48:57 UTC (rev 2692) +++ trunk/Lib/io/mio.py 2007-02-08 17:07:46 UTC (rev 2693) @@ -7,10 +7,10 @@ import os import sys -from numpy import * +from scipy.io.mio4 import MatFile4Reader, MatFile4Writer +from scipy.io.mio5 import MatFile5Reader, MatFile5Writer -from mio4 import MatFile4Reader, MatFile4Writer -from mio5 import MatFile5Reader, MatFile5Writer +__all__ = ['find_mat_file','mat_reader_factory','loadmat', 'savemat'] def find_mat_file(file_name, appendmat=True): ''' Try to find .mat file on system path Modified: trunk/Lib/io/mio4.py =================================================================== --- trunk/Lib/io/mio4.py 2007-02-08 13:48:57 UTC (rev 2692) +++ trunk/Lib/io/mio4.py 2007-02-08 17:07:46 UTC (rev 2693) @@ -1,9 +1,9 @@ ''' Classes for read / write of matlab (TM) 4 files ''' -from numpy import * +import numpy as N -from miobase import * +from scipy.io.miobase import * miDOUBLE = 0 miSINGLE = 1 @@ -76,7 +76,7 @@ header['mclass'] = T header['dims'] = (data['mrows'], data['ncols']) header['is_complex'] = data['imagf'] == 1 - remaining_bytes = header['dtype'].itemsize * product(header['dims']) + remaining_bytes = header['dtype'].itemsize * N.product(header['dims']) if header['is_complex'] and not header['mclass'] == mxSPARSE_CLASS: remaining_bytes *= 2 next_pos = self.mat_stream.tell() + remaining_bytes @@ -109,7 +109,7 @@ num_bytes = dt.itemsize for d in dims: num_bytes *= d - arr = ndarray(shape=dims, + arr = N.ndarray(shape=dims, dtype=dt, buffer=self.mat_stream.read(num_bytes), order='F') @@ -122,9 +122,9 @@ def __init__(self, array_reader, header): super(Mat4FullGetter, self).__init__(array_reader, header) if header['is_complex']: - self.mat_dtype = dtype(complex128) + self.mat_dtype = N.dtype(N.complex128) else: - self.mat_dtype = dtype(float64) + self.mat_dtype = N.dtype(N.float64) def get_raw_array(self): if self.header['is_complex']: @@ -137,12 +137,12 @@ class Mat4CharGetter(Mat4MatrixGetter): def get_raw_array(self): - arr = self.read_array().astype(uint8) + arr = self.read_array().astype(N.uint8) # ascii to unicode S = arr.tostring().decode('ascii') - return ndarray(shape=self.header['dims'], - dtype=dtype('U1'), - buffer = array(S)).copy() + return N.ndarray(shape=self.header['dims'], + dtype=N.dtype('U1'), + buffer = N.array(S)).copy() class Mat4SparseGetter(Mat4MatrixGetter): @@ -166,7 +166,7 @@ res = self.read_array() tmp = res[:-1,:] dims = res[-1,0:2] - ij = transpose(tmp[:,0:2]) - 1 # for 1-based indexing + ij = N.transpose(tmp[:,0:2]) - 1 # for 1-based indexing vals = tmp[:,2] if res.shape[1] == 4: vals = vals + res[:-1,3] * 1j @@ -196,15 +196,15 @@ def format_looks_right(self): # Mat4 files have a zero somewhere in first 4 bytes self.mat_stream.seek(0) - mopt_bytes = ndarray(shape=(4,), - dtype=uint8, + mopt_bytes = N.ndarray(shape=(4,), + dtype=N.uint8, buffer = self.mat_stream.read(4)) self.mat_stream.seek(0) return 0 in mopt_bytes def guess_byte_order(self): self.mat_stream.seek(0) - mopt = self.read_dtype(dtype('i4')) + mopt = self.read_dtype(N.dtype('i4')) self.mat_stream.seek(0) if mopt < 0 or mopt > 5000: return ByteOrder.swapped_code @@ -222,7 +222,7 @@ ''' if dims is None: dims = self.arr.shape - header = empty((), mdtypes_template['header']) + header = N.empty((), mdtypes_template['header']) M = not ByteOrder.little_endian O = 0 header['mopt'] = (M * 1000 + @@ -237,10 +237,10 @@ self.write_string(self.name + '\0') def arr_to_2d(self): - self.arr = atleast_2d(self.arr) + self.arr = N.atleast_2d(self.arr) dims = self.arr.shape if len(dims) > 2: - dims = [product(dims[:-1]), dims[-1]] + dims = [N.product(dims[:-1]), dims[-1]] self.arr = reshape(self.arr, dims) def write(self): @@ -280,12 +280,12 @@ T=mxCHAR_CLASS) if self.arr.dtype.kind == 'U': # Recode unicode to ascii - n_chars = product(dims) - st_arr = ndarray(shape=(), + n_chars = N.product(dims) + st_arr = N.ndarray(shape=(), dtype=self.arr_dtype_number(n_chars), buffer=self.arr) st = st_arr.item().encode('ascii') - self.arr = ndarray(shape=dims, dtype='S1', buffer=st) + self.arr = N.ndarray(shape=dims, dtype='S1', buffer=st) self.write_bytes(self.arr) @@ -296,9 +296,9 @@ See docstring for Mat4SparseGetter ''' imagf = self.arr.dtype.kind == 'c' - N = self.arr.nnz - ijd = zeros((N+1, 3+imagf), dtype='f8') - for i in range(N): + nnz = self.arr.nnz + ijd = N.zeros((nnz+1, 3+imagf), dtype='f8') + for i in range(nnz): ijd[i,0], ijd[i,1] = self.arr.rowcol(i) ijd[:-1,0:2] += 1 # 1 based indexing if imagf: @@ -322,13 +322,13 @@ if have_sparse: if scipy.sparse.issparse(arr): return Mat4SparseWriter(stream, arr, name) - arr = array(arr) + arr = N.array(arr) dtt = arr.dtype.type - if dtt is object_: + if dtt is N.object_: raise TypeError, 'Cannot save object arrays in Mat4' - elif dtt is void: + elif dtt is N.void: raise TypeError, 'Cannot save void type arrays' - elif dtt in (unicode_, string_): + elif dtt in (N.unicode_, N.string_): return Mat4CharWriter(stream, arr, name) else: return Mat4NumericWriter(stream, arr, name) Modified: trunk/Lib/io/mio5.py =================================================================== --- trunk/Lib/io/mio5.py 2007-02-08 13:48:57 UTC (rev 2692) +++ trunk/Lib/io/mio5.py 2007-02-08 17:07:46 UTC (rev 2693) @@ -29,9 +29,9 @@ import zlib from copy import copy as pycopy from cStringIO import StringIO -from numpy import * +import numpy as N -from miobase import * +from scipy.io.miobase import * try: # Python 2.3 support from sets import Set as set @@ -154,7 +154,7 @@ def read_element(self, copy=True): raw_tag = self.mat_stream.read(8) - tag = ndarray(shape=(), + tag = N.ndarray(shape=(), dtype=self.dtypes['tag_full'], buffer = raw_tag) mdtype = tag['mdtype'] @@ -165,7 +165,7 @@ mdtype = mdtype & 0xFFFF dt = self.dtypes[mdtype] el_count = byte_count / dt.itemsize - return ndarray(shape=(el_count,), + return N.ndarray(shape=(el_count,), dtype=dt, buffer=raw_tag[4:]) byte_count = tag['byte_count'] @@ -180,7 +180,7 @@ else: # numeric data dt = self.dtypes[mdtype] el_count = byte_count / dt.itemsize - el = ndarray(shape=(el_count,), + el = N.ndarray(shape=(el_count,), dtype=dt, buffer=self.mat_stream.read(byte_count)) if copy: @@ -285,7 +285,7 @@ self.mat_dtype = 'f8' def get_raw_array(self): - return array([[]]) + return N.array([[]]) class Mat5NumericMatrixGetter(Mat5MatrixGetter): @@ -293,7 +293,7 @@ def __init__(self, array_reader, header): super(Mat5NumericMatrixGetter, self).__init__(array_reader, header) if header['is_logical']: - self.mat_dtype = dtype('bool') + self.mat_dtype = N.dtype('bool') else: self.mat_dtype = self.class_dtypes[header['mclass']] @@ -305,7 +305,7 @@ res = res + (res_j * 1j) else: res = self.read_element() - return ndarray(shape=self.header['dims'], + return N.ndarray(shape=self.header['dims'], dtype=res.dtype, buffer=res, order='F') @@ -334,14 +334,14 @@ stored in column order, this gives the column corresponding to each rowind ''' - cols = empty((len(res)), dtype=rowind.dtype) - col_counts = diff(colind) + cols = N.empty((len(res)), dtype=rowind.dtype) + col_counts = N.diff(colind) start_row = 0 - for i in where(col_counts)[0]: + for i in N.where(col_counts)[0]: end_row = start_row + col_counts[i] cols[start_row:end_row] = i start_row = end_row - ij = vstack((rowind[:len(res)], cols)) + ij = N.vstack((rowind[:len(res)], cols)) if have_sparse: result = scipy.sparse.csc_matrix((res,ij), self.header['dims']) @@ -354,19 +354,19 @@ def get_raw_array(self): res = self.read_element() # Convert non-string types to unicode - if isinstance(res, ndarray): - if res.dtype.type == uint16: + if isinstance(res, N.ndarray): + if res.dtype.type == N.uint16: codec = miUINT16_codec if self.codecs['uint16_len'] == 1: - res = res.astype(uint8) - elif res.dtype.type in (uint8, int8): + res = res.astype(N.uint8) + elif res.dtype.type in (N.uint8, N.int8): codec = 'ascii' else: raise TypeError, 'Did not expect type %s' % res.dtype res = res.tostring().decode(codec) - return ndarray(shape=self.header['dims'], - dtype=dtype('U1'), - buffer=array(res), + return N.ndarray(shape=self.header['dims'], + dtype=N.dtype('U1'), + buffer=N.array(res), order='F').copy() @@ -374,12 +374,11 @@ def get_raw_array(self): # Account for fortran indexing of cells tupdims = tuple(self.header['dims'][::-1]) - length = product(tupdims) - result = empty(length, dtype=object) + length = N.product(tupdims) + result = N.empty(length, dtype=object) for i in range(length): result[i] = self.get_item() - result = transpose(reshape(result,tupdims)) - return result + return result.reshape(tupdims).T def get_item(self): return self.read_element() @@ -516,8 +515,8 @@ def format_looks_right(self): # Mat4 files have a zero somewhere in first 4 bytes self.mat_stream.seek(0) - mopt_bytes = ndarray(shape=(4,), - dtype=uint8, + mopt_bytes = N.ndarray(shape=(4,), + dtype=N.uint8, buffer = self.mat_stream.read(4)) self.mat_stream.seek(0) return 0 not in mopt_bytes @@ -525,7 +524,7 @@ class Mat5MatrixWriter(MatStreamWriter): - mat_tag = zeros((), mdtypes_template['tag_full']) + mat_tag = N.zeros((), mdtypes_template['tag_full']) mat_tag['mdtype'] = miMATRIX def __init__(self, file_stream, arr, name, is_global=False): @@ -555,14 +554,14 @@ self._mat_tag_pos = self.file_stream.tell() self.write_dtype(self.mat_tag) # write array flags (complex, global, logical, class, nzmax) - af = zeros((), mdtypes_template['array_flags']) + af = N.zeros((), mdtypes_template['array_flags']) af['data_type'] = miUINT32 af['byte_count'] = 8 flags = is_complex << 3 | is_global << 2 | is_logical << 1 af['flags_class'] = mclass | flags << 8 af['nzmax'] = nzmax self.write_dtype(af) - self.write_element(array(self.arr.shape, dtype='i4')) + self.write_element(N.array(self.arr.shape, dtype='i4')) self.write_element(self.name) def update_matrix_tag(self): @@ -598,12 +597,12 @@ T=mxCHAR_CLASS) if self.arr.dtype.kind == 'U': # Recode unicode to ascii - n_chars = product(dims) - st_arr = ndarray(shape=(), + n_chars = N.product(dims) + st_arr = N.ndarray(shape=(), dtype=self.arr_dtype_number(n_chars), buffer=self.arr) st = st_arr.item().encode('ascii') - self.arr = ndarray(shape=dims, dtype='S1', buffer=st) + self.arr = N.ndarray(shape=dims, dtype='S1', buffer=st) self.write_bytes(self.arr) @@ -615,7 +614,7 @@ ''' imagf = self.arr.dtype.kind == 'c' N = self.arr.nnz - ijd = zeros((N+1, 3+imagf), dtype='f8') + ijd = N.zeros((N+1, 3+imagf), dtype='f8') for i in range(N): ijd[i,0], ijd[i,1] = self.arr.rowcol(i) ijd[:-1,0:2] += 1 # 1 based indexing @@ -649,7 +648,7 @@ if have_sparse: if scipy.sparse.issparse(arr): return Mat5SparseWriter(self.stream, arr, name, is_global) - arr = array(arr) + arr = N.array(arr) if arr.dtype.hasobject: types, arr_type = classify_mobjects(arr) if arr_type == 'c': @@ -680,13 +679,13 @@ o - object array ''' N = objarr.size - types = empty((N,), dtype='S1') + types = N.empty((N,), dtype='S1') types[:] = 'i' type_set = set() flato = objarr.flat for i in range(N): obj = flato[i] - if isinstance(obj, ndarray): + if isinstance(obj, N.ndarray): types[i] = 'a' continue try: @@ -743,7 +742,7 @@ ).write() if self.do_compression: str = zlib.compress(stream.getvalue()) - tag = empty((), mdtypes_template['tag_full']) + tag = N.empty((), mdtypes_template['tag_full']) tag['mdtype'] = miCOMPRESSED tag['byte_count'] = len(str) self.file_stream.write(tag.tostring() + str) Modified: trunk/Lib/io/miobase.py =================================================================== --- trunk/Lib/io/miobase.py 2007-02-08 13:48:57 UTC (rev 2692) +++ trunk/Lib/io/miobase.py 2007-02-08 17:07:46 UTC (rev 2693) @@ -6,7 +6,7 @@ import sys -from numpy import * +import numpy as N try: import scipy.sparse @@ -71,10 +71,10 @@ a_dtype is assumed to be correct endianness ''' num_bytes = a_dtype.itemsize - arr = ndarray(shape=(), - dtype=a_dtype, - buffer=self.mat_stream.read(num_bytes), - order='F') + arr = N.ndarray(shape=(), + dtype=a_dtype, + buffer=self.mat_stream.read(num_bytes), + order='F') return arr def read_ztstring(self, num_bytes): @@ -184,7 +184,7 @@ def convert_dtypes(self, dtype_template): dtypes = dtype_template.copy() for k in dtypes: - dtypes[k] = dtype(dtypes[k]).newbyteorder( + dtypes[k] = N.dtype(dtypes[k]).newbyteorder( self.order_code) return dtypes @@ -227,10 +227,10 @@ if len(dims) >= 2: # return array of strings dtt = self.order_code + 'U' n_dims = dims[:-1] - str_arr = reshape(arr, - (small_product(n_dims), - dims[-1])) - arr = empty(n_dims, dtype=object) + str_arr = arr.reshape( + (small_product(n_dims), + dims[-1])) + arr = N.empty(n_dims, dtype=object) for i in range(0, n_dims[-1]): arr[...,i] = self.chars_to_str(str_arr[i]) else: # return string @@ -241,9 +241,9 @@ if getter.mat_dtype is not None: arr = arr.astype(getter.mat_dtype) if self.squeeze_me: - arr = squeeze(arr) + arr = N.squeeze(arr) if not arr.size: - arr = array([]) + arr = N.array([]) elif not arr.shape: # 0d coverted to scalar arr = arr.item() return arr @@ -251,8 +251,8 @@ def chars_to_str(self, str_arr): ''' Convert string array to string ''' - dt = dtype('U' + str(small_product(str_arr.shape))) - return ndarray(shape=(), + dt = N.dtype('U' + str(small_product(str_arr.shape))) + return N.ndarray(shape=(), dtype = dt, buffer = str_arr.copy()).item() @@ -354,7 +354,7 @@ def arr_dtype_number(self, num): ''' Return dtype for given number of items per element''' - return dtype(self.arr.dtype.str[:2] + str(num)) + return N.dtype(self.arr.dtype.str[:2] + str(num)) def arr_to_chars(self): ''' Convert string array to char array ''' @@ -362,7 +362,7 @@ if not dims: dims = [1] dims.append(int(self.arr.dtype.str[2:])) - self.arr = ndarray(shape=dims, + self.arr = N.ndarray(shape=dims, dtype=self.arr_dtype_number(1), buffer=self.arr) Modified: trunk/Lib/io/npfile.py =================================================================== --- trunk/Lib/io/npfile.py 2007-02-08 13:48:57 UTC (rev 2692) +++ trunk/Lib/io/npfile.py 2007-02-08 17:07:46 UTC (rev 2693) @@ -8,6 +8,8 @@ import numpy as N +__all__ = ['sys_endian_code', 'npfile'] + sys_endian_code = (sys.byteorder == 'little') and '<' or '>' class npfile(object): From scipy-svn at scipy.org Thu Feb 8 15:42:36 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 8 Feb 2007 14:42:36 -0600 (CST) Subject: [Scipy-svn] r2694 - in trunk/Lib: interpolate interpolate/fitpack sandbox/spline sandbox/spline/fitpack sandbox/spline/tests Message-ID: <20070208204236.45FDB39C033@new.scipy.org> Author: jtravs Date: 2007-02-08 14:42:26 -0600 (Thu, 08 Feb 2007) New Revision: 2694 Added: trunk/Lib/sandbox/spline/tests/visual_check_parametric.py Modified: trunk/Lib/interpolate/fitpack.py trunk/Lib/interpolate/fitpack/insert.f trunk/Lib/sandbox/spline/fitpack.py trunk/Lib/sandbox/spline/fitpack.pyf trunk/Lib/sandbox/spline/fitpack/insert.f Log: Add patch to fitpack/insert.f for Zach Pincus. Add insert function to sandbox.spline. Modified: trunk/Lib/interpolate/fitpack/insert.f =================================================================== --- trunk/Lib/interpolate/fitpack/insert.f 2007-02-08 17:07:46 UTC (rev 2693) +++ trunk/Lib/interpolate/fitpack/insert.f 2007-02-08 20:42:26 UTC (rev 2694) @@ -61,7 +61,7 @@ c celestijnenlaan 200a, b-3001 heverlee, belgium. c e-mail : Paul.Dierckx at cs.kuleuven.ac.be c -c latest update : march 1987 +c latest update : february 2007 (second interval search added) c c ..scalar arguments.. integer iopt,n,k,nn,nest,ier @@ -69,7 +69,7 @@ c ..array arguments.. real*8 t(nest),c(nest),tt(nest),cc(nest) c ..local scalars.. - integer kk,k1,l,nk,nk1 + integer kk,k1,l,nk c .. c before starting computations a data check is made. if the input data c are invalid control is immediately repassed to the calling program. @@ -79,11 +79,18 @@ nk = n-k if(x.lt.t(k1) .or. x.gt.t(nk)) go to 40 c search for knot interval t(l) <= x < t(l+1). - nk1 = nk-1 l = k1 - 10 if(x.lt.t(l+1) .or. l.eq.nk1) go to 20 + 10 if(x.lt.t(l+1)) go to 20 l = l+1 + if(l.eq.nk) go to 14 go to 10 +c if no interval found above, then reverse the search and +c look for knot interval t(l) < x <= t(l+1). + 14 l = nk-1 + 16 if(x.gt.t(l)) go to 20 + l = l-1 + if(l.eq.k) go to 40 + go to 16 20 if(t(l).ge.t(l+1)) go to 40 if(iopt.eq.0) go to 30 kk = 2*k Modified: trunk/Lib/interpolate/fitpack.py =================================================================== --- trunk/Lib/interpolate/fitpack.py 2007-02-08 17:07:46 UTC (rev 2693) +++ trunk/Lib/interpolate/fitpack.py 2007-02-08 20:42:26 UTC (rev 2694) @@ -818,7 +818,6 @@ if ier: raise TypeError,"An error occurred" return (tt, cc, k) - if __name__ == "__main__": import sys,string runtest=range(10) Modified: trunk/Lib/sandbox/spline/fitpack/insert.f =================================================================== --- trunk/Lib/sandbox/spline/fitpack/insert.f 2007-02-08 17:07:46 UTC (rev 2693) +++ trunk/Lib/sandbox/spline/fitpack/insert.f 2007-02-08 20:42:26 UTC (rev 2694) @@ -61,7 +61,7 @@ c celestijnenlaan 200a, b-3001 heverlee, belgium. c e-mail : Paul.Dierckx at cs.kuleuven.ac.be c -c latest update : march 1987 +c latest update : february 2007 (second interval search added) c c ..scalar arguments.. integer iopt,n,k,nn,nest,ier @@ -69,7 +69,7 @@ c ..array arguments.. real*8 t(nest),c(nest),tt(nest),cc(nest) c ..local scalars.. - integer kk,k1,l,nk,nk1 + integer kk,k1,l,nk c .. c before starting computations a data check is made. if the input data c are invalid control is immediately repassed to the calling program. @@ -79,11 +79,18 @@ nk = n-k if(x.lt.t(k1) .or. x.gt.t(nk)) go to 40 c search for knot interval t(l) <= x < t(l+1). - nk1 = nk-1 l = k1 - 10 if(x.lt.t(l+1) .or. l.eq.nk1) go to 20 + 10 if(x.lt.t(l+1)) go to 20 l = l+1 + if(l.eq.nk) go to 14 go to 10 +c if no interval found above, then reverse the search and +c look for knot interval t(l) < x <= t(l+1). + 14 l = nk-1 + 16 if(x.gt.t(l)) go to 20 + l = l-1 + if(l.eq.k) go to 40 + go to 16 20 if(t(l).ge.t(l+1)) go to 40 if(iopt.eq.0) go to 30 kk = 2*k Modified: trunk/Lib/sandbox/spline/fitpack.py =================================================================== --- trunk/Lib/sandbox/spline/fitpack.py 2007-02-08 17:07:46 UTC (rev 2693) +++ trunk/Lib/sandbox/spline/fitpack.py 2007-02-08 20:42:26 UTC (rev 2694) @@ -30,11 +30,11 @@ """ __all__ = ['splrep', 'splprep', 'splev', 'splint', 'sproot', 'spalde', - 'bisplrep', 'bisplev'] + 'bisplrep', 'bisplev', 'insert'] __version__ = "$Revision$"[10:-1] from numpy import atleast_1d, array, ones, zeros, sqrt, ravel, transpose, \ - dot, sin, cos, pi, arange, empty, int32, where + dot, sin, cos, pi, arange, empty, int32, where, resize myasarray = atleast_1d # f2py-generated interface to fitpack @@ -210,7 +210,11 @@ else: nest=m+k+1 nest=max(nest,2*k+3) if task==0: - u,ub,ue,n,t,c,fp,wrk,iwrk,ier=dfitpack.parcur_smth0(ipar,idim,u,x, + if per: + u,n,t,c,fp,wrk,iwrk,ier=dfitpack.clocur_smth0(ipar,idim,u,x,w,nest, + k=k,s=s) + else: + u,ub,ue,n,t,c,fp,wrk,iwrk,ier=dfitpack.parcur_smth0(ipar,idim,u,x, w,ub,ue,nest,k=k,s=s) if task==1: try: @@ -589,7 +593,63 @@ raise TypeError,"Invalid input data. t(k)<=x<=t(n-k+1) must hold." raise TypeError,"Unknown error" +def insert(x,tck,m=1,per=0): + """Insert knots into a B-spline. + Description: + + Given the knots and coefficients of a B-spline representation, create a + new B-spline with a knot inserted m times at point x. + This is a wrapper around the FORTRAN routine insert of FITPACK. + + Inputs: + + x (u) -- A 1-D point at which to insert a new knot(s). If tck was returned + from splprep, then the parameter values, u should be given. + tck -- A sequence of length 3 returned by splrep or splprep containg the + knots, coefficients, and degree of the spline. + m -- The number of times to insert the given knot (its multiplicity). + per -- If non-zero, input spline is considered periodic. + + Outputs: tck + + tck -- (t,c,k) a tuple containing the vector of knots, the B-spline + coefficients, and the degree of the new spline. + + Requirements: + t(k+1) <= x <= t(n-k), where k is the degree of the spline. + In case of a periodic spline (per != 0) there must be + either at least k interior knots t(j) satisfying t(k+1) 0 && idim < 11) :: idim + integer intent(hide),depend(u,k),check(m>k) :: m=len(u) + real*8 dimension(m), intent(in,out) :: u + integer intent(hide),depend(x,idim,m),check(mx>=idim*m) :: mx=len(x) + real*8 dimension(mx) :: x + real*8 dimension(m) :: w + integer check(1<=k && k<=5) :: k=3.0 + real*8 check(s>=0.0) :: s = 0.0 + integer intent(in) :: nest + integer intent(out) :: n + real*8 dimension(nest), intent(out) :: t + integer intent(hide), depend(nest,idim) :: nc=idim*nest + real*8 dimension(nc), intent(out) :: c + real*8 intent(out) :: fp + real*8 dimension(lwrk), intent(out) :: wrk + integer intent(hide),depend(m,k,nest,idim) :: lwrk=m*(k+1)+nest*(7+idim+5*k) + integer dimension(nest), intent(out) :: iwrk + integer intent(out) :: ier + end subroutine clocur_smth0 + subroutine parcur_smth0(iopt,ipar,idim,m,u,mx,x,w,ub,ue,k,s,nest,n,t,nc,& c,fp,wrk,lwrk,iwrk,ier) - !u,ub,ue,n,t,c,fp,wrk,iwrk,ier=parcur_smth0(ipar,idim,u,x,w, - ! ub,ue,nest,[k,s]) fortranname parcur integer intent(hide) :: iopt = 0 integer check(ipar == 1 || ipar == 0) :: ipar @@ -285,6 +309,21 @@ integer intent(out) :: ier end subroutine parcur_smth0 + subroutine insert(iopt,t,n,c,k,x,tt,nn,cc,nest,ier) + ! tt, nn, cc, ier = insert(per, t, c, k, x, nest) + integer intent(in):: iopt + real*8 dimension(nest),intent(in) :: t + integer intent(in) :: n + real*8 dimension(nest),depend(nest),intent(in) :: c + integer intent(in) :: k + real*8 intent(in) :: x + real*8 dimension(nest),depend(nest),intent(out) :: tt + integer intent(out) :: nn + real*8 dimension(nest),depend(nest),intent(out) :: cc + integer intent(hide),depend(t) :: nest=len(t) + integer intent(out) :: ier + end subroutine insert + subroutine fpcurf_smth0(iopt,x,y,w,m,xb,xe,k,s,nest,tol,maxit,k1,k2,n,t,& c,fp,fpint,wrk,nrdata,ier) ! x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier = \ Added: trunk/Lib/sandbox/spline/tests/visual_check_parametric.py =================================================================== --- trunk/Lib/sandbox/spline/tests/visual_check_parametric.py 2007-02-08 17:07:46 UTC (rev 2693) +++ trunk/Lib/sandbox/spline/tests/visual_check_parametric.py 2007-02-08 20:42:26 UTC (rev 2694) @@ -0,0 +1,51 @@ +from scipy import arange, cos, linspace, randn, pi, sin +from scipy.sandbox.spline import splprep, splev +import matplotlib +matplotlib.use('WXAgg') +import pylab + +# make ascending spiral in 3-space +t=linspace(0,1.75*2*pi,100) + +x = sin(t) +y = cos(t) +z = t + +# add noise +x+= 0.1*randn(*x.shape) +y+= 0.1*randn(*y.shape) +z+= 0.1*randn(*z.shape) + +# spline parameters +s=3.0 # smoothness parameter +k=2 # spline order +nest=-1 # estimate of number of knots needed (-1 = maximal) + +# find the knot points +tckp,u = splprep([x,y,z],s=s,k=k,nest=-1) + +# evaluate spline, including interpolated points +xnew,ynew,znew = splev(linspace(0,1,400),tckp) + +pylab.subplot(2,2,1) +data,=pylab.plot(x,y,'bo-',label='data') +fit,=pylab.plot(xnew,ynew,'r-',label='fit') +pylab.legend() +pylab.xlabel('x') +pylab.ylabel('y') + +pylab.subplot(2,2,2) +data,=pylab.plot(x,z,'bo-',label='data') +fit,=pylab.plot(xnew,znew,'r-',label='fit') +pylab.legend() +pylab.xlabel('x') +pylab.ylabel('z') + +pylab.subplot(2,2,3) +data,=pylab.plot(y,z,'bo-',label='data') +fit,=pylab.plot(ynew,znew,'r-',label='fit') +pylab.legend() +pylab.xlabel('y') +pylab.ylabel('z') + +pylab.show() \ No newline at end of file From scipy-svn at scipy.org Fri Feb 9 13:15:42 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 9 Feb 2007 12:15:42 -0600 (CST) Subject: [Scipy-svn] r2695 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070209181542.7E4B739C0EF@new.scipy.org> Author: mattknox_ca Date: 2007-02-09 12:15:38 -0600 (Fri, 09 Feb 2007) New Revision: 2695 Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py Log: Added a file remotely Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-08 20:42:26 UTC (rev 2694) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 18:15:38 UTC (rev 2695) @@ -0,0 +1,900 @@ +""" +Classes to plot TimeSeries w/ matplotlib. + +:author: Pierre GF Gerard-Marchant +:contact: pierregm_at_uga_edu +:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ +:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" +__version__ = '1.0' +__revision__ = "$Revision: 2676 $" +__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' + + +import matplotlib +from matplotlib import pylab, rcParams +from matplotlib.artist import setp +from matplotlib.axes import Subplot, PolarSubplot +from matplotlib.cbook import flatten +from matplotlib.collections import LineCollection +from matplotlib.contour import ContourSet +from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ + DateFormatter +from matplotlib.figure import Figure +from matplotlib.legend import Legend +from matplotlib.mlab import meshgrid +from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ + Locator, FixedLocator + +#from matplotlib.transforms import nonsingular + +import numpy as N +import maskedarray as MA + +import timeseries +from timeseries import date_array, Date, DateArray, TimeSeries +#from tdates import date_array, Date +#import tseries +#from tseries import TimeSeries + +import warnings + +#####--------------------------------------------------------------------------- +#---- --- Matplotlib extensions --- +#####--------------------------------------------------------------------------- + +def add_generic_subplot(figure_instance, *args, **kwargs): + """Generalizes the `add_subplot` figure method to generic subplots. +The specific Subplot object class to add is given through the keywords +`SubplotClass` or `class`. + +:Parameters: + `figure_instance` : Figure object + Figure to which the generic subplot should be attached. + `args` : Misc + Miscellaneous arguments to the subplot. + `kwargs` : Dictionary + Keywords. Same keywords as `Subplot`, with the addition of + - `SubplotClass` : Type of subplot + - `subclass` : Shortcut to `SubplotClass`. + - any keyword required by the `SubplotClass` subclass. + """ + + key = figure_instance._make_key(*args, **kwargs) + #TODO: Find why, sometimes, key is not hashable (even if tuple) + # else, there's a fix below + try: + key.__hash__() + except TypeError: + key = str(key) + # + if figure_instance._seen.has_key(key): + ax = figure_instance._seen[key] + figure_instance.sca(ax) + return ax + # + if not len(args): + return +# if hasattr(args[0], '__array__'): +# fixedargs = args[1:] +# else: +# fixedargs = args + # + SubplotClass = kwargs.pop("SubplotClass", Subplot) + SubplotClass = kwargs.pop("subclass", SubplotClass) + if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): + a = args[0] + assert(a.get_figure() is figure_instance) +# a.set_figure(figure_instance) + else: + ispolar = kwargs.pop('polar', False) + if ispolar: + a = PolarSubplot(figure_instance, *args, **kwargs) + else: + a = SubplotClass(figure_instance, *args, **kwargs) + + figure_instance.axes.append(a) + figure_instance._axstack.push(a) + figure_instance.sca(a) + figure_instance._seen[key] = a + return a + + +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + ''' + Ensure the endpoints of a range are not too close together. + + "too close" means the interval is smaller than 'tiny' times + the maximum absolute value. + + If they are too close, each will be moved by the 'expander'. + If 'increasing' is True and vmin > vmax, they will be swapped. + ''' + #TODO: Remove that when matplotlib incorporate it by default + swapped = False + if vmax < vmin: + vmin, vmax = vmax, vmin + swapped = True + if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: + if vmin == 0.0: + vmin = -expander + vmax = expander + else: + vmin -= expander*abs(vmin) + vmax += expander*abs(vmax) + if swapped and not increasing: + vmin, vmax = vmax, vmin + return vmin, vmax + +##### ------------------------------------------------------------------------- +#---- --- Locators --- +##### ------------------------------------------------------------------------- + +def _get_default_annual_spacing(nyears): + """Returns a default spacing between consecutive ticks for annual data.""" + if nyears < 20: + (min_spacing, maj_spacing) = (1, 2) + elif nyears < 50: + (min_spacing, maj_spacing) = (1, 5) + elif nyears < 100: + (min_spacing, maj_spacing) = (5, 10) + elif nyears < 200: + (min_spacing, maj_spacing) = (5, 20) + elif nyears < 400: + (min_spacing, maj_spacing) = (5, 25) + elif nyears < 1000: + (min_spacing, maj_spacing) = (10, 50) + else: + (min_spacing, maj_spacing) = (20, 100) + return (min_spacing, maj_spacing) + +def _get_default_quarterly_spacing(nquarters): + """Returns a default spacing between consecutive ticks for quarterly data.""" + if nquarters <= 3*4: + (min_spacing, maj_spacing) = (1, 4) + elif nquarters <= 11*4: + (min_spacing, maj_spacing) = (1, 4) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) + min_spacing = min_anndef * 4 + maj_spacing = maj_anndef * 4 + return (min_spacing, maj_spacing) + +def _get_default_monthly_spacing(nmonths): + """Returns a default spacing between consecutive ticks for monthly data.""" + if nmonths <= 10: + (min_spacing, maj_spacing) = (1, 3) + elif nmonths <= 2*12: + (min_spacing, maj_spacing) = (1, 6) + elif nmonths <= 3*12: + (min_spacing, maj_spacing) = (1, 12) + elif nmonths <= 11*12: + (min_spacing, maj_spacing) = (3, 12) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) + min_spacing = min_anndef * 12 + maj_spacing = maj_anndef * 12 + return (min_spacing, maj_spacing) + +#............................................................................... +class TimeSeries_DateLocator(Locator): + "Locates the ticks along an axis controlled by a DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + self.freqstr = freq + self.base = base + (self.quarter, self.month, self.day) = (quarter, month, day) + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + + def _initialize_dates(self, start_val, end_val): + "Returns a DateArray for the current frequency." + freq = self.freqstr + dates = date_array(start_date=Date(freq, value=int(start_val)), + end_date=Date(freq, value=int(end_val)), + freq=freq) + return dates + + def _get_default_spacing(self, span): + "Returns the default ticks spacing." + raise NotImplementedError('Derived must override') + + def __call__(self): + 'Return the locations of the ticks.' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + base = self._get_default_spacing(vmax-vmin+1) + else: + base = self.base + d = vmin // base + vmin = (d+1) * base + self.offset + locs = range(vmin, vmax+1, base) + print vmin, vmax+1 + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + if self.isdynamic: + base = self._get_default_spacing(dmax-dmin+1) + else: + base = self.base + (d, m) = divmod(dmin, base) + if m < base/2: + vmin = d * base + else: + vmin = (d+1) * base + (d, m) = divmod(dmax, base) + vmax = (d+1) * base + if vmin == vmax: + vmin -= 1 + vmax += 1 + return nonsingular(vmin, vmax) + +#............................................................................... +class TimeSeries_AnnualLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by an annual DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'A', minor_locator, dynamic_mode, + base, quarter, month, day) + + def _get_default_spacing(self, span): + "Returns the default tick spacing for annual data." + (minor, major) = _get_default_annual_spacing(span) + if self.isminor: + return minor + return major +#............................................................................... +class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a quarterly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'Q', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset=1 + + def _get_default_spacing(self, span): + "Returns the default tick spacing for quarterly data." + (minor, major) = _get_default_quarterly_spacing(span) + if self.isminor: + return minor + return major +#............................................................................... +class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a monthly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'M', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset = 1 + + def _get_default_spacing(self, span): + "Returns the default tick spacing for monthly data." + (minor, major) = _get_default_monthly_spacing(span) + if self.isminor: + return minor + return major + +#............................................................................... +class TimeSeries_DailyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a daily DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, + base, quarter, month, day) + if self.freqstr == 'B': + self.daysinyear = 261 + else: + self.daysinyear = 365 + self._cacheddates = None + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for daily data." + daysperyear = self.daysinyear + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + + if self.isminor: + self.fmt_strs, self.label_flags = [], [] + + if span <= daysperyear//12: + + if self.isminor: + minor_ticks = default + + self.fmt_strs.append('%d') + self.label_flags.append(minor_ticks) + + self.fmt_strs.append('%b') + self.label_flags.append(default[(dates.month != (dates-1).month)]) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + else: + major_ticks = default[(dates.month != (dates-1).month)] + + elif span <= daysperyear//4: + + major_ticks = default[(dates.month != (dates-1).month)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('%d') + self.label_flags.append(default[(dates.day_of_week == 1)]) + + self.fmt_strs.append('%b') + self.label_flags.append(major_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + + elif span <= 1.5 * daysperyear: + + _month_starts = (dates.month != (dates-1).month) + + major_ticks = default[_month_starts] + + if self.isminor: + + _temp_idx = (dates.day_of_week == 1) | _month_starts + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%b') + self.label_flags.append(major_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + + + elif span <= 3 * daysperyear: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + _temp_idx = (dates.month != (dates-1).month) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%b') + self.label_flags.append(default[(dates.quarter != (dates-1).quarter)]) + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + elif span <= 11 * daysperyear: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + _temp_idx = (dates.quarter != (dates-1).quarter) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) + annual = (dates.year != (dates-1).year) + + major_ticks = default[annual & (dates.years % maj_anndef == 0)] + + if self.isminor: + _temp_idx = annual & (dates.years % min_anndef == 0) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + if self.isminor: + + if default.size > 0: + """ensure that at least one value for every + level of label is output""" + for x, lf in enumerate(self.label_flags): + if lf.size == 0: + self.label_flags[x] = default[0:1] + + return minor_ticks + return major_ticks + + def __call__(self): + 'Return the locations of the ticks' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + locs = self._get_default_locs(vmin, vmax) + else: + base = self.base + (d, m) = divmod(vmin, base) + vmin = (d+1) * base + locs = range(vmin, vmax+1, base) + + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + locs = self._get_default_locs(dmin, dmax) + (vmin, vmax) = locs[[0, -1]] + if vmin == vmax: + vmin -= 1 + vmax += 1 + + return nonsingular(vmin, vmax) + +#............................................................................... +class TimeSeries_YearLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) year. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive annual ticks. + - `quarter` : Integer *[1]* + Tells on which quarter the ticks should be. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + if freq == 'Q': + locs = (dates.quarters == self.quarter) + elif freq == 'M': + locs = (dates.months == self.month) + elif freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return dates.tovalue()[locs] +#............................................... +class TimeSeries_QuarterLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) quarter. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'Q': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq == 'M': + locs = (dates.months % 4 == self.month) + elif freq in 'BDU': + locs = (dates.months % 4 == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] +#............................................................................... +class TimeSeries_MonthLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) month. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'AQ': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'M': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] + +#####--------------------------------------------------------------------------- +#---- --- Formatter --- +#####--------------------------------------------------------------------------- +class TimeSeries_DateFormatter(Formatter): + """Formats the ticks along a DateArray axis.""" + + def __init__(self, freq, fmt=None, locator=None): + if fmt is None: + fmt = Date.default_fmtstr[freq] + self.fmt = fmt + self.freqstr = freq + self.locator = locator + + def __call__(self, x, pos=0): + + if self.locator is not None: + lc = self.locator + if self.freqstr in 'BDU': + minor_val, major_val, super_val = None, None, None + + if lc.isminor: + nls = '\n' + retval = '' + for flags, fmt in zip(lc.label_flags, lc.fmt_strs): + if N.where(flags == x)[0].size > 0: + retval += Date(self.freqstr, value=int(x)).strfmt(fmt) + nls + else: + retval += nls + retval = retval.rstrip(nls) + else: + retval = '' + else: + retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) + else: + retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) + + return retval + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries plots --- +#####-------------------------------------------------------------------------- +class TimeSeriesPlot(Subplot, object): + """Defines a time series based subclass of Subplot.""" + def __init__(self, fig=None, *args, **kwargs): + """ +Accepts the same keywords as a standard subplot, plus a specific `series` keyword. + +:Parameters: + `fig` : Figure + Base figure. + +:Keywords: + `series` : TimeSeries + Data to plot + + """ + # Retrieve the series ................... + _series = kwargs.pop('series', None) + Subplot.__init__(self, fig, *args, **kwargs) +# # Force fig to be defined ..... +# if fig is None: +# fig = TSFigure(_series) + # Process options ....................... + if _series is not None: + assert hasattr(_series, "dates") + self._series = _series.ravel() + self.xdata = _series.dates + self.freqstr = _series.dates.freqstr + self.xaxis.set_major_locator + + else: + self._series = None + self.xdata = None + self.freqstr = None + self._austoscale = False + # Get the data to plot + self.legendsymbols = [] + self.legendlabels = [] + #............................................ + def set_ydata(self, series=None): + """Sets the base time series.""" + if self._series is not None: + print "WARNING ! Base series is being changed.""" + self._series = series.ravel() + if isinstance(series, TimeSeries): + self.xdata = self.series.dates + #.... + def get_ydata(self): + """Gets the base time series.""" + return self._series + ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') + #............................................ + def _check_plot_params(self, *args): + """Defines the plot coordinates (and basic plotting arguments).""" + remaining = list(args) + # No args ? Use defaults, if any + if len(args) == 0: + if self.xdata is None: + raise ValueError, "No date information available!" + return (self.xdata, self.ydata) + output = [] + while len(remaining) > 0: + a = remaining.pop(0) + # The argument is a format: use default dates/ + if isinstance(a, str): + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, self.ydata, a]) + # The argument is a TimeSeries: use its dates for x + elif isinstance(a, TimeSeries): + (x, y) = (a._dates, a._series) + if len(remaining) > 0 and isinstance(remaining[0], str): + b = remaining.pop(0) + output.extend([x, y, b]) + else: + output.extend([x, y]) + # The argument is a DateArray............ + elif isinstance(a, (Date, DateArray)): + # Force to current freq + if self.freqstr is not None: + if a.freqstr != self.freqstr: + a = a.asfreq(self.freqstr) + # There's an argument after + if len(remaining) > 0: + #...and it's a format string + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata, b]) + #... and it's another date: use the default + elif isinstance(remaining[0], DateArray): + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata]) + #... and it must be some data + else: + b = remaining.pop(0) + if len(remaining) > 0: + if isinstance(remaining[0], str): + c = remaining.pop(0) + output.extend([a, b, c]) + else: + output.extend([a, b]) + # continue + else: + if self.ydata is None: + raise ValueError, "No data information available!" + #else: + # break + # Otherwise.............................. + elif len(remaining) > 0: + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a, b]) + #continue + elif self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a]) + #continue + # Reinitialize the plot if needed ........... + if self.xdata is None: + self.xdata = output[0] + self.freqstr = self.xdata.freqstr + # Force the xdata to the current frequency + elif output[0].freqstr != self.freqstr: + output = list(output) + output[0] = output[0].asfreq(self.freqstr) + return output + #............................................ + def tsplot(self, *parms, **kwargs): + """Plots the data parsed in argument. +This command accepts the same keywords as `matplotlib.plot`.""" + #print "Parameters: %s - %i" % (parms, len(parms)) + parms = self._check_plot_params(*parms) + self.legendlabels.append(kwargs.get('label', None)) + Subplot.plot(self, *parms, **kwargs) + pylab.draw_if_interactive() +# #............................................ +# def ybaseline(self,ybase,**kwargs): +# """Plots an horizontal baseline on each subplot.""" +# self.axhline(ybase,**kwargs) + #............................................ + def format_dateaxis(self, maj_spacing=None, min_spacing=None, + strformat="%Y", rotate=False): + """Pretty-formats the date axis (x-axis). + +:Parameters: + `major` : Integer *[5]* + Major tick locator, in years (major tick every `major` years). + `minor` : Integer *[12]* + Minor tick locator, in months (minor ticks every `minor` months). + `strformat` : String *['%Y']* + String format for major ticks ("%Y"). + """ + # Get the locator class ................. + if self.freqstr in 'BDU': + locator = TimeSeries_DailyLocator + major_locator = locator(self.freqstr, + minor_locator=False, + dynamic_mode=True) + minor_locator = locator(self.freqstr, + minor_locator=True, + dynamic_mode=True) + self.xaxis.set_major_locator(locator(self.freqstr, + minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_locator(locator(self.freqstr, + minor_locator=True, + dynamic_mode=True)) + else: + if self.freqstr == 'A': + locator = TimeSeries_AnnualLocator + elif self.freqstr == 'Q': + locator = TimeSeries_QuarterlyLocator + elif self.freqstr == 'M': + locator = TimeSeries_MonthlyLocator + major_locator = locator(minor_locator=False, + dynamic_mode=True) + minor_locator = locator(minor_locator=True, + dynamic_mode=True) + + self.xaxis.set_major_locator(major_locator) + self.xaxis.set_minor_locator(minor_locator) + #........................................ + self.xaxis.set_major_formatter( + TimeSeries_DateFormatter(self.freqstr, locator=major_locator)) + self.xaxis.set_minor_formatter( + TimeSeries_DateFormatter(self.freqstr, locator=minor_locator)) + + if rcParams['backend'] == 'PS': + rotate = False + warnings.warn("dateplot: PS backend detected, rotate disabled") + if self.is_last_row(): + if rotate: + setp(self.get_xticklabels(), rotation=45) +# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) +# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) +# else: +# self.set_xticklabels([]) +# self.set_xlabel('') +# #............................................ +# def plot_shifts(self,shifts,**kwargs): +# """Plots regime shifts. +#:param shifts: Shifts/trends to plot. +#:type shifts: `RegimeShift` +# """ +# self.tsplot(self.xdata,shifts.regimes,**kwargs) +# for x in shifts.xshifts[0]: +# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) + #............................................ +TSPlot = TimeSeriesPlot + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries Figures --- +#####-------------------------------------------------------------------------- +class TimeSeriesFigure(Figure): + """Time Series Figure: all subplots share the same time series. + """ + def __init__(self, series=None, **kwargs): + self._series = series + Figure.__init__(self, **kwargs) + fspnum = kwargs.pop('fspnum', None) + if fspnum is not None: + self.add_tsplot(fspnum, series=series) + #......... + def add_tsplot(self, *args, **kwargs): + """Adds a `TimeSeriesPlot` subplot to the figure.""" + kwargs.update(SubplotClass=TimeSeriesPlot, + series=self._series) + return add_generic_subplot(self, *args, **kwargs) + add_plot = add_tsplot +TSFigure = TimeSeriesFigure +#Figure.add_tsplot = +#................................................ +def tsfigure(series, **figargs): + """Creates a new `TimeSeriesFigure` object. + +:Parameters: + `series` : TimeSeries object + Input data. + `figargs` : Dictionary + Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. + """ + figargs.update(FigureClass=TSFigure) + figargs.update(series=series) +# print "figargs:",figargs +# num = figargs.pop('num',None) + fig = pylab.figure(**figargs) + return fig + +def add_tsplot(axes, *args, **kwargs): + kwargs.update(SubplotClass=TimeSeriesPlot) + if 'series' not in kwargs.keys(): + kwargs['series'] = None + return add_generic_subplot(axes, *args, **kwargs) +Figure.add_tsplot = add_tsplot + + +def tsplot(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = pylab.ishold() + h = kwargs.pop('hold', None) + if h is not None: + pylab.hold(h) + try: + ret = pylab.gca().add_tsplot(*args, **kwargs) + pylab.draw_if_interactive() + except: + pylab.hold(b) + raise + + pylab.hold(b) + return ret + +################################################################################ +if __name__ == '__main__': + + da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), + length=51) + ser = timeseries.time_series(MA.arange(len(da)), dates=da) + ser[4] = MA.masked + ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) + + pylab.figure() + pylab.gcf().add_tsplot(111) + pylab.gca().tsplot(ser, 'ko-') + pylab.gca().format_dateaxis() + pylab.gca().tsplot(ser_2, 'rs') + pylab.show() + \ No newline at end of file From scipy-svn at scipy.org Fri Feb 9 14:52:03 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 9 Feb 2007 13:52:03 -0600 (CST) Subject: [Scipy-svn] r2696 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070209195203.5FC3539C106@new.scipy.org> Author: mattknox_ca Date: 2007-02-09 13:52:01 -0600 (Fri, 09 Feb 2007) New Revision: 2696 Removed: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py Log: Removed file/folder Deleted: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 18:15:38 UTC (rev 2695) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 19:52:01 UTC (rev 2696) @@ -1,900 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu -:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ -:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ -""" -__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" -__version__ = '1.0' -__revision__ = "$Revision: 2676 $" -__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator - -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass", SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: - (min_spacing, maj_spacing) = (10, 50) - else: - (min_spacing, maj_spacing) = (20, 100) - return (min_spacing, maj_spacing) - -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1, 4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1, 4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) - -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1, 3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1, 6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1, 12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3, 12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freqstr = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - base = self._get_default_spacing(vmax-vmin+1) - else: - base = self.base - d = vmin // base - vmin = (d+1) * base + self.offset - locs = range(vmin, vmax+1, base) - print vmin, vmax+1 - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - if self.isdynamic: - base = self._get_default_spacing(dmax-dmin+1) - else: - base = self.base - (d, m) = divmod(dmin, base) - if m < base/2: - vmin = d * base - else: - vmin = (d+1) * base - (d, m) = divmod(dmax, base) - vmax = (d+1) * base - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_spacing(self, span): - "Returns the default tick spacing for annual data." - (minor, major) = _get_default_annual_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for quarterly data." - (minor, major) = _get_default_quarterly_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) - if self.isminor: - return minor - return major - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - - if self.isminor: - self.fmt_strs, self.label_flags = [], [] - - if span <= daysperyear//12: - - if self.isminor: - minor_ticks = default - - self.fmt_strs.append('%d') - self.label_flags.append(minor_ticks) - - self.fmt_strs.append('%b') - self.label_flags.append(default[(dates.month != (dates-1).month)]) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - else: - major_ticks = default[(dates.month != (dates-1).month)] - - elif span <= daysperyear//4: - - major_ticks = default[(dates.month != (dates-1).month)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('%d') - self.label_flags.append(default[(dates.day_of_week == 1)]) - - self.fmt_strs.append('%b') - self.label_flags.append(major_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - - elif span <= 1.5 * daysperyear: - - _month_starts = (dates.month != (dates-1).month) - - major_ticks = default[_month_starts] - - if self.isminor: - - _temp_idx = (dates.day_of_week == 1) | _month_starts - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%b') - self.label_flags.append(major_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - - - elif span <= 3 * daysperyear: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - _temp_idx = (dates.month != (dates-1).month) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%b') - self.label_flags.append(default[(dates.quarter != (dates-1).quarter)]) - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - elif span <= 11 * daysperyear: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - _temp_idx = (dates.quarter != (dates-1).quarter) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.year != (dates-1).year) - - major_ticks = default[annual & (dates.years % maj_anndef == 0)] - - if self.isminor: - _temp_idx = annual & (dates.years % min_anndef == 0) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - if self.isminor: - - if default.size > 0: - """ensure that at least one value for every - level of label is output""" - for x, lf in enumerate(self.label_flags): - if lf.size == 0: - self.label_flags[x] = default[0:1] - - return minor_ticks - return major_ticks - - def __call__(self): - 'Return the locations of the ticks' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d, m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0, -1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, fmt=None, locator=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt - self.freqstr = freq - self.locator = locator - - def __call__(self, x, pos=0): - - if self.locator is not None: - lc = self.locator - if self.freqstr in 'BDU': - minor_val, major_val, super_val = None, None, None - - if lc.isminor: - nls = '\n' - retval = '' - for flags, fmt in zip(lc.label_flags, lc.fmt_strs): - if N.where(flags == x)[0].size > 0: - retval += Date(self.freqstr, value=int(x)).strfmt(fmt) + nls - else: - retval += nls - retval = retval.rstrip(nls) - else: - retval = '' - else: - retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) - else: - retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) - - return retval - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series', None) - Subplot.__init__(self, fig, *args, **kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freqstr = _series.dates.freqstr - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freqstr = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self, *args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a, str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x, y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x, y, b]) - else: - output.extend([x, y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a, b, c]) - else: - output.extend([a, b]) - # continue - else: - if self.ydata is None: - raise ValueError, "No data information available!" - #else: - # break - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - #continue - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - #continue - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freqstr = self.xdata.freqstr - # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: - output = list(output) - output[0] = output[0].asfreq(self.freqstr) - return output - #............................................ - def tsplot(self, *parms, **kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label', None)) - Subplot.plot(self, *parms, **kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) - #............................................ - def format_dateaxis(self, maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=False): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - major_locator = locator(self.freqstr, - minor_locator=False, - dynamic_mode=True) - minor_locator = locator(self.freqstr, - minor_locator=True, - dynamic_mode=True) - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - major_locator = locator(minor_locator=False, - dynamic_mode=True) - minor_locator = locator(minor_locator=True, - dynamic_mode=True) - - self.xaxis.set_major_locator(major_locator) - self.xaxis.set_minor_locator(minor_locator) - #........................................ - self.xaxis.set_major_formatter( - TimeSeries_DateFormatter(self.freqstr, locator=major_locator)) - self.xaxis.set_minor_formatter( - TimeSeries_DateFormatter(self.freqstr, locator=minor_locator)) - - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(), rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self, **kwargs) - fspnum = kwargs.pop('fspnum', None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#Figure.add_tsplot = -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), - length=51) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file From scipy-svn at scipy.org Fri Feb 9 14:52:40 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 9 Feb 2007 13:52:40 -0600 (CST) Subject: [Scipy-svn] r2697 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070209195240.7D9AD39C106@new.scipy.org> Author: mattknox_ca Date: 2007-02-09 13:52:34 -0600 (Fri, 09 Feb 2007) New Revision: 2697 Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py Log: Added a file remotely Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 19:52:01 UTC (rev 2696) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 19:52:34 UTC (rev 2697) @@ -0,0 +1,1013 @@ +""" +Classes to plot TimeSeries w/ matplotlib. + +:author: Pierre GF Gerard-Marchant +:contact: pierregm_at_uga_edu +:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ +:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" +__version__ = '1.0' +__revision__ = "$Revision: 2676 $" +__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' + + +import matplotlib +from matplotlib import pylab, rcParams +from matplotlib.artist import setp +from matplotlib.axes import Subplot, PolarSubplot +from matplotlib.cbook import flatten +from matplotlib.collections import LineCollection +from matplotlib.contour import ContourSet +from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ + DateFormatter +from matplotlib.figure import Figure +from matplotlib.legend import Legend +from matplotlib.mlab import meshgrid +from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ + Locator, FixedLocator + +#from matplotlib.transforms import nonsingular + +import numpy as N +import maskedarray as MA + +import timeseries +from timeseries import date_array, Date, DateArray, TimeSeries +#from tdates import date_array, Date +#import tseries +#from tseries import TimeSeries + +import warnings + +#####--------------------------------------------------------------------------- +#---- --- Matplotlib extensions --- +#####--------------------------------------------------------------------------- + +def add_generic_subplot(figure_instance, *args, **kwargs): + """Generalizes the `add_subplot` figure method to generic subplots. +The specific Subplot object class to add is given through the keywords +`SubplotClass` or `class`. + +:Parameters: + `figure_instance` : Figure object + Figure to which the generic subplot should be attached. + `args` : Misc + Miscellaneous arguments to the subplot. + `kwargs` : Dictionary + Keywords. Same keywords as `Subplot`, with the addition of + - `SubplotClass` : Type of subplot + - `subclass` : Shortcut to `SubplotClass`. + - any keyword required by the `SubplotClass` subclass. + """ + + key = figure_instance._make_key(*args, **kwargs) + #TODO: Find why, sometimes, key is not hashable (even if tuple) + # else, there's a fix below + try: + key.__hash__() + except TypeError: + key = str(key) + # + if figure_instance._seen.has_key(key): + ax = figure_instance._seen[key] + figure_instance.sca(ax) + return ax + # + if not len(args): + return +# if hasattr(args[0], '__array__'): +# fixedargs = args[1:] +# else: +# fixedargs = args + # + SubplotClass = kwargs.pop("SubplotClass", Subplot) + SubplotClass = kwargs.pop("subclass", SubplotClass) + if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): + a = args[0] + assert(a.get_figure() is figure_instance) +# a.set_figure(figure_instance) + else: + ispolar = kwargs.pop('polar', False) + if ispolar: + a = PolarSubplot(figure_instance, *args, **kwargs) + else: + a = SubplotClass(figure_instance, *args, **kwargs) + + figure_instance.axes.append(a) + figure_instance._axstack.push(a) + figure_instance.sca(a) + figure_instance._seen[key] = a + return a + + +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + ''' + Ensure the endpoints of a range are not too close together. + + "too close" means the interval is smaller than 'tiny' times + the maximum absolute value. + + If they are too close, each will be moved by the 'expander'. + If 'increasing' is True and vmin > vmax, they will be swapped. + ''' + #TODO: Remove that when matplotlib incorporate it by default + swapped = False + if vmax < vmin: + vmin, vmax = vmax, vmin + swapped = True + if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: + if vmin == 0.0: + vmin = -expander + vmax = expander + else: + vmin -= expander*abs(vmin) + vmax += expander*abs(vmax) + if swapped and not increasing: + vmin, vmax = vmax, vmin + return vmin, vmax + +##### ------------------------------------------------------------------------- +#---- --- Locators --- +##### ------------------------------------------------------------------------- + +def _get_default_annual_spacing(nyears): + """Returns a default spacing between consecutive ticks for annual data.""" + + if nyears < 11: + (min_spacing, maj_spacing) = (1, 1) + elif nyears < 20: + (min_spacing, maj_spacing) = (1, 2) + elif nyears < 50: + (min_spacing, maj_spacing) = (1, 5) + elif nyears < 100: + (min_spacing, maj_spacing) = (5, 10) + elif nyears < 200: + (min_spacing, maj_spacing) = (5, 20) + elif nyears < 400: + (min_spacing, maj_spacing) = (5, 25) + elif nyears < 1000: + (min_spacing, maj_spacing) = (10, 50) + else: + (min_spacing, maj_spacing) = (20, 100) + return (min_spacing, maj_spacing) + +def _get_default_quarterly_spacing(nquarters): + """Returns a default spacing between consecutive ticks for quarterly data.""" + if nquarters <= 3*4: + (min_spacing, maj_spacing) = (1, 4) + elif nquarters <= 11*4: + (min_spacing, maj_spacing) = (1, 4) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) + min_spacing = min_anndef * 4 + maj_spacing = maj_anndef * 4 + return (min_spacing, maj_spacing) + +def _get_default_monthly_spacing(nmonths): + """Returns a default spacing between consecutive ticks for monthly data.""" + if nmonths <= 10: + (min_spacing, maj_spacing) = (1, 3) + elif nmonths <= 2*12: + (min_spacing, maj_spacing) = (1, 6) + elif nmonths <= 3*12: + (min_spacing, maj_spacing) = (1, 12) + elif nmonths <= 11*12: + (min_spacing, maj_spacing) = (3, 12) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) + min_spacing = min_anndef * 12 + maj_spacing = maj_anndef * 12 + return (min_spacing, maj_spacing) + +#............................................................................... +class TimeSeries_DateLocator(Locator): + "Locates the ticks along an axis controlled by a DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + self.freqstr = freq + self.base = base + (self.quarter, self.month, self.day) = (quarter, month, day) + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + + def _initialize_dates(self, start_val, end_val): + "Returns a DateArray for the current frequency." + freq = self.freqstr + dates = date_array(start_date=Date(freq, value=int(start_val)), + end_date=Date(freq, value=int(end_val)), + freq=freq) + return dates + + def _get_default_spacing(self, span): + "Returns the default ticks spacing." + raise NotImplementedError('Derived must override') + + def _get_default_locs(self, span): + "Returns the default ticks spacing." + raise NotImplementedError('Derived must override') + + def __call__(self): + 'Return the locations of the ticks' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + locs = self._get_default_locs(vmin, vmax) + else: + base = self.base + (d, m) = divmod(vmin, base) + vmin = (d+1) * base + locs = range(vmin, vmax+1, base) + + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + locs = self._get_default_locs(dmin, dmax) + (vmin, vmax) = locs[[0, -1]] + if vmin == vmax: + vmin -= 1 + vmax += 1 + + return nonsingular(vmin, vmax) + + +#............................................................................... +class TimeSeries_AnnualLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by an annual DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'A', minor_locator, dynamic_mode, + base, quarter, month, day) + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for daily data." + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span) + + major_ticks = default[(dates.years % maj_anndef == 0)] + + if self.isminor: + minor_ticks = default[(dates.years % min_anndef == 0)] + + self.fmt_strs = ['%Y'] + self.label_flags = [major_ticks] + + return minor_ticks + + return major_ticks + + +#............................................................................... +class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a quarterly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'Q', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset=1 + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for daily data." + + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + + if self.isminor: + self.fmt_strs, self.label_flags = [], [] + + if span <= 3 * 4: + + major_ticks = default[(dates.year != (dates - 1).year)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('Q%q') + self.label_flags.append(default) + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + elif span <= 11 * 4: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + else: + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/4) + annual = (dates.year != (dates-1).year) + + major_ticks = default[annual & (dates.years % maj_anndef == 0)] + + if self.isminor: + _temp_idx = annual & (dates.years % min_anndef == 0) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + if self.isminor: + if default.size > 0: + """ensure that at least one value for every + level of label is output""" + for x, lf in enumerate(self.label_flags): + if lf.size == 0: + self.label_flags[x] = default[0:1] + + return minor_ticks + return major_ticks + +#............................................................................... +class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a monthly DateArray." + + def __init__(self, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, 'M', minor_locator, dynamic_mode, + base, quarter, month, day) + self.offset = 1 + + def _get_default_spacing(self, span): + "Returns the default tick spacing for monthly data." + (minor, major) = _get_default_monthly_spacing(span) + if self.isminor: + return minor + return major + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for monthly data." + + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + + if self.isminor: + self.fmt_strs, self.label_flags = [], [] + + if span <= 1.5 * 12: + + major_ticks = default[(dates.year != (dates - 1).year)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('%b') + self.label_flags.append(minor_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + elif span <= 3 * 12: + + major_ticks = default[(dates.year != (dates - 1).year)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('%b') + self.label_flags.append(default[(dates.month - 1)% 2 == 0]) + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + elif span <= 11 * 12: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + minor_ticks = default[(dates.quarter != (dates - 1).quarter)] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + else: + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/12) + annual = (dates.year != (dates-1).year) + + major_ticks = default[annual & (dates.years % maj_anndef == 0)] + + if self.isminor: + _temp_idx = annual & (dates.years % min_anndef == 0) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + if self.isminor: + if default.size > 0: + """ensure that at least one value for every + level of label is output""" + for x, lf in enumerate(self.label_flags): + if lf.size == 0: + self.label_flags[x] = default[0:1] + + return minor_ticks + return major_ticks + + +#............................................................................... +class TimeSeries_DailyLocator(TimeSeries_DateLocator): + "Locates the ticks along an axis controlled by a daily DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, + base, quarter, month, day) + if self.freqstr == 'B': + self.daysinyear = 261 + else: + self.daysinyear = 365 + self._cacheddates = None + + def _get_default_locs(self, vmin, vmax): + "Returns the default tick locations for daily data." + daysperyear = self.daysinyear + span = vmax - vmin + 1 + dates = self._initialize_dates(vmin, vmax) + default = N.arange(vmin, vmax+1) + + if self.isminor: + self.fmt_strs, self.label_flags = [], [] + + if span <= daysperyear//12: + + major_ticks = default[(dates.month != (dates-1).month)] + + if self.isminor: + minor_ticks = default + + self.fmt_strs.append('%d') + self.label_flags.append(minor_ticks) + + self.fmt_strs.append('%b') + self.label_flags.append(major_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + + elif span <= daysperyear//4: + + major_ticks = default[(dates.month != (dates-1).month)] + + if self.isminor: + + minor_ticks = default + + self.fmt_strs.append('%d') + self.label_flags.append(default[(dates.day_of_week == 1)]) + + self.fmt_strs.append('%b') + self.label_flags.append(major_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + + elif span <= 1.5 * daysperyear: + + _month_starts = (dates.month != (dates-1).month) + + major_ticks = default[_month_starts] + + if self.isminor: + + _temp_idx = (dates.day_of_week == 1) | _month_starts + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%b') + self.label_flags.append(major_ticks) + + self.fmt_strs.append('%Y') + self.label_flags.append(default[(dates.year != (dates-1).year)]) + + + elif span <= 3 * daysperyear: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + _temp_idx = (dates.month != (dates-1).month) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%b') + self.label_flags.append(default[(dates.quarter != (dates-1).quarter)]) + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + elif span <= 11 * daysperyear: + + major_ticks = default[(dates.year != (dates-1).year)] + + if self.isminor: + + _temp_idx = (dates.quarter != (dates-1).quarter) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) + annual = (dates.year != (dates-1).year) + + major_ticks = default[annual & (dates.years % maj_anndef == 0)] + + if self.isminor: + _temp_idx = annual & (dates.years % min_anndef == 0) + if _temp_idx.size > 0: _temp_idx[0] = True + minor_ticks = default[_temp_idx] + + self.fmt_strs.append('%Y') + self.label_flags.append(major_ticks) + + if self.isminor: + + if default.size > 0: + """ensure that at least one value for every + level of label is output""" + for x, lf in enumerate(self.label_flags): + if lf.size == 0: + self.label_flags[x] = default[0:1] + + return minor_ticks + return major_ticks + + +#............................................................................... +class TimeSeries_YearLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) year. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive annual ticks. + - `quarter` : Integer *[1]* + Tells on which quarter the ticks should be. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + if freq == 'Q': + locs = (dates.quarters == self.quarter) + elif freq == 'M': + locs = (dates.months == self.month) + elif freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return dates.tovalue()[locs] +#............................................... +class TimeSeries_QuarterLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) quarter. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'A': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'Q': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq == 'M': + locs = (dates.months % 4 == self.month) + elif freq in 'BDU': + locs = (dates.months % 4 == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] +#............................................................................... +class TimeSeries_MonthLocator(TimeSeries_DateLocator): + """Locates ticks along a Date axis, for each (multiple of) month. + +:Ivariables: + - `base` : Integer + Gives the spacing between two consecutive quarter ticks. + - `month` : Integer *[1]* + Tells on which month the ticks should be. + - `day` : Integer *[1]* + Tells on which day the ticks should be. + """ + + def __init__(self, freq, minor_locator=False, + base=1, quarter=1, month=1, day=1): + TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, + base, quarter, month, day) + + def __call__(self): + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + freq = self.freqstr + if freq == 'AQ': + msg = "The current frequency ('%s') is too coarse!" % freq + raise ValueError, msg + elif freq == 'M': + return range(vmin, vmax+1, self.base) + else: + dates = self._initialize_dates() + values = dates.tovalue() + if freq in 'BDU': + locs = (dates.months == self.month) & (dates.day == self.day) + if self.base > 1: + locs &= (locs.cumsum() % self.base == 1) + return values[locs] + +#####--------------------------------------------------------------------------- +#---- --- Formatter --- +#####--------------------------------------------------------------------------- +class TimeSeries_DateFormatter(Formatter): + """Formats the ticks along a DateArray axis.""" + + def __init__(self, freq, fmt=None, locator=None): + if fmt is None: + fmt = Date.default_fmtstr[freq] + self.fmt = fmt + self.freqstr = freq + self.locator = locator + + def __call__(self, x, pos=0): + + if self.locator is not None: + lc = self.locator + + if lc.isminor: + nls = '\n' + retval = '' + for flags, fmt in zip(lc.label_flags, lc.fmt_strs): + if N.where(flags == x)[0].size > 0: + retval += Date(self.freqstr, value=int(x)).strfmt(fmt) + nls + else: + retval += nls + retval = retval.rstrip(nls) + else: + retval = '' + else: + retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) + + return retval + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries plots --- +#####-------------------------------------------------------------------------- +class TimeSeriesPlot(Subplot, object): + """Defines a time series based subclass of Subplot.""" + def __init__(self, fig=None, *args, **kwargs): + """ +Accepts the same keywords as a standard subplot, plus a specific `series` keyword. + +:Parameters: + `fig` : Figure + Base figure. + +:Keywords: + `series` : TimeSeries + Data to plot + + """ + # Retrieve the series ................... + _series = kwargs.pop('series', None) + Subplot.__init__(self, fig, *args, **kwargs) +# # Force fig to be defined ..... +# if fig is None: +# fig = TSFigure(_series) + # Process options ....................... + if _series is not None: + assert hasattr(_series, "dates") + self._series = _series.ravel() + self.xdata = _series.dates + self.freqstr = _series.dates.freqstr + self.xaxis.set_major_locator + + else: + self._series = None + self.xdata = None + self.freqstr = None + self._austoscale = False + # Get the data to plot + self.legendsymbols = [] + self.legendlabels = [] + #............................................ + def set_ydata(self, series=None): + """Sets the base time series.""" + if self._series is not None: + print "WARNING ! Base series is being changed.""" + self._series = series.ravel() + if isinstance(series, TimeSeries): + self.xdata = self.series.dates + #.... + def get_ydata(self): + """Gets the base time series.""" + return self._series + ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') + #............................................ + def _check_plot_params(self, *args): + """Defines the plot coordinates (and basic plotting arguments).""" + remaining = list(args) + # No args ? Use defaults, if any + if len(args) == 0: + if self.xdata is None: + raise ValueError, "No date information available!" + return (self.xdata, self.ydata) + output = [] + while len(remaining) > 0: + a = remaining.pop(0) + # The argument is a format: use default dates/ + if isinstance(a, str): + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, self.ydata, a]) + # The argument is a TimeSeries: use its dates for x + elif isinstance(a, TimeSeries): + (x, y) = (a._dates, a._series) + if len(remaining) > 0 and isinstance(remaining[0], str): + b = remaining.pop(0) + output.extend([x, y, b]) + else: + output.extend([x, y]) + # The argument is a DateArray............ + elif isinstance(a, (Date, DateArray)): + # Force to current freq + if self.freqstr is not None: + if a.freqstr != self.freqstr: + a = a.asfreq(self.freqstr) + # There's an argument after + if len(remaining) > 0: + #...and it's a format string + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata, b]) + #... and it's another date: use the default + elif isinstance(remaining[0], DateArray): + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata]) + #... and it must be some data + else: + b = remaining.pop(0) + if len(remaining) > 0: + if isinstance(remaining[0], str): + c = remaining.pop(0) + output.extend([a, b, c]) + else: + output.extend([a, b]) + # continue + else: + if self.ydata is None: + raise ValueError, "No data information available!" + #else: + # break + # Otherwise.............................. + elif len(remaining) > 0: + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a, b]) + #continue + elif self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a]) + #continue + # Reinitialize the plot if needed ........... + if self.xdata is None: + self.xdata = output[0] + self.freqstr = self.xdata.freqstr + # Force the xdata to the current frequency + elif output[0].freqstr != self.freqstr: + output = list(output) + output[0] = output[0].asfreq(self.freqstr) + return output + #............................................ + def tsplot(self, *parms, **kwargs): + """Plots the data parsed in argument. +This command accepts the same keywords as `matplotlib.plot`.""" + #print "Parameters: %s - %i" % (parms, len(parms)) + parms = self._check_plot_params(*parms) + self.legendlabels.append(kwargs.get('label', None)) + Subplot.plot(self, *parms, **kwargs) + pylab.draw_if_interactive() +# #............................................ +# def ybaseline(self,ybase,**kwargs): +# """Plots an horizontal baseline on each subplot.""" +# self.axhline(ybase,**kwargs) + #............................................ + def format_dateaxis(self, maj_spacing=None, min_spacing=None, + strformat="%Y", rotate=False): + """Pretty-formats the date axis (x-axis). + +:Parameters: + `major` : Integer *[5]* + Major tick locator, in years (major tick every `major` years). + `minor` : Integer *[12]* + Minor tick locator, in months (minor ticks every `minor` months). + `strformat` : String *['%Y']* + String format for major ticks ("%Y"). + """ + # Get the locator class ................. + if self.freqstr in 'BDU': + locator = TimeSeries_DailyLocator + major_locator = locator(self.freqstr, + minor_locator=False, + dynamic_mode=True) + minor_locator = locator(self.freqstr, + minor_locator=True, + dynamic_mode=True) + self.xaxis.set_major_locator(locator(self.freqstr, + minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_locator(locator(self.freqstr, + minor_locator=True, + dynamic_mode=True)) + else: + if self.freqstr == 'A': + locator = TimeSeries_AnnualLocator + elif self.freqstr == 'Q': + locator = TimeSeries_QuarterlyLocator + elif self.freqstr == 'M': + locator = TimeSeries_MonthlyLocator + major_locator = locator(minor_locator=False, + dynamic_mode=True) + minor_locator = locator(minor_locator=True, + dynamic_mode=True) + + self.xaxis.set_major_locator(major_locator) + self.xaxis.set_minor_locator(minor_locator) + #........................................ + self.xaxis.set_major_formatter( + TimeSeries_DateFormatter(self.freqstr, locator=major_locator)) + self.xaxis.set_minor_formatter( + TimeSeries_DateFormatter(self.freqstr, locator=minor_locator)) + + if rcParams['backend'] == 'PS': + rotate = False + warnings.warn("dateplot: PS backend detected, rotate disabled") + if self.is_last_row(): + if rotate: + setp(self.get_xticklabels(), rotation=45) +# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) +# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) +# else: +# self.set_xticklabels([]) +# self.set_xlabel('') +# #............................................ +# def plot_shifts(self,shifts,**kwargs): +# """Plots regime shifts. +#:param shifts: Shifts/trends to plot. +#:type shifts: `RegimeShift` +# """ +# self.tsplot(self.xdata,shifts.regimes,**kwargs) +# for x in shifts.xshifts[0]: +# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) + #............................................ +TSPlot = TimeSeriesPlot + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries Figures --- +#####-------------------------------------------------------------------------- +class TimeSeriesFigure(Figure): + """Time Series Figure: all subplots share the same time series. + """ + def __init__(self, series=None, **kwargs): + self._series = series + Figure.__init__(self, **kwargs) + fspnum = kwargs.pop('fspnum', None) + if fspnum is not None: + self.add_tsplot(fspnum, series=series) + #......... + def add_tsplot(self, *args, **kwargs): + """Adds a `TimeSeriesPlot` subplot to the figure.""" + kwargs.update(SubplotClass=TimeSeriesPlot, + series=self._series) + return add_generic_subplot(self, *args, **kwargs) + add_plot = add_tsplot +TSFigure = TimeSeriesFigure +#Figure.add_tsplot = +#................................................ +def tsfigure(series, **figargs): + """Creates a new `TimeSeriesFigure` object. + +:Parameters: + `series` : TimeSeries object + Input data. + `figargs` : Dictionary + Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. + """ + figargs.update(FigureClass=TSFigure) + figargs.update(series=series) +# print "figargs:",figargs +# num = figargs.pop('num',None) + fig = pylab.figure(**figargs) + return fig + +def add_tsplot(axes, *args, **kwargs): + kwargs.update(SubplotClass=TimeSeriesPlot) + if 'series' not in kwargs.keys(): + kwargs['series'] = None + return add_generic_subplot(axes, *args, **kwargs) +Figure.add_tsplot = add_tsplot + + +def tsplot(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = pylab.ishold() + h = kwargs.pop('hold', None) + if h is not None: + pylab.hold(h) + try: + ret = pylab.gca().add_tsplot(*args, **kwargs) + pylab.draw_if_interactive() + except: + pylab.hold(b) + raise + + pylab.hold(b) + return ret + +################################################################################ +if __name__ == '__main__': + + da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), + length=51) + ser = timeseries.time_series(MA.arange(len(da)), dates=da) + ser[4] = MA.masked + ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) + + pylab.figure() + pylab.gcf().add_tsplot(111) + pylab.gca().tsplot(ser, 'ko-') + pylab.gca().format_dateaxis() + pylab.gca().tsplot(ser_2, 'rs') + pylab.show() + \ No newline at end of file From scipy-svn at scipy.org Fri Feb 9 15:03:52 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 9 Feb 2007 14:03:52 -0600 (CST) Subject: [Scipy-svn] r2698 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070209200352.8CC4239C106@new.scipy.org> Author: mattknox_ca Date: 2007-02-09 14:03:49 -0600 (Fri, 09 Feb 2007) New Revision: 2698 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py Log: deleted old method Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 19:52:34 UTC (rev 2697) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-09 20:03:49 UTC (rev 2698) @@ -351,13 +351,6 @@ base, quarter, month, day) self.offset = 1 - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) - if self.isminor: - return minor - return major - def _get_default_locs(self, vmin, vmax): "Returns the default tick locations for monthly data." From scipy-svn at scipy.org Sat Feb 10 19:33:21 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sat, 10 Feb 2007 18:33:21 -0600 (CST) Subject: [Scipy-svn] r2699 - trunk/Lib/sandbox/timeseries/src Message-ID: <20070211003321.2E6A039C020@new.scipy.org> Author: mattknox_ca Date: 2007-02-10 18:33:13 -0600 (Sat, 10 Feb 2007) New Revision: 2699 Modified: trunk/Lib/sandbox/timeseries/src/cseries.c Log: removed some outdated comments Modified: trunk/Lib/sandbox/timeseries/src/cseries.c =================================================================== --- trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-09 20:03:49 UTC (rev 2698) +++ trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-11 00:33:13 UTC (rev 2699) @@ -7,11 +7,6 @@ static char cseries_doc[] = "Speed sensitive time series operations"; -/* -these are the earliest values at each frequency that can be converted -to frequencies higher than daily (ie. Hourly, Minutely, Secondly) -*/ - #define FANN 1000 /* Annual */ #define FQTR 2000 /* Quarterly */ #define FMTH 3000 /* Monthly */ @@ -22,18 +17,6 @@ #define FMIN 8000 /* Minutely */ #define FSEC 9000 /* Secondly */ #define FUND -9999 /* Undefined */ -/* -static long FANN = 1000; -static long FQTR = 2000; -static long FMTH = 3000; -static long FWK = 4000; -static long FBUS = 5000; -static long FDL = 6000; -static long FHR = 7000; -static long FMIN = 8000; -static long FSEC = 9000; -static long FUND = -9999; -*/ static long minval_D_toHighFreq = 719163; From scipy-svn at scipy.org Sun Feb 11 18:24:28 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 11 Feb 2007 17:24:28 -0600 (CST) Subject: [Scipy-svn] r2700 - in trunk/Lib/sandbox/maskedarray: . tests Message-ID: <20070211232428.55C3539C0DA@new.scipy.org> Author: pierregm Date: 2007-02-11 17:24:17 -0600 (Sun, 11 Feb 2007) New Revision: 2700 Added: trunk/Lib/sandbox/maskedarray/core_ini.py trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py Modified: trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/extras.py trunk/Lib/sandbox/maskedarray/tests/test_core.py trunk/Lib/sandbox/maskedarray/tests/test_extras.py trunk/Lib/sandbox/maskedarray/testutils.py trunk/Lib/sandbox/maskedarray/timer_comparison.py Log: major update ! core : MaskedArray now really behave like ndarrays. : got rid of (most) class default. : now using views instead of calls to self.__class__. : added a _smallmask attribute to prevent the automatic compression of masks. : added a _baseclass attribute, to record the class type of the input data. : _data is now simply a view of self as _baseclass. extras : corrected a pb w/ apply_along_axis. core_ini : previous version of core Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -1,3 +1,4 @@ +# pylint: disable-msg=E1002 """MA: a facility for dealing with missing observations MA is generally used as a numpy.array look-alike. by Paul F. Dubois. @@ -22,36 +23,36 @@ __all__ = ['MAError', 'MaskType', 'MaskedArray', 'bool_', 'complex_', 'float_', 'int_', 'object_', 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', - 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', - 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', - 'arctanh', 'argmax', 'argmin', 'argsort', 'around', - 'array', 'asarray', + 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', + 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', + 'arctanh', 'argmax', 'argmin', 'argsort', 'around', + 'array', 'asarray', 'bitwise_and', 'bitwise_or', 'bitwise_xor', - 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate', + 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate', 'cos', 'cosh', 'count', 'diagonal', 'divide', 'dump', 'dumps', - 'empty', 'empty_like', 'equal', 'exp', - 'fabs', 'fmod', 'filled', 'floor', 'floor_divide', - 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot', + 'empty', 'empty_like', 'equal', 'exp', + 'fabs', 'fmod', 'filled', 'floor', 'floor_divide', + 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot', 'ids', 'inner', 'innerproduct', 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', - 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', + 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', - 'make_mask', 'make_mask_none', 'mask_or', 'masked', + 'make_mask', 'make_mask_none', 'mask_or', 'masked', 'masked_array', 'masked_equal', 'masked_greater', - 'masked_greater_equal', 'masked_inside', 'masked_less', - 'masked_less_equal', 'masked_not_equal', 'masked_object', + 'masked_greater_equal', 'masked_inside', 'masked_less', + 'masked_less_equal', 'masked_not_equal', 'masked_object', 'masked_outside', 'masked_print_option', 'masked_singleton', 'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min', - 'minimum', 'multiply', + 'minimum', 'multiply', 'negative', 'nomask', 'nonzero', 'not_equal', - 'ones', 'outer', 'outerproduct', - 'power', 'product', 'ptp', 'put', 'putmask', - 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', - 'right_shift', 'round_', - 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std', + 'ones', 'outer', 'outerproduct', + 'power', 'product', 'ptp', 'put', 'putmask', + 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', + 'right_shift', 'round_', + 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std', 'subtract', 'sum', 'swapaxes', - 'take', 'tan', 'tanh', 'transpose', 'true_divide', + 'take', 'tan', 'tanh', 'transpose', 'true_divide', 'var', 'where', 'zeros'] @@ -62,7 +63,7 @@ import numpy from numpy import bool_, complex_, float_, int_, object_, str_ -import numpy.core.umath as umath +import numpy.core.umath as umath import numpy.core.fromnumeric as fromnumeric from numpy.core.numeric import ndarray from numpy.core.fromnumeric import amax, amin @@ -81,38 +82,38 @@ divide_tolerance = 1.e-35 numpy.seterr(all='ignore') -#####-------------------------------------------------------------------------- -#---- --- Helper functions --- -#####-------------------------------------------------------------------------- -def convert_typecode(f,dtchar): - """Converts the type of `f` to a type compatible with `dtchar`, for inline operations.""" - ftype = f.dtype.char - if dtchar == ftype: - return f - elif dtchar in typecodes['Integer']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - elif dtchar in typecodes['Float']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - elif ftype in typecodes['Float']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - elif dtchar in typecodes['Complex']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - elif ftype in typecodes['Float']: - f = f.astype(dtchar) - elif ftype in typecodes['Complex']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - else: - raise TypeError, 'Incorrect type for in-place operation.' - return f +######-------------------------------------------------------------------------- +##---- --- Helper functions --- +######-------------------------------------------------------------------------- +#def convert_typecode(f,dtchar): +# """Converts the type of `f` to a type compatible with `dtchar`, for inline operations.""" +# ftype = f.dtype.char +# if dtchar == ftype: +# return f +# elif dtchar in typecodes['Integer']: +# if ftype in typecodes['Integer']: +# f = f.astype(dtchar) +# else: +# raise TypeError, 'Incorrect type for in-place operation.' +# elif dtchar in typecodes['Float']: +# if ftype in typecodes['Integer']: +# f = f.astype(dtchar) +# elif ftype in typecodes['Float']: +# f = f.astype(dtchar) +# else: +# raise TypeError, 'Incorrect type for in-place operation.' +# elif dtchar in typecodes['Complex']: +# if ftype in typecodes['Integer']: +# f = f.astype(dtchar) +# elif ftype in typecodes['Float']: +# f = f.astype(dtchar) +# elif ftype in typecodes['Complex']: +# f = f.astype(dtchar) +# else: +# raise TypeError, 'Incorrect type for in-place operation.' +# else: +# raise TypeError, 'Incorrect type for in-place operation.' +# return f #####-------------------------------------------------------------------------- #---- --- Exceptions --- @@ -139,56 +140,36 @@ 'O' : '?', 'S' : 'N/A', 'u' : 999999, - 'V' : '???', + 'V' : '???', } -#{0: , -# 1: , -# 2: , -# 3: , -# 4: , -# 5: , -# 6: , -# 7: , -# 8: , -# 9: , -# 10: , -# 11: , -# 12: , -# 13: , -# 14: , -# 15: , -# 16: , -# 17: , -# 18: , -# 19: , -# 20: , -max_filler = ntypes._minvals -max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]]) +max_filler = ntypes._minvals +max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]]) min_filler = ntypes._maxvals -min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]]) +min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]]) if 'float128' in ntypes.typeDict: max_filler.update([(numpy.float128,-numeric.inf)]) - min_filler.update([(numpy.float128, numeric.inf)]) + min_filler.update([(numpy.float128, numeric.inf)]) -def default_fill_value (obj): +def default_fill_value(obj): "Calculates the default fill value for an object `obj`." if hasattr(obj,'dtype'): - return default_filler[obj.dtype.kind] + defval = default_filler[obj.dtype.kind] + elif isinstance(obj, numeric.dtype): + defval = default_filler[obj.kind] elif isinstance(obj, float): - return default_filler['f'] + defval = default_filler['f'] elif isinstance(obj, int) or isinstance(obj, long): - return default_filler['i'] + defval = default_filler['i'] elif isinstance(obj, str): - return default_filler['S'] + defval = default_filler['S'] elif isinstance(obj, complex): - return default_filler['c'] - elif isinstance(obj, numeric.dtype): - return default_filler[obj.kind] + defval = default_filler['c'] else: - return default_filler['O'] + defval = default_filler['O'] + return defval -def minimum_fill_value (obj): +def minimum_fill_value(obj): "Calculates the default fill value suitable for taking the minimum of `obj`." if hasattr(obj, 'dtype'): objtype = obj.dtype @@ -207,7 +188,7 @@ else: raise TypeError, 'Unsuitable type for calculating minimum.' -def maximum_fill_value (obj): +def maximum_fill_value(obj): "Calculates the default fill value suitable for taking the maximum of `obj`." if hasattr(obj, 'dtype'): objtype = obj.dtype @@ -226,12 +207,12 @@ else: raise TypeError, 'Unsuitable type for calculating minimum.' -def set_fill_value (a, fill_value): +def set_fill_value(a, fill_value): "Sets the fill value of `a` if it is a masked array." if isinstance(a, MaskedArray): a.set_fill_value(fill_value) -def get_fill_value (a): +def get_fill_value(a): """Returns the fill value of `a`, if any. Otherwise, returns the default fill value for that type. """ @@ -241,11 +222,11 @@ result = default_fill_value(a) return result -def common_fill_value (a, b): +def common_fill_value(a, b): "Returns the common fill_value of `a` and `b`, if any, or `None`." t1 = get_fill_value(a) t2 = get_fill_value(b) - if t1 == t2: + if t1 == t2: return t1 return None @@ -257,18 +238,37 @@ If `a` is already a contiguous numeric array, `a` itself is returned. -`filled(a)` can be used to be sure that the result is numeric when passing +`filled(a)` can be used to be sure that the result is numeric when passing an object a to other software ignorant of MA, in particular to numpy itself. """ if hasattr(a, 'filled'): return a.filled(value) elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']: return a - elif isinstance(a, types.DictType): + elif isinstance(a, dict): return numeric.array(a, 'O') else: return numeric.array(a) - + +def get_masked_subclass(*arrays): + """Returns the youngest subclass of MaskedArray from a list of arrays, + or MaskedArray. In case of siblings, the first takes over.""" + if len(arrays) == 1: + arr = arrays[0] + if isinstance(arr, MaskedArray): + rcls = type(arr) + else: + rcls = MaskedArray + else: + arrcls = [type(a) for a in arrays] + rcls = arrcls[0] + if not issubclass(rcls, MaskedArray): + rcls = MaskedArray + for cls in arrcls[1:]: + if issubclass(cls, rcls): + rcls = cls + return rcls + #####-------------------------------------------------------------------------- #---- --- Ufuncs --- #####-------------------------------------------------------------------------- @@ -276,7 +276,7 @@ ufunc_fills = {} class domain_check_interval: - """Defines a valid interval, + """Defines a valid interval, so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`.""" def __init__(self, a, b): "domain_check_interval(a,b)(x) = true where x < a or y > b" @@ -312,7 +312,7 @@ def __init__(self, critical_value): "domain_greater(v)(x) = true where x <= v" self.critical_value = critical_value - + def __call__ (self, x): "Execute the call behavior." return umath.less_equal(x, self.critical_value) @@ -329,12 +329,12 @@ #.............................................................................. class masked_unary_operation: """Defines masked version of unary operations, -where invalid values are pre-masked. +where invalid values are pre-masked. :IVariables: - `f` : function. - `fill` : Default filling value *[0]*. - - `domain` : Default domain *[None]*. + - `domain` : Default domain *[None]*. """ def __init__ (self, mufunc, fill=0, domain=None): """ masked_unary_operation(aufunc, fill=0, domain=None) @@ -357,24 +357,31 @@ d1 = filled(a, self.fill) if self.domain is not None: m = mask_or(m, numeric.asarray(self.domain(d1))) - result = self.f(d1, *args, **kwargs) - # - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) + # Take care of the masked singletong first ... + if m.ndim == 0 and m: + return masked + # Get the result.... + if isinstance(a, MaskedArray): + result = self.f(d1, *args, **kwargs).view(type(a)) + else: + result = self.f(d1, *args, **kwargs).view(MaskedArray) + # Fix the mask if we don't have a scalar + if result.ndim > 0: + result._mask = m + return result # def __str__ (self): return "Masked version of %s. [Invalid values are masked]" % str(self.f) #.............................................................................. class masked_binary_operation: """Defines masked version of binary operations, -where invalid values are pre-masked. +where invalid values are pre-masked. :IVariables: - `f` : function. - `fillx` : Default filling value for first array*[0]*. - `filly` : Default filling value for second array*[0]*. - - `domain` : Default domain *[None]*. + - `domain` : Default domain *[None]*. """ def __init__ (self, mbfunc, fillx=0, filly=0): """abfunc(fillx, filly) must be defined. @@ -391,21 +398,19 @@ def __call__ (self, a, b, *args, **kwargs): "Execute the call behavior." m = mask_or(getmask(a), getmask(b)) + if (not m.ndim) and m: + return masked d1 = filled(a, self.fillx) d2 = filled(b, self.filly) - result = self.f(d1, d2, *args, **kwargs) -# if isinstance(result, ndarray) \ -# and m.ndim != 0 \ -# and m.shape != result.shape: -# m = mask_or(getmaskarray(a), getmaskarray(b)) - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) + result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b)) + if result.ndim > 0: + result._mask = m + return result # def reduce (self, target, axis=0, dtype=None): """Reduces `target` along the given `axis`.""" if isinstance(target, MaskedArray): - tclass = target.__class__ + tclass = type(target) else: tclass = MaskedArray m = getmask(target) @@ -416,19 +421,20 @@ m = make_mask(m, copy=1) m.shape = (1,) if m is nomask: - return tclass(self.f.reduce (t, axis)) - else: - t = tclass(t, mask=m) - # XXX: "or t.dtype" below is a workaround for what appears - # XXX: to be a bug in reduce. - t = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype) - m = umath.logical_and.reduce(m, axis) - if isinstance(t, ndarray): - return tclass(t, mask=m, fill_value=get_fill_value(target)) - elif m: - return masked - else: - return t + return self.f.reduce(t, axis).view(tclass) + t = t.view(tclass) + t._mask = m + # XXX: "or t.dtype" below is a workaround for what appears + # XXX: to be a bug in reduce. + tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype) + mr = umath.logical_and.reduce(m, axis) + tr = tr.view(tclass) + if mr.ndim > 0: + tr._mask = mr + return tr + elif mr: + return masked + return tr def outer (self, a, b): "Returns the function applied to the outer product of a and b." @@ -440,34 +446,37 @@ ma = getmaskarray(a) mb = getmaskarray(b) m = umath.logical_or.outer(ma, mb) - d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)) - if isinstance(d, MaskedArray): - return d.__class__(d, mask=m) - return masked_array(d, mask=m) + if (not m.ndim) and m: + return masked + rcls = get_masked_subclass(a,b) + d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls) + if d.ndim > 0: + d._mask = m + return d def accumulate (self, target, axis=0): """Accumulates `target` along `axis` after filling with y fill value.""" if isinstance(target, MaskedArray): - tclass = target.__class__ + tclass = type(target) else: tclass = masked_array t = filled(target, self.filly) - return tclass(self.f.accumulate(t, axis)) - + return self.f.accumulate(t, axis).view(tclass) + def __str__ (self): return "Masked version of " + str(self.f) #.............................................................................. class domained_binary_operation: - """Defines binary operations that have a domain, like divide. - -These are complicated so they are a separate class. + """Defines binary operations that have a domain, like divide. + +These are complicated so they are a separate class. They have no reduce, outer or accumulate. :IVariables: - `f` : function. - `fillx` : Default filling value for first array*[0]*. - `filly` : Default filling value for second array*[0]*. - - `domain` : Default domain *[None]*. + - `domain` : Default domain *[None]*. """ def __init__ (self, dbfunc, domain, fillx=0, filly=0): """abfunc(fillx, filly) must be defined. @@ -494,10 +503,12 @@ d2 = numeric.where(t, self.filly, d2) mb = mask_or(mb, t) m = mask_or(ma, mb) - result = self.f(d1, d2) - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) + if (not m.ndim) and m: + return masked + result = self.f(d1, d2).view(get_masked_subclass(a,b)) + if result.ndim > 0: + result._mask = m + return result def __str__ (self): return "Masked version of " + str(self.f) @@ -526,12 +537,12 @@ log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0)) log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0)) tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35)) -arcsin = masked_unary_operation(umath.arcsin, 0.0, +arcsin = masked_unary_operation(umath.arcsin, 0.0, domain_check_interval(-1.0, 1.0)) -arccos = masked_unary_operation(umath.arccos, 0.0, +arccos = masked_unary_operation(umath.arccos, 0.0, domain_check_interval(-1.0, 1.0)) arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0)) -arctanh = masked_unary_operation(umath.arctanh, 0.0, +arctanh = masked_unary_operation(umath.arctanh, 0.0, domain_check_interval(-1.0+1e-15, 1.0-1e-15)) # Binary ufuncs add = masked_binary_operation(umath.add) @@ -561,11 +572,11 @@ hypot = masked_binary_operation(umath.hypot) # Domained binary ufuncs divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1) -true_divide = domained_binary_operation(umath.true_divide, +true_divide = domained_binary_operation(umath.true_divide, domain_safe_divide(), 0, 1) -floor_divide = domained_binary_operation(umath.floor_divide, +floor_divide = domained_binary_operation(umath.floor_divide, domain_safe_divide(), 0, 1) -remainder = domained_binary_operation(umath.remainder, +remainder = domained_binary_operation(umath.remainder, domain_safe_divide(), 0, 1) fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1) @@ -604,7 +615,7 @@ def make_mask(m, copy=False, small_mask=True, flag=None): """make_mask(m, copy=0, small_mask=0) Returns `m` as a mask, creating a copy if necessary or requested. -The function can accept any sequence of integers or `nomask`. +The function can accept any sequence of integers or `nomask`. Does not check that contents must be 0s and 1s. If `small_mask=True`, returns `nomask` if `m` contains no true elements. @@ -651,11 +662,11 @@ - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. """ - if m1 is nomask: + if m1 is nomask: return make_mask(m2, copy=copy, small_mask=small_mask) - if m2 is nomask: + if m2 is nomask: return make_mask(m1, copy=copy, small_mask=small_mask) - if m1 is m2 and is_mask(m1): + if m1 is m2 and is_mask(m1): return m1 return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask) @@ -710,7 +721,7 @@ # return array(d, mask=m, copy=copy) def masked_inside(x, v1, v2, copy=True): - """Shortcut to `masked_where`, where `condition` is True for x inside + """Shortcut to `masked_where`, where `condition` is True for x inside the interval `[v1,v2]` ``(v1 <= x <= v2)``. The boundaries `v1` and `v2` can be given in either order. """ @@ -721,7 +732,7 @@ return masked_where(condition, x, copy=copy) def masked_outside(x, v1, v2, copy=True): - """Shortcut to `masked_where`, where `condition` is True for x outside + """Shortcut to `masked_where`, where `condition` is True for x outside the interval `[v1,v2]` ``(x < v1)|(x > v2)``. The boundaries `v1` and `v2` can be given in either order. """ @@ -734,7 +745,7 @@ # def masked_object(x, value, copy=True): """Masks the array `x` where the data are exactly equal to `value`. -This function is suitable only for `object` arrays: for floating point, +This function is suitable only for `object` arrays: for floating point, please use `masked_values` instead. The mask is set to `nomask` if posible. @@ -810,6 +821,156 @@ #####-------------------------------------------------------------------------- #---- --- MaskedArray class --- #####-------------------------------------------------------------------------- +#def _getoptions(a_out, a_in): +# "Copies standards options of a_in to a_out." +# for att in ['] +class _mathmethod(object): + """Defines a wrapper for arithmetic methods. +Instead of directly calling a ufunc, the corresponding method of the `array._data` +object is called instead. + """ + def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): + """ +:Parameters: + - `methodname` (String) : Method name. + - `fill_self` (Float *[0]*) : Fill value for the instance. + - `fill_other` (Float *[0]*) : Fill value for the target. + - `domain` (Domain object *[None]*) : Domain of non-validity. + """ + self.methodname = methodname + self.fill_self = fill_self + self.fill_other = fill_other + self.domain = domain + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self.methodname).__doc__ + except: + return getattr(ndarray, self.methodname).__doc__ + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, other, *args): + "Execute the call behavior." + instance = self.obj + m_self = instance._mask + m_other = getmask(other) + base = instance.filled(self.fill_self) + target = filled(other, self.fill_other) + if self.domain is not None: + # We need to force the domain to a ndarray only. + if self.fill_other > self.fill_self: + domain = self.domain(base, target) + else: + domain = self.domain(target, base) + if domain.any(): + #If `other` is a subclass of ndarray, `filled` must have the + # same subclass, else we'll lose some info. + #The easiest then is to fill `target` instead of creating + # a pure ndarray. + #Oh, and we better make a copy! + if isinstance(other, ndarray): + # We don't want to modify other: let's copy target, then + target = target.copy() + target[fromnumeric.asarray(domain)] = self.fill_other + else: + target = numeric.where(fromnumeric.asarray(domain), + self.fill_other, target) + m_other = mask_or(m_other, domain) + m = mask_or(m_self, m_other) + method = getattr(base, self.methodname) + result = method(target, *args).view(type(instance)) + try: + result._mask = m + except AttributeError: + if m: + result = masked + return result +#............................................................................... +class _arraymethod(object): + """Defines a wrapper for basic array methods. +Upon call, returns a masked array, where the new `_data` array is the output +of the corresponding method called on the original `_data`. + +If `onmask` is True, the new mask is the output of the method calld on the initial mask. +If `onmask` is False, the new mask is just a reference to the initial mask. + +:Parameters: + `funcname` : String + Name of the function to apply on data. + `onmask` : Boolean *[True]* + Whether the mask must be processed also (True) or left alone (False). + """ + def __init__(self, funcname, onmask=True): + self._name = funcname + self._onmask = onmask + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + methdoc = getattr(ndarray, self._name, None) + methdoc = getattr(numpy, self._name, methdoc) +# methdoc = getattr(MaskedArray, self._name, methdoc) + if methdoc is not None: + return methdoc.__doc__ +# try: +# return getattr(MaskedArray, self._name).__doc__ +# except: +# try: +# return getattr(numpy, self._name).__doc__ +# except: +# return getattr(ndarray, self._name).__doc + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__(self, *args, **params): + methodname = self._name + data = self.obj._data + mask = self.obj._mask + cls = type(self.obj) + result = getattr(data, methodname)(*args, **params).view(cls) + if result.ndim: + if not self._onmask: + result._mask = mask + elif mask is not nomask: + result._set_mask(getattr(mask, methodname)(*args, **params)) + return result +#.......................................................... + +class flatiter(object): + "Defines an interator." + def __init__(self, ma): + self.ma = ma + self.ma_iter = numpy.asarray(ma).flat + + if ma._mask is nomask: + self.maskiter = None + else: + self.maskiter = ma._mask.flat + + def __iter__(self): + return self + + ### This won't work is ravel makes a copy + def __setitem__(self, index, value): + a = self.ma.ravel() + a[index] = value + + def next(self): + d = self.ma_iter.next() + if self.maskiter is not None and self.maskiter.next(): + d = masked + return d + + class MaskedArray(numeric.ndarray): """Arrays with possibly masked values. Masked values of True exclude the corresponding element from any computation. @@ -819,8 +980,8 @@ mask = nomask, fill_value=None, small_mask=True) If copy=False, every effort is made not to copy the data: -If `data` is a MaskedArray, and argument mask=nomask, then the candidate data -is `data._data` and the mask used is `data._mask`. +If `data` is a MaskedArray, and argument mask=nomask, then the candidate data +is `data._data` and the mask used is `data._mask`. If `data` is a numeric array, it is used as the candidate raw data. If `dtype` is not None and is different from data.dtype.char then a data copy is required. Otherwise, the candidate is used. @@ -828,81 +989,56 @@ If a data copy is required, the raw (unmasked) data stored is the result of: numeric.array(data, dtype=dtype.char, copy=copy) -If `mask` is `nomask` there are no masked values. +If `mask` is `nomask` there are no masked values. Otherwise mask must be convertible to an array of booleans with the same shape as x. If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`. Otherwise, the mask is not compressed. -fill_value is used to fill in masked values when necessary, such as when +fill_value is used to fill in masked values when necessary, such as when printing and in method/function filled(). The fill_value is not used for computation within this module. """ __array_priority__ = 10.1 _defaultmask = nomask _defaulthardmask = False - #TODO: There some reorganization to do round here + _baseclass = numeric.ndarray def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None, keep_mask=True, small_mask=True, hard_mask=False, flag=None, **options): """array(data, dtype=None, copy=True, mask=nomask, fill_value=None) - + If `data` is already a ndarray, its dtype becomes the default value of dtype. - """ + """ if flag is not None: warnings.warn("The flag 'flag' is now called 'small_mask'!", DeprecationWarning) small_mask = flag - # 1. Argument is MA ........... - if isinstance(data, MaskedArray) or\ - (hasattr(data,"_mask") and hasattr(data,"_data")) : + # Process data........................... + _data = numeric.array(data, dtype=dtype, copy=copy, subok=True) + _baseclass = getattr(_data, '_baseclass', type(_data)) +# _data = numeric.ndarray.__new__(cls, shape=_data.shape, +# dtype=_data.dtype, buffer=_data) + _data = _data.view(cls) + # Process mask .......................... + if hasattr(data,"_mask"): if data is masked: return masked.view(cls) + # Keep the original mask if needed .. if keep_mask: if mask is nomask: if copy: - cls._defaultmask = data._mask.copy() + mask = data._mask.copy() else: - cls._defaultmask = data._mask + mask = data._mask else: - cls._defaultmask = mask_or(data._mask, mask, - copy=copy, small_mask=small_mask) + mask = mask_or(data._mask, mask, + copy=copy, small_mask=small_mask) + # Create a new mask from scratch .... else: - cls._defaultmask = make_mask(mask, copy=copy, small_mask=small_mask) - # Update fille_value - if fill_value is None: - cls._fill_value = data._fill_value - else: - cls._fill_value = fill_value - cls._defaulthardmask = hard_mask - _data = data._data - if dtype is not None and _data.dtype != numeric.dtype(dtype): - return _data.astype(dtype).view(cls) - elif copy: - return _data.copy().view(cls) - else: - return _data.view(cls) - # 2. Argument is not MA ....... - if isinstance(data, ndarray): - if dtype is not None and data.dtype != numeric.dtype(dtype): - _data = data.astype(dtype) - elif copy: - _data = data.copy() - else: - _data = data + mask = make_mask(mask, copy=copy, small_mask=small_mask) else: - _data = numeric.array(data, dtype=dtype, copy=copy) -# try: -# _data = numeric.array(data, dtype=dtype, copy=copy) -# except TypeError: -# _data = empty(len(data), dtype=dtype) -# for (k,v) in enumerate(data): -# _data[k] = v -# if mask is nomask: -# cls.__defaultmask = getmask(_data) -# return _data.view(cls) - # Define mask ................. - mask = make_mask(mask, copy=copy, small_mask=small_mask) - #....Check shapes compatibility + mask = make_mask(mask, copy=copy, small_mask=small_mask) + # Check shape compatibility between mask and data if mask is not nomask: (nd, nm) = (_data.size, mask.size) if (nm != nd): @@ -917,160 +1053,146 @@ raise MAError, msg % (nd, nm) elif (mask.shape != _data.shape): mask = mask.reshape(_data.shape) -# mask = _data.shape - #.... - cls._fill_value = fill_value - cls._defaulthardmask = hard_mask - cls._defaultmask = mask - cls._defaultoptions = options - return numeric.asanyarray(_data).view(cls) + + # Update fille_value + _data._fill_value = getattr(data, '_fill_value', fill_value) + if _data._fill_value is None: + _data._fill_value = default_fill_value(_data) + _data._hardmask = hard_mask + _data._smallmask = small_mask + _data._baseclass = _baseclass + _data._mask = mask + return _data #.................................. def __array_wrap__(self, obj, context=None): """Special hook for ufuncs. Wraps the numpy array and sets the mask according to context. """ -# mclass = self.__class__ + result = obj.view(type(self)) #.......... if context is None: -# return mclass(obj, mask=self._mask, copy=False) - return MaskedArray(obj, mask=self._mask, copy=False, - dtype=obj.dtype, - fill_value=self.fill_value, ) + m = self._mask #.......... - (func, args) = context[:2] - m = reduce(mask_or, [getmask(arg) for arg in args]) - # Get domain mask - domain = ufunc_domain.get(func, None) - if domain is not None: - m = mask_or(m, domain(*[getattr(arg, '_data', arg) for arg in args])) - # Update mask - if m is not nomask: - try: - dshape = obj.shape - except AttributeError: - pass - else: - if m.shape != dshape: + else: + (func, args, out_index) = context + m = reduce(mask_or, [getmask(arg) for arg in args]) + # Get domain mask + domain = ufunc_domain.get(func, None) + if domain is not None: + if len(args) > 2: + d = reduce(domain, args) + else: + d = domain(*args) + m = mask_or(m, d) + # Update mask + if m is not nomask and hasattr(obj,'shape'): + if m.shape != obj.shape: m = reduce(mask_or, [getmaskarray(arg) for arg in args]) -# return mclass(obj, copy=False, mask=m) - return MaskedArray(obj, copy=False, mask=m,) -# dtype=obj.dtype, fill_value=self._fill_value) + #.... +# result = obj.view(type(self)) + result._mask = m + result._fill_value = self._fill_value + result._hardmask = self._hardmask + result._smallmask = self._smallmask + result._baseclass = self._baseclass + return result #........................ - #TODO: there should be some reorganization to do round here. def __array_finalize__(self,obj): """Finalizes the masked array. """ - # - if isinstance(obj, MaskedArray): - # We came here from a MaskedArray - self._data = obj._data - self._mask = obj._mask - self._hardmask = obj._hardmask - self._fill_value = obj._fill_value - self.options = obj.options - else: - # We came here from a .view() - if hasattr(obj,'_data') and hasattr(obj, '_mask'): - # obj is an old masked array or a smart record - self._data = obj._data - self._mask = obj._mask - else: - # obj is anything but... - self._data = obj - self._mask = self._defaultmask - # Set the instance default - self._hardmask = self._defaulthardmask - self.fill_value = self._fill_value - self.options = self._defaultoptions - # Reset the class default - MaskedArray._defaultmask = nomask - MaskedArray._defaulthardmask = False - MaskedArray._fill_value = None -# # - return + # Finalize mask ............... + self._mask = getattr(obj, '_mask', self._defaultmask) + if self._mask is not nomask: + self._mask.shape = self.shape +# if mask is not nomask and mask.shape != self.shape: +# # try and set the shape if we're coming from a assignment to shape +# # __array_interface__['data'] is REALLY faster than ctypes.data +# if self.__array_interface__['data'] == obj.__array_interface__['data']: +# try: +# mask.shape = self.shape +# except: +# mask = nomask +# else: +# mask = nomask +# self._mask = mask + # Get the remaining options ... + self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask) + self._smallmask = getattr(obj, '_smallmask', True) + self._baseclass = getattr(obj, '_baseclass', type(obj)) + self._fill_value = getattr(obj, '_fill_value', None) + return #............................................ def __getitem__(self, indx): """x.__getitem__(y) <==> x[y] Returns the item described by i. Not a copy as in previous versions. """ - if getmask(indx) is not nomask: - msg = "Masked arrays must be filled before they can be used as indices!" - raise IndexError, msg - dout = self._data[indx] + # This test is useful, but we should keep things light... +# if getmask(indx) is not nomask: +# msg = "Masked arrays must be filled before they can be used as indices!" +# raise IndexError, msg + # super() can't work here if the underlying data is a matrix... + dout = (self._data).__getitem__(indx) m = self._mask - scalardout = (len(numeric.shape(dout))==0) - # - if m is nomask: - if scalardout: - return dout - else: - return self.__class__(dout, mask=nomask, keep_mask=True, - fill_value=self._fill_value, - **self.options) - #.... - mi = m[indx] - if mi.size == 1: - if mi: - return masked - return dout - else: - return self.__class__(dout, mask=mi, fill_value=self._fill_value, - **self.options) + if hasattr(dout, 'shape') and len(dout.shape) > 0: + # Not a scalar: make sure that dout is a MA + dout = dout.view(type(self)) + if m is not nomask: + # use _set_mask to take care of the shape + dout._set_mask(m[indx]) + elif m is not nomask and m[indx]: + dout = masked + return dout #........................ - def __setitem__(self, index, value): + def __setitem__(self, indx, value): """x.__setitem__(i, y) <==> x[i]=y Sets item described by index. If value is masked, masks those locations. """ if self is masked: raise MAError, 'Cannot alter the masked element.' - if getmask(index) is not nomask: - msg = "Masked arrays must be filled before they can be used as indices!" - raise IndexError, msg +# if getmask(indx) is not nomask: +# msg = "Masked arrays must be filled before they can be used as indices!" +# raise IndexError, msg #.... - (d, m) = (self._data, self._mask) + m = self._mask #.... if value is masked: if m is nomask: - m = make_mask_none(d.shape) + m = make_mask_none(self.shape) else: m = m.copy() - m[index] = True + m[indx] = True self._mask = m return - #.... + #.... + dval = numeric.asarray(value).astype(self.dtype) + valmask = getmask(value) if m is nomask: - d[index] = filled(value) - valmask = getmask(value) if valmask is not nomask: - m = make_mask_none(d.shape) - m[index] = valmask + m = make_mask_none(self.shape) + m[indx] = valmask elif not self._hardmask: - d[index] = filled(value) - valmask = getmask(value) m = m.copy() if valmask is nomask: - m[index] = False + m[indx] = False else: - m[index] = valmask - elif hasattr(index, 'dtype') and (index.dtype==bool_): - index *= ~m - d[index] = filled(value) + m[indx] = valmask + elif hasattr(indx, 'dtype') and (indx.dtype==bool_): + indx = indx * ~m # elif isinstance(index, int): - else: - mindx = m[index] - value = masked_array(value, mask=mindx, keep_mask=True) - valdata = filled(value) - valmask = getmask(value) - if valmask is nomask: - d[index] = valdata - elif valmask.size > 1: - dindx = d[index] - numeric.putmask(dindx, ~valmask, valdata) - d[index] = dindx - numeric.putmask(mindx, valmask, True) - m[index] = mindx - #..... - if not m.any(): + else: + mindx = mask_or(m[indx], valmask, copy=True) + dindx = self._data[indx] + if dindx.size > 1: + dindx[~mindx] = dval + elif mindx is nomask: + dindx = dval + dval = dindx + m[indx] = mindx + # Set data .......... + #dval = filled(value).astype(self.dtype) + ndarray.__setitem__(self._data,indx,dval) + #..... + if m is nomask or not m.any(): self._mask = nomask else: self._mask = m @@ -1079,66 +1201,18 @@ """x.__getslice__(i, j) <==> x[i:j] Returns the slice described by i, j. The use of negative indices is not supported.""" - m = self._mask - dout = self._data[i:j] - if m is nomask: - return self.__class__(dout, fill_value=self._fill_value, - **self.options) - else: - return self.__class__(dout, mask=m[i:j], fill_value=self._fill_value, - **self.options) + return self.__getitem__(slice(i,j)) #........................ def __setslice__(self, i, j, value): """x.__setslice__(i, j, value) <==> x[i:j]=value -Sets a slice i:j to `value`. +Sets a slice i:j to `value`. If `value` is masked, masks those locations.""" - if self is masked: - #TODO: Well, maybe we could/should - raise MAError, "Cannot alter the 'masked' object." - #.... - (d, m) = (self._data, self._mask) - #.... - if value is masked: - if m is nomask: - m = make_mask_none(d.shape) - m[i:j] = True - self._mask = m - return - #.... - if m is nomask: - valmask = getmask(value) - valdata = filled(value) - d[i:j] = valdata - if valmask is not nomask: - m = make_mask_none(d.shape) - m[i:j] = valmask - elif not self._hardmask: - valmask = getmask(value) - valdata = filled(value) - d[i:j] = valdata - if valmask is nomask: - m[i:j] = False - else: - m[i:j] = valmask - else: - mindx = m[i:j] - value = masked_array(value, mask=mindx, keep_mask=True) - valmask = value._mask - if valmask is nomask: - d[i:j][~mindx] = filled(value) - elif valmask.size > 1: - d[i:j][~mindx] = value[~valmask] - m[i:j][valmask] = True - #..... - if not m.any(): - self._mask = nomask - else: - self._mask = m + self.__setitem__(slice(i,j), value) #............................................ # If we don't want to crash the performance, we better leave __getattribute__ alone... # def __getattribute__(self, name): # """x.__getattribute__('name') = x.name -#Returns the chosen attribute. +#Returns the chosen attribute. #If the attribute cannot be directly accessed, checks the _data section. # """ # try: @@ -1152,7 +1226,7 @@ #............................................ def __str__(self): """x.__str__() <==> str(x) -Calculates the string representation, using masked for fill if it is enabled. +Calculates the string representation, using masked for fill if it is enabled. Otherwise, fills with fill value. """ if masked_print_option.enabled(): @@ -1170,14 +1244,14 @@ # res = numeric.empty(self._data.shape, object_) # numeric.putmask(res,~m,self._data) res = self._data.astype("|O8") - res[self._mask] = f + res[m] = f else: res = self.filled(self.fill_value) return str(res) def __repr__(self): """x.__repr__() <==> repr(x) -Calculates the repr representation, using masked for fill if it is enabled. +Calculates the repr representation, using masked for fill if it is enabled. Otherwise fill with fill value. """ with_mask = """\ @@ -1208,87 +1282,65 @@ 'fill': str(self.fill_value), } #............................................ - def __abs__(self): - """x.__abs__() <==> abs(x) -Returns a masked array of the current subclass, with the new `_data` -the absolute of the inital `_data`. - """ - return self.__class__(self._data.__abs__(), mask=self._mask, - fill_value = self._fill_value, **self.options) - # - def __neg__(self): - """x.__abs__() <==> neg(x) -Returns a masked array of the current subclass, with the new `_data` -the negative of the inital `_data`.""" - try: - return self.__class__(self._data.__neg__(), mask=self._mask, - fill_value = self._fill_value, **self.options) - except MAError: - return negative(self) - # def __iadd__(self, other): "Adds other to self in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) + ndarray.__iadd__(self._data,other) m = getmask(other) - self._data += f if self._mask is nomask: self._mask = m elif m is not nomask: self._mask += m return self - # + #.... def __isub__(self, other): "Subtracts other from self in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) + ndarray.__isub__(self._data,other) m = getmask(other) - self._data -= f if self._mask is nomask: self._mask = m elif m is not nomask: self._mask += m return self - # + #.... def __imul__(self, other): "Multiplies self by other in place." - f = convert_typecode(filled(other, 1), self._data.dtype.char) + ndarray.__imul__(self._data,other) m = getmask(other) - self._data *= f if self._mask is nomask: self._mask = m elif m is not nomask: self._mask += m return self - # + #.... def __idiv__(self, other): "Divides self by other in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) - mo = getmask(other) - result = divide(self, masked_array(f, mask=mo)) - self._data = result._data - dm = result._mask - if dm is not self._mask: - self._mask = dm - return self + dom_mask = domain_safe_divide().__call__(self, filled(other,1)) + other_mask = getmask(other) + new_mask = mask_or(other_mask, dom_mask) + ndarray.__idiv__(self._data, other) + self._mask = mask_or(self._mask, new_mask) + return self + #.... + __add__ = _mathmethod('__add__') + __radd__ = _mathmethod('__add__') + __sub__ = _mathmethod('__sub__') + __rsub__ = _mathmethod('__rsub__') + __pow__ = _mathmethod('__pow__') + __mul__ = _mathmethod('__mul__', 1, 1) + __rmul__ = _mathmethod('__mul__', 1, 1) + __div__ = _mathmethod('__div__', 0, 1, domain_safe_divide()) + __rdiv__ = _mathmethod('__rdiv__', 1, 0, domain_safe_divide()) + __truediv__ = _mathmethod('__truediv__', 0, 1, domain_safe_divide()) + __rtruediv__ = _mathmethod('__rtruediv__', 1, 0, domain_safe_divide()) + __floordiv__ = _mathmethod('__floordiv__', 0, 1, domain_safe_divide()) + __rfloordiv__ = _mathmethod('__rfloordiv__', 1, 0, domain_safe_divide()) + __eq__ = _mathmethod('__eq__') + __ne__ = _mathmethod('__ne__') + __le__ = _mathmethod('__le__') + __lt__ = _mathmethod('__lt__') + __ge__ = _mathmethod('__ge__') + __gt__ = _mathmethod('__gt__') -# # -# def __eq__(self, other): -# return equal(self,other) -# -# def __ne__(self, other): -# return not_equal(self,other) -# -# def __lt__(self, other): -# return less(self,other) -# -# def __le__(self, other): -# return less_equal(self,other) -# -# def __gt__(self, other): -# return greater(self,other) -# -# def __ge__(self, other): -# return greater_equal(self,other) - #............................................ def __float__(self): "Converts self to float." @@ -1296,33 +1348,13 @@ warnings.warn("Warning: converting a masked element to nan.") return numpy.nan #raise MAError, 'Cannot convert masked element to a Python float.' - return float(self._data.item()) + return float(self.item()) def __int__(self): "Converts self to int." if self._mask is not nomask: raise MAError, 'Cannot convert masked element to a Python int.' - return int(self._data.item()) - - @property - def dtype(self): - """returns the data type of `_data`.""" - return self._data.dtype - - def astype (self, tc): - """Returns self as an array of given type. -Subclassing is preserved.""" - if tc == self._data.dtype: - return self - try: - return self.__class__(self, mask=self._mask, dtype=tc, copy=True, - **self.options) - except: -# d = self._data.astype(tc) - return self.__class__(self._data.astype(tc), mask=self._mask, - dtype=tc, **self.options) -# -# + return int(self.item()) #............................................ def harden_mask(self): "Forces the mask to hard" @@ -1330,19 +1362,11 @@ def soften_mask(self): "Forces the mask to soft" self._hardmask = False - #............................................ - #TODO: FIX THAT: THAT"S NOT A REAL FLATITER + #............................................ def _get_flat(self): """Calculates the flat value. """ - if self._mask is nomask: - return masked_array(self._data.ravel(), mask=nomask, copy=False, - fill_value = self._fill_value, - **self.options) - else: - return masked_array(self._data.ravel(), mask=self._mask.ravel(), - copy=False, fill_value = self._fill_value, - **self.options) + return flatiter(self) # def _set_flat (self, value): "x.flat = value" @@ -1350,107 +1374,85 @@ y[:] = value # flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") - # #............................................ - def _get_real(self): - "Returns the real part of a complex array." - return self.__class__(self._data.real, mask=self.mask, - fill_value = self._fill_value, **self.options) - def _set_real (self, value): - "Sets the real part of a complex array to `value`." - y = self.real - y[...] = value - - real = property(fget=_get_real, fset=_set_real, doc="Get it real!") - - def _get_imaginary(self): - "Returns the imaginary part of a complex array." - return self.__class__(self._data.imag, mask=self.mask, - fill_value = self._fill_value, **self.options) - - def _set_imaginary (self, value): - "Sets the imaginary part of a complex array to `value`." - y = self.imaginary - y[...] = value - - imag = property(fget=_get_imaginary, fset=_set_imaginary, - doc="Imaginary part.") - imaginary = imag - #............................................ def _get_mask(self): """Returns the current mask.""" return self._mask def _set_mask(self, mask): """Sets the mask to `mask`.""" - mask = make_mask(mask, copy=False, small_mask=True) + mask = make_mask(mask, copy=False, small_mask=self._smallmask) if mask is not nomask: - if mask.size != self._data.size: + if mask.size != self.size: raise ValueError, "Inconsistent shape between data and mask!" - if mask.shape != self._data.shape: - mask.shape = self._data.shape - self._mask = mask + if mask.shape != self.shape: + mask.shape = self.shape + self._mask = mask else: self._mask = nomask mask = property(fget=_get_mask, fset=_set_mask, doc="Mask") #............................................ + def _get_data(self): + "Returns the current data (as a view of the original underlying data)>" + return self.view(self._baseclass) + _data = property(fget=_get_data) + #............................................ def get_fill_value(self): "Returns the filling value." + if self._fill_value is None: + self._fill_value = default_fill_value(self) return self._fill_value - + def set_fill_value(self, value=None): - """Sets the filling value to `value`. + """Sets the filling value to `value`. If None, uses the default, based on the data type.""" if value is None: - value = default_fill_value(self._data) + value = default_fill_value(self) self._fill_value = value - + fill_value = property(fget=get_fill_value, fset=set_fill_value, doc="Filling value") - + def filled(self, fill_value=None): """Returns an array of the same class as `_data`, with masked values filled with `fill_value`. Subclassing is preserved. - + If `fill_value` is None, uses self.fill_value. """ - d = self._data m = self._mask if m is nomask: - return d + return self._data # if fill_value is None: - value = self.fill_value - else: - value = fill_value + fill_value = self.fill_value # if self is masked_singleton: - result = numeric.asanyarray(value) + result = numeric.asanyarray(fill_value) else: - result = d.copy() + result = self._data.copy() try: - result[m] = value + result[m] = fill_value except (TypeError, AttributeError): - value = numeric.array(value, dtype=object) - d = d.astype(object) - result = fromnumeric.choose(m, (d, value)) + fill_value = numeric.array(fill_value, dtype=object) + d = result.astype(object) + result = fromnumeric.choose(m, (d, fill_value)) except IndexError: #ok, if scalar - if d.shape: + if self._data.shape: raise elif m: - result = numeric.array(value, dtype=d.dtype) + result = numeric.array(fill_value, dtype=self.dtype) else: - result = d + result = self._data return result - + def compressed(self): "A 1-D array of all the non-masked data." - d = self._data.ravel() + d = self.ravel() if self._mask is nomask: return d else: - return d[~self._mask.ravel()] + return d[numeric.logical_not(d._mask)] #............................................ def count(self, axis=None): """Counts the non-masked elements of the array along a given axis, @@ -1458,7 +1460,7 @@ If `axis` is None, counts all the non-masked elements, and returns either a scalar or the masked singleton.""" m = self._mask - s = self._data.shape + s = self.shape ls = len(s) if m is nomask: if ls == 0: @@ -1466,12 +1468,12 @@ if ls == 1: return s[0] if axis is None: - return self._data.size + return self.size else: n = s[axis] t = list(s) del t[axis] - return numeric.ones(t) * n + return numeric.ones(t) * n n1 = fromnumeric.size(m, axis) n2 = m.astype(int_).sum(axis) if axis is None: @@ -1479,135 +1481,108 @@ else: return masked_array(n1 - n2) #............................................ - def _get_shape(self): - "Returns the current shape." - return self._data.shape +# def _get_shape(self): +# "Returns the current shape." +# return self._data.shape +# # +# def _set_shape (self, newshape): +# "Sets the array's shape." +# self._data.shape = newshape +# if self._mask is not nomask: +# #self._mask = self._mask.copy() +# self._mask.shape = newshape +# # +# shape = property(fget=_get_shape, fset=_set_shape, +# doc="Shape of the array, as a tuple.") # - def _set_shape (self, newshape): - "Sets the array's shape." - self._data.shape = newshape - if self._mask is not nomask: - #self._mask = self._mask.copy() - self._mask.shape = newshape - # - shape = property(fget=_get_shape, fset=_set_shape, - doc="Shape of the array, as a tuple.") - # - def _get_size(self): - "Returns the current size." - return self._data.size - size = property(fget=_get_size, - doc="Size (number of elements) of the array.") - # - def _get_ndim(self): - "Returns the number of dimensions." - return self._data.ndim - ndim = property(fget=_get_ndim, - doc="Number of dimensions of the array.") - # def reshape (self, *s): """Reshapes the array to shape s. -Returns a new masked array. +Returns a new masked array. If you want to modify the shape in place, please use `a.shape = s`""" + # TODO: Do we keep super, or reshape _data and take a view ? + result = super(MaskedArray, self).reshape(*s) if self._mask is not nomask: - return self.__class__(self._data.reshape(*s), - mask=self._mask.reshape(*s), - fill_value=self.fill_value, **self.options) - else: - return self.__class__(self._data.reshape(*s), - fill_value=self.fill_value, **self.options) + result._mask = self._mask.reshape(*s) + return result # - def repeat(self, repeats, axis=None): - """Repeat elements of `a` `repeats` times along `axis`. -`repeats` is a sequence of length `a.shape[axis]` telling how many times -each element should be repeated. -The mask is repeated accordingly. - """ - f = self.filled() - if isinstance(repeats, types.IntType): - if axis is None: - num = f.size - else: - num = f.shape[axis] - repeats = tuple([repeats]*num) - - m = self._mask - if m is not nomask: - m = fromnumeric.repeat(m, repeats, axis) - d = fromnumeric.repeat(f, repeats, axis) - return self.__class__(d, mask=m, fill_value=self.fill_value, - **self.options) + repeat = _arraymethod('repeat') # def resize(self, newshape, refcheck=True, order=False): - """Attempts to modify size and shape of self inplace. - The array must own its own memory and not be referenced by other arrays. + """Attempts to modify size and shape of self inplace. + The array must own its own memory and not be referenced by other arrays. Returns None. """ try: - self._data.resize(newshape,) + self._data.resize(newshape, refcheck, order) if self.mask is not nomask: - self._mask.resize(newshape,) + self._mask.resize(newshape, refcheck, order) except ValueError: - msg = "Cannot resize an array that has been referenced or "+\ - "is referencing another array in this way.\n"+\ - "Use the resize function." - raise ValueError, msg + raise ValueError("Cannot resize an array that has been referenced " + "or is referencing another array in this way.\n" + "Use the resize function.") return None # - def flatten(self): - """Flattens the array in place. - """ - flatsize = self.size - self._data.resize((flatsize,)) - if self.mask is not nomask: - self._mask.resize((flatsize,)) - return self - + flatten = _arraymethod('flatten') # def put(self, indices, values, mode='raise'): """Sets storage-indexed locations to corresponding values. a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices. -`values` can be scalar or an array shorter than indices, and it will be repeat, +`values` can be scalar or an array shorter than indices, and it will be repeated, if necessary. If `values` has some masked values, the initial mask is updated in consequence, else the corresponding values are unmasked. """ - #TODO: Check that - (d, m) = (self._data, self._mask) - ind = filled(indices) - v = filled(values) - d.put(ind, v, mode=mode) - if m is not nomask: - if getmask(values) is not nomask: - m.put(ind, values._mask, mode=mode) + m = self._mask + # Hard mask: Get rid of the values/indices that fall on masked data + if self._hardmask and self._mask is not nomask: + mask = self._mask[indices] + indices = numeric.asarray(indices) + values = numeric.asanyarray(values) + values.resize(indices.shape) + indices = indices[~mask] + values = values[~mask] + #.... + self._data.put(indices, values, mode=mode) + #.... + if m is nomask: + m = getmask(values) + else: + m = m.copy() + if getmask(values) is nomask: + m.put(indices, False, mode=mode) else: - m.put(ind, False, mode=mode) - self._mask = make_mask(m, copy=False, small_mask=True) + m.put(indices, values._mask, mode=mode) + m = make_mask(m, copy=False, small_mask=True) + self._mask = m #............................................ def ids (self): - """Return the ids of the data and mask areas.""" - return (id(self._data), id(self._mask)) + """Return the address of the data and mask areas.""" + return (self.ctypes.data, self._mask.ctypes.data) #............................................ - def all(self, axis=None): + def all(self, axis=None, out=None): """a.all(axis) returns True if all entries along the axis are True. Returns False otherwise. If axis is None, uses the flatten array. Masked data are considered as True during computation. Outputs a masked array, where the mask is True if all data are masked along the axis. + Note: the out argument is not really operational... """ - d = filled(self, True).all(axis) - m = self._mask.all(axis) - return self.__class__(d, mask=m, dtype=bool_, - fill_value=self._fill_value, **self.options) - def any(self, axis=None): + d = self.filled(True).all(axis=axis, out=out).view(type(self)) + if d.ndim > 0: + d._set_mask(self._mask.all(axis)) + return d + + def any(self, axis=None, out=None): """a.any(axis) returns True if some or all entries along the axis are True. Returns False otherwise. If axis is None, uses the flatten array. Masked data are considered as False during computation. Outputs a masked array, where the mask is True if all data are masked along the axis. + Note: the out argument is not really operational... """ - d = filled(self, False).any(axis) - m = self._mask.all(axis) - return self.__class__(d, mask=m, dtype=bool_, - fill_value=self._fill_value, **self.options) + d = self.filled(False).any(axis=axis, out=out).view(type(self)) + if d.ndim > 0: + d._set_mask(self._mask.all(axis)) + return d + def nonzero(self): """a.nonzero() returns a tuple of arrays @@ -1627,97 +1602,74 @@ """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None) Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`. """ - #TODO: What are we doing with `out`? - (d,m) = (self._data, self._mask) + # TODO: What are we doing with `out`? + m = self._mask if m is nomask: - return d.trace(offset=offset, axis1=axis1, axis2=axis2, - out=out).astype(dtype) + result = super(MaskedArray, self).trace(offset=offset, axis1=axis1, + axis2=axis2, out=out) + return result.astype(dtype) else: - D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2, - ).astype(dtype) - return D.sum(axis=None) + D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2) + return D.astype(dtype).sum(axis=None) #............................................ def sum(self, axis=None, dtype=None): - """a.sum(axis=None, dtype=None) + """a.sum(axis=None, dtype=None) Sums the array `a` over the given axis `axis`. Masked values are set to 0. If `axis` is None, applies to a flattened version of the array. """ if self._mask is nomask: -# if axis is None: -# return self._data.sum(None, dtype=dtype) - return self.__class__(self._data.sum(axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) + mask = nomask else: -# if axis is None: -# return self.filled(0).sum(None, dtype=dtype) - return self.__class__(self.filled(0).sum(axis, dtype=dtype), - mask=self._mask.all(axis), - fill_value=self.fill_value, **self.options) - + mask = self._mask.all(axis) + if (not mask.ndim) and mask: + return masked + result = self.filled(0).sum(axis, dtype=dtype).view(type(self)) + if result.ndim > 0: + result._set_mask(mask) + return result + def cumsum(self, axis=None, dtype=None): """a.cumprod(axis=None, dtype=None) -Returns the cumulative sum of the elements of array `a` along the given axis `axis`. +Returns the cumulative sum of the elements of array `a` along the given axis `axis`. Masked values are set to 0. If `axis` is None, applies to a flattened version of the array. """ - if self._mask is nomask: -# if axis is None: -# return self._data.cumsum(None, dtype=dtype) - return self.__class__(self._data.cumsum(axis=axis, dtype=dtype), - fill_value=self.fill_value, **self.options) - else: -# if axis is None: -# return self.filled(0).cumsum(None, dtype=dtype) - return self.__class__(self.filled(0).cumsum(axis=axis, dtype=dtype), - mask=self._mask, fill_value=self.fill_value, - **self.options) - + result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self)) + result._set_mask(self.mask) + return result + def prod(self, axis=None, dtype=None): """a.prod(axis=None, dtype=None) -Returns the product of the elements of array `a` along the given axis `axis`. +Returns the product of the elements of array `a` along the given axis `axis`. Masked elements are set to 1. If `axis` is None, applies to a flattened version of the array. """ if self._mask is nomask: -# if axis is None: -# return self._data.prod(None, dtype=dtype) - return self.__class__(self._data.prod(axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) -# return self.__class__(self._data.prod(axis=axis, dtype=dtype)) + mask = nomask else: -# if axis is None: -# return self.filled(1).prod(None, dtype=dtype) - return self.__class__(self.filled(1).prod(axis=axis, dtype=dtype), - mask=self._mask.all(axis), - fill_value=self.fill_value, - **self.options) + mask = self._mask.all(axis) + if (not mask.ndim) and mask: + return masked + result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self)) + if result.ndim: + result._set_mask(mask) + return result product = prod - + def cumprod(self, axis=None, dtype=None): """a.cumprod(axis=None, dtype=None) -Returns the cumulative product of ethe lements of array `a` along the given axis `axis`. +Returns the cumulative product of ethe lements of array `a` along the given axis `axis`. Masked values are set to 1. If `axis` is None, applies to a flattened version of the array. """ - if self._mask is nomask: -# if axis is None: -# return self._data.cumprod(None, dtype=dtype) - return self.__class__(self._data.cumprod(axis=axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) - else: -# if axis is None: -# return self.filled(1).cumprod(None, dtype=dtype) - return self.__class__(self.filled(1).cumprod(axis=axis, dtype=dtype), - mask=self._mask, - fill_value=self.fill_value, **self.options) - + result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self)) + result._set_mask(self.mask) + return result + def mean(self, axis=None, dtype=None): """a.mean(axis=None, dtype=None) - + Averages the array over the given axis. If the axis is None, averages over all dimensions of the array. Equivalent to @@ -1729,30 +1681,22 @@ Returns a masked array, of the same class as a. """ if self._mask is nomask: -# if axis is None: -# return self._data.mean(axis=None, dtype=dtype) - return self.__class__(self._data.mean(axis=axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) + return super(MaskedArray, self).mean(axis=axis, dtype=dtype) else: - dsum = fromnumeric.sum(self.filled(0), axis=axis, dtype=dtype) + dsum = self.sum(axis=axis, dtype=dtype) cnt = self.count(axis=axis) - mask = self._mask.all(axis) - if axis is None and mask: - return masked - return self.__class__(dsum*1./cnt, mask=mask, - fill_value=self.fill_value, **self.options) - + return dsum*1./cnt + def anom(self, axis=None, dtype=None): """a.anom(axis=None, dtype=None) Returns the anomalies, or deviation from the average. - """ + """ m = self.mean(axis, dtype) if not axis: return (self - m) else: return (self - expand_dims(m,axis)) - + def var(self, axis=None, dtype=None): """a.var(axis=None, dtype=None) Returns the variance, a measure of the spread of a distribution. @@ -1761,23 +1705,18 @@ i.e. var = mean((x - x.mean())**2). """ if self._mask is nomask: -# if axis is None: -# return self._data.var(axis=None, dtype=dtype) - return self.__class__(self._data.var(axis=axis, dtype=dtype), - mask=nomask, - fill_value=self.fill_value, **self.options) + # TODO: Do we keep super, or var _data and take a view ? + return super(MaskedArray, self).var(axis=axis, dtype=dtype) else: cnt = self.count(axis=axis) danom = self.anom(axis=axis, dtype=dtype) danom *= danom dvar = danom.sum(axis) / cnt # dvar /= cnt - if axis is None: - return dvar - return self.__class__(dvar, - mask=mask_or(self._mask.all(axis), (cnt==1)), - fill_value=self.fill_value, **self.options) - + if axis is not None: + dvar._mask = mask_or(self._mask.all(axis), (cnt==1)) + return dvar + def std(self, axis=None, dtype=None): """a.std(axis=None, dtype=None) Returns the standard deviation, a measure of the spread of a distribution. @@ -1786,20 +1725,14 @@ deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)). """ dvar = self.var(axis,dtype) - if axis is None: - if dvar is masked: - return masked - else: - # Should we use umath.sqrt instead ? - return sqrt(dvar) - return self.__class__(sqrt(dvar._data), mask=dvar._mask, - dtype = self.dtype, - fill_value=self.fill_value, **self.options) + if axis is not None or dvar is not masked: + dvar = sqrt(dvar) + return dvar #............................................ def argsort(self, axis=None, fill_value=None, kind='quicksort', order=None): """Returns an array of indices that sort 'a' along the specified axis. - Masked values are filled beforehand to `fill_value`. + Masked values are filled beforehand to `fill_value`. If `fill_value` is None, uses the default for the data type. Returns a numpy array. @@ -1834,16 +1767,14 @@ less space than sorts along other axis. """ if fill_value is None: - fill_value = default_fill_value(self._data) + fill_value = default_fill_value(self) d = self.filled(fill_value) - if axis is None: - return d.argsort(kind=kind, order=order) - return d.argsort(axis, kind=kind, order=order) - + return d.argsort(axis=axis, kind=kind, order=order) + #........................ def argmin(self, axis=None, fill_value=None): - """Returns the array of indices for the minimum values of `a` along the + """Returns a ndarray of indices for the minimum values of `a` along the specified axis. - Masked values are treated as if they had the value `fill_value`. + Masked values are treated as if they had the value `fill_value`. If `fill_value` is None, the default for the data type is used. Returns a numpy array. @@ -1854,16 +1785,14 @@ Default filling value. If None, uses the data type default. """ if fill_value is None: - fill_value = default_fill_value(self._data) + fill_value = minimum_fill_value(self) d = self.filled(fill_value) - if axis is None: - return d.argmin() return d.argmin(axis) - + #........................ def argmax(self, axis=None, fill_value=None): - """Returns the array of indices for the maximum values of `a` along the + """Returns the array of indices for the maximum values of `a` along the specified axis. - Masked values are treated as if they had the value `fill_value`. + Masked values are treated as if they had the value `fill_value`. If `fill_value` is None, the default for the data type is used. Returns a numpy array. @@ -1874,17 +1803,12 @@ Default filling value. If None, uses the data type default. """ if fill_value is None: - fill_value = default_fill_value(self._data) - try: - fill_value = - fill_value - except: - pass + fill_value = maximum_fill_value(self._data) d = self.filled(fill_value) - if axis is None: - return d.argmax() return d.argmax(axis) - - def sort(self, axis=-1, kind='quicksort', order=None, endwith=True): + + def sort(self, axis=-1, kind='quicksort', order=None, + endwith=True, fill_value=None): """ Sort a along the given axis. @@ -1925,26 +1849,123 @@ and use less space than sorts along other axis. """ - - if endwith: - filler = minimum_fill_value(self.dtype) + if fill_value is None: + if endwith: + filler = minimum_fill_value(self) + else: + filler = maximum_fill_value(self) else: - filler = maximum_fill_value(self.dtype) + filler = fill_value indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order) - self._data = self._data[indx] - m = self._mask - if m is not nomask: - self._mask = m[indx] - return + self[:] = self[indx] + return #............................................ + def min(self, axis=None, fill_value=None): + """Returns the minimum/a along the given axis. +If `axis` is None, applies to the flattened array. Masked values are filled +with `fill_value` during processing. If `fill_value is None, it is set to the +maximum_fill_value corresponding to the data type.""" + mask = self._mask + # Check all/nothing case ...... + if mask is nomask: + return super(MaskedArray, self).min(axis=axis) + elif (not mask.ndim) and mask: + return masked + # Get the mask ................ + if axis is None: + mask = umath.logical_and.reduce(mask.flat) + else: + mask = umath.logical_and.reduce(mask, axis=axis) + # Get the fil value ........... + if fill_value is None: + fill_value = minimum_fill_value(self) + # Get the data ................ + result = self.filled(fill_value).min(axis=axis).view(type(self)) + if result.ndim > 0: + result._mask = mask + return result + #........................ + def max(self, axis=None, fill_value=None): + """Returns the maximum/a along the given axis. +If `axis` is None, applies to the flattened array. Masked values are filled +with `fill_value` during processing. If `fill_value is None, it is set to the +maximum_fill_value corresponding to the data type.""" + mask = self._mask + # Check all/nothing case ...... + if mask is nomask: + return super(MaskedArray, self).max(axis=axis) + elif (not mask.ndim) and mask: + return masked + # Check teh mask .............. + if axis is None: + mask = umath.logical_and.reduce(mask.flat) + else: + mask = umath.logical_and.reduce(mask, axis=axis) + # Get the fill value .......... + if fill_value is None: + fill_value = maximum_fill_value(self) + # Get the data ................ + result = self.filled(fill_value).max(axis=axis).view(type(self)) + if result.ndim > 0: + result._mask = mask + return result + #........................ + def ptp(self, axis=None, fill_value=None): + """Returns the visible data range (max-min) along the given axis. +If the axis is `None`, applies on a flattened array. Masked values are filled +with `fill_value` for processing. If `fill_value` is None, the maximum is uses +the maximum default, the minimum uses the minimum default.""" + return self.max(axis, fill_value) - self.min(axis, fill_value) + + # Array methods --------------------------------------- + conj = conjugate = _arraymethod('conjugate') + copy = _arraymethod('copy') + diagonal = _arraymethod('diagonal') + take = _arraymethod('take') + ravel = _arraymethod('ravel') + transpose = _arraymethod('transpose') + T = property(fget=lambda self:self.transpose()) + swapaxes = _arraymethod('swapaxes') + clip = _arraymethod('clip', onmask=False) + compress = _arraymethod('compress') + copy = _arraymethod('copy') + squeeze = _arraymethod('squeeze') + #-------------------------------------------- + def tolist(self, fill_value=None): + """Copies the data portion of the array to a hierarchical python list and + returns that list. Data items are converted to the nearest compatible Python + type. Masked values are filled with `fill_value`""" + return self.filled(fill_value).tolist() + #........................ + def tostring(self, fill_value=None): + """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string. + + Keyword arguments: + order : order of the data item in the copy {"C","F","A"} (default "C") + fill_value : value used in lieu of missing data + + Construct a Python string containing the raw bytes in the array. The order + of the data in arrays with ndim > 1 is specified by the 'order' keyword and + this keyword overrides the order of the array. The + choices are: + + "C" -- C order (row major) + "Fortran" -- Fortran order (column major) + "Any" -- Current order of array. + None -- Same as "Any" + + Masked data are filled with fill_value. If fill_value is None, the data-type- + dependent default is used.""" + return self.filled(fill_value).tostring() + #-------------------------------------------- # Backwards Compatibility. Heck... @property def data(self): - """Returns the `_data` part of the MaskedArray. + """Returns the `_data` part of the MaskedArray. You should really use `_data` instead...""" return self._data def raw_data(self): - """Returns the `_data` part of the MaskedArray. + """Returns the `_data` part of the MaskedArray. You should really use `_data` instead...""" return self._data @@ -1954,7 +1975,7 @@ #class _arithmethods: # """Defines a wrapper for arithmetic methods. -#Instead of directly calling a ufunc, the corresponding method of the `array._data` +#Instead of directly calling a ufunc, the corresponding method of the `array._data` #object is called instead. # """ # def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): @@ -1963,7 +1984,7 @@ # - `methodname` (String) : Method name. # - `fill_self` (Float *[0]*) : Fill value for the instance. # - `fill_other` (Float *[0]*) : Fill value for the target. -# - `domain` (Domain object *[None]*) : Domain of non-validity. +# - `domain` (Domain object *[None]*) : Domain of non-validity. # """ # self.methodname = methodname # self.fill_self = fill_self @@ -1983,7 +2004,7 @@ # else: # domain = self.domain(target, base) # if domain.any(): -# #If `other` is a subclass of ndarray, `filled` must have the +# #If `other` is a subclass of ndarray, `filled` must have the # # same subclass, else we'll lose some info. # #The easiest then is to fill `target` instead of creating # # a pure ndarray. @@ -1992,155 +2013,25 @@ # if target is other: # # We don't want to modify other: let's copy target, then # target = target.copy() -# target[:] = numeric.where(fromnumeric.asarray(domain), +# target[:] = numeric.where(fromnumeric.asarray(domain), # self.fill_other, target) # else: -# target = numeric.where(fromnumeric.asarray(domain), +# target = numeric.where(fromnumeric.asarray(domain), # self.fill_other, target) # m_other = mask_or(m_other, domain) -# m = mask_or(m_self, m_other) -# method = getattr(base, self.methodname) +# m = mask_or(m_self, m_other) +# method = getattr(base, self.methodname) # return instance.__class__(method(target, *args), mask=m) # # # def patch(self): # """Applies the method `func` from class `method` to MaskedArray""" # return types.MethodType(self,None,MaskedArray) #.............................................................................. -class _arithmethods(object): - """Defines a wrapper for arithmetic methods. -Instead of directly calling a ufunc, the corresponding method of the `array._data` -object is called instead. - """ - def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): - """ -:Parameters: - - `methodname` (String) : Method name. - - `fill_self` (Float *[0]*) : Fill value for the instance. - - `fill_other` (Float *[0]*) : Fill value for the target. - - `domain` (Domain object *[None]*) : Domain of non-validity. - """ - self.methodname = methodname - self.fill_self = fill_self - self.fill_other = fill_other - self.domain = domain - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.methodname).__doc__ - except: - return getattr(numpy, self.methodname).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - m_self = instance._mask - m_other = getmask(other) - base = filled(instance,self.fill_self) - target = filled(other, self.fill_other) - if self.domain is not None: - # We need to force the domain to a ndarray only. - if self.fill_other > self.fill_self: - domain = self.domain(base, target) - else: - domain = self.domain(target, base) - if domain.any(): - #If `other` is a subclass of ndarray, `filled` must have the - # same subclass, else we'll lose some info. - #The easiest then is to fill `target` instead of creating - # a pure ndarray. - #Oh, and we better make a copy! - if isinstance(other, ndarray): - if target is other or target is base: - # We don't want to modify other: let's copy target, then - # Same if target us base, instead... - target = target.copy() - target[:] = numeric.where(fromnumeric.asarray(domain), - self.fill_other, target) - else: - target = numeric.where(fromnumeric.asarray(domain), - self.fill_other, target) - m_other = mask_or(m_other, domain) - m = mask_or(m_self, m_other) - method = getattr(base, self.methodname) - return instance.__class__(method(target, *args), mask=m, - fill_value=instance.fill_value, - **instance.options) -#...................................... -class _compamethods(object): - """Defines comparison methods (eq, ge, gt...). -Instead of calling a ufunc, the method of the masked object is called. - """ - def __init__ (self, methodname, fill_self=0, fill_other=0): - """ -:Parameters: - - `methodname` (String) : Method name. - - `fill_self` (Float *[0]*) : Fill value for the instance. - - `fill_other` (Float *[0]*) : Fill value for the target. - - `domain` (Domain object *[None]*) : Domain of non-validity. - """ - self.methodname = methodname - self.fill_self = fill_self - self.fill_other = fill_other - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.methodname).__doc__ - except: - return getattr(numpy, self.methodname).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - m = mask_or(instance._mask, getmask(other), small_mask=False) - base = instance.filled(self.fill_self) - target = filled(other, self.fill_other) - method = getattr(base, self.methodname) - return instance.__class__(method(target, *args), mask=m, - **instance.options) -#.......................................................... -MaskedArray.__add__ = _arithmethods('__add__') -MaskedArray.__radd__ = _arithmethods('__add__') -MaskedArray.__sub__ = _arithmethods('__sub__') -MaskedArray.__rsub__ = _arithmethods('__rsub__') -MaskedArray.__pow__ = _arithmethods('__pow__') -MaskedArray.__mul__ = _arithmethods('__mul__', 1, 1) -MaskedArray.__rmul__ = _arithmethods('__mul__', 1, 1) -MaskedArray.__div__ = _arithmethods('__div__', 0, 1, - domain_safe_divide()) -MaskedArray.__rdiv__ = _arithmethods('__rdiv__', 1, 0, - domain_safe_divide()) -MaskedArray.__truediv__ = _arithmethods('__truediv__', 0, 1, - domain_safe_divide()) -MaskedArray.__rtruediv__ = _arithmethods('__rtruediv__', 1, 0, - domain_safe_divide()) -MaskedArray.__floordiv__ = _arithmethods('__floordiv__', 0, 1, - domain_safe_divide()) -MaskedArray.__rfloordiv__ = _arithmethods('__rfloordiv__', 1, 0, - domain_safe_divide()) -MaskedArray.__eq__ = _compamethods('__eq__') -MaskedArray.__ne__ = _compamethods('__ne__') -MaskedArray.__le__ = _compamethods('__le__') -MaskedArray.__lt__ = _compamethods('__lt__') -MaskedArray.__ge__ = _compamethods('__ge__') -MaskedArray.__gt__ = _compamethods('__gt__') + #####-------------------------------------------------------------------------- #---- --- Shortcuts --- #####--------------------------------------------------------------------------- -def isMaskedArray (x): +def isMaskedArray(x): "Is x a masked array, that is, an instance of MaskedArray?" return isinstance(x, MaskedArray) isarray = isMaskedArray @@ -2150,15 +2041,16 @@ masked = masked_singleton masked_array = MaskedArray -def array(data, dtype=None, copy=False, order=False, mask=nomask, +def array(data, dtype=None, copy=False, order=False, mask=nomask, keep_mask=True, small_mask=True, hard_mask=None, fill_value=None): - """array(data, dtype=None, copy=True, order=False, mask=nomask, + """array(data, dtype=None, copy=True, order=False, mask=nomask, keep_mask=True, small_mask=True, fill_value=None) Acts as shortcut to MaskedArray, with options in a different order for convenience. And backwards compatibility... """ - return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, - keep_mask=keep_mask, small_mask=small_mask, + #TODO: we should try to put 'order' somwehere + return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, + keep_mask=keep_mask, small_mask=small_mask, hard_mask=hard_mask, fill_value=fill_value) def is_masked(x): @@ -2181,10 +2073,10 @@ # #If `onmask` is True, the new mask is the output of the method calld on the initial mask. #If `onmask` is False, the new mask is just a reference to the initial mask. -# +# #:Parameters: # `funcname` : String -# Name of the function to apply on data. +# Name of the function to apply on data. # `onmask` : Boolean *[True]* # Whether the mask must be processed also (True) or left alone (False). # """ @@ -2202,13 +2094,13 @@ # return C(getattr(d,methodname).__call__(*args, **params), # mask=getattr(m,methodname)(*args, **params) ) # else: -# return C(getattr(d,methodname).__call__(*args, **params), mask=m) -# +# return C(getattr(d,methodname).__call__(*args, **params), mask=m) +# # def patch(self): # "Adds the new method to MaskedArray." # return types.MethodType(self, None, MaskedArray) ##...................................... -#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() +#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() #MaskedArray.diagonal = _arraymethod('diagonal').patch() #MaskedArray.take = _arraymethod('take').patch() #MaskedArray.ravel = _arraymethod('ravel').patch() @@ -2220,112 +2112,39 @@ #MaskedArray.resize = _arraymethod('resize').patch() #MaskedArray.copy = _arraymethod('copy').patch() -class _arraymethod(object): - """Defines a wrapper for basic array methods. -Upon call, returns a masked array, where the new `_data` array is the output -of the corresponding method called on the original `_data`. -If `onmask` is True, the new mask is the output of the method calld on the initial mask. -If `onmask` is False, the new mask is just a reference to the initial mask. - -:Parameters: - `funcname` : String - Name of the function to apply on data. - `onmask` : Boolean *[True]* - Whether the mask must be processed also (True) or left alone (False). - """ - def __init__(self, funcname, onmask=True): - self._name = funcname - self._onmask = onmask - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self._name).__doc__ - except: - return getattr(numpy, self._name).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__(self, *args, **params): - methodname = self._name - obj = self.obj - (d, m) = (obj._data, obj._mask) - (t, f) = (obj.dtype, obj._fill_value) - C = self.obj.__class__ - if m is nomask: - return C(getattr(d,methodname).__call__(*args, **params), - dtype=t, fill_value=f) - elif self._onmask: - return C(getattr(d,methodname).__call__(*args, **params), - mask=getattr(m,methodname)(*args, **params), - dtype=t, fill_value=f, **obj.options) - else: - return C(getattr(d,methodname).__call__(*args, **params), mask=m, - dtype=t, fill_value=f, **obj.options) -#...................................... -MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate') -MaskedArray.copy = _arraymethod('copy') -MaskedArray.diagonal = _arraymethod('diagonal') -MaskedArray.take = _arraymethod('take') -MaskedArray.ravel = _arraymethod('ravel') -MaskedArray.transpose = _arraymethod('transpose') -MaskedArray.T = property(fget=lambda self:self.transpose()) -MaskedArray.swapaxes = _arraymethod('swapaxes') -MaskedArray.clip = _arraymethod('clip', onmask=False) -MaskedArray.compress = _arraymethod('compress') -MaskedArray.copy = _arraymethod('copy') -MaskedArray.squeeze = _arraymethod('squeeze') -#####-------------------------------------------------------------------------- +#####--------------------------------------------------------------------------- #---- --- Extrema functions --- -#####-------------------------------------------------------------------------- -class _minimum_operation: - "Object to calculate minima" - def __init__ (self): - """minimum(a, b) or minimum(a) -In one argument case, returns the scalar minimum. - """ - pass - #......... - def __call__ (self, a, b=None): - "Execute the call behavior." +#####--------------------------------------------------------------------------- +class _extrema_operation(object): + def __call__(self, a, b=None): + "Executes the call behavior." if b is None: - m = getmask(a) - if m is nomask: - d = amin(filled(a).ravel()) - return d - ac = a.compressed() - if len(ac) == 0: - return masked - else: - return amin(ac) - else: - return where(less(a, b), a, b) + return self.reduce(a) + return where(self.compare(a, b), a, b) #......... - def reduce(self, target, axis=0): - """Reduces `target` along the given `axis`.""" + def reduce(self, target, axis=None): + """Reduces target along the given axis.""" m = getmask(target) + if axis is not None: + kargs = { 'axis' : axis } + else: + kargs = {} + if m is nomask: - t = filled(target) - return masked_array (umath.minimum.reduce (t, axis)) + t = self.ufunc.reduce(target, **kargs) else: - t = umath.minimum.reduce(filled(target, minimum_fill_value(target)), - axis) - m = umath.logical_and.reduce(m, axis) -# return masked_array(t, mask=m, fill_value=get_fill_value(target)) - try: - return target.__class__(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - except AttributeError: - return masked_array(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) + target = target.filled(self.fill_value_func(target)).view(type(target)) + t = self.ufunc.reduce(target, **kargs) + m = umath.logical_and.reduce(m, **kargs) + if hasattr(t, '_mask'): + t._mask = m + elif m: + t = masked + return t #......... - def outer(self, a, b): + def outer (self, a, b): "Returns the function applied to the outer product of a and b." ma = getmask(a) mb = getmask(b) @@ -2335,9 +2154,32 @@ ma = getmaskarray(a) mb = getmaskarray(b) m = logical_or.outer(ma, mb) - d = umath.minimum.outer(filled(a), filled(b)) - return masked_array(d, mask=m) - + result = self.ufunc.outer(filled(a), filled(b)) + result._mask = m + return result +#............................ +class _minimum_operation(_extrema_operation): + "Object to calculate minima" + def __init__ (self): + """minimum(a, b) or minimum(a) +In one argument case, returns the scalar minimum. + """ + self.ufunc = umath.minimum + self.afunc = amin + self.compare = less + self.fill_value_func = minimum_fill_value +#............................ +class _maximum_operation(_extrema_operation): + "Object to calculate maxima" + def __init__ (self): + """maximum(a, b) or maximum(a) + In one argument case returns the scalar maximum. + """ + self.ufunc = umath.maximum + self.afunc = amax + self.compare = greater + self.fill_value_func = maximum_fill_value +#.......................................................... def min(array, axis=None, out=None): """Returns the minima along the given axis. If `axis` is None, applies to the flattened array.""" @@ -2347,60 +2189,7 @@ return minimum(array) else: return minimum.reduce(array, axis) -#................................................ -class _maximum_operation: - "Object to calculate maxima" - def __init__ (self): - """maximum(a, b) or maximum(a) - In one argument case returns the scalar maximum. - """ - pass - #......... - def __call__ (self, a, b=None): - "Executes the call behavior." - if b is None: - m = getmask(a) - if m is nomask: - d = amax(filled(a).ravel()) - return d - ac = a.compressed() - if len(ac) == 0: - return masked - else: - return amax(ac) - else: - return where(greater(a, b), a, b) - #......... - def reduce (self, target, axis=0): - """Reduces target along the given axis.""" - m = getmask(target) - if m is nomask: - t = filled(target) - return masked_array(umath.maximum.reduce (t, axis)) - else: - t = umath.maximum.reduce(filled(target, maximum_fill_value(target)), - axis) - m = umath.logical_and.reduce(m, axis) - try: - return target.__class__(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - except AttributeError: - return masked_array(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - #......... - def outer (self, a, b): - "Returns the function applied to the outer product of a and b." - ma = getmask(a) - mb = getmask(b) - if ma is nomask and mb is nomask: - m = nomask - else: - ma = getmaskarray(a) - mb = getmaskarray(b) - m = logical_or.outer(ma, mb) - d = umath.maximum.outer(filled(a), filled(b)) - return masked_array(d, mask=m) - +#............................ def max(obj, axis=None, out=None): """Returns the maxima along the given axis. If `axis` is None, applies to the flattened array.""" @@ -2410,18 +2199,15 @@ return maximum(obj) else: return maximum.reduce(obj, axis) -#................................................ +#............................. def ptp(obj, axis=None): """a.ptp(axis=None) = a.max(axis)-a.min(axis)""" try: return obj.max(axis)-obj.min(axis) except AttributeError: return max(obj, axis=axis) - min(obj, axis=axis) -#................................................ -MaskedArray.min = min -MaskedArray.max = max -MaskedArray.ptp = ptp + #####--------------------------------------------------------------------------- #---- --- Definition of functions from the corresponding methods --- #####--------------------------------------------------------------------------- @@ -2438,19 +2224,20 @@ return getattr(MaskedArray, self._methodname).__doc__ except: return getattr(numpy, self._methodname).__doc__ - def __call__(self, x, *args, **params): - if isinstance(x, MaskedArray): - return getattr(x, self._methodname).__call__(*args, **params) - #FIXME: As x is not a MaskedArray, we transform it to a ndarray with asarray - #FIXME: ... and call the corresponding method. - #FIXME: Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2))) - #FIXME: we end up with a "SystemError: NULL result without error in PyObject_Call" - #FIXME: A dirty trick is then to call the initial numpy function... - method = getattr(fromnumeric.asarray(x), self._methodname) + def __call__(self, a, *args, **params): + if isinstance(a, MaskedArray): + return getattr(a, self._methodname).__call__(*args, **params) + #FIXME ---- + #As x is not a MaskedArray, we transform it to a ndarray with asarray + #... and call the corresponding method. + #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2))) + #we end up with a "SystemError: NULL result without error in PyObject_Call" + #A dirty trick is then to call the initial numpy function... + method = getattr(fromnumeric.asarray(a), self._methodname) try: return method(*args, **params) except SystemError: - return getattr(numpy,self._methodname).__call__(x, *args, **params) + return getattr(numpy,self._methodname).__call__(a, *args, **params) all = _frommethod('all') anomalies = anom = _frommethod('anom') @@ -2494,9 +2281,9 @@ return masked_array(umath.power(fa, fb), m) #.............................................................................. -def argsort(a, axis=None, kind='quicksort', fill_value=None): +def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None): """Returns an array of indices that sort 'a' along the specified axis. - Masked values are filled beforehand to `fill_value`. + Masked values are filled beforehand to `fill_value`. If `fill_value` is None, uses the default for the data type. Returns a numpy array. @@ -2534,13 +2321,13 @@ fill_value = default_fill_value(a) d = filled(a, fill_value) if axis is None: - return d.argsort(kind=kind) - return d.argsort(axis, kind) + return d.argsort(kind=kind, order=order) + return d.argsort(axis, kind=kind, order=order) def argmin(a, axis=None, fill_value=None): - """Returns the array of indices for the minimum values of `a` along the + """Returns the array of indices for the minimum values of `a` along the specified axis. - Masked values are treated as if they had the value `fill_value`. + Masked values are treated as if they had the value `fill_value`. If `fill_value` is None, the default for the data type is used. Returns a numpy array. @@ -2553,14 +2340,12 @@ if fill_value is None: fill_value = default_fill_value(a) d = filled(a, fill_value) - if axis is None: - return d.argmin(axis=None) return d.argmin(axis=axis) def argmax(a, axis=None, fill_value=None): - """Returns the array of indices for the maximum values of `a` along the + """Returns the array of indices for the maximum values of `a` along the specified axis. - Masked values are treated as if they had the value `fill_value`. + Masked values are treated as if they had the value `fill_value`. If `fill_value` is None, the default for the data type is used. Returns a numpy array. @@ -2577,11 +2362,9 @@ except: pass d = filled(a, fill_value) - if axis is None: - return d.argmax(axis=None) return d.argmax(axis=axis) -def sort(a, axis=-1, kind='quicksort', order=None, endwith=True): +def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): """ Sort a along the given axis. @@ -2623,14 +2406,17 @@ """ a = numeric.asanyarray(a) - if endwith: - filler = minimum_fill_value(a) + if fill_value is None: + if endwith: + filler = minimum_fill_value(a) + else: + filler = maximum_fill_value(a) else: - filler = maximum_fill_value(a) + filler = fill_value # return indx = numpy.indices(a.shape).tolist() indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order) - return a[indx] + return a[indx] def compressed(x): """Returns a compressed version of a masked array (or just the array if it @@ -2647,36 +2433,29 @@ def concatenate(arrays, axis=0): "Concatenates the arrays along the given axis" - #TODO: We lose the subclass, here! We should keep track of the classes... - #TODO: ...and find the max ? the lowest according to MRO? - d = [] + d = numeric.concatenate([filled(a) for a in arrays], axis) + rcls = get_masked_subclass(*arrays) + data = d.view(rcls) for x in arrays: - d.append(filled(x)) - d = numeric.concatenate(d, axis) - for x in arrays: - if getmask(x) is not nomask: + if getmask(x) is not nomask: break else: - return masked_array(d) - dm = [] - for x in arrays: - dm.append(getmaskarray(x)) - dm = make_mask(numeric.concatenate(dm, axis), copy=False, small_mask=True) - return masked_array(d, mask=dm) + return data + dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis) + dm = make_mask(dm, copy=False, small_mask=True) + data._mask = dm + return data def expand_dims(x,axis): """Expand the shape of a by including newaxis before given axis.""" + result = n_expand_dims(x,axis) if isinstance(x, MaskedArray): - (d,m) = (x._data, x._mask) - if m is nomask: - return masked_array(n_expand_dims(d,axis), - dtype=d.dtype, fill_value=x._fill_value) - else: - return masked_array(n_expand_dims(d,axis), - mask=n_expand_dims(m,axis), - dtype=d.dtype, fill_value=x._fill_value) - else: - return n_expand_dims(x,axis) + new_shape = result.shape + result = x.view() + result.shape = new_shape + if result._mask is not nomask: + result._mask.shape = new_shape + return result #...................................... def left_shift (a, n): @@ -2719,7 +2498,7 @@ return fromnumeric.asarray(a).putmask(values, mask) def transpose(a,axes=None): - """Returns a view of the array with dimensions permuted according to axes. + """Returns a view of the array with dimensions permuted according to axes. If `axes` is None (default), returns array with dimensions reversed. """ #We can't use 'frommethod', as 'transpose' doesn't take keywords @@ -2727,7 +2506,7 @@ return a.transpose(axes) except AttributeError: return fromnumeric.asarray(a).transpose(axes) - + def reshape(a, new_shape): """Changes the shape of the array `a` to `new_shape`.""" #We can't use 'frommethod', it whine about some parameters. Dmmit. @@ -2738,7 +2517,7 @@ def resize(x, new_shape): """resize(a,new_shape) returns a new array with the specified shape. - The total size of the original array can be any size. + The total size of the original array can be any size. The new array is filled with repeated copies of a. If a was masked, the new array will be masked, and the new mask will be a repetition of the old one. """ @@ -2746,11 +2525,9 @@ m = getmask(x) if m is not nomask: m = fromnumeric.resize(m, new_shape) - if isinstance(x, MaskedArray): - result = x.__class__(fromnumeric.resize(filled(x), new_shape), mask=m) - else: - result = masked_array(fromnumeric.resize(filled(x), new_shape), mask=m) - result.set_fill_value(get_fill_value(x)) + result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x)) + if result.ndim: + result._mask = m return result @@ -2766,10 +2543,10 @@ return fromnumeric.shape(filled(obj)) # def size(obj, axis=None): - """Returns the number of elements in the array along the given axis, + """Returns the number of elements in the array along the given axis, or in the sequence if `axis` is None. """ - return fromnumeric.size(filled(obj), axis) + return fromnumeric.size(filled(obj), axis) #................................................ #####-------------------------------------------------------------------------- @@ -2798,15 +2575,15 @@ #TODO: implement options `out` and `mode`, if possible. def fmask (x): "Returns the filled array, or True if ``masked``." - if x is masked: + if x is masked: return 1 return filled(x) def nmask (x): "Returns the mask, True if ``masked``, False if ``nomask``." - if x is masked: + if x is masked: return 1 m = getmask(x) - if m is nomask: + if m is nomask: return 0 return m c = filled(indices, 0) @@ -2832,22 +2609,20 @@ real and imaginary parts of complex numbers are rounded separately. Nothing is done if the array is not of float type and 'decimals' is greater than or equal to 0.""" - if not hasattr(a, "_mask"): - mask = nomask + result = fromnumeric.round_(filled(a), decimals, out) + if isinstance(a,MaskedArray): + result = result.view(type(a)) + result._mask = a._mask else: - mask = a._mask - if out is None: - return a.__class__(fromnumeric.round_(a, decimals, None), mask=mask) - else: - out = a.__class__(fromnumeric.round_(a, decimals, out), mask=mask) - return out + result = result.view(MaskedArray) + return result def arange(start, stop=None, step=1, dtype=None): """Just like range() except it returns a array whose type can be specified by the keyword argument dtype. """ - return array(numeric.arange(start, stop, step, dtype), mask=nomask) - + return array(numeric.arange(start, stop, step, dtype),mask=nomask) + def inner(a, b): """inner(a,b) returns the dot product of two arrays, which has shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the @@ -2856,9 +2631,9 @@ """ fa = filled(a, 0) fb = filled(b, 0) - if len(fa.shape) == 0: + if len(fa.shape) == 0: fa.shape = (1,) - if len(fb.shape) == 0: + if len(fb.shape) == 0: fb.shape = (1,) return masked_array(numeric.inner(fa, fb)) innerproduct = inner @@ -2876,7 +2651,7 @@ mb = getmaskarray(b) m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0) return masked_array(d, mask=m) -outerproduct = outer +outerproduct = outer def allequal (a, b, fill_value=True): """ @@ -2897,13 +2672,13 @@ return dm.filled(True).all(None) else: return False - + def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8): """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances. If `fill_value` is True, masked values are considered equal. If `fill_value` is False, masked values considered unequal. The relative error rtol should be positive and << 1.0 -The absolute error `atol` comes into play for those elements of `b` +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)) @@ -2927,24 +2702,24 @@ """empty((d1,...,dn),dtype=float,order='C') Returns a new array of shape (d1,...,dn) and given type with all its entries uninitialized. This can be faster than zeros.""" - return masked_array(numeric.empty(new_shape, dtype), mask=nomask) + return numeric.empty(new_shape, dtype).view(MaskedArray) def empty_like(a): """empty_like(a) Returns an empty (uninitialized) array of the shape and typecode of a. -Note that this does NOT initialize the returned array. +Note that this does NOT initialize the returned array. If you require your array to be initialized, you should use zeros_like().""" - return masked_array(numeric.empty_like(a), mask=nomask) + return numeric.empty_like(a).view(MaskedArray) def ones(new_shape, dtype=float): - """ones(shape, dtype=None) + """ones(shape, dtype=None) Returns an array of the given dimensions, initialized to all ones.""" - return masked_array(numeric.ones(new_shape, dtype), mask=nomask) + return numeric.ones(new_shape, dtype).view(MaskedArray) def zeros(new_shape, dtype=float): - """zeros(new_shape, dtype=None) + """zeros(new_shape, dtype=None) Returns an array of the given dimensions, initialized to all zeros.""" - return masked_array(numeric.zeros(new_shape, dtype), mask=nomask) + return numeric.zeros(new_shape, dtype).view(MaskedArray) #####-------------------------------------------------------------------------- #---- --- Pickling --- @@ -2960,13 +2735,13 @@ def _getstate(a): "Returns the internal state of the masked array, for pickling purposes." state = (1, - a.shape, + a.shape, a.dtype, a.flags.fnc, - (a._data).__reduce__()[-1][-1], - getmaskarray(a).__reduce__()[-1][-1]) + a.tostring(), + getmaskarray(a).tostring()) return state - + def _setstate(a, state): """Restores the internal state of the masked array, for pickling purposes. `state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: @@ -2978,15 +2753,15 @@ - a binary string for the mask. """ (ver, shp, typ, isf, raw, msk) = state - (a._data).__setstate__((shp, typ, isf, raw)) + super(MaskedArray, a).__setstate__((shp, typ, isf, raw)) (a._mask).__setstate__((shp, dtype('|b1'), isf, msk)) - + def _reduce(a): """Returns a 3-tuple for pickling a MaskedArray.""" return (_mareconstruct, (a.__class__, (0,), 'b', ), a.__getstate__()) - + def dump(a,F): """Pickles the MaskedArray `a` to the file `F`. `F` can either be the handle of an exiting file, or a string representing a file name. @@ -2994,15 +2769,15 @@ if not hasattr(F,'readline'): F = open(F,'w') return cPickle.dump(a,F) - + def dumps(a): """Returns a string corresponding to the pickling of the MaskedArray.""" - return cPickle.dumps(a) + return cPickle.dumps(a) def load(F): """Wrapper around ``cPickle.load`` which accepts either a file-like object or a filename.""" - if not hasattr(F, 'readline'): + if not hasattr(F, 'readline'): F = open(F,'r') return cPickle.load(F) @@ -3019,14 +2794,6 @@ ################################################################################ if __name__ == '__main__': - if 1: - a = masked_array([0,0]) - b = a/a - assert (b._mask == [1,1]).all() - assert (a._data == [0,0]).all() - if 1: - a = arange(4) - a[1:-1] = masked - b = a[:-5] - if 1: - assert(masked_array(masked) is masked) + import numpy as N + from maskedarray.testutils import assert_equal, assert_array_equal + pi = N.pi Added: trunk/Lib/sandbox/maskedarray/core_ini.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core_ini.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/core_ini.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -0,0 +1,3033 @@ +"""MA: a facility for dealing with missing observations +MA is generally used as a numpy.array look-alike. +by Paul F. Dubois. + +Copyright 1999, 2000, 2001 Regents of the University of California. +Released for unlimited redistribution. +Adapted for numpy_core 2005 by Travis Oliphant and +(mainly) Paul Dubois. + +Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant. +pgmdevlist_at_gmail_dot_com + +:author: Pierre Gerard-Marchant +:contact: pierregm_at_uga_dot_edu +:version: $Id$ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author$)" +__version__ = '1.0' +__revision__ = "$Revision$" +__date__ = '$Date$' + +__all__ = ['MAError', 'MaskType', 'MaskedArray', + 'bool_', 'complex_', 'float_', 'int_', 'object_', + 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', + 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', + 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', + 'arctanh', 'argmax', 'argmin', 'argsort', 'around', + 'array', 'asarray', + 'bitwise_and', 'bitwise_or', 'bitwise_xor', + 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate', + 'cos', 'cosh', 'count', + 'diagonal', 'divide', 'dump', 'dumps', + 'empty', 'empty_like', 'equal', 'exp', + 'fabs', 'fmod', 'filled', 'floor', 'floor_divide', + 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot', + 'ids', 'inner', 'innerproduct', + 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', + 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', + 'logical_and', 'logical_not', 'logical_or', 'logical_xor', + 'make_mask', 'make_mask_none', 'mask_or', 'masked', + 'masked_array', 'masked_equal', 'masked_greater', + 'masked_greater_equal', 'masked_inside', 'masked_less', + 'masked_less_equal', 'masked_not_equal', 'masked_object', + 'masked_outside', 'masked_print_option', 'masked_singleton', + 'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min', + 'minimum', 'multiply', + 'negative', 'nomask', 'nonzero', 'not_equal', + 'ones', 'outer', 'outerproduct', + 'power', 'product', 'ptp', 'put', 'putmask', + 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', + 'right_shift', 'round_', + 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std', + 'subtract', 'sum', 'swapaxes', + 'take', 'tan', 'tanh', 'transpose', 'true_divide', + 'var', 'where', + 'zeros'] + +import sys +import types +import cPickle +# +import numpy +from numpy import bool_, complex_, float_, int_, object_, str_ + +import numpy.core.umath as umath +import numpy.core.fromnumeric as fromnumeric +from numpy.core.numeric import ndarray +from numpy.core.fromnumeric import amax, amin +import numpy.core.numerictypes as ntypes +from numpy.core.numerictypes import bool_, typecodes +from numpy.core.multiarray import dtype +import numpy.core.numeric as numeric +from numpy.lib.shape_base import expand_dims as n_expand_dims +import warnings + + + +MaskType = bool_ +nomask = MaskType(0) + +divide_tolerance = 1.e-35 +numpy.seterr(all='ignore') + +#####-------------------------------------------------------------------------- +#---- --- Helper functions --- +#####-------------------------------------------------------------------------- +def convert_typecode(f,dtchar): + """Converts the type of `f` to a type compatible with `dtchar`, for inline operations.""" + ftype = f.dtype.char + if dtchar == ftype: + return f + elif dtchar in typecodes['Integer']: + if ftype in typecodes['Integer']: + f = f.astype(dtchar) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif dtchar in typecodes['Float']: + if ftype in typecodes['Integer']: + f = f.astype(dtchar) + elif ftype in typecodes['Float']: + f = f.astype(dtchar) + else: + raise TypeError, 'Incorrect type for in-place operation.' + elif dtchar in typecodes['Complex']: + if ftype in typecodes['Integer']: + f = f.astype(dtchar) + elif ftype in typecodes['Float']: + f = f.astype(dtchar) + elif ftype in typecodes['Complex']: + f = f.astype(dtchar) + else: + raise TypeError, 'Incorrect type for in-place operation.' + else: + raise TypeError, 'Incorrect type for in-place operation.' + return f + +#####-------------------------------------------------------------------------- +#---- --- Exceptions --- +#####-------------------------------------------------------------------------- +class MAError(Exception): + "Class for MA related errors." + def __init__ (self, args=None): + "Creates an exception." + Exception.__init__(self,args) + self.args = args + def __str__(self): + "Calculates the string representation." + return str(self.args) + __repr__ = __str__ + +#####-------------------------------------------------------------------------- +#---- --- Filling options --- +#####-------------------------------------------------------------------------- +# b: boolean - c: complex - f: floats - i: integer - O: object - S: string +default_filler = {'b': True, + 'c' : 1.e20 + 0.0j, + 'f' : 1.e20, + 'i' : 999999, + 'O' : '?', + 'S' : 'N/A', + 'u' : 999999, + 'V' : '???', + } +#{0: , +# 1: , +# 2: , +# 3: , +# 4: , +# 5: , +# 6: , +# 7: , +# 8: , +# 9: , +# 10: , +# 11: , +# 12: , +# 13: , +# 14: , +# 15: , +# 16: , +# 17: , +# 18: , +# 19: , +# 20: , +max_filler = ntypes._minvals +max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]]) +min_filler = ntypes._maxvals +min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]]) +if 'float128' in ntypes.typeDict: + max_filler.update([(numpy.float128,-numeric.inf)]) + min_filler.update([(numpy.float128, numeric.inf)]) + + +def default_fill_value (obj): + "Calculates the default fill value for an object `obj`." + if hasattr(obj,'dtype'): + return default_filler[obj.dtype.kind] + elif isinstance(obj, float): + return default_filler['f'] + elif isinstance(obj, int) or isinstance(obj, long): + return default_filler['i'] + elif isinstance(obj, str): + return default_filler['S'] + elif isinstance(obj, complex): + return default_filler['c'] + elif isinstance(obj, numeric.dtype): + return default_filler[obj.kind] + else: + return default_filler['O'] + +def minimum_fill_value (obj): + "Calculates the default fill value suitable for taking the minimum of `obj`." + if hasattr(obj, 'dtype'): + objtype = obj.dtype + filler = min_filler[objtype] + if filler is None: + raise TypeError, 'Unsuitable type for calculating minimum.' + return filler + elif isinstance(obj, float): + return min_filler[ntypes.typeDict['float_']] + elif isinstance(obj, int): + return min_filler[ntypes.typeDict['int_']] + elif isinstance(obj, long): + return min_filler[ntypes.typeDict['uint']] + elif isinstance(obj, numeric.dtype): + return min_filler[obj] + else: + raise TypeError, 'Unsuitable type for calculating minimum.' + +def maximum_fill_value (obj): + "Calculates the default fill value suitable for taking the maximum of `obj`." + if hasattr(obj, 'dtype'): + objtype = obj.dtype + filler = max_filler[objtype] + if filler is None: + raise TypeError, 'Unsuitable type for calculating minimum.' + return filler + elif isinstance(obj, float): + return max_filler[ntypes.typeDict['float_']] + elif isinstance(obj, int): + return max_filler[ntypes.typeDict['int_']] + elif isinstance(obj, long): + return max_filler[ntypes.typeDict['uint']] + elif isinstance(obj, numeric.dtype): + return max_filler[obj] + else: + raise TypeError, 'Unsuitable type for calculating minimum.' + +def set_fill_value (a, fill_value): + "Sets the fill value of `a` if it is a masked array." + if isinstance(a, MaskedArray): + a.set_fill_value(fill_value) + +def get_fill_value (a): + """Returns the fill value of `a`, if any. + Otherwise, returns the default fill value for that type. + """ + if isinstance(a, MaskedArray): + result = a.fill_value + else: + result = default_fill_value(a) + return result + +def common_fill_value (a, b): + "Returns the common fill_value of `a` and `b`, if any, or `None`." + t1 = get_fill_value(a) + t2 = get_fill_value(b) + if t1 == t2: + return t1 + return None + +#................................................ +def filled(a, value = None): + """Returns `a` as an array with masked data replaced by `value`. +If `value` is `None` or the special element `masked`, `get_fill_value(a)` +is used instead. + +If `a` is already a contiguous numeric array, `a` itself is returned. + +`filled(a)` can be used to be sure that the result is numeric when passing +an object a to other software ignorant of MA, in particular to numpy itself. + """ + if hasattr(a, 'filled'): + return a.filled(value) + elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']: + return a + elif isinstance(a, types.DictType): + return numeric.array(a, 'O') + else: + return numeric.array(a) + +#####-------------------------------------------------------------------------- +#---- --- Ufuncs --- +#####-------------------------------------------------------------------------- +ufunc_domain = {} +ufunc_fills = {} + +class domain_check_interval: + """Defines a valid interval, +so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`.""" + def __init__(self, a, b): + "domain_check_interval(a,b)(x) = true where x < a or y > b" + if (a > b): + (a, b) = (b, a) + self.a = a + self.b = b + + def __call__ (self, x): + "Execute the call behavior." + return umath.logical_or(umath.greater (x, self.b), + umath.less(x, self.a)) +#............................ +class domain_tan: + """Defines a valid interval for the `tan` function, +so that `domain_tan(eps) = True where `abs(cos(x)) < eps`""" + def __init__(self, eps): + "domain_tan(eps) = true where abs(cos(x)) < eps)" + self.eps = eps + def __call__ (self, x): + "Execute the call behavior." + return umath.less(umath.absolute(umath.cos(x)), self.eps) +#............................ +class domain_safe_divide: + """defines a domain for safe division.""" + def __init__ (self, tolerance=divide_tolerance): + self.tolerance = tolerance + def __call__ (self, a, b): + return umath.absolute(a) * self.tolerance >= umath.absolute(b) +#............................ +class domain_greater: + "domain_greater(v)(x) = true where x <= v" + def __init__(self, critical_value): + "domain_greater(v)(x) = true where x <= v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less_equal(x, self.critical_value) +#............................ +class domain_greater_equal: + "domain_greater_equal(v)(x) = true where x < v" + def __init__(self, critical_value): + "domain_greater_equal(v)(x) = true where x < v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less(x, self.critical_value) +#.............................................................................. +class masked_unary_operation: + """Defines masked version of unary operations, +where invalid values are pre-masked. + +:IVariables: + - `f` : function. + - `fill` : Default filling value *[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, mufunc, fill=0, domain=None): + """ masked_unary_operation(aufunc, fill=0, domain=None) + aufunc(fill) must be defined + self(x) returns aufunc(x) + with masked values where domain(x) is true or getmask(x) is true. + """ + self.f = mufunc + self.fill = fill + self.domain = domain + self.__doc__ = getattr(mufunc, "__doc__", str(mufunc)) + self.__name__ = getattr(mufunc, "__name__", str(mufunc)) + ufunc_domain[mufunc] = domain + ufunc_fills[mufunc] = fill + # + def __call__ (self, a, *args, **kwargs): + "Execute the call behavior." +# numeric tries to return scalars rather than arrays when given scalars. + m = getmask(a) + d1 = filled(a, self.fill) + if self.domain is not None: + m = mask_or(m, numeric.asarray(self.domain(d1))) + result = self.f(d1, *args, **kwargs) + # + if isinstance(result, MaskedArray): + return result.__class__(result, mask=m) + return masked_array(result, mask=m) + # + def __str__ (self): + return "Masked version of %s. [Invalid values are masked]" % str(self.f) +#.............................................................................. +class masked_binary_operation: + """Defines masked version of binary operations, +where invalid values are pre-masked. + +:IVariables: + - `f` : function. + - `fillx` : Default filling value for first array*[0]*. + - `filly` : Default filling value for second array*[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, mbfunc, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = mbfunc + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc)) + self.__name__ = getattr(mbfunc, "__name__", str(mbfunc)) + ufunc_domain[mbfunc] = None + ufunc_fills[mbfunc] = (fillx, filly) + # + def __call__ (self, a, b, *args, **kwargs): + "Execute the call behavior." + m = mask_or(getmask(a), getmask(b)) + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + result = self.f(d1, d2, *args, **kwargs) +# if isinstance(result, ndarray) \ +# and m.ndim != 0 \ +# and m.shape != result.shape: +# m = mask_or(getmaskarray(a), getmaskarray(b)) + if isinstance(result, MaskedArray): + return result.__class__(result, mask=m) + return masked_array(result, mask=m) + # + def reduce (self, target, axis=0, dtype=None): + """Reduces `target` along the given `axis`.""" + if isinstance(target, MaskedArray): + tclass = target.__class__ + else: + tclass = MaskedArray + m = getmask(target) + t = filled(target, self.filly) + if t.shape == (): + t = t.reshape(1) + if m is not nomask: + m = make_mask(m, copy=1) + m.shape = (1,) + if m is nomask: + return tclass(self.f.reduce (t, axis)) + else: + t = tclass(t, mask=m) + # XXX: "or t.dtype" below is a workaround for what appears + # XXX: to be a bug in reduce. + t = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype) + m = umath.logical_and.reduce(m, axis) + if isinstance(t, ndarray): + return tclass(t, mask=m, fill_value=get_fill_value(target)) + elif m: + return masked + else: + return t + + def outer (self, a, b): + "Returns the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + m = nomask + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = umath.logical_or.outer(ma, mb) + d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)) + if isinstance(d, MaskedArray): + return d.__class__(d, mask=m) + return masked_array(d, mask=m) + + def accumulate (self, target, axis=0): + """Accumulates `target` along `axis` after filling with y fill value.""" + if isinstance(target, MaskedArray): + tclass = target.__class__ + else: + tclass = masked_array + t = filled(target, self.filly) + return tclass(self.f.accumulate(t, axis)) + + def __str__ (self): + return "Masked version of " + str(self.f) +#.............................................................................. +class domained_binary_operation: + """Defines binary operations that have a domain, like divide. + +These are complicated so they are a separate class. +They have no reduce, outer or accumulate. + +:IVariables: + - `f` : function. + - `fillx` : Default filling value for first array*[0]*. + - `filly` : Default filling value for second array*[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, dbfunc, domain, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = dbfunc + self.domain = domain + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc)) + self.__name__ = getattr(dbfunc, "__name__", str(dbfunc)) + ufunc_domain[dbfunc] = domain + ufunc_fills[dbfunc] = (fillx, filly) + + def __call__(self, a, b): + "Execute the call behavior." + ma = getmask(a) + mb = getmask(b) + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + t = numeric.asarray(self.domain(d1, d2)) + if fromnumeric.sometrue(t, None): + d2 = numeric.where(t, self.filly, d2) + mb = mask_or(mb, t) + m = mask_or(ma, mb) + result = self.f(d1, d2) + if isinstance(result, MaskedArray): + return result.__class__(result, mask=m) + return masked_array(result, mask=m) + + def __str__ (self): + return "Masked version of " + str(self.f) + +#.............................................................................. +# Unary ufuncs +exp = masked_unary_operation(umath.exp) +conjugate = masked_unary_operation(umath.conjugate) +sin = masked_unary_operation(umath.sin) +cos = masked_unary_operation(umath.cos) +tan = masked_unary_operation(umath.tan) +arctan = masked_unary_operation(umath.arctan) +arcsinh = masked_unary_operation(umath.arcsinh) +sinh = masked_unary_operation(umath.sinh) +cosh = masked_unary_operation(umath.cosh) +tanh = masked_unary_operation(umath.tanh) +abs = absolute = masked_unary_operation(umath.absolute) +fabs = masked_unary_operation(umath.fabs) +negative = masked_unary_operation(umath.negative) +floor = masked_unary_operation(umath.floor) +ceil = masked_unary_operation(umath.ceil) +around = masked_unary_operation(fromnumeric.round_) +logical_not = masked_unary_operation(umath.logical_not) +# Domained unary ufuncs +sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0)) +log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0)) +log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0)) +tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35)) +arcsin = masked_unary_operation(umath.arcsin, 0.0, + domain_check_interval(-1.0, 1.0)) +arccos = masked_unary_operation(umath.arccos, 0.0, + domain_check_interval(-1.0, 1.0)) +arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0)) +arctanh = masked_unary_operation(umath.arctanh, 0.0, + domain_check_interval(-1.0+1e-15, 1.0-1e-15)) +# Binary ufuncs +add = masked_binary_operation(umath.add) +subtract = masked_binary_operation(umath.subtract) +multiply = masked_binary_operation(umath.multiply, 1, 1) +arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0) +equal = masked_binary_operation(umath.equal) +equal.reduce = None +not_equal = masked_binary_operation(umath.not_equal) +not_equal.reduce = None +less_equal = masked_binary_operation(umath.less_equal) +less_equal.reduce = None +greater_equal = masked_binary_operation(umath.greater_equal) +greater_equal.reduce = None +less = masked_binary_operation(umath.less) +less.reduce = None +greater = masked_binary_operation(umath.greater) +greater.reduce = None +logical_and = masked_binary_operation(umath.logical_and) +alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce +logical_or = masked_binary_operation(umath.logical_or) +sometrue = logical_or.reduce +logical_xor = masked_binary_operation(umath.logical_xor) +bitwise_and = masked_binary_operation(umath.bitwise_and) +bitwise_or = masked_binary_operation(umath.bitwise_or) +bitwise_xor = masked_binary_operation(umath.bitwise_xor) +hypot = masked_binary_operation(umath.hypot) +# Domained binary ufuncs +divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1) +true_divide = domained_binary_operation(umath.true_divide, + domain_safe_divide(), 0, 1) +floor_divide = domained_binary_operation(umath.floor_divide, + domain_safe_divide(), 0, 1) +remainder = domained_binary_operation(umath.remainder, + domain_safe_divide(), 0, 1) +fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1) + + +#####-------------------------------------------------------------------------- +#---- --- Mask creation functions --- +#####-------------------------------------------------------------------------- +def getmask(a): + """Returns the mask of `a`, if any, or `nomask`. +Returns `nomask` if `a` is not a masked array. +To get an array for sure use getmaskarray.""" + if hasattr(a, "_mask"): + return a._mask + else: + return nomask + +def getmaskarray(a): + """Returns the mask of `a`, if any. +Otherwise, returns an array of `False`, with the same shape as `a`. + """ + m = getmask(a) + if m is nomask: + return make_mask_none(fromnumeric.shape(a)) + else: + return m + +def is_mask(m): + """Returns `True` if `m` is a legal mask. +Does not check contents, only type. + """ + try: + return m.dtype.type is MaskType + except AttributeError: + return False +# +def make_mask(m, copy=False, small_mask=True, flag=None): + """make_mask(m, copy=0, small_mask=0) +Returns `m` as a mask, creating a copy if necessary or requested. +The function can accept any sequence of integers or `nomask`. +Does not check that contents must be 0s and 1s. +If `small_mask=True`, returns `nomask` if `m` contains no true elements. + +:Parameters: + - `m` (ndarray) : Mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. + """ + if flag is not None: + warnings.warn("The flag 'flag' is now called 'small_mask'!", + DeprecationWarning) + small_mask = flag + if m is nomask: + return nomask + elif isinstance(m, ndarray): + m = filled(m, True) + if m.dtype.type is MaskType: + if copy: + result = numeric.array(m, dtype=MaskType, copy=copy) + else: + result = m + else: + result = numeric.array(m, dtype=MaskType) + else: + result = numeric.array(filled(m, True), dtype=MaskType) + # Bas les masques ! + if small_mask and not result.any(): + return nomask + else: + return result + +def make_mask_none(s): + "Returns a mask of shape `s`, filled with `False`." + result = numeric.zeros(s, dtype=MaskType) + return result + +def mask_or (m1, m2, copy=False, small_mask=True): + """Returns the combination of two masks `m1` and `m2`. +The masks are combined with the `logical_or` operator, treating `nomask` as false. +The result may equal m1 or m2 if the other is nomask. + +:Parameters: + - `m` (ndarray) : Mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. + """ + if m1 is nomask: + return make_mask(m2, copy=copy, small_mask=small_mask) + if m2 is nomask: + return make_mask(m1, copy=copy, small_mask=small_mask) + if m1 is m2 and is_mask(m1): + return m1 + return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask) + +#####-------------------------------------------------------------------------- +#--- --- Masking functions --- +#####-------------------------------------------------------------------------- +def masked_where(condition, x, copy=True): + """Returns `x` as an array masked where `condition` is true. +Masked values of `x` or `condition` are kept. + +:Parameters: + - `condition` (ndarray) : Masking condition. + - `x` (ndarray) : Array to mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + """ + cm = filled(condition,1) + if isinstance(x,MaskedArray): + m = mask_or(x._mask, cm) + return x.__class__(x._data, mask=m, copy=copy) + else: + return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm) + +def masked_greater(x, value, copy=1): + "Shortcut to `masked_where`, with ``condition = (x > value)``." + return masked_where(greater(x, value), x, copy=copy) + +def masked_greater_equal(x, value, copy=1): + "Shortcut to `masked_where`, with ``condition = (x >= value)``." + return masked_where(greater_equal(x, value), x, copy=copy) + +def masked_less(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x < value)``." + return masked_where(less(x, value), x, copy=copy) + +def masked_less_equal(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x <= value)``." + return masked_where(less_equal(x, value), x, copy=copy) + +def masked_not_equal(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x != value)``." + return masked_where((x != value), x, copy=copy) + +# +def masked_equal(x, value, copy=True): + """Shortcut to `masked_where`, with ``condition = (x == value)``. +For floating point, consider `masked_values(x, value)` instead. + """ + return masked_where((x == value), x, copy=copy) +# d = filled(x, 0) +# c = umath.equal(d, value) +# m = mask_or(c, getmask(x)) +# return array(d, mask=m, copy=copy) + +def masked_inside(x, v1, v2, copy=True): + """Shortcut to `masked_where`, where `condition` is True for x inside +the interval `[v1,v2]` ``(v1 <= x <= v2)``. +The boundaries `v1` and `v2` can be given in either order. + """ + if v2 < v1: + (v1, v2) = (v2, v1) + xf = filled(x) + condition = (xf >= v1) & (xf <= v2) + return masked_where(condition, x, copy=copy) + +def masked_outside(x, v1, v2, copy=True): + """Shortcut to `masked_where`, where `condition` is True for x outside +the interval `[v1,v2]` ``(x < v1)|(x > v2)``. +The boundaries `v1` and `v2` can be given in either order. + """ + if v2 < v1: + (v1, v2) = (v2, v1) + xf = filled(x) + condition = (xf < v1) | (xf > v2) + return masked_where(condition, x, copy=copy) + +# +def masked_object(x, value, copy=True): + """Masks the array `x` where the data are exactly equal to `value`. +This function is suitable only for `object` arrays: for floating point, +please use `masked_values` instead. +The mask is set to `nomask` if posible. + +:parameter copy (Boolean, *[True]*): Returns a copy of `x` if true. """ + if isMaskedArray(x): + condition = umath.equal(x._data, value) + mask = x._mask + else: + condition = umath.equal(fromnumeric.asarray(x), value) + mask = nomask + mask = mask_or(mask, make_mask(condition, small_mask=True)) + return masked_array(x, mask=mask, copy=copy, fill_value=value) + +def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True): + """Masks the array `x` where the data are approximately equal to `value` +(that is, ``abs(x - value) <= atol+rtol*abs(value)``). +Suitable only for floating points. For integers, please use `masked_equal`. +The mask is set to `nomask` if posible. + +:Parameters: + - `rtol` (Float, *[1e-5]*): Tolerance parameter. + - `atol` (Float, *[1e-8]*): Tolerance parameter. + - `copy` (boolean, *[False]*) : Returns a copy of `x` if True. + """ + abs = umath.absolute + xnew = filled(x, value) + if issubclass(xnew.dtype.type, numeric.floating): + condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value)) + try: + mask = x._mask + except AttributeError: + mask = nomask + else: + condition = umath.equal(xnew, value) + mask = nomask + mask = mask_or(mask, make_mask(condition, small_mask=True)) + return masked_array(xnew, mask=mask, copy=copy, fill_value=value) + +#####-------------------------------------------------------------------------- +#---- --- Printing options --- +#####-------------------------------------------------------------------------- +class _MaskedPrintOption: + """Handles the string used to represent missing data in a masked array.""" + def __init__ (self, display): + "Creates the masked_print_option object." + self._display = display + self._enabled = True + + def display(self): + "Displays the string to print for masked values." + return self._display + + def set_display (self, s): + "Sets the string to print for masked values." + self._display = s + + def enabled(self): + "Is the use of the display value enabled?" + return self._enabled + + def enable(self, small_mask=1): + "Set the enabling small_mask to `small_mask`." + self._enabled = small_mask + + def __str__ (self): + return str(self._display) + + __repr__ = __str__ + +#if you single index into a masked location you get this object. +masked_print_option = _MaskedPrintOption('--') + +#####-------------------------------------------------------------------------- +#---- --- MaskedArray class --- +#####-------------------------------------------------------------------------- +class MaskedArray(numeric.ndarray): + """Arrays with possibly masked values. +Masked values of True exclude the corresponding element from any computation. + +Construction: + x = array(data, dtype=None, copy=True, order=False, + mask = nomask, fill_value=None, small_mask=True) + +If copy=False, every effort is made not to copy the data: +If `data` is a MaskedArray, and argument mask=nomask, then the candidate data +is `data._data` and the mask used is `data._mask`. +If `data` is a numeric array, it is used as the candidate raw data. +If `dtype` is not None and is different from data.dtype.char then a data copy is required. +Otherwise, the candidate is used. + +If a data copy is required, the raw (unmasked) data stored is the result of: +numeric.array(data, dtype=dtype.char, copy=copy) + +If `mask` is `nomask` there are no masked values. +Otherwise mask must be convertible to an array of booleans with the same shape as x. +If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`. +Otherwise, the mask is not compressed. + +fill_value is used to fill in masked values when necessary, such as when +printing and in method/function filled(). +The fill_value is not used for computation within this module. + """ + __array_priority__ = 10.1 + _defaultmask = nomask + _defaulthardmask = False + #TODO: There some reorganization to do round here + def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None, + keep_mask=True, small_mask=True, hard_mask=False, flag=None, + **options): + """array(data, dtype=None, copy=True, mask=nomask, fill_value=None) + +If `data` is already a ndarray, its dtype becomes the default value of dtype. + """ + if flag is not None: + warnings.warn("The flag 'flag' is now called 'small_mask'!", + DeprecationWarning) + small_mask = flag + # 1. Argument is MA ........... + if isinstance(data, MaskedArray) or\ + (hasattr(data,"_mask") and hasattr(data,"_data")) : + if data is masked: + return masked.view(cls) + if keep_mask: + if mask is nomask: + if copy: + cls._defaultmask = data._mask.copy() + else: + cls._defaultmask = data._mask + else: + cls._defaultmask = mask_or(data._mask, mask, + copy=copy, small_mask=small_mask) + else: + cls._defaultmask = make_mask(mask, copy=copy, small_mask=small_mask) + # Update fille_value + if fill_value is None: + cls._fill_value = data._fill_value + else: + cls._fill_value = fill_value + cls._defaulthardmask = hard_mask + _data = data._data + if dtype is not None and _data.dtype != numeric.dtype(dtype): + return _data.astype(dtype).view(cls) + elif copy: + return _data.copy().view(cls) + else: + return _data.view(cls) + # 2. Argument is not MA ....... + if isinstance(data, ndarray): + if dtype is not None and data.dtype != numeric.dtype(dtype): + _data = data.astype(dtype) + elif copy: + _data = data.copy() + else: + _data = data + else: + _data = numeric.array(data, dtype=dtype, copy=copy) +# try: +# _data = numeric.array(data, dtype=dtype, copy=copy) +# except TypeError: +# _data = empty(len(data), dtype=dtype) +# for (k,v) in enumerate(data): +# _data[k] = v +# if mask is nomask: +# cls.__defaultmask = getmask(_data) +# return _data.view(cls) + # Define mask ................. + mask = make_mask(mask, copy=copy, small_mask=small_mask) + #....Check shapes compatibility + if mask is not nomask: + (nd, nm) = (_data.size, mask.size) + if (nm != nd): + # We need to resize w/ a function, in case _data is only a reference + if nm == 1: + mask = fromnumeric.resize(mask, _data.shape) + elif nd == 1: + _data = fromnumeric.resize(_data, mask.shape) + else: + msg = "Mask and data not compatible: data size is %i, "+\ + "mask size is %i." + raise MAError, msg % (nd, nm) + elif (mask.shape != _data.shape): + mask = mask.reshape(_data.shape) +# mask = _data.shape + #.... + cls._fill_value = fill_value + cls._defaulthardmask = hard_mask + cls._defaultmask = mask + cls._defaultoptions = options + return numeric.asanyarray(_data).view(cls) + #.................................. + def __array_wrap__(self, obj, context=None): + """Special hook for ufuncs. +Wraps the numpy array and sets the mask according to context. + """ +# mclass = self.__class__ + #.......... + if context is None: +# return mclass(obj, mask=self._mask, copy=False) + return MaskedArray(obj, mask=self._mask, copy=False, + dtype=obj.dtype, + fill_value=self.fill_value, ) + #.......... + (func, args) = context[:2] + m = reduce(mask_or, [getmask(arg) for arg in args]) + # Get domain mask + domain = ufunc_domain.get(func, None) + if domain is not None: + m = mask_or(m, domain(*[getattr(arg, '_data', arg) for arg in args])) + # Update mask + if m is not nomask: + try: + dshape = obj.shape + except AttributeError: + pass + else: + if m.shape != dshape: + m = reduce(mask_or, [getmaskarray(arg) for arg in args]) +# return mclass(obj, copy=False, mask=m) + return MaskedArray(obj, copy=False, mask=m,) +# dtype=obj.dtype, fill_value=self._fill_value) + #........................ + #TODO: there should be some reorganization to do round here. + def __array_finalize__(self,obj): + """Finalizes the masked array. + """ + # + if isinstance(obj, MaskedArray): + # We came here from a MaskedArray + self._data = obj._data + self._mask = obj._mask + self._hardmask = obj._hardmask + self._fill_value = obj._fill_value + self.options = obj.options + else: + # We came here from a .view() + if hasattr(obj,'_data') and hasattr(obj, '_mask'): + # obj is an old masked array or a smart record + self._data = obj._data + self._mask = obj._mask + else: + # obj is anything but... + self._data = obj + self._mask = self._defaultmask + # Set the instance default + self._hardmask = self._defaulthardmask + self.fill_value = self._fill_value + self.options = self._defaultoptions + # Reset the class default + MaskedArray._defaultmask = nomask + MaskedArray._defaulthardmask = False + MaskedArray._fill_value = None +# # + return + #............................................ + def __getitem__(self, indx): + """x.__getitem__(y) <==> x[y] +Returns the item described by i. Not a copy as in previous versions. + """ + if getmask(indx) is not nomask: + msg = "Masked arrays must be filled before they can be used as indices!" + raise IndexError, msg + dout = self._data[indx] + m = self._mask + scalardout = (len(numeric.shape(dout))==0) + # + if m is nomask: + if scalardout: + return dout + else: + return self.__class__(dout, mask=nomask, keep_mask=True, + fill_value=self._fill_value, + **self.options) + #.... + mi = m[indx] + if mi.size == 1: + if mi: + return masked + return dout + else: + return self.__class__(dout, mask=mi, fill_value=self._fill_value, + **self.options) + #........................ + def __setitem__(self, index, value): + """x.__setitem__(i, y) <==> x[i]=y +Sets item described by index. If value is masked, masks those locations. + """ + if self is masked: + raise MAError, 'Cannot alter the masked element.' + if getmask(index) is not nomask: + msg = "Masked arrays must be filled before they can be used as indices!" + raise IndexError, msg + #.... + (d, m) = (self._data, self._mask) + #.... + if value is masked: + if m is nomask: + m = make_mask_none(d.shape) + else: + m = m.copy() + m[index] = True + self._mask = m + return + #.... + if m is nomask: + d[index] = filled(value) + valmask = getmask(value) + if valmask is not nomask: + m = make_mask_none(d.shape) + m[index] = valmask + elif not self._hardmask: + d[index] = filled(value) + valmask = getmask(value) + m = m.copy() + if valmask is nomask: + m[index] = False + else: + m[index] = valmask + elif hasattr(index, 'dtype') and (index.dtype==bool_): + index *= ~m + d[index] = filled(value) +# elif isinstance(index, int): + else: + mindx = m[index] + value = masked_array(value, mask=mindx, keep_mask=True) + valdata = filled(value) + valmask = getmask(value) + if valmask is nomask: + d[index] = valdata + elif valmask.size > 1: + dindx = d[index] + numeric.putmask(dindx, ~valmask, valdata) + d[index] = dindx + numeric.putmask(mindx, valmask, True) + m[index] = mindx + #..... + if not m.any(): + self._mask = nomask + else: + self._mask = m + #............................................ + def __getslice__(self, i, j): + """x.__getslice__(i, j) <==> x[i:j] +Returns the slice described by i, j. +The use of negative indices is not supported.""" + m = self._mask + dout = self._data[i:j] + if m is nomask: + return self.__class__(dout, fill_value=self._fill_value, + **self.options) + else: + return self.__class__(dout, mask=m[i:j], fill_value=self._fill_value, + **self.options) + #........................ + def __setslice__(self, i, j, value): + """x.__setslice__(i, j, value) <==> x[i:j]=value +Sets a slice i:j to `value`. +If `value` is masked, masks those locations.""" + if self is masked: + #TODO: Well, maybe we could/should + raise MAError, "Cannot alter the 'masked' object." + #.... + (d, m) = (self._data, self._mask) + #.... + if value is masked: + if m is nomask: + m = make_mask_none(d.shape) + m[i:j] = True + self._mask = m + return + #.... + if m is nomask: + valmask = getmask(value) + valdata = filled(value) + d[i:j] = valdata + if valmask is not nomask: + m = make_mask_none(d.shape) + m[i:j] = valmask + elif not self._hardmask: + valmask = getmask(value) + valdata = filled(value) + d[i:j] = valdata + if valmask is nomask: + m[i:j] = False + else: + m[i:j] = valmask + else: + mindx = m[i:j] + value = masked_array(value, mask=mindx, keep_mask=True) + valmask = value._mask + if valmask is nomask: + d[i:j][~mindx] = filled(value) + elif valmask.size > 1: + d[i:j][~mindx] = value[~valmask] + m[i:j][valmask] = True + #..... + if not m.any(): + self._mask = nomask + else: + self._mask = m + #............................................ + # If we don't want to crash the performance, we better leave __getattribute__ alone... +# def __getattribute__(self, name): +# """x.__getattribute__('name') = x.name +#Returns the chosen attribute. +#If the attribute cannot be directly accessed, checks the _data section. +# """ +# try: +# return ndarray.__getattribute__(self, name) +# except AttributeError: +# pass +# try: +# return self._data.__getattribute__(name) +# except AttributeError: +# raise AttributeError + #............................................ + def __str__(self): + """x.__str__() <==> str(x) +Calculates the string representation, using masked for fill if it is enabled. +Otherwise, fills with fill value. + """ + if masked_print_option.enabled(): + f = masked_print_option + if self is masked: + return str(f) + m = self._mask + if m is nomask: + res = self._data + else: + if m.shape == () and m: + return str(f) + # convert to object array to make filled work +#CHECK: the two lines below seem more robust than the self._data.astype +# res = numeric.empty(self._data.shape, object_) +# numeric.putmask(res,~m,self._data) + res = self._data.astype("|O8") + res[self._mask] = f + else: + res = self.filled(self.fill_value) + return str(res) + + def __repr__(self): + """x.__repr__() <==> repr(x) +Calculates the repr representation, using masked for fill if it is enabled. +Otherwise fill with fill value. + """ + with_mask = """\ +masked_%(name)s(data = + %(data)s, + mask = + %(mask)s, + fill_value=%(fill)s) +""" + with_mask1 = """\ +masked_%(name)s(data = %(data)s, + mask = %(mask)s, + fill_value=%(fill)s) +""" + n = len(self.shape) + name = repr(self._data).split('(')[0] + if n <= 1: + return with_mask1 % { + 'name': name, + 'data': str(self), + 'mask': str(self._mask), + 'fill': str(self.fill_value), + } + return with_mask % { + 'name': name, + 'data': str(self), + 'mask': str(self._mask), + 'fill': str(self.fill_value), + } + #............................................ + def __abs__(self): + """x.__abs__() <==> abs(x) +Returns a masked array of the current subclass, with the new `_data` +the absolute of the inital `_data`. + """ + return self.__class__(self._data.__abs__(), mask=self._mask, + fill_value = self._fill_value, **self.options) + # + def __neg__(self): + """x.__abs__() <==> neg(x) +Returns a masked array of the current subclass, with the new `_data` +the negative of the inital `_data`.""" + try: + return self.__class__(self._data.__neg__(), mask=self._mask, + fill_value = self._fill_value, **self.options) + except MAError: + return negative(self) + # + def __iadd__(self, other): + "Adds other to self in place." + f = convert_typecode(filled(other, 0), self._data.dtype.char) + m = getmask(other) + self._data += f + if self._mask is nomask: + self._mask = m + elif m is not nomask: + self._mask += m + return self + # + def __isub__(self, other): + "Subtracts other from self in place." + f = convert_typecode(filled(other, 0), self._data.dtype.char) + m = getmask(other) + self._data -= f + if self._mask is nomask: + self._mask = m + elif m is not nomask: + self._mask += m + return self + # + def __imul__(self, other): + "Multiplies self by other in place." + f = convert_typecode(filled(other, 1), self._data.dtype.char) + m = getmask(other) + self._data *= f + if self._mask is nomask: + self._mask = m + elif m is not nomask: + self._mask += m + return self + # + def __idiv__(self, other): + "Divides self by other in place." + f = convert_typecode(filled(other, 0), self._data.dtype.char) + mo = getmask(other) + result = divide(self, masked_array(f, mask=mo)) + self._data = result._data + dm = result._mask + if dm is not self._mask: + self._mask = dm + return self + +# # +# def __eq__(self, other): +# return equal(self,other) +# +# def __ne__(self, other): +# return not_equal(self,other) +# +# def __lt__(self, other): +# return less(self,other) +# +# def __le__(self, other): +# return less_equal(self,other) +# +# def __gt__(self, other): +# return greater(self,other) +# +# def __ge__(self, other): +# return greater_equal(self,other) + + #............................................ + def __float__(self): + "Converts self to float." + if self._mask is not nomask: + warnings.warn("Warning: converting a masked element to nan.") + return numpy.nan + #raise MAError, 'Cannot convert masked element to a Python float.' + return float(self._data.item()) + + def __int__(self): + "Converts self to int." + if self._mask is not nomask: + raise MAError, 'Cannot convert masked element to a Python int.' + return int(self._data.item()) + + @property + def dtype(self): + """returns the data type of `_data`.""" + return self._data.dtype + + def astype (self, tc): + """Returns self as an array of given type. +Subclassing is preserved.""" + if tc == self._data.dtype: + return self + try: + return self.__class__(self, mask=self._mask, dtype=tc, copy=True, + **self.options) + except: +# d = self._data.astype(tc) + return self.__class__(self._data.astype(tc), mask=self._mask, + dtype=tc, **self.options) +# +# + #............................................ + def harden_mask(self): + "Forces the mask to hard" + self._hardmask = True + def soften_mask(self): + "Forces the mask to soft" + self._hardmask = False + #............................................ + #TODO: FIX THAT: THAT"S NOT A REAL FLATITER + def _get_flat(self): + """Calculates the flat value. + """ + if self._mask is nomask: + return masked_array(self._data.ravel(), mask=nomask, copy=False, + fill_value = self._fill_value, + **self.options) + else: + return masked_array(self._data.ravel(), mask=self._mask.ravel(), + copy=False, fill_value = self._fill_value, + **self.options) + # + def _set_flat (self, value): + "x.flat = value" + y = self.ravel() + y[:] = value + # + flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") + # + #............................................ + def _get_real(self): + "Returns the real part of a complex array." + return self.__class__(self._data.real, mask=self.mask, + fill_value = self._fill_value, **self.options) + def _set_real (self, value): + "Sets the real part of a complex array to `value`." + y = self.real + y[...] = value + + real = property(fget=_get_real, fset=_set_real, doc="Get it real!") + + def _get_imaginary(self): + "Returns the imaginary part of a complex array." + return self.__class__(self._data.imag, mask=self.mask, + fill_value = self._fill_value, **self.options) + + def _set_imaginary (self, value): + "Sets the imaginary part of a complex array to `value`." + y = self.imaginary + y[...] = value + + imag = property(fget=_get_imaginary, fset=_set_imaginary, + doc="Imaginary part.") + imaginary = imag + #............................................ + def _get_mask(self): + """Returns the current mask.""" + return self._mask + def _set_mask(self, mask): + """Sets the mask to `mask`.""" + mask = make_mask(mask, copy=False, small_mask=True) + if mask is not nomask: + if mask.size != self._data.size: + raise ValueError, "Inconsistent shape between data and mask!" + if mask.shape != self._data.shape: + mask.shape = self._data.shape + self._mask = mask + else: + self._mask = nomask + mask = property(fget=_get_mask, fset=_set_mask, doc="Mask") + #............................................ + def get_fill_value(self): + "Returns the filling value." + return self._fill_value + + def set_fill_value(self, value=None): + """Sets the filling value to `value`. +If None, uses the default, based on the data type.""" + if value is None: + value = default_fill_value(self._data) + self._fill_value = value + + fill_value = property(fget=get_fill_value, fset=set_fill_value, + doc="Filling value") + + def filled(self, fill_value=None): + """Returns an array of the same class as `_data`, + with masked values filled with `fill_value`. +Subclassing is preserved. + +If `fill_value` is None, uses self.fill_value. + """ + d = self._data + m = self._mask + if m is nomask: + return d + # + if fill_value is None: + value = self.fill_value + else: + value = fill_value + # + if self is masked_singleton: + result = numeric.asanyarray(value) + else: + result = d.copy() + try: + result[m] = value + except (TypeError, AttributeError): + value = numeric.array(value, dtype=object) + d = d.astype(object) + result = fromnumeric.choose(m, (d, value)) + except IndexError: + #ok, if scalar + if d.shape: + raise + elif m: + result = numeric.array(value, dtype=d.dtype) + else: + result = d + return result + + def compressed(self): + "A 1-D array of all the non-masked data." + d = self._data.ravel() + if self._mask is nomask: + return d + else: + return d[~self._mask.ravel()] + #............................................ + def count(self, axis=None): + """Counts the non-masked elements of the array along a given axis, +and returns a masked array where the mask is True where all data are masked. +If `axis` is None, counts all the non-masked elements, and returns either a +scalar or the masked singleton.""" + m = self._mask + s = self._data.shape + ls = len(s) + if m is nomask: + if ls == 0: + return 1 + if ls == 1: + return s[0] + if axis is None: + return self._data.size + else: + n = s[axis] + t = list(s) + del t[axis] + return numeric.ones(t) * n + n1 = fromnumeric.size(m, axis) + n2 = m.astype(int_).sum(axis) + if axis is None: + return (n1-n2) + else: + return masked_array(n1 - n2) + #............................................ + def _get_shape(self): + "Returns the current shape." + return self._data.shape + # + def _set_shape (self, newshape): + "Sets the array's shape." + self._data.shape = newshape + if self._mask is not nomask: + #self._mask = self._mask.copy() + self._mask.shape = newshape + # + shape = property(fget=_get_shape, fset=_set_shape, + doc="Shape of the array, as a tuple.") + # + def _get_size(self): + "Returns the current size." + return self._data.size + size = property(fget=_get_size, + doc="Size (number of elements) of the array.") + # + def _get_ndim(self): + "Returns the number of dimensions." + return self._data.ndim + ndim = property(fget=_get_ndim, + doc="Number of dimensions of the array.") + # + def reshape (self, *s): + """Reshapes the array to shape s. +Returns a new masked array. +If you want to modify the shape in place, please use `a.shape = s`""" + if self._mask is not nomask: + return self.__class__(self._data.reshape(*s), + mask=self._mask.reshape(*s), + fill_value=self.fill_value, **self.options) + else: + return self.__class__(self._data.reshape(*s), + fill_value=self.fill_value, **self.options) + # + def repeat(self, repeats, axis=None): + """Repeat elements of `a` `repeats` times along `axis`. +`repeats` is a sequence of length `a.shape[axis]` telling how many times +each element should be repeated. +The mask is repeated accordingly. + """ + f = self.filled() + if isinstance(repeats, types.IntType): + if axis is None: + num = f.size + else: + num = f.shape[axis] + repeats = tuple([repeats]*num) + + m = self._mask + if m is not nomask: + m = fromnumeric.repeat(m, repeats, axis) + d = fromnumeric.repeat(f, repeats, axis) + return self.__class__(d, mask=m, fill_value=self.fill_value, + **self.options) + # + def resize(self, newshape, refcheck=True, order=False): + """Attempts to modify size and shape of self inplace. + The array must own its own memory and not be referenced by other arrays. + Returns None. + """ + try: + self._data.resize(newshape,) + if self.mask is not nomask: + self._mask.resize(newshape,) + except ValueError: + msg = "Cannot resize an array that has been referenced or "+\ + "is referencing another array in this way.\n"+\ + "Use the resize function." + raise ValueError, msg + return None + # + def flatten(self): + """Flattens the array in place. + """ + flatsize = self.size + self._data.resize((flatsize,)) + if self.mask is not nomask: + self._mask.resize((flatsize,)) + return self + + # + def put(self, indices, values, mode='raise'): + """Sets storage-indexed locations to corresponding values. +a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices. +`values` can be scalar or an array shorter than indices, and it will be repeat, +if necessary. +If `values` has some masked values, the initial mask is updated in consequence, +else the corresponding values are unmasked. + """ + #TODO: Check that + (d, m) = (self._data, self._mask) + ind = filled(indices) + v = filled(values) + d.put(ind, v, mode=mode) + if m is not nomask: + if getmask(values) is not nomask: + m.put(ind, values._mask, mode=mode) + else: + m.put(ind, False, mode=mode) + self._mask = make_mask(m, copy=False, small_mask=True) + #............................................ + def ids (self): + """Return the ids of the data and mask areas.""" + return (id(self._data), id(self._mask)) + #............................................ + def all(self, axis=None): + """a.all(axis) returns True if all entries along the axis are True. + Returns False otherwise. If axis is None, uses the flatten array. + Masked data are considered as True during computation. + Outputs a masked array, where the mask is True if all data are masked along the axis. + """ + d = filled(self, True).all(axis) + m = self._mask.all(axis) + return self.__class__(d, mask=m, dtype=bool_, + fill_value=self._fill_value, **self.options) + def any(self, axis=None): + """a.any(axis) returns True if some or all entries along the axis are True. + Returns False otherwise. If axis is None, uses the flatten array. + Masked data are considered as False during computation. + Outputs a masked array, where the mask is True if all data are masked along the axis. + """ + d = filled(self, False).any(axis) + m = self._mask.all(axis) + return self.__class__(d, mask=m, dtype=bool_, + fill_value=self._fill_value, **self.options) + def nonzero(self): + """a.nonzero() returns a tuple of arrays + + Returns a tuple of arrays, one for each dimension of a, + containing the indices of the non-zero elements in that + dimension. The corresponding non-zero values can be obtained + with + a[a.nonzero()]. + + To group the indices by element, rather than dimension, use + transpose(a.nonzero()) + instead. The result of this is always a 2d array, with a row for + each non-zero element.""" + return numeric.asarray(self.filled(0)).nonzero() + #............................................ + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): + """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None) +Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`. + """ + #TODO: What are we doing with `out`? + (d,m) = (self._data, self._mask) + if m is nomask: + return d.trace(offset=offset, axis1=axis1, axis2=axis2, + out=out).astype(dtype) + else: + D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2, + ).astype(dtype) + return D.sum(axis=None) + #............................................ + def sum(self, axis=None, dtype=None): + """a.sum(axis=None, dtype=None) +Sums the array `a` over the given axis `axis`. +Masked values are set to 0. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: +# if axis is None: +# return self._data.sum(None, dtype=dtype) + return self.__class__(self._data.sum(axis, dtype=dtype), + mask=nomask, fill_value=self.fill_value, + **self.options) + else: +# if axis is None: +# return self.filled(0).sum(None, dtype=dtype) + return self.__class__(self.filled(0).sum(axis, dtype=dtype), + mask=self._mask.all(axis), + fill_value=self.fill_value, **self.options) + + def cumsum(self, axis=None, dtype=None): + """a.cumprod(axis=None, dtype=None) +Returns the cumulative sum of the elements of array `a` along the given axis `axis`. +Masked values are set to 0. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: +# if axis is None: +# return self._data.cumsum(None, dtype=dtype) + return self.__class__(self._data.cumsum(axis=axis, dtype=dtype), + fill_value=self.fill_value, **self.options) + else: +# if axis is None: +# return self.filled(0).cumsum(None, dtype=dtype) + return self.__class__(self.filled(0).cumsum(axis=axis, dtype=dtype), + mask=self._mask, fill_value=self.fill_value, + **self.options) + + def prod(self, axis=None, dtype=None): + """a.prod(axis=None, dtype=None) +Returns the product of the elements of array `a` along the given axis `axis`. +Masked elements are set to 1. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: +# if axis is None: +# return self._data.prod(None, dtype=dtype) + return self.__class__(self._data.prod(axis, dtype=dtype), + mask=nomask, fill_value=self.fill_value, + **self.options) +# return self.__class__(self._data.prod(axis=axis, dtype=dtype)) + else: +# if axis is None: +# return self.filled(1).prod(None, dtype=dtype) + return self.__class__(self.filled(1).prod(axis=axis, dtype=dtype), + mask=self._mask.all(axis), + fill_value=self.fill_value, + **self.options) + product = prod + + def cumprod(self, axis=None, dtype=None): + """a.cumprod(axis=None, dtype=None) +Returns the cumulative product of ethe lements of array `a` along the given axis `axis`. +Masked values are set to 1. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: +# if axis is None: +# return self._data.cumprod(None, dtype=dtype) + return self.__class__(self._data.cumprod(axis=axis, dtype=dtype), + mask=nomask, fill_value=self.fill_value, + **self.options) + else: +# if axis is None: +# return self.filled(1).cumprod(None, dtype=dtype) + return self.__class__(self.filled(1).cumprod(axis=axis, dtype=dtype), + mask=self._mask, + fill_value=self.fill_value, **self.options) + + def mean(self, axis=None, dtype=None): + """a.mean(axis=None, dtype=None) + + Averages the array over the given axis. If the axis is None, + averages over all dimensions of the array. Equivalent to + + a.sum(axis, dtype) / size(a, axis). + + The optional dtype argument is the data type for intermediate + calculations in the sum. + + Returns a masked array, of the same class as a. + """ + if self._mask is nomask: +# if axis is None: +# return self._data.mean(axis=None, dtype=dtype) + return self.__class__(self._data.mean(axis=axis, dtype=dtype), + mask=nomask, fill_value=self.fill_value, + **self.options) + else: + dsum = fromnumeric.sum(self.filled(0), axis=axis, dtype=dtype) + cnt = self.count(axis=axis) + mask = self._mask.all(axis) + if axis is None and mask: + return masked + return self.__class__(dsum*1./cnt, mask=mask, + fill_value=self.fill_value, **self.options) + + def anom(self, axis=None, dtype=None): + """a.anom(axis=None, dtype=None) + Returns the anomalies, or deviation from the average. + """ + m = self.mean(axis, dtype) + if not axis: + return (self - m) + else: + return (self - expand_dims(m,axis)) + + def var(self, axis=None, dtype=None): + """a.var(axis=None, dtype=None) +Returns the variance, a measure of the spread of a distribution. + +The variance is the average of the squared deviations from the mean, +i.e. var = mean((x - x.mean())**2). + """ + if self._mask is nomask: +# if axis is None: +# return self._data.var(axis=None, dtype=dtype) + return self.__class__(self._data.var(axis=axis, dtype=dtype), + mask=nomask, + fill_value=self.fill_value, **self.options) + else: + cnt = self.count(axis=axis) + danom = self.anom(axis=axis, dtype=dtype) + danom *= danom + dvar = danom.sum(axis) / cnt +# dvar /= cnt + if axis is None: + return dvar + return self.__class__(dvar, + mask=mask_or(self._mask.all(axis), (cnt==1)), + fill_value=self.fill_value, **self.options) + + def std(self, axis=None, dtype=None): + """a.std(axis=None, dtype=None) +Returns the standard deviation, a measure of the spread of a distribution. + +The standard deviation is the square root of the average of the squared +deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)). + """ + dvar = self.var(axis,dtype) + if axis is None: + if dvar is masked: + return masked + else: + # Should we use umath.sqrt instead ? + return sqrt(dvar) + return self.__class__(sqrt(dvar._data), mask=dvar._mask, + dtype = self.dtype, + fill_value=self.fill_value, **self.options) + #............................................ + def argsort(self, axis=None, fill_value=None, kind='quicksort', + order=None): + """Returns an array of indices that sort 'a' along the specified axis. + Masked values are filled beforehand to `fill_value`. + If `fill_value` is None, uses the default for the data type. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `kind` : String *['quicksort']* + Sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort' + + Returns: array of indices that sort 'a' along the specified axis. + + This method executes an indirect sort along the given axis using the + algorithm specified by the kind keyword. It returns an array of indices of + the same shape as 'a' that index data along the given axis in sorted order. + + The various sorts are characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order. The three + available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is not + along the last axis. Consequently, sorts along the last axis are faster and use + less space than sorts along other axis. + """ + if fill_value is None: + fill_value = default_fill_value(self._data) + d = self.filled(fill_value) + if axis is None: + return d.argsort(kind=kind, order=order) + return d.argsort(axis, kind=kind, order=order) + + def argmin(self, axis=None, fill_value=None): + """Returns the array of indices for the minimum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(self._data) + d = self.filled(fill_value) + if axis is None: + return d.argmin() + return d.argmin(axis) + + def argmax(self, axis=None, fill_value=None): + """Returns the array of indices for the maximum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(self._data) + try: + fill_value = - fill_value + except: + pass + d = self.filled(fill_value) + if axis is None: + return d.argmax() + return d.argmax(axis) + + def sort(self, axis=-1, kind='quicksort', order=None, endwith=True): + """ + Sort a along the given axis. + + Keyword arguments: + + axis -- axis to be sorted (default -1) + kind -- sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort'. + order -- If a has fields defined, then the order keyword can be the + field name to sort on or a list (or tuple) of field names + to indicate the order that fields should be used to define + the sort. + endwith--Boolean flag indicating whether missing values (if any) should + be forced in the upper indices (at the end of the array) or + lower indices (at the beginning). + + Returns: None. + + This method sorts 'a' in place along the given axis using the algorithm + specified by the kind keyword. + + The various sorts may characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order and is most + useful when used with argsort where the key might differ from the items + being sorted. The three available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is + not along the last axis. Consequently, sorts along the last axis are faster + and use less space than sorts along other axis. + + """ + + if endwith: + filler = minimum_fill_value(self.dtype) + else: + filler = maximum_fill_value(self.dtype) + indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order) + self._data = self._data[indx] + m = self._mask + if m is not nomask: + self._mask = m[indx] + return + #............................................ + # Backwards Compatibility. Heck... + @property + def data(self): + """Returns the `_data` part of the MaskedArray. +You should really use `_data` instead...""" + return self._data + def raw_data(self): + """Returns the `_data` part of the MaskedArray. +You should really use `_data` instead...""" + return self._data + +##.............................................................................. + + + +#class _arithmethods: +# """Defines a wrapper for arithmetic methods. +#Instead of directly calling a ufunc, the corresponding method of the `array._data` +#object is called instead. +# """ +# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): +# """ +#:Parameters: +# - `methodname` (String) : Method name. +# - `fill_self` (Float *[0]*) : Fill value for the instance. +# - `fill_other` (Float *[0]*) : Fill value for the target. +# - `domain` (Domain object *[None]*) : Domain of non-validity. +# """ +# self.methodname = methodname +# self.fill_self = fill_self +# self.fill_other = fill_other +# self.domain = domain +# # +# def __call__ (self, instance, other, *args): +# "Execute the call behavior." +# m_self = instance._mask +# m_other = getmask(other) +# base = filled(instance,self.fill_self) +# target = filled(other, self.fill_other) +# if self.domain is not None: +# # We need to force the domain to a ndarray only. +# if self.fill_other > self.fill_self: +# domain = self.domain(base, target) +# else: +# domain = self.domain(target, base) +# if domain.any(): +# #If `other` is a subclass of ndarray, `filled` must have the +# # same subclass, else we'll lose some info. +# #The easiest then is to fill `target` instead of creating +# # a pure ndarray. +# #Oh, and we better make a copy! +# if isinstance(other, ndarray): +# if target is other: +# # We don't want to modify other: let's copy target, then +# target = target.copy() +# target[:] = numeric.where(fromnumeric.asarray(domain), +# self.fill_other, target) +# else: +# target = numeric.where(fromnumeric.asarray(domain), +# self.fill_other, target) +# m_other = mask_or(m_other, domain) +# m = mask_or(m_self, m_other) +# method = getattr(base, self.methodname) +# return instance.__class__(method(target, *args), mask=m) +# # +# def patch(self): +# """Applies the method `func` from class `method` to MaskedArray""" +# return types.MethodType(self,None,MaskedArray) +#.............................................................................. +class _arithmethods(object): + """Defines a wrapper for arithmetic methods. +Instead of directly calling a ufunc, the corresponding method of the `array._data` +object is called instead. + """ + def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): + """ +:Parameters: + - `methodname` (String) : Method name. + - `fill_self` (Float *[0]*) : Fill value for the instance. + - `fill_other` (Float *[0]*) : Fill value for the target. + - `domain` (Domain object *[None]*) : Domain of non-validity. + """ + self.methodname = methodname + self.fill_self = fill_self + self.fill_other = fill_other + self.domain = domain + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self.methodname).__doc__ + except: + return getattr(numpy, self.methodname).__doc__ + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, other, *args): + "Execute the call behavior." + instance = self.obj + m_self = instance._mask + m_other = getmask(other) + base = filled(instance,self.fill_self) + target = filled(other, self.fill_other) + if self.domain is not None: + # We need to force the domain to a ndarray only. + if self.fill_other > self.fill_self: + domain = self.domain(base, target) + else: + domain = self.domain(target, base) + if domain.any(): + #If `other` is a subclass of ndarray, `filled` must have the + # same subclass, else we'll lose some info. + #The easiest then is to fill `target` instead of creating + # a pure ndarray. + #Oh, and we better make a copy! + if isinstance(other, ndarray): + if target is other or target is base: + # We don't want to modify other: let's copy target, then + # Same if target us base, instead... + target = target.copy() + target[:] = numeric.where(fromnumeric.asarray(domain), + self.fill_other, target) + else: + target = numeric.where(fromnumeric.asarray(domain), + self.fill_other, target) + m_other = mask_or(m_other, domain) + m = mask_or(m_self, m_other) + method = getattr(base, self.methodname) + return instance.__class__(method(target, *args), mask=m, + fill_value=instance.fill_value, + **instance.options) +#...................................... +class _compamethods(object): + """Defines comparison methods (eq, ge, gt...). +Instead of calling a ufunc, the method of the masked object is called. + """ + def __init__ (self, methodname, fill_self=0, fill_other=0): + """ +:Parameters: + - `methodname` (String) : Method name. + - `fill_self` (Float *[0]*) : Fill value for the instance. + - `fill_other` (Float *[0]*) : Fill value for the target. + - `domain` (Domain object *[None]*) : Domain of non-validity. + """ + self.methodname = methodname + self.fill_self = fill_self + self.fill_other = fill_other + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self.methodname).__doc__ + except: + return getattr(numpy, self.methodname).__doc__ + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, other, *args): + "Execute the call behavior." + instance = self.obj + m = mask_or(instance._mask, getmask(other), small_mask=False) + base = instance.filled(self.fill_self) + target = filled(other, self.fill_other) + method = getattr(base, self.methodname) + return instance.__class__(method(target, *args), mask=m, + **instance.options) +#.......................................................... +MaskedArray.__add__ = _arithmethods('__add__') +MaskedArray.__radd__ = _arithmethods('__add__') +MaskedArray.__sub__ = _arithmethods('__sub__') +MaskedArray.__rsub__ = _arithmethods('__rsub__') +MaskedArray.__pow__ = _arithmethods('__pow__') +MaskedArray.__mul__ = _arithmethods('__mul__', 1, 1) +MaskedArray.__rmul__ = _arithmethods('__mul__', 1, 1) +MaskedArray.__div__ = _arithmethods('__div__', 0, 1, + domain_safe_divide()) +MaskedArray.__rdiv__ = _arithmethods('__rdiv__', 1, 0, + domain_safe_divide()) +MaskedArray.__truediv__ = _arithmethods('__truediv__', 0, 1, + domain_safe_divide()) +MaskedArray.__rtruediv__ = _arithmethods('__rtruediv__', 1, 0, + domain_safe_divide()) +MaskedArray.__floordiv__ = _arithmethods('__floordiv__', 0, 1, + domain_safe_divide()) +MaskedArray.__rfloordiv__ = _arithmethods('__rfloordiv__', 1, 0, + domain_safe_divide()) +MaskedArray.__eq__ = _compamethods('__eq__') +MaskedArray.__ne__ = _compamethods('__ne__') +MaskedArray.__le__ = _compamethods('__le__') +MaskedArray.__lt__ = _compamethods('__lt__') +MaskedArray.__ge__ = _compamethods('__ge__') +MaskedArray.__gt__ = _compamethods('__gt__') +#####-------------------------------------------------------------------------- +#---- --- Shortcuts --- +#####--------------------------------------------------------------------------- +def isMaskedArray (x): + "Is x a masked array, that is, an instance of MaskedArray?" + return isinstance(x, MaskedArray) +isarray = isMaskedArray +isMA = isMaskedArray #backward compatibility +#masked = MaskedArray(0, int, mask=1) +masked_singleton = MaskedArray(0, dtype=int_, mask=True) +masked = masked_singleton + +masked_array = MaskedArray +def array(data, dtype=None, copy=False, order=False, mask=nomask, + keep_mask=True, small_mask=True, hard_mask=None, fill_value=None): + """array(data, dtype=None, copy=True, order=False, mask=nomask, + keep_mask=True, small_mask=True, fill_value=None) +Acts as shortcut to MaskedArray, with options in a different order for convenience. +And backwards compatibility... + """ + return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, + keep_mask=keep_mask, small_mask=small_mask, + hard_mask=hard_mask, fill_value=fill_value) + +def is_masked(x): + """Returns whether x has some masked values.""" + m = getmask(x) + if m is nomask: + return False + elif m.any(): + return True + return False + + +#####-------------------------------------------------------------------------- +#---- --- Patch methods --- +#####-------------------------------------------------------------------------- +#class _arraymethod: +# """Defines a wrapper for basic array methods. +#Upon call, returns a masked array, where the new `_data` array is the output +#of the corresponding method called on the original `_data`. +# +#If `onmask` is True, the new mask is the output of the method calld on the initial mask. +#If `onmask` is False, the new mask is just a reference to the initial mask. +# +#:Parameters: +# `funcname` : String +# Name of the function to apply on data. +# `onmask` : Boolean *[True]* +# Whether the mask must be processed also (True) or left alone (False). +# """ +# def __init__(self, funcname, onmask=True): +# self._name = funcname +# self._onmask = onmask +# self.__doc__ = getattr(ndarray, self._name).__doc__ +# def __call__(self, instance, *args, **params): +# methodname = self._name +# (d,m) = (instance._data, instance._mask) +# C = instance.__class__ +# if m is nomask: +# return C(getattr(d,methodname).__call__(*args, **params)) +# elif self._onmask: +# return C(getattr(d,methodname).__call__(*args, **params), +# mask=getattr(m,methodname)(*args, **params) ) +# else: +# return C(getattr(d,methodname).__call__(*args, **params), mask=m) +# +# def patch(self): +# "Adds the new method to MaskedArray." +# return types.MethodType(self, None, MaskedArray) +##...................................... +#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() +#MaskedArray.diagonal = _arraymethod('diagonal').patch() +#MaskedArray.take = _arraymethod('take').patch() +#MaskedArray.ravel = _arraymethod('ravel').patch() +#MaskedArray.transpose = _arraymethod('transpose').patch() +#MaskedArray.T = _arraymethod('transpose').patch() +#MaskedArray.swapaxes = _arraymethod('swapaxes').patch() +#MaskedArray.clip = _arraymethod('clip', onmask=False).patch() +#MaskedArray.compress = _arraymethod('compress').patch() +#MaskedArray.resize = _arraymethod('resize').patch() +#MaskedArray.copy = _arraymethod('copy').patch() + +class _arraymethod(object): + """Defines a wrapper for basic array methods. +Upon call, returns a masked array, where the new `_data` array is the output +of the corresponding method called on the original `_data`. + +If `onmask` is True, the new mask is the output of the method calld on the initial mask. +If `onmask` is False, the new mask is just a reference to the initial mask. + +:Parameters: + `funcname` : String + Name of the function to apply on data. + `onmask` : Boolean *[True]* + Whether the mask must be processed also (True) or left alone (False). + """ + def __init__(self, funcname, onmask=True): + self._name = funcname + self._onmask = onmask + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self._name).__doc__ + except: + return getattr(numpy, self._name).__doc__ + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__(self, *args, **params): + methodname = self._name + obj = self.obj + (d, m) = (obj._data, obj._mask) + (t, f) = (obj.dtype, obj._fill_value) + C = self.obj.__class__ + if m is nomask: + return C(getattr(d,methodname).__call__(*args, **params), + dtype=t, fill_value=f) + elif self._onmask: + return C(getattr(d,methodname).__call__(*args, **params), + mask=getattr(m,methodname)(*args, **params), + dtype=t, fill_value=f, **obj.options) + else: + return C(getattr(d,methodname).__call__(*args, **params), mask=m, + dtype=t, fill_value=f, **obj.options) +#...................................... +MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate') +MaskedArray.copy = _arraymethod('copy') +MaskedArray.diagonal = _arraymethod('diagonal') +MaskedArray.take = _arraymethod('take') +MaskedArray.ravel = _arraymethod('ravel') +MaskedArray.transpose = _arraymethod('transpose') +MaskedArray.T = property(fget=lambda self:self.transpose()) +MaskedArray.swapaxes = _arraymethod('swapaxes') +MaskedArray.clip = _arraymethod('clip', onmask=False) +MaskedArray.compress = _arraymethod('compress') +MaskedArray.copy = _arraymethod('copy') +MaskedArray.squeeze = _arraymethod('squeeze') + +#####-------------------------------------------------------------------------- +#---- --- Extrema functions --- +#####-------------------------------------------------------------------------- +class _minimum_operation: + "Object to calculate minima" + def __init__ (self): + """minimum(a, b) or minimum(a) +In one argument case, returns the scalar minimum. + """ + pass + #......... + def __call__ (self, a, b=None): + "Execute the call behavior." + if b is None: + m = getmask(a) + if m is nomask: + d = amin(filled(a).ravel()) + return d + ac = a.compressed() + if len(ac) == 0: + return masked + else: + return amin(ac) + else: + return where(less(a, b), a, b) + #......... + def reduce(self, target, axis=0): + """Reduces `target` along the given `axis`.""" + m = getmask(target) + if m is nomask: + t = filled(target) + return masked_array (umath.minimum.reduce (t, axis)) + else: + t = umath.minimum.reduce(filled(target, minimum_fill_value(target)), + axis) + m = umath.logical_and.reduce(m, axis) +# return masked_array(t, mask=m, fill_value=get_fill_value(target)) + try: + return target.__class__(t, mask=m, dtype=t.dtype, + fill_value=get_fill_value(target)) + except AttributeError: + return masked_array(t, mask=m, dtype=t.dtype, + fill_value=get_fill_value(target)) + #......... + def outer(self, a, b): + "Returns the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + m = nomask + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + d = umath.minimum.outer(filled(a), filled(b)) + return masked_array(d, mask=m) + +def min(array, axis=None, out=None): + """Returns the minima along the given axis. +If `axis` is None, applies to the flattened array.""" + if out is not None: + raise TypeError("Output arrays Unsupported for masked arrays") + if axis is None: + return minimum(array) + else: + return minimum.reduce(array, axis) +#................................................ +class _maximum_operation: + "Object to calculate maxima" + def __init__ (self): + """maximum(a, b) or maximum(a) + In one argument case returns the scalar maximum. + """ + pass + #......... + def __call__ (self, a, b=None): + "Executes the call behavior." + if b is None: + m = getmask(a) + if m is nomask: + d = amax(filled(a).ravel()) + return d + ac = a.compressed() + if len(ac) == 0: + return masked + else: + return amax(ac) + else: + return where(greater(a, b), a, b) + #......... + def reduce (self, target, axis=0): + """Reduces target along the given axis.""" + m = getmask(target) + if m is nomask: + t = filled(target) + return masked_array(umath.maximum.reduce (t, axis)) + else: + t = umath.maximum.reduce(filled(target, maximum_fill_value(target)), + axis) + m = umath.logical_and.reduce(m, axis) + try: + return target.__class__(t, mask=m, dtype=t.dtype, + fill_value=get_fill_value(target)) + except AttributeError: + return masked_array(t, mask=m, dtype=t.dtype, + fill_value=get_fill_value(target)) + #......... + def outer (self, a, b): + "Returns the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + m = nomask + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + d = umath.maximum.outer(filled(a), filled(b)) + return masked_array(d, mask=m) + +def max(obj, axis=None, out=None): + """Returns the maxima along the given axis. +If `axis` is None, applies to the flattened array.""" + if out is not None: + raise TypeError("Output arrays Unsupported for masked arrays") + if axis is None: + return maximum(obj) + else: + return maximum.reduce(obj, axis) +#................................................ +def ptp(obj, axis=None): + """a.ptp(axis=None) = a.max(axis)-a.min(axis)""" + try: + return obj.max(axis)-obj.min(axis) + except AttributeError: + return max(obj, axis=axis) - min(obj, axis=axis) +#................................................ +MaskedArray.min = min +MaskedArray.max = max +MaskedArray.ptp = ptp + +#####--------------------------------------------------------------------------- +#---- --- Definition of functions from the corresponding methods --- +#####--------------------------------------------------------------------------- +class _frommethod: + """Defines functions from existing MaskedArray methods. +:ivar _methodname (String): Name of the method to transform. + """ + def __init__(self, methodname): + self._methodname = methodname + self.__doc__ = self.getdoc() + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self._methodname).__doc__ + except: + return getattr(numpy, self._methodname).__doc__ + def __call__(self, x, *args, **params): + if isinstance(x, MaskedArray): + return getattr(x, self._methodname).__call__(*args, **params) + #FIXME: As x is not a MaskedArray, we transform it to a ndarray with asarray + #FIXME: ... and call the corresponding method. + #FIXME: Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2))) + #FIXME: we end up with a "SystemError: NULL result without error in PyObject_Call" + #FIXME: A dirty trick is then to call the initial numpy function... + method = getattr(fromnumeric.asarray(x), self._methodname) + try: + return method(*args, **params) + except SystemError: + return getattr(numpy,self._methodname).__call__(x, *args, **params) + +all = _frommethod('all') +anomalies = anom = _frommethod('anom') +any = _frommethod('any') +conjugate = _frommethod('conjugate') +ids = _frommethod('ids') +nonzero = _frommethod('nonzero') +diagonal = _frommethod('diagonal') +maximum = _maximum_operation() +mean = _frommethod('mean') +minimum = _minimum_operation () +product = _frommethod('prod') +ptp = _frommethod('ptp') +ravel = _frommethod('ravel') +repeat = _frommethod('repeat') +std = _frommethod('std') +sum = _frommethod('sum') +swapaxes = _frommethod('swapaxes') +take = _frommethod('take') +var = _frommethod('var') + +#.............................................................................. +def power(a, b, third=None): + """Computes a**b elementwise. + Masked values are set to 1.""" + if third is not None: + raise MAError, "3-argument power not supported." + ma = getmask(a) + mb = getmask(b) + m = mask_or(ma, mb) + fa = filled(a, 1) + fb = filled(b, 1) + if fb.dtype.char in typecodes["Integer"]: + return masked_array(umath.power(fa, fb), m) + md = make_mask((fa < 0), small_mask=1) + m = mask_or(m, md) + if m is nomask: + return masked_array(umath.power(fa, fb)) + else: + fa[m] = 1 + return masked_array(umath.power(fa, fb), m) + +#.............................................................................. +def argsort(a, axis=None, kind='quicksort', fill_value=None): + """Returns an array of indices that sort 'a' along the specified axis. + Masked values are filled beforehand to `fill_value`. + If `fill_value` is None, uses the default for the data type. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `kind` : String *['quicksort']* + Sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort' + + Returns: array of indices that sort 'a' along the specified axis. + + This method executes an indirect sort along the given axis using the + algorithm specified by the kind keyword. It returns an array of indices of + the same shape as 'a' that index data along the given axis in sorted order. + + The various sorts are characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order. The three + available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is not + along the last axis. Consequently, sorts along the last axis are faster and use + less space than sorts along other axis. + """ + if fill_value is None: + fill_value = default_fill_value(a) + d = filled(a, fill_value) + if axis is None: + return d.argsort(kind=kind) + return d.argsort(axis, kind) + +def argmin(a, axis=None, fill_value=None): + """Returns the array of indices for the minimum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(a) + d = filled(a, fill_value) + if axis is None: + return d.argmin(axis=None) + return d.argmin(axis=axis) + +def argmax(a, axis=None, fill_value=None): + """Returns the array of indices for the maximum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(a) + try: + fill_value = - fill_value + except: + pass + d = filled(a, fill_value) + if axis is None: + return d.argmax(axis=None) + return d.argmax(axis=axis) + +def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): + """ + Sort a along the given axis. + +Keyword arguments: + +axis -- axis to be sorted (default -1) +kind -- sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort'. +order -- If a has fields defined, then the order keyword can be the + field name to sort on or a list (or tuple) of field names + to indicate the order that fields should be used to define + the sort. +endwith--Boolean flag indicating whether missing values (if any) should + be forced in the upper indices (at the end of the array) or + lower indices (at the beginning). + +Returns: None. + +This method sorts 'a' in place along the given axis using the algorithm +specified by the kind keyword. + +The various sorts may characterized by average speed, worst case +performance, need for work space, and whether they are stable. A stable +sort keeps items with the same key in the same relative order and is most +useful when used with argsort where the key might differ from the items +being sorted. The three available algorithms have the following properties: + +|------------------------------------------------------| +| kind | speed | worst case | work space | stable| +|------------------------------------------------------| +|'quicksort'| 1 | O(n^2) | 0 | no | +|'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | +|'heapsort' | 3 | O(n*log(n)) | 0 | no | +|------------------------------------------------------| + +All the sort algorithms make temporary copies of the data when the sort is +not along the last axis. Consequently, sorts along the last axis are faster +and use less space than sorts along other axis. + +""" + a = numeric.asanyarray(a) + if fill_value is None: + if endwith: + filler = minimum_fill_value(a) + else: + filler = maximum_fill_value(a) + else: + filler = fill_value +# return + indx = numpy.indices(a.shape).tolist() + indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order) + return a[indx] + +def compressed(x): + """Returns a compressed version of a masked array (or just the array if it + wasn't masked first).""" + if getmask(x) is None: + return x + else: + return x.compressed() + +def count(a, axis = None): + "Count of the non-masked elements in a, or along a certain axis." + a = masked_array(a) + return a.count(axis) + +def concatenate(arrays, axis=0): + "Concatenates the arrays along the given axis" + #TODO: We lose the subclass, here! We should keep track of the classes... + #TODO: ...and find the max ? the lowest according to MRO? + d = [] + for x in arrays: + d.append(filled(x)) + d = numeric.concatenate(d, axis) + for x in arrays: + if getmask(x) is not nomask: + break + else: + return masked_array(d) + dm = [] + for x in arrays: + dm.append(getmaskarray(x)) + dm = make_mask(numeric.concatenate(dm, axis), copy=False, small_mask=True) + return masked_array(d, mask=dm) + +def expand_dims(x,axis): + """Expand the shape of a by including newaxis before given axis.""" + if isinstance(x, MaskedArray): + (d,m) = (x._data, x._mask) + if m is nomask: + return masked_array(n_expand_dims(d,axis), + dtype=d.dtype, fill_value=x._fill_value) + else: + return masked_array(n_expand_dims(d,axis), + mask=n_expand_dims(m,axis), + dtype=d.dtype, fill_value=x._fill_value) + else: + return n_expand_dims(x,axis) + +#...................................... +def left_shift (a, n): + "Left shift n bits" + m = getmask(a) + if m is nomask: + d = umath.left_shift(filled(a), n) + return masked_array(d) + else: + d = umath.left_shift(filled(a, 0), n) + return masked_array(d, mask=m) + +def right_shift (a, n): + "Right shift n bits" + m = getmask(a) + if m is nomask: + d = umath.right_shift(filled(a), n) + return masked_array(d) + else: + d = umath.right_shift(filled(a, 0), n) + return masked_array(d, mask=m) +#...................................... +def put(a, indices, values, mode='raise'): + """Sets storage-indexed locations to corresponding values. + Values and indices are filled if necessary.""" + # We can't use 'frommethod', the order of arguments is different + try: + return a.put(indices, values, mode=mode) + except AttributeError: + return fromnumeric.asarray(a).put(indices, values, mode=mode) + +def putmask(a, mask, values): #, mode='raise'): + """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true. +If `v` is shorter than `mask`, it will be repeated as necessary. +In particular `v` can be a scalar or length 1 array.""" + # We can't use 'frommethod', the order of arguments is different + try: + return a.putmask(values, mask) + except AttributeError: + return fromnumeric.asarray(a).putmask(values, mask) + +def transpose(a,axes=None): + """Returns a view of the array with dimensions permuted according to axes. +If `axes` is None (default), returns array with dimensions reversed. + """ + #We can't use 'frommethod', as 'transpose' doesn't take keywords + try: + return a.transpose(axes) + except AttributeError: + return fromnumeric.asarray(a).transpose(axes) + +def reshape(a, new_shape): + """Changes the shape of the array `a` to `new_shape`.""" + #We can't use 'frommethod', it whine about some parameters. Dmmit. + try: + return a.reshape(new_shape) + except AttributeError: + return fromnumeric.asarray(a).reshape(new_shape) + +def resize(x, new_shape): + """resize(a,new_shape) returns a new array with the specified shape. + The total size of the original array can be any size. + The new array is filled with repeated copies of a. If a was masked, the new + array will be masked, and the new mask will be a repetition of the old one. + """ + # We can't use _frommethods here, as N.resize is notoriously whiny. + m = getmask(x) + if m is not nomask: + m = fromnumeric.resize(m, new_shape) + if isinstance(x, MaskedArray): + result = x.__class__(fromnumeric.resize(filled(x), new_shape), mask=m) + else: + result = masked_array(fromnumeric.resize(filled(x), new_shape), mask=m) + result.set_fill_value(get_fill_value(x)) + return result + + +#................................................ +def rank(obj): + """Gets the rank of sequence a (the number of dimensions, not a matrix rank) +The rank of a scalar is zero.""" + return fromnumeric.rank(filled(obj)) +# +def shape(obj): + """Returns the shape of `a` (as a function call which also works on nested sequences). + """ + return fromnumeric.shape(filled(obj)) +# +def size(obj, axis=None): + """Returns the number of elements in the array along the given axis, +or in the sequence if `axis` is None. + """ + return fromnumeric.size(filled(obj), axis) +#................................................ + +#####-------------------------------------------------------------------------- +#---- --- Extra functions --- +#####-------------------------------------------------------------------------- +def where (condition, x, y): + """where(condition, x, y) is x where condition is nonzero, y otherwise. + condition must be convertible to an integer array. + Answer is always the shape of condition. + The type depends on x and y. It is integer if both x and y are + the value masked. + """ + fc = filled(not_equal(condition, 0), 0) + xv = filled(x) + xm = getmask(x) + yv = filled(y) + ym = getmask(y) + d = numeric.choose(fc, (yv, xv)) + md = numeric.choose(fc, (ym, xm)) + m = getmask(condition) + m = make_mask(mask_or(m, md), copy=False, small_mask=True) + return masked_array(d, mask=m) + +def choose (indices, t, out=None, mode='raise'): + "Returns array shaped like indices with elements chosen from t" + #TODO: implement options `out` and `mode`, if possible. + def fmask (x): + "Returns the filled array, or True if ``masked``." + if x is masked: + return 1 + return filled(x) + def nmask (x): + "Returns the mask, True if ``masked``, False if ``nomask``." + if x is masked: + return 1 + m = getmask(x) + if m is nomask: + return 0 + return m + c = filled(indices, 0) + masks = [nmask(x) for x in t] + a = [fmask(x) for x in t] + d = numeric.choose(c, a) + m = numeric.choose(c, masks) + m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1) + return masked_array(d, mask=m) + +def round_(a, decimals=0, out=None): + """Returns reference to result. Copies a and rounds to 'decimals' places. + + Keyword arguments: + decimals -- number of decimals to round to (default 0). May be negative. + out -- existing array to use for output (default copy of a). + + Return: + Reference to out, where None specifies a copy of the original array a. + + Round to the specified number of decimals. When 'decimals' is negative it + specifies the number of positions to the left of the decimal point. The + real and imaginary parts of complex numbers are rounded separately. + Nothing is done if the array is not of float type and 'decimals' is greater + than or equal to 0.""" + if not hasattr(a, "_mask"): + mask = nomask + else: + mask = a._mask + if out is None: + return a.__class__(fromnumeric.round_(a, decimals, None), mask=mask) + else: + out = a.__class__(fromnumeric.round_(a, decimals, out), mask=mask) + return out + +def arange(start, stop=None, step=1, dtype=None): + """Just like range() except it returns a array whose type can be specified + by the keyword argument dtype. + """ + return array(numeric.arange(start, stop, step, dtype), mask=nomask) + +def inner(a, b): + """inner(a,b) returns the dot product of two arrays, which has + shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the + product of the elements from the last dimensions of a and b. + Masked elements are replace by zeros. + """ + fa = filled(a, 0) + fb = filled(b, 0) + if len(fa.shape) == 0: + fa.shape = (1,) + if len(fb.shape) == 0: + fb.shape = (1,) + return masked_array(numeric.inner(fa, fb)) +innerproduct = inner + +def outer(a, b): + """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))""" + fa = filled(a, 0).ravel() + fb = filled(b, 0).ravel() + d = numeric.outer(fa, fb) + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + return masked_array(d) + ma = getmaskarray(a) + mb = getmaskarray(b) + m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0) + return masked_array(d, mask=m) +outerproduct = outer + +def allequal (a, b, fill_value=True): + """ +Returns `True` if all entries of a and b are equal, using +fill_value as a truth value where either or both are masked. + """ + m = mask_or(getmask(a), getmask(b)) + if m is nomask: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + return d.all() + elif fill_value: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + dm = array(d, mask=m, copy=False) + return dm.filled(True).all(None) + else: + return False + +def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8): + """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances. +If `fill_value` is True, masked values are considered equal. +If `fill_value` is False, masked values considered unequal. +The relative error rtol should be positive and << 1.0 +The absolute error `atol` comes into play for those elements of `b` + that are very small or zero; it says how small `a` must be also. + """ + m = mask_or(getmask(a), getmask(b)) + d1 = filled(a) + d2 = filled(b) + x = filled(array(d1, copy=0, mask=m), fill_value).astype(float) + y = filled(array(d2, copy=0, mask=m), 1).astype(float) + d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y)) + return fromnumeric.alltrue(fromnumeric.ravel(d)) + +#.............................................................................. +def asarray(a, dtype=None): + """asarray(data, dtype) = array(data, dtype, copy=0) +Returns `a` as an masked array. +No copy is performed if `a` is already an array. +Subclasses are converted to base class MaskedArray. + """ + return masked_array(a, dtype=dtype, copy=False, keep_mask=True) + +def empty(new_shape, dtype=float): + """empty((d1,...,dn),dtype=float,order='C') +Returns a new array of shape (d1,...,dn) and given type with all its +entries uninitialized. This can be faster than zeros.""" + return masked_array(numeric.empty(new_shape, dtype), mask=nomask) + +def empty_like(a): + """empty_like(a) +Returns an empty (uninitialized) array of the shape and typecode of a. +Note that this does NOT initialize the returned array. +If you require your array to be initialized, you should use zeros_like().""" + return masked_array(numeric.empty_like(a), mask=nomask) + +def ones(new_shape, dtype=float): + """ones(shape, dtype=None) +Returns an array of the given dimensions, initialized to all ones.""" + return masked_array(numeric.ones(new_shape, dtype), mask=nomask) + +def zeros(new_shape, dtype=float): + """zeros(new_shape, dtype=None) +Returns an array of the given dimensions, initialized to all zeros.""" + return masked_array(numeric.zeros(new_shape, dtype), mask=nomask) + +#####-------------------------------------------------------------------------- +#---- --- Pickling --- +#####-------------------------------------------------------------------------- +#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data +def _mareconstruct(subtype, baseshape, basetype,): + """Internal function that builds a new MaskedArray from the information stored +in a pickle.""" + _data = ndarray.__new__(ndarray, baseshape, basetype) + _mask = ndarray.__new__(ndarray, baseshape, basetype) + return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False) + +def _getstate(a): + "Returns the internal state of the masked array, for pickling purposes." + state = (1, + a.shape, + a.dtype, + a.flags.fnc, + (a._data).__reduce__()[-1][-1], + getmaskarray(a).__reduce__()[-1][-1]) + return state + +def _setstate(a, state): + """Restores the internal state of the masked array, for pickling purposes. +`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: + + - class name + - a tuple giving the shape of the data + - a typecode for the data + - a binary string for the data + - a binary string for the mask. + """ + (ver, shp, typ, isf, raw, msk) = state + (a._data).__setstate__((shp, typ, isf, raw)) + (a._mask).__setstate__((shp, dtype('|b1'), isf, msk)) + +def _reduce(a): + """Returns a 3-tuple for pickling a MaskedArray.""" + return (_mareconstruct, + (a.__class__, (0,), 'b', ), + a.__getstate__()) + +def dump(a,F): + """Pickles the MaskedArray `a` to the file `F`. +`F` can either be the handle of an exiting file, or a string representing a file name. + """ + if not hasattr(F,'readline'): + F = open(F,'w') + return cPickle.dump(a,F) + +def dumps(a): + """Returns a string corresponding to the pickling of the MaskedArray.""" + return cPickle.dumps(a) + +def load(F): + """Wrapper around ``cPickle.load`` which accepts either a file-like object or + a filename.""" + if not hasattr(F, 'readline'): + F = open(F,'r') + return cPickle.load(F) + +def loads(strg): + "Loads a pickle from the current string.""" + return cPickle.loads(strg) + +MaskedArray.__getstate__ = _getstate +MaskedArray.__setstate__ = _setstate +MaskedArray.__reduce__ = _reduce +MaskedArray.__dump__ = dump +MaskedArray.__dumps__ = dumps + +################################################################################ + +if __name__ == '__main__': + import numpy as N + if 1: + x = N.array([[ 0.13, 0.26, 0.90], + [ 0.28, 0.33, 0.63], + [ 0.31, 0.87, 0.70]]) + m = N.array([[ True, False, False], + [False, False, False], + [True, True, False]], dtype=N.bool_) + X = N.asmatrix(x) + mX = masked_array(X, mask=m) \ No newline at end of file Property changes on: trunk/Lib/sandbox/maskedarray/core_ini.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/maskedarray/extras.py =================================================================== --- trunk/Lib/sandbox/maskedarray/extras.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/extras.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -193,7 +193,7 @@ if asscalar: dtypes.append(numeric.asarray(res).dtype) outarr = zeros(outshape, object_) - outarr[ind] = res + outarr[tuple(ind)] = res Ntot = numeric.product(outshape) k = 1 while k < Ntot: @@ -206,7 +206,7 @@ n -= 1 i.put(indlist,ind) res = func1d(arr[tuple(i.tolist())],*args) - outarr[ind] = res + outarr[tuple(ind)] = res dtypes.append(asarray(res).dtype) k += 1 else: Modified: trunk/Lib/sandbox/maskedarray/tests/test_core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -1,6 +1,5 @@ # pylint: disable-msg=W0611, W0612, W0511,R0201 -"""Tests suite for MaskedArray. -Adapted from the original test_ma by Pierre Gerard-Marchant +"""Tests suite for MaskedArray & subclassing. :author: Pierre Gerard-Marchant :contact: pierregm_at_uga_dot_edu @@ -22,8 +21,8 @@ reload(maskedarray.testutils) from maskedarray.testutils import * -import maskedarray.core -reload(maskedarray.core) +import maskedarray.core as coremodule +reload(coremodule) from maskedarray.core import * pi = N.pi @@ -50,7 +49,7 @@ xm.set_fill_value(1.e+20) self.d = (x, y, a10, m1, m2, xm, ym, z, zm, xf) #........................ - def check_testBasic1d(self): + def check_basic1d(self): "Test of basic array creation and properties in 1 dimension." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d assert(not isMaskedArray(x)) @@ -68,7 +67,7 @@ assert_array_equal(filled(xm, 1.e20), xf) assert_array_equal(x, xm) #........................ - def check_testBasic2d(self): + def check_basic2d(self): "Test of basic array creation and properties in 2 dimensions." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d for s in [(4,3), (6,2)]: @@ -88,7 +87,7 @@ assert_equal(filled(xm, 1.e20), xf) assert_equal(x, xm) #........................ - def check_testArithmetic (self): + def check_basic_arithmetic (self): "Test of basic arithmetic." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d a2d = array([[1,2],[0,4]]) @@ -123,14 +122,131 @@ assert_equal(N.multiply(x,y), multiply(xm, ym)) assert_equal(N.divide(x,y), divide(xm, ym)) #........................ - def check_testMixedArithmetic(self): + def check_mixed_arithmetic(self): "Tests mixed arithmetics." na = N.array([1]) ma = array([1]) self.failUnless(isinstance(na + ma, MaskedArray)) - self.failUnless(isinstance(ma + na, MaskedArray)) + self.failUnless(isinstance(ma + na, MaskedArray)) + #........................ + def check_inplace_arithmetic(self): + """Test of inplace operations and rich comparisons""" + # addition + x = arange(10) + y = arange(10) + xm = arange(10) + xm[2] = masked + x += 1 + assert_equal(x, y+1) + xm += 1 + assert_equal(xm, y+1) + # subtraction + x = arange(10) + xm = arange(10) + xm[2] = masked + x -= 1 + assert_equal(x, y-1) + xm -= 1 + assert_equal(xm, y-1) + # multiplication + x = arange(10)*1.0 + xm = arange(10)*1.0 + xm[2] = masked + x *= 2.0 + assert_equal(x, y*2) + xm *= 2.0 + assert_equal(xm, y*2) + # division + x = arange(10)*2 + xm = arange(10)*2 + xm[2] = masked + x /= 2 + assert_equal(x, y) + xm /= 2 + assert_equal(xm, y) + # division, pt 2 + x = arange(10)*1.0 + xm = arange(10)*1.0 + xm[2] = masked + x /= 2.0 + assert_equal(x, y/2.0) + xm /= arange(10) + assert_equal(xm, ones((10,))) + + x = arange(10).astype(float_) + xm = arange(10) + xm[2] = masked +# id1 = id(x.raw_data()) + id1 = x.raw_data().ctypes.data + x += 1. +# assert id1 == id(x.raw_data()) + assert (id1 == x.raw_data().ctypes.data) + assert_equal(x, y+1.) + # addition w/ array + x = arange(10, dtype=float_) + xm = arange(10, dtype=float_) + xm[2] = masked + m = xm.mask + a = arange(10, dtype=float_) + a[-1] = masked + x += a + xm += a + assert_equal(x,y+a) + assert_equal(xm,y+a) + assert_equal(xm.mask, mask_or(m,a.mask)) + # subtraction w/ array + x = arange(10, dtype=float_) + xm = arange(10, dtype=float_) + xm[2] = masked + m = xm.mask + a = arange(10, dtype=float_) + a[-1] = masked + x -= a + xm -= a + assert_equal(x,y-a) + assert_equal(xm,y-a) + assert_equal(xm.mask, mask_or(m,a.mask)) + # multiplication w/ array + x = arange(10, dtype=float_) + xm = arange(10, dtype=float_) + xm[2] = masked + m = xm.mask + a = arange(10, dtype=float_) + a[-1] = masked + x *= a + xm *= a + assert_equal(x,y*a) + assert_equal(xm,y*a) + assert_equal(xm.mask, mask_or(m,a.mask)) + # division w/ array + x = arange(10, dtype=float_) + xm = arange(10, dtype=float_) + xm[2] = masked + m = xm.mask + a = arange(10, dtype=float_) + a[-1] = masked + x /= a + xm /= a + assert_equal(x,y/a) + assert_equal(xm,y/a) + assert_equal(xm.mask, mask_or(mask_or(m,a.mask), (a==0))) + #.......................... + def check_scalararithmetic(self): + "Tests some scalar arithmetics on MaskedArrays." + xm = array(0, mask=1) + assert((1/array(0)).mask) + assert((1 + xm).mask) + assert((-xm).mask) + assert((-xm).mask) + assert(maximum(xm, xm).mask) + assert(minimum(xm, xm).mask) + assert(xm.filled().dtype is xm.data.dtype) + x = array(0, mask=0) +# assert(x.filled() is x.data) + assert_equal(x.filled().ctypes.data, x.ctypes.data) + assert_equal(str(xm), str(masked_print_option)) #......................... - def check_testUfuncs1 (self): + def check_basic_ufuncs (self): "Test various functions such as sin, cos." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d assert_equal(N.cos(x), cos(xm)) @@ -155,15 +271,11 @@ assert_equal(N.less_equal(x,y), less_equal(xm, ym)) assert_equal(N.greater_equal(x,y), greater_equal(xm, ym)) assert_equal(N.conjugate(x), conjugate(xm)) - assert_equal(N.concatenate((x,y)), concatenate((xm,ym))) - assert_equal(N.concatenate((x,y)), concatenate((x,y))) - assert_equal(N.concatenate((x,y)), concatenate((xm,y))) - assert_equal(N.concatenate((x,y,x)), concatenate((x,ym,x))) #........................ - def check_xtestCount (self): + def check_count_func (self): "Tests count" ott = array([0.,1.,2.,3.], mask=[1,0,0,0]) - assert( isinstance(count(ott), types.IntType)) + assert( isinstance(count(ott), int)) assert_equal(3, count(ott)) assert_equal(1, count(1)) assert_equal(0, array(1,mask=[1])) @@ -174,7 +286,7 @@ assert getmask(count(ott,0)) is nomask assert_equal([1,2],count(ott,0)) #........................ - def check_testMinMax (self): + def check_minmax_func (self): "Tests minimum and maximum." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d xr = N.ravel(x) #max doesn't work if shaped @@ -192,8 +304,25 @@ assert_equal(maximum(x,y), where(greater(x,y), x, y)) assert minimum(x) == 0 assert maximum(x) == 4 + + def check_minmax_methods(self): + "Additional tests on max/min" + (_, _, _, _, _, xm, _, _, _, _) = self.d + xm.shape = (xm.size,) + assert_equal(xm.max(), 10) + assert(xm[0].max() is masked) + assert(xm[0].max(0) is masked) + assert(xm[0].max(-1) is masked) + assert_equal(xm.min(), -10.) + assert(xm[0].min() is masked) + assert(xm[0].min(0) is masked) + assert(xm[0].min(-1) is masked) + assert_equal(xm.ptp(), 20.) + assert(xm[0].ptp() is masked) + assert(xm[0].ptp(0) is masked) + assert(xm[0].ptp(-1) is masked) #........................ - def check_testAddSumProd (self): + def check_addsumprod (self): "Tests add, sum, product." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d assert_equal(N.add.reduce(x), add.reduce(x)) @@ -217,6 +346,12 @@ def check_concat(self): "Tests concatenations." (x, y, a10, m1, m2, xm, ym, z, zm, xf) = self.d + # basic concatenation + assert_equal(N.concatenate((x,y)), concatenate((xm,ym))) + assert_equal(N.concatenate((x,y)), concatenate((x,y))) + assert_equal(N.concatenate((x,y)), concatenate((xm,y))) + assert_equal(N.concatenate((x,y,x)), concatenate((x,ym,x))) + # Concatenation along an axis s = (3,4) x.shape = y.shape = xm.shape = ym.shape = s assert_equal(xm.mask, N.reshape(m1, s)) @@ -225,7 +360,7 @@ assert_equal(N.concatenate((x,y),1), xmym) assert_equal(N.concatenate((xm.mask,ym.mask),1), xmym._mask) #........................ - def check_testCI(self): + def check_indexing(self): "Tests conversions and indexing" x1 = N.array([1,2,4,3]) x2 = array(x1, mask=[1,0,0,0]) @@ -275,7 +410,7 @@ assert_equal(s1, s2) assert x1[1:1].shape == (0,) #........................ - def check_testCopySize(self): + def check_copy(self): "Tests of some subtle points of copying and sizing." n = [0,0,1,0,0] m = make_mask(n) @@ -286,21 +421,27 @@ x1 = N.arange(5) y1 = array(x1, mask=m) - assert( y1._data is x1) + #assert( y1._data is x1) + assert_equal(y1._data.__array_interface__, x1.__array_interface__) assert( allequal(x1,y1.raw_data())) - assert( y1.mask is m) + #assert( y1.mask is m) + assert_equal(y1._mask.__array_interface__, m.__array_interface__) y1a = array(y1) - assert( y1a.raw_data() is y1.raw_data()) + #assert( y1a.raw_data() is y1.raw_data()) + assert( y1a._data.__array_interface__ == y1._data.__array_interface__) assert( y1a.mask is y1.mask) y2 = array(x1, mask=m) - assert( y2.raw_data() is x1) - assert( y2.mask is m) + #assert( y2.raw_data() is x1) + assert (y2._data.__array_interface__ == x1.__array_interface__) + #assert( y2.mask is m) + assert (y2._mask.__array_interface__ == m.__array_interface__) assert( y2[2] is masked) y2[2] = 9 assert( y2[2] is not masked) - assert( y2.mask is not m) + #assert( y2.mask is not m) + assert (y2._mask.__array_interface__ != m.__array_interface__) assert( allequal(y2.mask, 0)) y3 = array(x1*1.0, mask=m) @@ -327,14 +468,18 @@ x = masked_array([1,2,3], mask=[0,1,0]) # Copy is False by default y = masked_array(x) - assert_equal(id(y._data), id(x._data)) - assert_equal(id(y._mask), id(x._mask)) +# assert_equal(id(y._data), id(x._data)) +# assert_equal(id(y._mask), id(x._mask)) + assert_equal(y._data.ctypes.data, x._data.ctypes.data) + assert_equal(y._mask.ctypes.data, x._mask.ctypes.data) y = masked_array(x, copy=True) - assert_not_equal(id(y._data), id(x._data)) - assert_not_equal(id(y._mask), id(x._mask)) +# assert_not_equal(id(y._data), id(x._data)) +# assert_not_equal(id(y._mask), id(x._mask)) + assert_not_equal(y._data.ctypes.data, x._data.ctypes.data) + assert_not_equal(y._mask.ctypes.data, x._mask.ctypes.data) #........................ - def check_testOddFeatures_1(self): + def check_oddfeatures_1(self): "Test of other odd features" x = arange(20) x = x.reshape(4,5) @@ -380,7 +525,7 @@ assert_equal(x,z) # #........................ - def check_testOddFeatures_2(self): + def check_oddfeatures_2(self): "Tests some more features." x = array([1.,2.,3.,4.,5.]) c = array([1,1,1,0,0]) @@ -410,7 +555,7 @@ z = where(c, 1, masked) assert_equal(z, [99, 1, 1, 99, 99, 99]) #........................ - def check_testOddFeatures_3(self): + def check_oddfeatures_3(self): """Tests some generic features.""" atest = ones((10,10,10), dtype=float_) btest = zeros(atest.shape, MaskType) @@ -436,7 +581,7 @@ assert_equal(masked_not_equal(array([2,2,1,2,1], mask=[1,0,0,0,0]), 2).mask, [1,0,1,0,1]) assert_equal(masked_where([1,1,0,0,0], [1,2,3,4,5]), [99,99,3,4,5]) #........................ - def check_testTakeTransposeInnerOuter(self): + def check_TakeTransposeInnerOuter(self): "Test of take, transpose, inner, outer products" x = arange(24) y = N.arange(24) @@ -456,107 +601,7 @@ assert t[1] == 2 assert t[2] == 3 #........................ - def check_testInplace(self): - """Test of inplace operations and rich comparisons""" - y = arange(10) - - x = arange(10) - xm = arange(10) - xm[2] = masked - x += 1 - assert_equal(x, y+1) - xm += 1 - assert_equal(xm, y+1) - - x = arange(10) - xm = arange(10) - xm[2] = masked - x -= 1 - assert_equal(x, y-1) - xm -= 1 - assert_equal(xm, y-1) - - x = arange(10)*1.0 - xm = arange(10)*1.0 - xm[2] = masked - x *= 2.0 - assert_equal(x, y*2) - xm *= 2.0 - assert_equal(xm, y*2) - - x = arange(10)*2 - xm = arange(10)*2 - xm[2] = masked - x /= 2 - assert_equal(x, y) - xm /= 2 - assert_equal(xm, y) - - x = arange(10)*1.0 - xm = arange(10)*1.0 - xm[2] = masked - x /= 2.0 - assert_equal(x, y/2.0) - xm /= arange(10) - assert_equal(xm, ones((10,))) - - x = arange(10).astype(float_) - xm = arange(10) - xm[2] = masked - id1 = id(x.raw_data()) - x += 1. - assert id1 == id(x.raw_data()) - assert_equal(x, y+1.) - - x = arange(10, dtype=float_) - xm = arange(10, dtype=float_) - xm[2] = masked - m = xm.mask - a = arange(10, dtype=float_) - a[-1] = masked - x += a - xm += a - assert_equal(x,y+a) - assert_equal(xm,y+a) - assert_equal(xm.mask, mask_or(m,a.mask)) - - x = arange(10, dtype=float_) - xm = arange(10, dtype=float_) - xm[2] = masked - m = xm.mask - a = arange(10, dtype=float_) - a[-1] = masked - x -= a - xm -= a - assert_equal(x,y-a) - assert_equal(xm,y-a) - assert_equal(xm.mask, mask_or(m,a.mask)) - - x = arange(10, dtype=float_) - xm = arange(10, dtype=float_) - xm[2] = masked - m = xm.mask - a = arange(10, dtype=float_) - a[-1] = masked - x *= a - xm *= a - assert_equal(x,y*a) - assert_equal(xm,y*a) - assert_equal(xm.mask, mask_or(m,a.mask)) - - x = arange(10, dtype=float_) - xm = arange(10, dtype=float_) - xm[2] = masked - m = xm.mask - a = arange(10, dtype=float_) - a[-1] = masked - x /= a - xm /= a - assert_equal(x,y/a) - assert_equal(xm,y/a) - assert_equal(xm.mask, mask_or(mask_or(m,a.mask), (a==0))) - #........................ - def check_testPickle(self): + def check_pickling(self): "Test of pickling" import pickle x = arange(12) @@ -566,7 +611,7 @@ y = pickle.loads(s) assert_equal(x,y) #....................... - def check_testMasked(self): + def check_maskedelement(self): "Test of masked element" x = arange(6) x[1] = masked @@ -579,7 +624,7 @@ #self.failUnlessRaises(Exception, lambda x,y: x+y, masked, xx) #self.failUnlessRaises(Exception, lambda x,y: x+y, xx, masked) #........................ - def check_testToPython(self): + def check_topython(self): "Tests some communication issues with Python." assert_equal(1, int(array(1))) assert_equal(1.0, float(array(1))) @@ -592,22 +637,8 @@ #TODO: self.failUnless(bool(array([0,0],mask=[0,1]))) #TODO: self.failIf(bool(array([0,0]))) #TODO: self.failIf(bool(array([0,0],mask=[0,0]))) - #.......................... - def check_testScalarArithmetic(self): - "Tests some scalar arithmetics on MaskedArrays." - xm = array(0, mask=1) - assert((1/array(0)).mask) - assert((1 + xm).mask) - assert((-xm).mask) - assert((-xm).mask) - assert(maximum(xm, xm).mask) - assert(minimum(xm, xm).mask) - assert(xm.filled().dtype is xm.data.dtype) - x = array(0, mask=0) - assert(x.filled() is x.data) - assert_equal(str(xm), str(masked_print_option)) #........................ - def check_testArrayMethods(self): + def check_arraymethods(self): "Tests some MaskedArray methods." a = array([1,3,2]) b = array([1,3,2], mask=[1,0,1]) @@ -626,7 +657,7 @@ assert_equal(a.take([1,2]), a.data.take([1,2])) assert_equal(m.transpose(), m.data.transpose()) #........................ - def check_testArrayAttributes(self): + def check_basicattributes(self): "Tests some basic array attributes." a = array([1,3,2]) b = array([1,3,2], mask=[1,0,1]) @@ -637,7 +668,7 @@ assert_equal(a.shape, (3,)) assert_equal(b.shape, (3,)) #........................ - def check_testSingleElementSubscript(self): + def check_single_element_subscript(self): "Tests single element subscripts of Maskedarrays." a = array([1,3,2]) b = array([1,3,2], mask=[1,0,1]) @@ -669,7 +700,7 @@ assert_equal(X._mask, x.mask) assert_equal(getmask(x), [0,0,1,0,0]) -#.............................................................................. +#............................................................................... class test_ufuncs(NumpyTestCase): "Test class for the application of ufuncs on MaskedArrays." @@ -704,7 +735,7 @@ uf = getattr(umath, f) except AttributeError: uf = getattr(fromnumeric, f) - mf = getattr(maskedarray, f) + mf = getattr(coremodule, f) args = self.d[:uf.nin] ur = uf(*args) mr = mf(*args) @@ -730,7 +761,7 @@ assert(amask.max(1)[0].mask) assert(amask.min(1)[0].mask) -#.............................................................................. +#............................................................................... class test_array_methods(NumpyTestCase): "Test class for miscellaneous MaskedArrays methods." @@ -767,7 +798,7 @@ self.d = (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) #------------------------------------------------------ - def test_trace(self): + def check_trace(self): "Tests trace on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d mXdiag = mX.diagonal() @@ -775,7 +806,7 @@ assert_almost_equal(mX.trace(), X.trace() - sum(mXdiag.mask*X.diagonal(),axis=0)) - def test_clip(self): + def check_clip(self): "Tests clip on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d clipped = mx.clip(2,8) @@ -783,7 +814,7 @@ assert_equal(clipped.data,x.clip(2,8)) assert_equal(clipped.data,mx.data.clip(2,8)) - def test_ptp(self): + def check_ptp(self): "Tests ptp on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d (n,m) = X.shape @@ -797,7 +828,7 @@ assert_equal(mX.ptp(0),cols) assert_equal(mX.ptp(1),rows) - def test_swapaxes(self): + def check_swapaxes(self): "Tests swapaxes on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d mXswapped = mX.swapaxes(0,1) @@ -805,7 +836,7 @@ mXXswapped = mXX.swapaxes(0,2) assert_equal(mXXswapped.shape,(2,2,3,3)) - def test_cumsumprod(self): + def check_cumsumprod(self): "Tests cumsum & cumprod on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d mXcp = mX.cumsum(0) @@ -818,7 +849,7 @@ mXcp = mX.cumprod(1) assert_equal(mXcp.data,mX.filled(1).cumprod(1)) - def test_varstd(self): + def check_varstd(self): "Tests var & std on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d assert_almost_equal(mX.var(axis=None),mX.compressed().var()) @@ -831,7 +862,7 @@ assert_almost_equal(mXvar0[k],mX[:,k].compressed().var()) assert_almost_equal(N.sqrt(mXvar0[k]), mX[:,k].compressed().std()) - def test_argmin(self): + def check_argmin(self): "Tests argmin & argmax on MaskedArrays." (x,X,XX,m,mx,mX,mXX,m2x,m2X,m2XX) = self.d # @@ -884,6 +915,15 @@ put(x, i, masked_array([0,2,4,6],[1,0,1,0])) assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) + + def check_put_hardmask(self): + "Tests put on hardmask" + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + xh = array(d+1, mask = m, hard_mask=True, copy=True) + xh.put([4,2,0,1,3],[1,2,3,4,5]) + assert_equal(xh._data, [3,4,2,4,5]) def check_take(self): "Tests take" @@ -982,8 +1022,8 @@ assert_equal(xs._data, [0,10,20,30,40]) assert(xh.mask is m) assert_equal(xs.mask, nomask) - xh[0] = maskedarray.core.masked - xs[0] = maskedarray.core.masked + xh[0] = masked + xs[0] = masked assert_equal(xh.mask, [1,0,0,1,1]) assert_equal(xs.mask, [1,0,0,0,0]) xh[:] = 1 @@ -999,7 +1039,7 @@ assert_equal(xh.mask, nomask) # Switch back to hard mask xh.harden_mask() - xh[xh<3] = maskedarray.core.masked + xh[xh<3] = masked assert_equal(xh._data, [0,1,2,3,4]) assert_equal(xh._mask, [1,1,1,0,0]) xh[filled(xh>1,False)] = 5 @@ -1060,35 +1100,21 @@ sortedx = sort(x, endwith=False) assert_equal(sortedx._data, [1,2,-2,-1,0]) assert_equal(sortedx._mask, [1,1,0,0,0]) + + def check_ravel(self): + a = array([[1,2,3,4,5]], mask=[[0,1,0,0,0]]) + aravel = a.ravel() + assert_equal(a._mask.shape, a.shape) + a = array([0,0], mask=[1,1]) + aravel = a.ravel() + assert_equal(a._mask.shape, a.shape) + a = array(N.matrix([1,2,3,4,5]), mask=[[0,1,0,0,0]]) + aravel = a.ravel() + assert_equal(a.shape,(1,5)) + assert_equal(a._mask.shape, a.shape) + #.............................................................................. -#.............................................................................. - -class test_subclassing(NumpyTestCase): - """Test suite for masked subclasses of ndarray.""" - - class SubArray(N.ndarray): - """Defines a generic N.ndarray subclass, that stores some metadata - in the dictionary `info`.""" - def __new__(cls,arr,info={}): - x = N.array(arr).view(cls) - x.info = info - return x - def __array_finalize__(self, obj): - if hasattr(obj,'info'): - self.info = obj.info - return - - def check_subclassing(self): - "Tests whether the subclass is kept." - x = N.arange(5) - m = [0,0,1,0,0] - xsub = test_subclassing.SubArray(x) - xmsub = masked_array(xsub, mask=m) - assert isinstance(xmsub, MaskedArray) - assert_equal(xmsub._data, xsub) - assert isinstance(xmsub._data, test_subclassing.SubArray) - ############################################################################### #------------------------------------------------------------------------------ if __name__ == "__main__": Modified: trunk/Lib/sandbox/maskedarray/tests/test_extras.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_extras.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/tests/test_extras.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -296,6 +296,15 @@ assert_equal(dx._data, N.r_[0,difx_d,0]) assert_equal(dx._mask, N.r_[1,0,0,0,0,1]) +class test_apply_along_axis(NumpyTestCase): + "Tests 2D functions" + def check_3d(self): + a = arange(12.).reshape(2,2,3) + def myfunc(b): + return b[1] + xa = apply_along_axis(myfunc,2,a) + assert_equal(xa,[[1,4],[7,10]]) + ############################################################################### #------------------------------------------------------------------------------ if __name__ == "__main__": Added: trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -0,0 +1,115 @@ +import numpy as N +import numpy.core.numeric as numeric + +from numpy.testing import NumpyTest, NumpyTestCase + +import maskedarray.testutils +reload(maskedarray.testutils) +from maskedarray.testutils import * + +import maskedarray.core as coremodule +reload(coremodule) +from maskedarray.core import * + + + +class SubArray(N.ndarray): + """Defines a generic N.ndarray subclass, that stores some metadata + in the dictionary `info`.""" + def __new__(cls,arr,info={}): + x = N.asanyarray(arr).view(cls) + x.info = info + return x + def __array_finalize__(self, obj): + self.info = getattr(obj,'info',{}) + return + def __add__(self, other): + result = N.ndarray.__add__(self, other) + result.info.update({'added':result.info.pop('added',0)+1}) + return result +subarray = SubArray + +class MSubArray(SubArray,MaskedArray): + def __new__(cls, data, info=None, mask=nomask): + subarr = SubArray(data, info) + _data = MaskedArray.__new__(cls, data=subarr, mask=mask) + _data.info = subarr.info + return _data + def __array_finalize__(self,obj): + SubArray.__array_finalize__(self, obj) + MaskedArray.__array_finalize__(self,obj) + return +msubarray = MSubArray + +class MMatrix(MaskedArray, N.matrix,): + def __new__(cls, data, mask=nomask): + mat = N.matrix(data) + _data = MaskedArray.__new__(cls, data=mat, mask=mask) + return _data + def __array_finalize__(self,obj): + N.matrix.__array_finalize__(self, obj) + MaskedArray.__array_finalize__(self,obj) + return +mmatrix = MMatrix + + + +class test_subclassing(NumpyTestCase): + """Test suite for masked subclasses of ndarray.""" + + def check_data_subclassing(self): + "Tests whether the subclass is kept." + x = N.arange(5) + m = [0,0,1,0,0] + xsub = SubArray(x) + xmsub = masked_array(xsub, mask=m) + assert isinstance(xmsub, MaskedArray) + assert_equal(xmsub._data, xsub) + assert isinstance(xmsub._data, SubArray) + + def check_maskedarray_subclassing(self): + "Tests subclassing MaskedArray" + x = N.arange(5) + mx = mmatrix(x,mask=[0,1,0,0,0]) + assert isinstance(mx._data, N.matrix) + "Tests masked_unary_operation" + assert isinstance(add(mx,mx), mmatrix) + assert isinstance(add(mx,x), mmatrix) + assert_equal(add(mx,x), mx+x) + assert isinstance(add(mx,mx)._data, N.matrix) + assert isinstance(add.outer(mx,mx), mmatrix) + "Tests masked_binary_operation" + assert isinstance(hypot(mx,mx), mmatrix) + assert isinstance(hypot(mx,x), mmatrix) + + def check_attributepropagation(self): + x = array(arange(5), mask=[0]+[1]*4) + my = masked_array(subarray(x)) + ym = msubarray(x) + # + z = (my+1) + assert isinstance(z,MaskedArray) + assert not isinstance(z, MSubArray) + assert isinstance(z._data, SubArray) + assert_equal(z._data.info, {}) + # + z = (ym+1) + assert isinstance(z, MaskedArray) + assert isinstance(z, MSubArray) + assert isinstance(z._data, SubArray) + assert z._data.info['added'] > 0 + +################################################################################ +if __name__ == '__main__': + NumpyTest().run() + if 1: + x = N.arange(5) + mx = mmatrix(x,mask=[0,1,0,0,0]) + + c = mx.ravel() + + a = add(mx,x) + b = mx+x + assert_equal(a,b) + + Property changes on: trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/maskedarray/testutils.py =================================================================== --- trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -11,6 +11,7 @@ import numpy as N +from numpy.core import ndarray from numpy.core.numerictypes import float_ import numpy.core.umath as umath from numpy.testing import NumpyTest, NumpyTestCase @@ -54,7 +55,6 @@ for k in range(len(desired)): assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k,err_msg)) return - from numpy.core import ndarray if isinstance(actual, ndarray) or isinstance(desired, ndarray): return assert_array_equal(actual, desired, err_msg) msg = build_err_msg([actual, desired], err_msg,) @@ -98,8 +98,8 @@ yf = filled(y) m = mask_or(getmask(x), getmask(y)) - x = filled(masked_array(xf, copy=False, mask=m), fill_value) - y = filled(masked_array(yf, copy=False, mask=m), fill_value) + x = masked_array(xf, copy=False, mask=m).filled(fill_value) + y = masked_array(yf, copy=False, mask=m).filled(fill_value) if (x.dtype.char != "O"): x = x.astype(float_) if isinstance(x, N.ndarray) and x.size > 1: Modified: trunk/Lib/sandbox/maskedarray/timer_comparison.py =================================================================== --- trunk/Lib/sandbox/maskedarray/timer_comparison.py 2007-02-11 00:33:13 UTC (rev 2699) +++ trunk/Lib/sandbox/maskedarray/timer_comparison.py 2007-02-11 23:24:17 UTC (rev 2700) @@ -5,8 +5,7 @@ from numpy import int_, float_, bool_ import numpy.core.fromnumeric as fromnumeric -from maskedarray.testutils import assert_equal,assert_array_equal, \ - fail_if_equal, assert_mask_equal +from numpy.testing.utils import build_err_msg, rand numpy.seterr(all='ignore') @@ -20,9 +19,10 @@ self.allequal = module.allequal self.arange = module.arange self.array = module.array - self.average = module.average +# self.average = module.average self.concatenate = module.concatenate self.count = module.count + self.equal = module.equal self.filled = module.filled self.getmask = module.getmask self.id = id @@ -32,6 +32,7 @@ self.masked_array = module.masked_array self.masked_values = module.masked_values self.mask_or = module.mask_or + self.nomask = module.nomask self.ones = module.ones self.outer = module.outer self.repeat = module.repeat @@ -45,8 +46,76 @@ self.umath = module.umath except AttributeError: self.umath = module.core.umath + self.testnames = [] + #........................ + def assert_array_compare(self, comparison, x, y, err_msg='', header='', + fill_value=True): + """Asserts that a comparison relation between two masked arrays is satisfied + elementwise.""" + xf = self.filled(x) + yf = self.filled(y) + m = self.mask_or(self.getmask(x), self.getmask(y)) + + x = self.filled(self.masked_array(xf, mask=m), fill_value) + y = self.filled(self.masked_array(yf, mask=m), fill_value) + if (x.dtype.char != "O"): + x = x.astype(float_) + if isinstance(x, numpy.ndarray) and x.size > 1: + x[numpy.isnan(x)] = 0 + elif numpy.isnan(x): + x = 0 + if (y.dtype.char != "O"): + y = y.astype(float_) + if isinstance(y, numpy.ndarray) and y.size > 1: + y[numpy.isnan(y)] = 0 + elif numpy.isnan(y): + y = 0 + try: + cond = (x.shape==() or y.shape==()) or x.shape == y.shape + if not cond: + msg = build_err_msg([x, y], + err_msg + + '\n(shapes %s, %s mismatch)' % (x.shape, + y.shape), + header=header, + names=('x', 'y')) + assert cond, msg + val = comparison(x,y) + if m is not self.nomask and fill_value: + val = self.masked_array(val, mask=m) + if isinstance(val, bool): + cond = val + reduced = [0] + else: + reduced = val.ravel() + cond = reduced.all() + reduced = reduced.tolist() + if not cond: + match = 100-100.0*reduced.count(1)/len(reduced) + msg = build_err_msg([x, y], + err_msg + + '\n(mismatch %s%%)' % (match,), + header=header, + names=('x', 'y')) + assert cond, msg + except ValueError: + msg = build_err_msg([x, y], err_msg, header=header, names=('x', 'y')) + raise ValueError(msg) + #............................ + def assert_array_equal(self, x, y, err_msg=''): + """Checks the elementwise equality of two masked arrays.""" + self.assert_array_compare(self.equal, x, y, err_msg=err_msg, + header='Arrays are not equal') #---------------------------------- + def test_0(self): + "Tests creation" + x = numpy.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) + m = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + xm = self.masked_array(x, mask=m) + xm[0] + #---------------------------------- def test_1(self): + "Tests creation" x = numpy.array([1.,1.,1.,-2., pi/2.0, 4., 5., -10., 10., 1., 2., 3.]) y = numpy.array([5.,0.,3., 2., -1., -4., 0., -10., 10., 1., 0., 3.]) a10 = 10. @@ -60,10 +129,10 @@ xm.set_fill_value(1.e+20) #..... assert((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(xm.size , reduce(lambda x,y:x*y, s)) - assert_equal(self.count(xm) , len(m1) - reduce(lambda x,y:x+y, m1)) + assert(xm.size == reduce(lambda x,y:x*y, s)) + assert(self.count(xm) == len(m1) - reduce(lambda x,y:x+y, m1)) #..... for s in [(4,3), (6,2)]: x.shape = s @@ -72,7 +141,7 @@ ym.shape = s xf.shape = s - assert_equal(self.count(xm) , len(m1) - reduce(lambda x,y:x+y, m1)) + assert(self.count(xm) == len(m1) - reduce(lambda x,y:x+y, m1)) #---------------------------------- def test_2(self): "Tests conversions and indexing" @@ -82,36 +151,36 @@ x4 = self.array(x1) # test conversion to strings junk, garbage = str(x2), repr(x2) - assert_equal(numpy.sort(x1), self.sort(x2, fill_value=0)) +# assert_equal(numpy.sort(x1), self.sort(x2, fill_value=0)) # tests of indexing assert type(x2[1]) is type(x1[1]) assert x1[1] == x2[1] - assert_equal(x1[2],x2[2]) - assert_equal(x1[2:5],x2[2:5]) - assert_equal(x1[:],x2[:]) - assert_equal(x1[1:], x3[1:]) +# assert self.allequal(x1[2],x2[2]) +# assert self.allequal(x1[2:5],x2[2:5]) +# assert self.allequal(x1[:],x2[:]) +# assert self.allequal(x1[1:], x3[1:]) x1[2] = 9 x2[2] = 9 - assert_equal(x1,x2) + self.assert_array_equal(x1,x2) x1[1:3] = 99 x2[1:3] = 99 - assert_equal(x1,x2) +# assert self.allequal(x1,x2) x2[1] = self.masked - assert_equal(x1,x2) +# assert self.allequal(x1,x2) x2[1:3] = self.masked - assert_equal(x1,x2) +# assert self.allequal(x1,x2) x2[:] = x1 x2[1] = self.masked - assert self.allequal(self.getmask(x2),self.array([0,1,0,0])) +# assert self.allequal(self.getmask(x2),self.array([0,1,0,0])) x3[:] = self.masked_array([1,2,3,4],[0,1,1,0]) - assert self.allequal(self.getmask(x3), self.array([0,1,1,0])) +# assert self.allequal(self.getmask(x3), self.array([0,1,1,0])) x4[:] = self.masked_array([1,2,3,4],[0,1,1,0]) - assert self.allequal(self.getmask(x4), self.array([0,1,1,0])) - assert self.allequal(x4, self.array([1,2,3,4])) +# assert self.allequal(self.getmask(x4), self.array([0,1,1,0])) +# assert self.allequal(x4, self.array([1,2,3,4])) x1 = numpy.arange(5)*1.0 x2 = self.masked_values(x1, 3.0) - assert_equal(x1,x2) - assert self.allequal(self.array([0,0,0,1,0], self.MaskType), x2.mask) +# assert self.allequal(x1,x2) +# assert self.allequal(self.array([0,0,0,1,0], self.MaskType), x2.mask) x1 = self.array([1,'hello',2,3],object) x2 = numpy.array([1,'hello',2,3],object) s1 = x1[1] @@ -125,31 +194,36 @@ m3 = self.make_mask(m, copy=1) assert(m is not m3) + #---------------------------------- + def test_3(self): + "Tests resize/repeat" x4 = self.arange(4) x4[2] = self.masked y4 = self.resize(x4, (8,)) - assert_equal(self.concatenate([x4,x4]), y4) - assert_equal(self.getmask(y4),[0,0,1,0,0,0,1,0]) + assert self.allequal(self.concatenate([x4,x4]), y4) + assert self.allequal(self.getmask(y4),[0,0,1,0,0,0,1,0]) y5 = self.repeat(x4, (2,2,2,2), axis=0) - assert_equal(y5, [0,0,1,1,2,2,3,3]) + self.assert_array_equal(y5, [0,0,1,1,2,2,3,3]) y6 = self.repeat(x4, 2, axis=0) - assert_equal(y5, y6) + assert self.allequal(y5, y6) y7 = x4.repeat((2,2,2,2), axis=0) - assert_equal(y5,y7) + assert self.allequal(y5,y7) y8 = x4.repeat(2,0) - assert_equal(y5,y8) + assert self.allequal(y5,y8) - #"Test of take, transpose, inner, outer products" + #---------------------------------- + def test_4(self): + "Test of take, transpose, inner, outer products" x = self.arange(24) y = numpy.arange(24) x[5:6] = self.masked x = x.reshape(2,3,4) y = y.reshape(2,3,4) - assert_equal(numpy.transpose(y,(2,0,1)), self.transpose(x,(2,0,1))) - assert_equal(numpy.take(y, (2,0,1), 1), self.take(x, (2,0,1), 1)) - assert_equal(numpy.inner(self.filled(x,0), self.filled(y,0)), + assert self.allequal(numpy.transpose(y,(2,0,1)), self.transpose(x,(2,0,1))) + assert self.allequal(numpy.take(y, (2,0,1), 1), self.take(x, (2,0,1), 1)) + assert self.allequal(numpy.inner(self.filled(x,0), self.filled(y,0)), self.inner(x, y)) - assert_equal(numpy.outer(self.filled(x,0), self.filled(y,0)), + assert self.allequal(numpy.outer(self.filled(x,0), self.filled(y,0)), self.outer(x, y)) y = self.array(['abc', 1, 'def', 2, 3], object) y[2] = self.masked @@ -157,56 +231,58 @@ assert t[0] == 'abc' assert t[1] == 2 assert t[2] == 3 - # Tests in place - y = self.arange(10) + #---------------------------------- + def test_5(self): + "Tests inplace" x = self.arange(10) + y = self.arange(10) xm = self.arange(10) xm[2] = self.masked x += 1 - assert_equal(x, y+1) + assert self.allequal(x, y+1) xm += 1 - assert_equal(xm, y+1) + assert self.allequal(xm, y+1) x = self.arange(10) xm = self.arange(10) xm[2] = self.masked x -= 1 - assert_equal(x, y-1) + assert self.allequal(x, y-1) xm -= 1 - assert_equal(xm, y-1) + assert self.allequal(xm, y-1) x = self.arange(10)*1.0 xm = self.arange(10)*1.0 xm[2] = self.masked x *= 2.0 - assert_equal(x, y*2) + assert self.allequal(x, y*2) xm *= 2.0 - assert_equal(xm, y*2) + assert self.allequal(xm, y*2) x = self.arange(10)*2 xm = self.arange(10)*2 xm[2] = self.masked x /= 2 - assert_equal(x, y) + assert self.allequal(x, y) xm /= 2 - assert_equal(xm, y) + assert self.allequal(xm, y) x = self.arange(10)*1.0 xm = self.arange(10)*1.0 xm[2] = self.masked x /= 2.0 - assert_equal(x, y/2.0) + assert self.allequal(x, y/2.0) xm /= self.arange(10) - assert_equal(xm, self.ones((10,))) + self.assert_array_equal(xm, self.ones((10,))) x = self.arange(10).astype(float_) xm = self.arange(10) xm[2] = self.masked id1 = self.id(x.raw_data()) x += 1. - assert id1 == self.id(x.raw_data()) - assert_equal(x, y+1.) + #assert id1 == self.id(x.raw_data()) + assert self.allequal(x, y+1.) x = self.arange(10, dtype=float_) xm = self.arange(10, dtype=float_) @@ -216,9 +292,9 @@ a[-1] = self.masked x += a xm += a - assert_equal(x,y+a) - assert_equal(xm,y+a) - assert_equal(xm.mask, self.mask_or(m,a.mask)) + assert self.allequal(x,y+a) + assert self.allequal(xm,y+a) + assert self.allequal(xm.mask, self.mask_or(m,a.mask)) x = self.arange(10, dtype=float_) xm = self.arange(10, dtype=float_) @@ -228,9 +304,9 @@ a[-1] = self.masked x -= a xm -= a - assert_equal(x,y-a) - assert_equal(xm,y-a) - assert_equal(xm.mask, self.mask_or(m,a.mask)) + assert self.allequal(x,y-a) + assert self.allequal(xm,y-a) + assert self.allequal(xm.mask, self.mask_or(m,a.mask)) x = self.arange(10, dtype=float_) xm = self.arange(10, dtype=float_) @@ -240,9 +316,9 @@ a[-1] = self.masked x *= a xm *= a - assert_equal(x,y*a) - assert_equal(xm,y*a) - assert_equal(xm.mask, self.mask_or(m,a.mask)) + assert self.allequal(x,y*a) + assert self.allequal(xm,y*a) + assert self.allequal(xm.mask, self.mask_or(m,a.mask)) x = self.arange(10, dtype=float_) xm = self.arange(10, dtype=float_) @@ -253,27 +329,28 @@ x /= a xm /= a #---------------------------------- - def test_3(self): + def test_6(self): + "Tests ufunc" d = (self.array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6), self.array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6),) for f in ['sqrt', 'log', 'log10', 'exp', 'conjugate', - 'sin', 'cos', 'tan', - 'arcsin', 'arccos', 'arctan', - 'sinh', 'cosh', 'tanh', - 'arcsinh', - 'arccosh', - 'arctanh', - 'absolute', 'fabs', 'negative', - # 'nonzero', 'around', - 'floor', 'ceil', - # 'sometrue', 'alltrue', - 'logical_not', - 'add', 'subtract', 'multiply', - 'divide', 'true_divide', 'floor_divide', - 'remainder', 'fmod', 'hypot', 'arctan2', - 'equal', 'not_equal', 'less_equal', 'greater_equal', - 'less', 'greater', - 'logical_and', 'logical_or', 'logical_xor', +# 'sin', 'cos', 'tan', +# 'arcsin', 'arccos', 'arctan', +# 'sinh', 'cosh', 'tanh', +# 'arcsinh', +# 'arccosh', +# 'arctanh', +# 'absolute', 'fabs', 'negative', +# # 'nonzero', 'around', +# 'floor', 'ceil', +# # 'sometrue', 'alltrue', +# 'logical_not', +# 'add', 'subtract', 'multiply', +# 'divide', 'true_divide', 'floor_divide', +# 'remainder', 'fmod', 'hypot', 'arctan2', +# 'equal', 'not_equal', 'less_equal', 'greater_equal', +# 'less', 'greater', +# 'logical_and', 'logical_or', 'logical_xor', ]: #print f try: @@ -284,81 +361,87 @@ args = d[:uf.nin] ur = uf(*args) mr = mf(*args) - assert_equal(ur.filled(0), mr.filled(0), f) - assert_mask_equal(ur.mask, mr.mask) + self.assert_array_equal(ur.filled(0), mr.filled(0), f) + self.assert_array_equal(ur._mask, mr._mask) + #---------------------------------- + def test_99(self): # test average ott = self.array([0.,1.,2.,3.], mask=[1,0,0,0]) - assert_equal(2.0, self.average(ott,axis=0)) - assert_equal(2.0, self.average(ott, weights=[1., 1., 2., 1.])) + self.assert_array_equal(2.0, self.average(ott,axis=0)) + self.assert_array_equal(2.0, self.average(ott, weights=[1., 1., 2., 1.])) result, wts = self.average(ott, weights=[1.,1.,2.,1.], returned=1) - assert_equal(2.0, result) + self.assert_array_equal(2.0, result) assert(wts == 4.0) ott[:] = self.masked - # assert(average(ott,axis=0) is masked) + assert(self.average(ott,axis=0) is self.masked) ott = self.array([0.,1.,2.,3.], mask=[1,0,0,0]) ott = ott.reshape(2,2) ott[:,1] = self.masked - assert_equal(self.average(ott,axis=0), [2.0, 0.0]) - # assert(average(ott,axis=1)[0] is masked) - assert_equal([2.,0.], self.average(ott, axis=0)) + self.assert_array_equal(self.average(ott,axis=0), [2.0, 0.0]) + assert(self.average(ott,axis=1)[0] is self.masked) + self.assert_array_equal([2.,0.], self.average(ott, axis=0)) result, wts = self.average(ott, axis=0, returned=1) - assert_equal(wts, [1., 0.]) + self.assert_array_equal(wts, [1., 0.]) w1 = [0,1,1,1,1,0] w2 = [[0,1,1,1,1,0],[1,0,0,0,0,1]] x = self.arange(6) - assert_equal(self.average(x, axis=0), 2.5) - assert_equal(self.average(x, axis=0, weights=w1), 2.5) + self.assert_array_equal(self.average(x, axis=0), 2.5) + self.assert_array_equal(self.average(x, axis=0, weights=w1), 2.5) y = self.array([self.arange(6), 2.0*self.arange(6)]) - assert_equal(self.average(y, None), numpy.add.reduce(numpy.arange(6))*3./12.) - assert_equal(self.average(y, axis=0), numpy.arange(6) * 3./2.) - assert_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0]) - assert_equal(self.average(y, None, weights=w2), 20./6.) - assert_equal(self.average(y, axis=0, weights=w2), [0.,1.,2.,3.,4.,10.]) - assert_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0]) + self.assert_array_equal(self.average(y, None), numpy.add.reduce(numpy.arange(6))*3./12.) + self.assert_array_equal(self.average(y, axis=0), numpy.arange(6) * 3./2.) + self.assert_array_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0]) + self.assert_array_equal(self.average(y, None, weights=w2), 20./6.) + self.assert_array_equal(self.average(y, axis=0, weights=w2), [0.,1.,2.,3.,4.,10.]) + self.assert_array_equal(self.average(y, axis=1), [self.average(x,axis=0), self.average(x,axis=0) * 2.0]) m1 = self.zeros(6) m2 = [0,0,1,1,0,0] m3 = [[0,0,1,1,0,0],[0,1,1,1,1,0]] m4 = self.ones(6) m5 = [0, 1, 1, 1, 1, 1] - assert_equal(self.average(self.masked_array(x, m1),axis=0), 2.5) - assert_equal(self.average(self.masked_array(x, m2),axis=0), 2.5) + self.assert_array_equal(self.average(self.masked_array(x, m1),axis=0), 2.5) + self.assert_array_equal(self.average(self.masked_array(x, m2),axis=0), 2.5) # assert(self.average(masked_array(x, m4),axis=0) is masked) - assert_equal(self.average(self.masked_array(x, m5),axis=0), 0.0) - assert_equal(self.count(self.average(self.masked_array(x, m4),axis=0)), 0) + self.assert_array_equal(self.average(self.masked_array(x, m5),axis=0), 0.0) + self.assert_array_equal(self.count(self.average(self.masked_array(x, m4),axis=0)), 0) z = self.masked_array(y, m3) - assert_equal(self.average(z, None), 20./6.) - assert_equal(self.average(z, axis=0), [0.,1.,99.,99.,4.0, 7.5]) - assert_equal(self.average(z, axis=1), [2.5, 5.0]) - assert_equal(self.average(z,axis=0, weights=w2), [0.,1., 99., 99., 4.0, 10.0]) + self.assert_array_equal(self.average(z, None), 20./6.) + self.assert_array_equal(self.average(z, axis=0), [0.,1.,99.,99.,4.0, 7.5]) + self.assert_array_equal(self.average(z, axis=1), [2.5, 5.0]) + self.assert_array_equal(self.average(z,axis=0, weights=w2), [0.,1., 99., 99., 4.0, 10.0]) + #------------------------ + def test_A(self): + x = self.arange(24) + y = numpy.arange(24) + x[5:6] = self.masked + x = x.reshape(2,3,4) ################################################################################ if __name__ == '__main__': setup_base = "from __main__ import moduletester\ntester = moduletester(module)" setup_old = "import numpy.core.ma as module\n"+setup_base - setup_new = "import maskedarray as module\n"+setup_base + setup_new = "import maskedarray.core_ini as module\n"+setup_base + setup_alt = "import maskedarray.core as module\n"+setup_base - nrepeat = 10 - nloop = 50 - print "#1"+50*'.' - old = timeit.Timer('tester.test_1()', setup_old).repeat(nrepeat, nloop*10) - new = timeit.Timer('tester.test_1()', setup_new).repeat(nrepeat, nloop*10) - old = numpy.sort(old) - new = numpy.sort(new) - print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) - print "maskedarray : %.3f - %.3f" % (new[0], new[1]) - print "#2"+50*'.' - old = timeit.Timer('tester.test_2()', setup_old).repeat(nrepeat, nloop) - new = timeit.Timer('tester.test_2()', setup_new).repeat(nrepeat, nloop) - old = numpy.sort(old) - new = numpy.sort(new) - print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) - print "maskedarray : %.3f - %.3f" % (new[0], new[1]) - print "#3"+50*'.' - old = timeit.Timer('tester.test_3()', setup_old).repeat(nrepeat, nloop) - new = timeit.Timer('tester.test_3()', setup_new).repeat(nrepeat, nloop) - old = numpy.sort(old) - new = numpy.sort(new) - print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) - print "maskedarray : %.3f - %.3f" % (new[0], new[1]) - \ No newline at end of file + nrepeat = 2 + nloop = 3 + + + for i in range(1,7): + func = 'tester.test_%i()' % i + old = timeit.Timer(func, setup_old).repeat(nrepeat, nloop*10) + new = timeit.Timer(func, setup_new).repeat(nrepeat, nloop*10) + alt = timeit.Timer(func, setup_alt).repeat(nrepeat, nloop*10) + old = numpy.sort(old) + new = numpy.sort(new) + alt = numpy.sort(alt) + print "#%i" % i +50*'.' + print eval("moduletester.test_%i.__doc__" % i) + print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) + print "core_ini : %.3f - %.3f" % (new[0], new[1]) + print "core_alt : %.3f - %.3f" % (alt[0], alt[1]) +# import maskedarray.core_alt as module +# tester = moduletester(module) +# tester.test_3() +# print "OK" \ No newline at end of file From scipy-svn at scipy.org Sun Feb 11 21:30:19 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 11 Feb 2007 20:30:19 -0600 (CST) Subject: [Scipy-svn] r2701 - trunk/Lib/ndimage Message-ID: <20070212023019.12CEB39C0B7@new.scipy.org> Author: chanley Date: 2007-02-11 20:30:16 -0600 (Sun, 11 Feb 2007) New Revision: 2701 Modified: trunk/Lib/ndimage/__init__.py Log: Added version number to ndimage to support STScI software release. Modified: trunk/Lib/ndimage/__init__.py =================================================================== --- trunk/Lib/ndimage/__init__.py 2007-02-11 23:24:17 UTC (rev 2700) +++ trunk/Lib/ndimage/__init__.py 2007-02-12 02:30:16 UTC (rev 2701) @@ -38,4 +38,5 @@ from info import __doc__ from numpy.testing import NumpyTest test = NumpyTest().test +__version__ = '2.0' From scipy-svn at scipy.org Sun Feb 11 22:13:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 11 Feb 2007 21:13:13 -0600 (CST) Subject: [Scipy-svn] r2702 - trunk/Lib/sandbox/maskedarray Message-ID: <20070212031313.758A639C1EA@new.scipy.org> Author: pierregm Date: 2007-02-11 21:13:11 -0600 (Sun, 11 Feb 2007) New Revision: 2702 Modified: trunk/Lib/sandbox/maskedarray/core.py Log: (Forgot to mention Reggie Dugard as a source ofmany suggestions/improvements) Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-12 02:30:16 UTC (rev 2701) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-12 03:13:11 UTC (rev 2702) @@ -9,7 +9,8 @@ (mainly) Paul Dubois. Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant. -pgmdevlist_at_gmail_dot_com +pgmdevlist_AT_gmail_DOT_com +Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com) :author: Pierre Gerard-Marchant :contact: pierregm_at_uga_dot_edu @@ -74,6 +75,8 @@ from numpy.lib.shape_base import expand_dims as n_expand_dims import warnings +import logging +logging.basicConfig(level=logging.DEBUG, format='%(name)-15s %(levelname)s %(message)s',) MaskType = bool_ @@ -857,6 +860,7 @@ # def __call__ (self, other, *args): "Execute the call behavior." +# logging.debug("_mathmethod : %s" % self.methodname) instance = self.obj m_self = instance._mask m_other = getmask(other) @@ -1009,6 +1013,7 @@ If `data` is already a ndarray, its dtype becomes the default value of dtype. """ +# logging.debug("__new__ received %s" % type(data)) if flag is not None: warnings.warn("The flag 'flag' is now called 'small_mask'!", DeprecationWarning) @@ -1068,7 +1073,9 @@ """Special hook for ufuncs. Wraps the numpy array and sets the mask according to context. """ +# logging.debug("wrap from : %s" % obj) result = obj.view(type(self)) +# logging.debug("wrap result: %s" % result) #.......... if context is None: m = self._mask @@ -1147,6 +1154,7 @@ """x.__setitem__(i, y) <==> x[i]=y Sets item described by index. If value is masked, masks those locations. """ + #logging.debug("__setitem__ %s to %s" % (index,value)) if self is masked: raise MAError, 'Cannot alter the masked element.' # if getmask(indx) is not nomask: @@ -1201,6 +1209,7 @@ """x.__getslice__(i, j) <==> x[i:j] Returns the slice described by i, j. The use of negative indices is not supported.""" + #logging.debug("__getslice__ (%s,%s)" % (i,j)) return self.__getitem__(slice(i,j)) #........................ def __setslice__(self, i, j, value): @@ -1316,6 +1325,7 @@ "Divides self by other in place." dom_mask = domain_safe_divide().__call__(self, filled(other,1)) other_mask = getmask(other) +# logging.debug("dom_mask: %s" % dom_mask) new_mask = mask_or(other_mask, dom_mask) ndarray.__idiv__(self._data, other) self._mask = mask_or(self._mask, new_mask) @@ -1425,6 +1435,7 @@ # if fill_value is None: fill_value = self.fill_value +# logging.debug("filled use %s instead of %s" % (self.fill_value, fill_value)) # if self is masked_singleton: result = numeric.asanyarray(fill_value) From scipy-svn at scipy.org Sun Feb 11 23:05:39 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 11 Feb 2007 22:05:39 -0600 (CST) Subject: [Scipy-svn] r2703 - in trunk/Lib/sandbox/models: . family robust tests Message-ID: <20070212040539.B693439C01A@new.scipy.org> Author: jarrod.millman Date: 2007-02-11 22:05:31 -0600 (Sun, 11 Feb 2007) New Revision: 2703 Modified: trunk/Lib/sandbox/models/__init__.py trunk/Lib/sandbox/models/family/varfuncs.py trunk/Lib/sandbox/models/formula.py trunk/Lib/sandbox/models/gam.py trunk/Lib/sandbox/models/glm.py trunk/Lib/sandbox/models/info.py trunk/Lib/sandbox/models/mixed.py trunk/Lib/sandbox/models/regression.py trunk/Lib/sandbox/models/rlm.py trunk/Lib/sandbox/models/robust/__init__.py trunk/Lib/sandbox/models/robust/scale.py trunk/Lib/sandbox/models/smoothers.py trunk/Lib/sandbox/models/tests/test_formula.py trunk/Lib/sandbox/models/tests/test_utils.py trunk/Lib/sandbox/models/utils.py Log: some minor documentation improvements Modified: trunk/Lib/sandbox/models/__init__.py =================================================================== --- trunk/Lib/sandbox/models/__init__.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/__init__.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -2,6 +2,8 @@ # models - Statistical Models # +__docformat__ = 'restructuredtext' + from scipy.sandbox.models.info import __doc__ import scipy.sandbox.models.model Modified: trunk/Lib/sandbox/models/family/varfuncs.py =================================================================== --- trunk/Lib/sandbox/models/family/varfuncs.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/family/varfuncs.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,5 @@ +__docformat__ = 'restructuredtext' + import numpy as N class VarianceFunction: @@ -4,7 +6,6 @@ """ Variance function that relates the variance of a random variable to its mean. Defaults to 1. - """ def __call__(self, mu): @@ -13,7 +14,6 @@ constant = VarianceFunction() class Power: - """ Variance function: @@ -27,7 +27,6 @@ return N.power(N.fabs(mu), self.power) class Binomial: - """ Binomial variance function @@ -50,4 +49,3 @@ mu_squared = Power(power=2) mu_cubed = Power(power=3) binary = Binomial() - Modified: trunk/Lib/sandbox/models/formula.py =================================================================== --- trunk/Lib/sandbox/models/formula.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/formula.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,6 @@ +""" +Provides the basic classes needed to specify statistical models. +""" import copy import types import numpy as N @@ -2,6 +5,7 @@ +__docformat__ = 'restructuredtext' + default_namespace = {} class term(object): - """ @@ -13,7 +17,6 @@ defaults to formula.default_namespace. When called in an instance of formula, the namespace used is that formula's namespace. - """ def __pow__(self, power): @@ -106,13 +109,9 @@ """ Return the columns associated to self in a design matrix. If the term has no 'func' attribute, it returns - - self.namespace[self.termname] - + ``self.namespace[self.termname]`` else, it returns - - self.func(*args, **kw) - + ``self.func(*args, **kw)`` """ if not hasattr(self, 'func'): @@ -243,7 +242,6 @@ return value class quantitative(term): - """ A subclass of term that can be used to apply point transformations of another term, i.e. to take powers: @@ -260,7 +258,6 @@ >>> x3.namespace = x.namespace >>> print N.allclose(x()**2, x3()) True - """ def __init__(self, name, func=None, termname=None, transform=lambda x: x): @@ -275,9 +272,7 @@ return self.transform(term.__call__(self, *args, **kw)) class formula(object): - """ - A formula object for manipulating design matrices in regression models, essentially consisting of a list of term instances. @@ -302,11 +297,9 @@ def __init__(self, termlist, namespace=default_namespace): """ Create a formula from either: - - i) a formula object - ii) a sequence of term instances - iii) one term - + i. a `formula` object + ii. a sequence of `term` instances + iii. one `term` """ @@ -457,7 +450,7 @@ def design(self, *args, **kw): """ - transpose(self(*args, **kw)) + ``transpose(self(*args, **kw))`` """ return self(*args, **kw).T @@ -607,12 +600,11 @@ only term in the formula, then a keywords argument \'nrow\' is needed. ->>> from formula import * +>>> from scipy.sandbox.models.formula import formula, I >>> I() -1 +array(1.0) >>> I(nrow=5) -array([1, 1, 1, 1, 1]) - +array([ 1., 1., 1., 1., 1.]) >>> f=formula(I) >>> f(nrow=5) array([1, 1, 1, 1, 1]) Modified: trunk/Lib/sandbox/models/gam.py =================================================================== --- trunk/Lib/sandbox/models/gam.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/gam.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,6 @@ +""" +Generalized additive models +""" import numpy as N from scipy.sandbox.models import family Modified: trunk/Lib/sandbox/models/glm.py =================================================================== --- trunk/Lib/sandbox/models/glm.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/glm.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,6 @@ +""" +General linear model +""" import numpy as N from scipy.sandbox.models import family from scipy.sandbox.models.regression import wls_model Modified: trunk/Lib/sandbox/models/info.py =================================================================== --- trunk/Lib/sandbox/models/info.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/info.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,18 +1,24 @@ """ Statistical models -================== -This module contains a several linear statistical models -- model formulae as in R (to some extent) -- OLS (ordinary least square regression) -- WLS (weighted least square regression) -- ARp regression -- GLMS (generalized linear models) -- robust linear models using M estimators (with a number of standard default robust norms as in R's rlm) -- robust scale estimates (MAD, Huber's proposal 2). -- mixed effects models -- generalized additive models (gam) + - model `formula` + - standard `regression` models + + - `ols_model` (ordinary least square regression) + - `wls_model` (weighted least square regression) + - `ar_model` (autoregression) + + - `glm.model` (generalized linear models) + - robust statistical models + + - `rlm.model` (robust linear models using M estimators) + - `robust.norms` estimates + - `robust.scale` estimates (MAD, Huber's proposal 2). + + - `mixed` effects models + - `gam` (generalized additive models) """ +__docformat__ = 'restructuredtext en' depends = ['weave', 'special.orthogonal', Modified: trunk/Lib/sandbox/models/mixed.py =================================================================== --- trunk/Lib/sandbox/models/mixed.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/mixed.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,7 @@ +""" +Mixed effects models +""" + import numpy as N import numpy.linalg as L from scipy.sandbox.models.formula import formula, I @@ -3,5 +7,4 @@ class Unit: - """ Individual experimental unit for Modified: trunk/Lib/sandbox/models/regression.py =================================================================== --- trunk/Lib/sandbox/models/regression.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/regression.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -16,6 +16,8 @@ """ +__docformat__ = 'restructuredtext en' + import numpy as N import numpy.linalg as L from scipy.linalg import norm, toeplitz @@ -24,10 +26,11 @@ from scipy.sandbox.models import utils class ols_model(likelihood_model): - """ A simple ordinary least squares model. + Examples + -------- >>> import numpy as N >>> >>> from scipy.sandbox.models.formula import term, I @@ -46,16 +49,22 @@ >>> results.t() array([ 0.98019606, 1.87867287]) >>> print results.Tcontrast([0,1]) - + >>> print results.Fcontrast(N.identity(2)) - ->>> + """ def logL(self, b, Y): return -norm(self.whiten(Y) - N.dot(self.wdesign, b))**2 / 2. def __init__(self, design): + """ + Create a `ols_model` from a design. + + :Parameters: + design : TODO + TODO + """ likelihood_model.__init__(self) self.initialize(design) @@ -63,6 +72,10 @@ """ Set design for model, prewhitening design matrix and precomputing covariance of coefficients (up to scale factor in front). + + :Parameters: + design : TODO + TODO """ self.design = design @@ -117,6 +130,11 @@ """ A regression model with an AR(p) covariance structure. + The linear autoregressive process of order p--AR(p)--is defined as: + TODO + + Examples + -------- >>> import numpy as N >>> import numpy.random as R >>> @@ -146,17 +164,14 @@ >>> results.t() array([ 30.796394 , -2.66543144]) >>> print results.Tcontrast([0,1]) - + >>> print results.Fcontrast(N.identity(2)) - - >>> - + + >>> >>> model.rho = N.array([0,0]) >>> model.iterative_fit(data['Y'], niter=3) >>> print model.rho [-0.61887622 -0.88137957] - >>> - """ def __init__(self, design, rho): @@ -175,7 +190,13 @@ def iterative_fit(self, Y, niter=3): """ Perform an iterative two-stage procedure to estimate AR(p) - paramters and regression coefficients simultaneously. + parameters and regression coefficients simultaneously. + + :Parameters: + Y : TODO + TODO + niter : ``integer`` + the number of iterations """ for i in range(niter): self.initialize(self.design) @@ -188,6 +209,9 @@ Whiten a series of columns according to an AR(p) covariance structure. + :Parameters: + X : TODO + TODO """ X = N.asarray(X, N.float64) _X = X.copy() @@ -198,16 +222,25 @@ def yule_walker(self, X, method="unbiased", df=None): """ Estimate AR(p) parameters from a sequence X using Yule-Walker equation. - Method can be "unbiased" or "MLE" and this determines - denominator in estimate of ACF at lag k. If "MLE", the denominator is - n=r.shape[0], if "unbiased" the denominator is n-k. - If df is supplied, then it is assumed the X has df degrees of - freedom rather than n. + unbiased or maximum-likelihood estimator (mle) See, for example: http://en.wikipedia.org/wiki/Autoregressive_moving_average_model + + :Parameters: + X : TODO + TODO + method : ``string`` + Method can be "unbiased" or "mle" and this determines + denominator in estimate of autocorrelation function (ACF) + at lag k. If "mle", the denominator is n=r.shape[0], if + "unbiased" the denominator is n-k. + df : ``integer`` + Specifies the degrees of freedom. If df is supplied, + then it is assumed the X has df degrees of + freedom rather than n. """ method = str(method).lower() @@ -237,7 +270,6 @@ class wls_model(ols_model): """ - A regression model with diagonal but non-identity covariance structure. The weights are presumed to be (proportional to the) inverse of the @@ -261,10 +293,9 @@ >>> results.t() array([ 0.35684428, 2.0652652 ]) >>> print results.Tcontrast([0,1]) - + >>> print results.Fcontrast(N.identity(2)) - - + """ def __init__(self, design, weights=1): Modified: trunk/Lib/sandbox/models/rlm.py =================================================================== --- trunk/Lib/sandbox/models/rlm.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/rlm.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,6 @@ +""" +Robust linear models +""" import numpy as N from scipy.sandbox.models.regression import wls_model Modified: trunk/Lib/sandbox/models/robust/__init__.py =================================================================== --- trunk/Lib/sandbox/models/robust/__init__.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/robust/__init__.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,3 +1,6 @@ +""" +Robust statistical models +""" import numpy as N import numpy.linalg as L @@ -3,5 +6,2 @@ from scipy.sandbox.models.robust import norms from scipy.sandbox.models.robust.scale import MAD - - - Modified: trunk/Lib/sandbox/models/robust/scale.py =================================================================== --- trunk/Lib/sandbox/models/robust/scale.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/robust/scale.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,6 +1,6 @@ import numpy as N -import scipy -import scipy.stats +from scipy import median +from scipy.stats import norm def MAD(a, c=0.6745): """ @@ -11,8 +11,8 @@ """ a = N.asarray(a, N.float64) - d = N.multiply.outer(scipy.median(a), N.ones(a.shape[1:])) - return scipy.median(N.fabs(a - d) / c) + d = N.multiply.outer(median(a), N.ones(a.shape[1:])) + return median(N.fabs(a - d) / c) class Huber: """ @@ -25,8 +25,8 @@ c = 1.5 tol = 1.0e-06 - tmp = 2 * scipy.stats.norm.cdf(c) - 1 - gamma = tmp + c**2 * (1 - tmp) - 2 * c * scipy.stats.norm.pdf(c) + tmp = 2 * norm.cdf(c) - 1 + gamma = tmp + c**2 * (1 - tmp) - 2 * c * norm.pdf(c) del(tmp) niter = 10 @@ -41,7 +41,7 @@ self.a = N.asarray(a, N.float64) if mu is None: self.n = self.a.shape[0] - 1 - self.mu = N.multiply.outer(scipy.median(self.a), N.ones(self.a.shape[1:])) + self.mu = N.multiply.outer(median(self.a), N.ones(self.a.shape[1:])) self.est_mu = True else: self.n = self.a.shape[0] Modified: trunk/Lib/sandbox/models/smoothers.py =================================================================== --- trunk/Lib/sandbox/models/smoothers.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/smoothers.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -1,7 +1,6 @@ """ This module contains scatterplot smoothers, that is classes who generate a smooth fit of a set of (x,y) pairs. - """ import numpy as N @@ -15,7 +14,6 @@ class poly_smoother: - """ Polynomial smoother up to a given order. Fit based on weighted least squares. Modified: trunk/Lib/sandbox/models/tests/test_formula.py =================================================================== --- trunk/Lib/sandbox/models/tests/test_formula.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/tests/test_formula.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -8,7 +8,6 @@ import numpy.random as R import numpy.linalg as L from numpy.testing import assert_almost_equal, NumpyTest, NumpyTestCase -import scipy from scipy.sandbox.models import utils, formula, contrast Modified: trunk/Lib/sandbox/models/tests/test_utils.py =================================================================== --- trunk/Lib/sandbox/models/tests/test_utils.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/tests/test_utils.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -6,7 +6,6 @@ import numpy.random as R from numpy.testing import assert_almost_equal, NumpyTest, NumpyTestCase -import scipy from scipy.sandbox.models import utils class test_Utils(NumpyTestCase): Modified: trunk/Lib/sandbox/models/utils.py =================================================================== --- trunk/Lib/sandbox/models/utils.py 2007-02-12 03:13:11 UTC (rev 2702) +++ trunk/Lib/sandbox/models/utils.py 2007-02-12 04:05:31 UTC (rev 2703) @@ -3,6 +3,8 @@ import scipy.interpolate import scipy.linalg +__docformat__ = 'restructuredtext' + def recipr(X): """ Return the reciprocal of an array, setting all entries less than or @@ -72,25 +74,27 @@ return N.asarray(N.transpose(value)).astype(N.float64) class StepFunction: - '''A basic step function: values at the ends are handled in the simplest way possible: everything to the left of x[0] is set to ival; everything to the right of x[-1] is set to y[-1]. - + """ + A basic step function: values at the ends are handled in the simplest + way possible: everything to the left of x[0] is set to ival; everything + to the right of x[-1] is set to y[-1]. + + Examples + -------- + >>> from numpy import arange + >>> from scipy.sandbox.models.utils import StepFunction >>> - >>> from numpy import * - >>> >>> x = arange(20) >>> y = arange(20) - >>> >>> f = StepFunction(x, y) >>> >>> print f(3.2) - 3 + 3.0 >>> print f([[3.2,4.5],[24,-3.1]]) - [[ 3 4] - [19 0]] - >>> + [[ 3. 4.] + [ 19. 0.]] + """ - ''' - def __init__(self, x, y, ival=0., sorted=False): _x = N.asarray(x) From scipy-svn at scipy.org Mon Feb 12 01:48:57 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 12 Feb 2007 00:48:57 -0600 (CST) Subject: [Scipy-svn] r2704 - trunk/Lib/integrate Message-ID: <20070212064857.4C2CC39C0AE@new.scipy.org> Author: stefan Date: 2007-02-12 00:48:44 -0600 (Mon, 12 Feb 2007) New Revision: 2704 Modified: trunk/Lib/integrate/odepack.py Log: Copy odeint input argument to prevent it from being changed. Modified: trunk/Lib/integrate/odepack.py =================================================================== --- trunk/Lib/integrate/odepack.py 2007-02-12 04:05:31 UTC (rev 2703) +++ trunk/Lib/integrate/odepack.py 2007-02-12 06:48:44 UTC (rev 2704) @@ -118,6 +118,7 @@ if mu is None: mu = -1 # changed to zero inside function call t = copy(t) + y0 = copy(y0) output = _odepack.odeint(func, y0, t, args, Dfun, col_deriv, ml, mu, full_output, rtol, atol, tcrit, h0, hmax, hmin, ixpr, mxstep, mxhnil, mxordn, mxords) From scipy-svn at scipy.org Mon Feb 12 09:50:19 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 12 Feb 2007 08:50:19 -0600 (CST) Subject: [Scipy-svn] r2705 - trunk/Lib/sandbox/timeseries Message-ID: <20070212145019.89D7039C1E9@new.scipy.org> Author: mattknox_ca Date: 2007-02-12 08:50:02 -0600 (Mon, 12 Feb 2007) New Revision: 2705 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: added header_justify option and fixed some bugs Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 06:48:44 UTC (rev 2704) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 14:50:02 UTC (rev 2705) @@ -12,6 +12,45 @@ - Mike Brown http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 + +:Examples: + + import numpy as np + import timeseries as ts + import maskedarray as ma + from timeseries import Report, wrap_onspace + + series1 = ts.time_series(np.random.uniform(-100,100,15), start_date=ts.thisday('b')-15) + series2 = ts.time_series(np.random.uniform(-100,100,13), start_date=ts.thisday('b')-10) + series3 = ts.time_series(['string1', 'another string', 'yet another string']*3, start_date=ts.thisday('b')-10) + + darray = ts.date_array(start_date=ts.thisday('b')-8, end_date=ts.thisday('b')-3) + + txt_o = open('myfile.txt', 'w') + html_o = open('myfile.html', 'w') + + # report containing only numerical series, showing 2 decimal places + num_report = Report(series1, series2, fmtfunc=lambda x:'%.2f' % x) + + # report containing some string and numerical data + mixed_report = Report(series1, series2, series3) + + # output a csv report suitable for excel to sys.stdout, show masked values as "N/A" + num_report(delim=', ', mask_rep='N/A') + + # format one column one with 2 decimal places, and column two with 4. + # Add a sum footer. Write the output to txt_o + num_report(fmtfunc=[(lambda x:'%.2f' % x), (lambda x:'%.4f' % x)], + footer_func=ma.sum, footer_label='sum', output=txt_o) + + # create an html table of the data over a specified range. + # Wrap text in cells to width 10. Output to html_o + html_o.write("") + mixed_report(series1, series2, series3, dates=darray, + delim="", + wrapfunc=wrap_onspace(10, nls='
'), output=html_o) + html_o.write("
", prefix="
", postfix="
") + """ __author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" __version__ = '1.0' @@ -19,9 +58,8 @@ __date__ = '$Date: 2007-01-30 13:40:17 -0500 (Tue, 30 Jan 2007) $' import sys -import cStringIO, operator, types, copy -import tseries as ts -import tdates as td +import operator, types, copy +import timeseries as ts __all__ = [ 'Report', 'wrap_onspace', 'wrap_onspace_strict', @@ -54,6 +92,7 @@ 'dates':None, 'header_row':None, 'header_char':'-', + 'header_justify':None, 'row_char':None, 'footer_label':None, 'footer_char':'-', @@ -94,6 +133,14 @@ line between the header and first row of data. None for no separator. This is ignored if `header_row` is None. + - `header_justify` (List of strings or single string, *[None]*) : Determines + how are data justified in their column. If not specified, all headers are + left justified. If a string is specified, it must be one of 'left', 'right', + or 'center' and all headers will be justified the same way. If a list is + specified, each header will be justified according to the specification for + that header in the list. Specifying the justification for the date column is + header is optional. + - `row_char` (string, *[None]*): Character to be used for the row separator line between each row of data. None for no separator @@ -110,8 +157,8 @@ - `footer_label` (string, *[None]*) : label for the footer row. This goes at the end of the date column. This is ignored if footer_func is None. - - `justify` (List of strings or single string, *[None]*) : Determines how are - data justified in their column. If not specified, the date column and string + - `justify` (List of strings or single string, *[None]*) : Determines how data + are justified in their column. If not specified, the date column and string columns are left justified, and everything else is right justified. If a string is specified, it must be one of 'left', 'right', or 'center' and all columns will be justified the same way. If a list is specified, each column @@ -153,46 +200,8 @@ - `fixed_width` (boolean, *[True]*): If True, columns are fixed width (ie. cells will be padded with spaces to ensure all cells in a given column are the same width). If False, `col_width` will be ignored and cells will not - be padded. - -:Examples: + be padded.""" - import numpy as np - import timeseries as ts - import maskedarray as ma - from timeseries import Report, wrap_onspace - - series1 = ts.time_series(np.random.uniform(-100,100,15), start_date=ts.thisday('b')-15) - series2 = ts.time_series(np.random.uniform(-100,100,13), start_date=ts.thisday('b')-10) - series3 = ts.time_series(['string1', 'another string', 'yet another string']*3, start_date=ts.thisday('b')-10) - - darray = ts.date_array(start_date=ts.thisday('b')-8, end_date=ts.thisday('b')-3) - - txt_o = open('myfile.txt', 'w') - html_o = open('myfile.html', 'w') - - # report containing only numerical series, showing 2 decimal places - num_report = Report(series1, series2, fmtfunc=lambda x:'%.2f' % x) - - # report containing some string and numerical data - mixed_report = Report(series1, series2, series3) - - # output a csv report suitable for excel to sys.stdout, show masked values as "N/A" - num_report(delim=', ', mask_rep='N/A') - - # format one column one with 2 decimal places, and column two with 4. - # Add a sum footer. Write the output to txt_o - num_report(fmtfunc=[(lambda x:'%.2f' % x), (lambda x:'%.4f' % x)], - footer_func=ma.sum, footer_label='sum', output=txt_o) - - # create an html table of the data over a specified range. - # Wrap text in cells to width 10. Output to html_o - html_o.write("") - mixed_report(series1, series2, series3, dates=darray, - delim="", - wrapfunc=wrap_onspace(10, nls='
'), output=html_o) - html_o.write("
", prefix="
", postfix="
")""" - def __init__(self, *tseries, **kwargs): self.options = {} @@ -207,7 +216,7 @@ option_list = list(_default_options) - for x in [kw for kw in option_list if kw in kwargs]: + for x in [kw for kw in kwargs if kw in option_list]: option_dict[x] = kwargs.pop(x) if len(kwargs) > 0: @@ -251,6 +260,7 @@ dates = option('dates') header_row = option('header_row') header_char = option('header_char') + header_justify = option('header_justify') row_char = option('row_char') footer_label = option('footer_label') footer_char = option('footer_char') @@ -283,24 +293,34 @@ rows=[] if fixed_width: - if justify is not None: - _justify = kwargs.pop('justify') - if isinstance(justify, str): + + def _standardize_justify(userspec): + if isinstance(userspec, str): # justify all columns the the same way - justify = [justify for x in range(len(tseries)+1)] - elif isinstance(justify, list): #assume it is a list or tuple, etc - if len(justify) == len(tseries): + return [userspec for x in range(len(tseries)+1)] + elif isinstance(userspec, list): + if len(userspec) == len(tseries): # justification for date column not included, so set that # to left by default - justify = ['left'] + justify + return ['left'] + userspec else: raise ValueError("invalid `justify` specification") + + if justify is not None: + justify = _standardize_justify(justify) else: # default column justification justify = ['left'] for ser in tseries: - if str(ser.dtype)[:2] == '|S': justify.append('left') + if ser.dtype.char in 'SUb': justify.append('left') else: justify.append('right') + + + if header_justify is not None: + header_justify = _standardize_justify(header_justify) + else: + # default column justification + header_justify = ['left' for x in range(len(tseries)+1)] else: justify = [None for x in range(len(tseries)+1)] @@ -311,7 +331,7 @@ if dates is None: tseries = ts.align_series(*tseries) - dates = td.date_array(start_date=tseries[0].start_date, + dates = ts.date_array(start_date=tseries[0].start_date, end_date=tseries[0].end_date) else: tseries = ts.align_series(start_date=dates[0], end_date=dates[-1], *tseries) @@ -422,8 +442,13 @@ for rowNum, physicalRows in enumerate(logicalRows): for row in physicalRows: + if rowNum == 0 and header_separator: + _justify = header_justify + else: + _justify = justify + output.write(prefix \ - + delim.join([justify_funcs[str(justify[colNum]).lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ + + delim.join([justify_funcs[str(_justify[colNum]).lower()](str(item),width) for (colNum,item,width) in zip(colNums,row,maxWidths)]) \ + postfix + nls) if row_separator and (data_start <= rowNum <= data_end): From scipy-svn at scipy.org Mon Feb 12 09:54:34 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 12 Feb 2007 08:54:34 -0600 (CST) Subject: [Scipy-svn] r2706 - trunk/Lib/sandbox/timeseries Message-ID: <20070212145434.0BF9739C1E9@new.scipy.org> Author: mattknox_ca Date: 2007-02-12 08:54:31 -0600 (Mon, 12 Feb 2007) New Revision: 2706 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: fixed error in the doc string for the Report class Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 14:50:02 UTC (rev 2705) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 14:54:31 UTC (rev 2706) @@ -134,12 +134,12 @@ is ignored if `header_row` is None. - `header_justify` (List of strings or single string, *[None]*) : Determines - how are data justified in their column. If not specified, all headers are - left justified. If a string is specified, it must be one of 'left', 'right', - or 'center' and all headers will be justified the same way. If a list is - specified, each header will be justified according to the specification for - that header in the list. Specifying the justification for the date column is - header is optional. + how headers are justified. If not specified, all headers are left justified. + If a string is specified, it must be one of 'left', 'right', or 'center' + and all headers will be justified the same way. If a list is specified, each + header will be justified according to the specification for that header in + the list. Specifying the justification for the date column is header is + optional. - `row_char` (string, *[None]*): Character to be used for the row separator line between each row of data. None for no separator From scipy-svn at scipy.org Mon Feb 12 15:50:20 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 12 Feb 2007 14:50:20 -0600 (CST) Subject: [Scipy-svn] r2707 - trunk/Lib/sandbox/timeseries Message-ID: <20070212205020.2B71239C251@new.scipy.org> Author: mattknox_ca Date: 2007-02-12 14:50:16 -0600 (Mon, 12 Feb 2007) New Revision: 2707 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: added dtype of object to be left justified by default Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 14:54:31 UTC (rev 2706) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-12 20:50:16 UTC (rev 2707) @@ -312,7 +312,7 @@ # default column justification justify = ['left'] for ser in tseries: - if ser.dtype.char in 'SUb': justify.append('left') + if ser.dtype.char in 'SUO': justify.append('left') else: justify.append('right') From scipy-svn at scipy.org Wed Feb 14 11:41:57 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 14 Feb 2007 10:41:57 -0600 (CST) Subject: [Scipy-svn] r2708 - in trunk/Lib/io: . tests Message-ID: <20070214164157.BB2B639C040@new.scipy.org> Author: matthew.brett at gmail.com Date: 2007-02-14 10:41:53 -0600 (Wed, 14 Feb 2007) New Revision: 2708 Modified: trunk/Lib/io/npfile.py trunk/Lib/io/tests/test_npfile.py Log: Added support for -1 shape values in read_array Modified: trunk/Lib/io/npfile.py =================================================================== --- trunk/Lib/io/npfile.py 2007-02-12 20:50:16 UTC (rev 2707) +++ trunk/Lib/io/npfile.py 2007-02-14 16:41:53 UTC (rev 2708) @@ -132,6 +132,13 @@ """Write string to file as raw bytes.""" return self.file.write(str) + def remaining_bytes(self): + cur_pos = self.tell() + self.seek(0, 2) + end_pos = self.tell() + self.seek(cur_pos) + return end_pos - cur_pos + def _endian_order(self, endian, order): ''' Housekeeping function to return endian, order from input args ''' if endian is None: @@ -167,13 +174,16 @@ data = data.byteswap() self.file.write(data.tostring(order=order)) - def read_array(self, shape, dt, endian=None, order=None): + def read_array(self, dt, shape=-1, endian=None, order=None): '''Read data from file and return it in a numpy array. Inputs ------ + dt - dtype of array to be read shape - shape of output array, or number of elements - dt - dtype of array to be read + (-1 as number of elements or element in shape + means unknown dimension as in reshape; size + of array calculated from remaining bytes in file) endian - endianness of data in file (can be None, 'dtype', '<', '>') (if None, get from self.endian) @@ -184,13 +194,26 @@ arr - array from file with given dtype (dt) ''' endian, order = self._endian_order(endian, order) + dt = N.dtype(dt) try: - shape = tuple(shape) + shape = list(shape) except TypeError: - shape = (shape,) - dt = N.dtype(dt) + shape = [shape] + minus_ones = shape.count(-1) + if minus_ones == 0: + pass + elif minus_ones == 1: + known_dimensions_size = -N.product(shape,axis=0) * dt.itemsize + unknown_dimension_size, illegal = divmod(self.remaining_bytes(), + known_dimensions_size) + if illegal: + raise ValueError("unknown dimension doesn't match filesize") + shape[shape.index(-1)] = unknown_dimension_size + else: + raise ValueError( + "illegal -1 count; can only specify one unknown dimension") + sz = dt.itemsize * N.product(shape) dt_endian = self._endian_from_dtype(dt) - sz = dt.itemsize * N.product(shape) buf = self.file.read(sz) arr = N.ndarray(shape=shape, dtype=dt, Modified: trunk/Lib/io/tests/test_npfile.py =================================================================== --- trunk/Lib/io/tests/test_npfile.py 2007-02-12 20:50:16 UTC (rev 2707) +++ trunk/Lib/io/tests/test_npfile.py 2007-02-14 16:41:53 UTC (rev 2708) @@ -20,8 +20,7 @@ npf.write_array(arr) npf.rewind() self.assertRaises(IOError, npf.read_array, - arr.shape, - arr.dtype) + arr.dtype, arr.shape) npf.close() os.remove(fname) @@ -48,6 +47,16 @@ npf.rewind() assert str == npf.read_raw(len(str)) + def test_remaining_bytes(self): + npf = npfile(StringIO()) + assert npf.remaining_bytes() == 0 + npf.write_raw('+' * 10) + assert npf.remaining_bytes() == 0 + npf.rewind() + assert npf.remaining_bytes() == 10 + npf.seek(5) + assert npf.remaining_bytes() == 5 + def test_read_write_array(self): npf = npfile(StringIO()) arr = N.reshape(N.arange(10), (5,2)) @@ -67,25 +76,29 @@ shp = arr.shape npf.write_array(arr) npf.rewind() - assert_array_equal(npf.read_array(shp, adt), arr) + assert_array_equal(npf.read_array(adt), arr.flatten()) npf.rewind() - assert_array_equal(npf.read_array(shp, adt, endian=swapped_code), + assert_array_equal(npf.read_array(adt, shp), arr) + npf.rewind() + assert_array_equal(npf.read_array(adt, shp, endian=swapped_code), bs_arr) npf.rewind() - assert_array_equal(npf.read_array(shp, adt, order='F'), + assert_array_equal(npf.read_array(adt, shp, order='F'), f_arr) npf.rewind() npf.write_array(arr, order='F') npf.rewind() - assert_array_equal(npf.read_array(shp, adt), + assert_array_equal(npf.read_array(adt), arr.flatten('F')) + npf.rewind() + assert_array_equal(npf.read_array(adt, shp), cf_arr) npf = npfile(StringIO(), endian='swapped', order='F') npf.write_array(arr) npf.rewind() - assert_array_equal(npf.read_array(shp, adt), arr) + assert_array_equal(npf.read_array(adt, shp), arr) npf.rewind() - assert_array_equal(npf.read_array(shp, adt, endian='dtype'), bs_arr) + assert_array_equal(npf.read_array(adt, shp, endian='dtype'), bs_arr) npf.rewind() - assert_array_equal(npf.read_array(shp, adt, order='C'), cf_arr) + assert_array_equal(npf.read_array(adt, shp, order='C'), cf_arr) From scipy-svn at scipy.org Thu Feb 15 11:41:44 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 10:41:44 -0600 (CST) Subject: [Scipy-svn] r2709 - in trunk/Lib/sandbox/timeseries: . archived_version Message-ID: <20070215164144.AA71839C01F@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 10:41:40 -0600 (Thu, 15 Feb 2007) New Revision: 2709 Added: trunk/Lib/sandbox/timeseries/archived_version/CHANGELOG Removed: trunk/Lib/sandbox/timeseries/CHANGELOG Log: Moved remotely Deleted: trunk/Lib/sandbox/timeseries/CHANGELOG =================================================================== --- trunk/Lib/sandbox/timeseries/CHANGELOG 2007-02-14 16:41:53 UTC (rev 2708) +++ trunk/Lib/sandbox/timeseries/CHANGELOG 2007-02-15 16:41:40 UTC (rev 2709) @@ -1,17 +0,0 @@ -#2007-01-23 : tdates : forced a 'U' to 'D' when using asfreq -#2007-01-15 : tmulti : Add the module and its tests, to support multi-variable series -# : tdates : Some minor bug fixes -# : tseries: Fixed a bug in __new__ when data are lists -# : tcore : Introduced the forward/backward_fill and interpol functions. -#2007-01-14 : Code reorganization: -# : - Moved Matt's initial version to archived_version -# : - Moved Pierre's version to base -# : tdates : Fixed a bug w/ definition of months and weeks -# : tdates : Renamed dateOf to asfreq + use cseries -#2007-01-04 : tdates : Corrected a bug w/ Date.__init__ and 'B' freq -#2007-01-03 : tseries: Allowed endpoints adjustment after convert -# : tseries: Put the estimation of the data length in its own function. -# : tseries: Added a timestep compatibility check. -# : tseries: The variables in a multi-series correspond now to the last axis. -# : tseries: Blocked transpose/swapaxes, temporarily. -# : tseries: Speed-up fill_missing_dates \ No newline at end of file Copied: trunk/Lib/sandbox/timeseries/archived_version/CHANGELOG (from rev 2708, trunk/Lib/sandbox/timeseries/CHANGELOG) From scipy-svn at scipy.org Thu Feb 15 11:41:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 10:41:55 -0600 (CST) Subject: [Scipy-svn] r2710 - in trunk/Lib/sandbox/timeseries: . archived_version Message-ID: <20070215164155.2253B39C01F@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 10:41:53 -0600 (Thu, 15 Feb 2007) New Revision: 2710 Added: trunk/Lib/sandbox/timeseries/archived_version/.project Removed: trunk/Lib/sandbox/timeseries/.project Log: Moved remotely Deleted: trunk/Lib/sandbox/timeseries/.project =================================================================== --- trunk/Lib/sandbox/timeseries/.project 2007-02-15 16:41:40 UTC (rev 2709) +++ trunk/Lib/sandbox/timeseries/.project 2007-02-15 16:41:53 UTC (rev 2710) @@ -1,17 +0,0 @@ - - - scipy_svn_timeseries - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - - Copied: trunk/Lib/sandbox/timeseries/archived_version/.project (from rev 2709, trunk/Lib/sandbox/timeseries/.project) From scipy-svn at scipy.org Thu Feb 15 11:42:20 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 10:42:20 -0600 (CST) Subject: [Scipy-svn] r2711 - in trunk/Lib/sandbox/timeseries: . archived_version Message-ID: <20070215164220.84DBC39C01F@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 10:42:18 -0600 (Thu, 15 Feb 2007) New Revision: 2711 Added: trunk/Lib/sandbox/timeseries/archived_version/MANIFEST Removed: trunk/Lib/sandbox/timeseries/MANIFEST Log: Moved remotely Deleted: trunk/Lib/sandbox/timeseries/MANIFEST =================================================================== --- trunk/Lib/sandbox/timeseries/MANIFEST 2007-02-15 16:41:53 UTC (rev 2710) +++ trunk/Lib/sandbox/timeseries/MANIFEST 2007-02-15 16:42:18 UTC (rev 2711) @@ -1,8 +0,0 @@ -setup.py -./__init__.py -./corelib.py -./cseries.c -./shiftingarray.py -./timeseries.py -./tsdate.py -./examples/example.py Copied: trunk/Lib/sandbox/timeseries/archived_version/MANIFEST (from rev 2710, trunk/Lib/sandbox/timeseries/MANIFEST) From scipy-svn at scipy.org Thu Feb 15 12:52:32 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 11:52:32 -0600 (CST) Subject: [Scipy-svn] r2712 - in trunk/Lib/sandbox/timeseries: . io io/fame io/fame/src io/fame/tests Message-ID: <20070215175232.EDD0A39C09C@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 11:52:27 -0600 (Thu, 15 Feb 2007) New Revision: 2712 Added: trunk/Lib/sandbox/timeseries/io/ trunk/Lib/sandbox/timeseries/io/__init__.py trunk/Lib/sandbox/timeseries/io/fame/ trunk/Lib/sandbox/timeseries/io/fame/__init__.py trunk/Lib/sandbox/timeseries/io/fame/fame.py trunk/Lib/sandbox/timeseries/io/fame/mapping.py trunk/Lib/sandbox/timeseries/io/fame/readme.txt trunk/Lib/sandbox/timeseries/io/fame/setup.py trunk/Lib/sandbox/timeseries/io/fame/src/ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c trunk/Lib/sandbox/timeseries/io/fame/tests/ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: Added: trunk/Lib/sandbox/timeseries/io/__init__.py =================================================================== Added: trunk/Lib/sandbox/timeseries/io/fame/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1 @@ +from fame import * \ No newline at end of file Added: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,657 @@ +import sys, types, re, os + +import timeseries as ts +import cfame +import mapping as mp + +import numpy +import maskedarray as ma +import thread + +fameLock = thread.allocate_lock() + +class CaseInsensitiveDict(dict): + def __init__(self, data={}): + for i, v in data.iteritems(): + self[i.upper()] = v + + def __getitem__(self, key): + if hasattr(key, 'upper'): key = key.upper() + return super(CaseInsensitiveDict, self).__getitem__(key) + + def __setitem__(self, key, item): + if hasattr(key, 'upper'): key = key.upper() + super(CaseInsensitiveDict, self).__setitem__(key, item) + +class DBError(Exception): pass + + +class FameDb(object): + """Fame database object + +:Construction: + x = FameDb(conn_str, mode='r', large=True) + +:Paramaters: + - `conn_str` (str) : valid connection string. Can be a physical path, + channel specification, etc. + - `mode` (str, *['r']*) : method of access to the database. Can be one + of the following: + 'r' => read only + 's' => shared + 'o' => overwrite + 'c' => create + 'u' => update + 'w' => write + 'd' => direct + - `large` (boolean, *[True]*) : Applies only when `mode` is 'o' or 'c'. + If True, a large size database will be created. If False, a standard size + database will be created. +""" + def __init__(self, conn_str, mode='r', large=True): + mode = mode.lower() + if mode == 'r': + intmode = mp.HRMODE + elif mode == 's': + intmode = mp.HSMODE + elif mode == 'u': + intmode = mp.HUMODE + elif mode == 'w': + intmode = mp.HWMODE + elif mode == 'd': + intmode = mp.HDMODE + elif mode == 'c': + intmode = mp.HCMODE + elif mode == 'o': + intmode = mp.HOMODE + else: + raise ValueError, "Database access mode not supported." + self.mode = mode + + try: + self.dbkey = cf_open(conn_str, intmode, int(large)) + self.dbIsOpen = True + except: + self.dbIsOpen = False + raise + + + def read(self, name, + start_date=None, end_date=None, + start_case=None, end_case=None, max_string_len=65): + + """read specified object(s) from database + +:Parameters: + - `name` (string or list of strings) : names of objects that will be + read from the database + + - `start_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or after `start_date` will be read. + If None, data will be read from the first value of the series. + - `end_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or before `end_date` will be read. + If None, data will be read to the last value of the series. + - `start_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or after `start_case` will be read. + If None, data will be read starting from case index 1 + - `end_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or before `end_case` will be read. + If None, data will be read to the last value of the series. + - `max_string_len` (int, *[65]*) : Applies only when readings strings + or series of strings. This is the maximum length of string that can + be read. Lower values result in less memory usage, so you should + specify this as low as is reasonable for your data. + +:Return: + if `name` is a list of strings: + case insensitive dictionary of the objects + if `name` is a single string: + object from database that is stored as `name`""" + + + if not self.dbIsOpen: + raise DBError("Database is not open") + + isSingle = False + if isinstance(name, types.StringType): + names = [name] + isSingle = True + else: + names = name + + items = CaseInsensitiveDict() + + #default to -1. This will get the entire range + _start_case = _end_case = -1 + _start_date = _end_date = -1 + + range_freq = None + if start_date is not None: + _start_date = start_date.value - mp.value_adjust[start_date.freq] + range_freq = mp.freqReverseMapping[start_date.freq] + + if end_date is not None: + if start_date is not None and start_date.freq != end_date.freq: + raise ValueError("start_date and end_date must be same frequency") + _end_date = end_date.value - mp.value_adjust[end_date.freq] + if range_freq is None: + range_freq = mp.freqReverseMapping[end_date.freq] + + if start_case is not None: _start_case = start_case + if end_case is not None: _end_case = end_case + + if len(set([_start_case, _end_case, _start_date, _end_date, -1])) != 1: + checkFreq = True + else: + checkFreq = False + + for objName in names: + objName = objName.upper() + + if checkFreq: + objFreq = self.get_freq(objName) + + if objFreq == range_freq: + start_index, end_index = _start_date, _end_date + elif objFreq == mp.HCASEX: + start_index, end_index = _start_case, _end_case + else: + start_index, end_index = -1, -1 + else: + start_index, end_index = -1, -1 + + result = cf_read(self.dbkey, objName, start_index, + end_index, max_string_len) + + if result['type'] == mp.HBOOLN: + numpyType = numpy.bool_ + else: + numpyType = mp.fametype_tonumpy(result['type']) + + if result['type'] == mp.HNAMEL: + pyObj = [x for x in result['data'][1:-1].split(", ") \ + if x != ''] + + elif result['class'] == mp.HSCALA: + if isinstance(result['data'], str): + if result['mask']: + pyObj = None + else: + pyObj = result['data'] + else: + if result['mask'][0]: + pyObj = None + else: + pyObj = result['data'][0] + if result['type'] >= 8: # date type + value = pyObj+ \ + mp.value_adjust[mp.freqMapping[result['type']]] + pyObj = ts.Date( + freq=mp.freqMapping[result['type']], + value=value) + else: + pyObj = numpyType(pyObj) + + elif result['class'] == mp.HSERIE: + + if 'data' in result: + vals = result['data'] + mask = result['mask'] + else: + vals = [] + mask = ma.nomask + + if result['type'] >= 8: # date type + valadj = mp.value_adjust[mp.freqMapping[result['type']]] + if len(vals) > 0: vals += valadj + data = ts.DateArray(vals, + freq=mp.freqMapping[result['type']]) + else: + data = numpy.array(vals, dtype=numpyType) + + if result['freq'] == mp.HCASEX: + pyObj = ma.array(data, mask=mask) + else: + observed = mp.observedMapping[result['observed']] + basis = mp.basisMapping[result['basis']] + freq = mp.freqMapping[result['freq']] + + if 'data' in result: + start_date = ts.Date( + freq=freq, + value=result['startindex']+mp.value_adjust[freq]) + else: + start_date = None + + pyObj = ts.time_series(data, freq=freq, + start_date=start_date, + observed=observed, mask=mask) + + items[objName] = pyObj + + if isSingle: + return items.values()[0] + + return items + + + def write_tser_dict(self, objdict, + overwrite=False, assume_exists=False, + start_date=None, end_date=None): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a time series (calls FameDb.write_tser on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object + names for keys and TimeSeries objects for values + - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in objects in `objdict` will be given priority over pre-existing data in + the db where there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `start_date` (Date, *[None]*) : If None, data will be written from the start of + the series. If specified, only data points on or after start_date will be written. + - `end_date` (Date, *[None]*) : If None, data will be written until the end of + the series. If specified, only data points on or before end_date will be written. +""" + for key, obj in objdict.iteritems(): + self.write_tser(key, obj, overwrite=overwrite, + assume_exists=assume_exists, + start_date=start_date, end_date=end_date) + + + def write_cser_dict(self, objdict, + overwrite=False, assume_exists=False, + zero_represents=1, start_case=None, end_case=None): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a case series (calls FameDb.write_tser on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of arrays to be written as Case Series. + Object names for keys and arrays for values + - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in objects in `objdict` will be given priority over pre-existing data in + the db where there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in + the array represents + - `start_case` (int, *[None]*) : If None, data will be written from the start of + the array. If specified, only data points on or after start_case will be written. + - `end_case` (int, *[None]*) : If None, data will be written until the end of + the array. If specified, only data points on or before end_case will be written. +""" + for key, obj in objdict.iteritems(): + self.write_cser(key, obj, overwrite=overwrite, + assume_exists=assume_exists, + zero_represents=zero_represents, + start_case=start_case, end_case=end_case) + + def write_scalar_dict(self, objdict): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a scalar (calls FameDb.write_scalar on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of items to be written as scalars. + Object names for keys and scalar items for values +""" + for key, obj in objdict.iteritems(): + self.write_scalar(key, obj) + + + def write_tser(self, name, tser, + overwrite=False, assume_exists=False, + start_date=None, end_date=None): + """write `tser` to the database as `name` as a time series. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates. + Use fill_missing_dates first on your series if you suspect this is the situation. + TimeSeries must be 1-dimensional + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `tser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `start_date` (Date, *[None]*) : If None, data will be written from the start of + `tser`. If specified, only data points on or after start_date will be written. + - `end_date` (Date, *[None]*) : If None, data will be written until the end of + `tser`. If specified, only data points on or before end_date will be written. +""" + + self.__check_writeable() + + if not isinstance(tser, ts.TimeSeries): + raise ValueError("tser is not a valid time series") + elif tser.has_missing_dates(): + raise ValueError("tser must not have any missing dates") + elif tser.ndim != 1: + raise ValueError("FAME db only supports 1-dimensional time series") + + if assume_exists and not self.exists(name): + raise DBError("%s does not exist" % name) + + if overwrite or not self.exists(name): create = True + else: create = False + + fame_type = mp.fametype_fromdata(tser._data) + fame_freq = mp.freqReverseMapping[tser.freq] + + if create: + + if hasattr(tser, "basis"): + fame_basis = mp.basisReverseMapping[tser.basis] + else: + fame_basis = mp.HBSDAY + + if hasattr(tser, "observed"): + fame_observed = mp.observedReverseMapping[tser.observed] + if fame_observed == 0: fame_observed = mp.HOBEND + else: + fame_observed = mp.HOBEND + + if self.exists(name): self.remove(name) + cf_create(self.dbkey, name, mp.HSERIE, fame_freq, fame_type, fame_basis, fame_observed) + + def get_boundary_date(bdate, attr): + if bdate is not None: + if bdate.freq != tser.freq: + raise ValueError(attr+" frequency must be same as tser frequency") + if tser.start_date > bdate or tser.end_date < bdate: + raise ValueError(attr+" outside range of series") + return bdate + else: + return getattr(tser, attr) + + start_date = get_boundary_date(start_date, "start_date") + end_date = get_boundary_date(end_date, "end_date") + + if start_date is not None: + + towrite = tser[start_date:end_date+1] + + start_index = start_date.value + end_index = end_date.value + + # convert integer types to floats since FAME does not have an integer type + newType = mp.fametype_tonumpy(fame_type) + if fame_type >= 8: + # date type + fame_data = towrite._data - mp.value_adjust[towrite._data.freq] + elif newType != tser._data.dtype: + fame_data = towrite._data.astype(newType) + else: + fame_data = towrite._data + + if towrite._mask is ma.nomask: + fame_mask = numpy.zeros(towrite._data.shape, dtype=numpy.bool_) + else: + fame_mask = towrite._mask + + start_index -= mp.value_adjust[towrite.freq] + end_index -= mp.value_adjust[towrite.freq] + + cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_index, end_index, fame_type, fame_freq) + + def write_cser(self, name, cser, overwrite=False, assume_exists=False, zero_represents=1, start_case=None, end_case=None): + """write `cser` to the database as `name` as a case series. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be + written. If `cser` is a MaskedArray, then masked values will be written as ND. + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `cser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in + the array represents + - `start_case` (int, *[None]*) : If None, data will be written from the start of + `cser`. If specified, only data points on or after start_case will be written. + - `end_case` (int, *[None]*) : If None, data will be written until the end of + `cser`. If specified, only data points on or before end_case will be written. +""" + + self.__check_writeable() + + if not isinstance(cser, numpy.ndarray): + raise ValueError("cser is not a valid ndarray") + elif cser.ndim != 1: + raise ValueError("FAME db only supports 1-dimensional arrays") + + if assume_exists and not self.exists(name): + raise DBError("%s does not exist" % name) + + if overwrite or not self.exists(name): create = True + else: create = False + + if hasattr(cser, "_data"): + fame_data = cser._data + if cser._mask is ma.nomask: + fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) + else: + fame_mask = cser._mask + else: + fame_data = cser + fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) + + fame_type = mp.fametype_fromdata(fame_data) + + if create: + if self.exists(name): self.remove(name) + cf_create(self.dbkey, name, mp.HSERIE, mp.HCASEX, fame_type, mp.HBSUND, mp.HOBUND) + + def get_boundary_case(bcase, attr): + if bcase is not None: + idx = bcase - zero_represents + if idx < 0 or idx > cser.size: + raise ValueError("%s outside range of series" % attr) + return bcase + else: + if cser.size == 0: + return None + else: + if attr == 'start_case': + return zero_represents + elif attr == 'end_case': + return zero_represents + cser.size - 1 + else: + raise ValueError("unexpected argument: %s " % attr) + + start_case = get_boundary_case(start_case, "start_case") + end_case = get_boundary_case(end_case, "end_case") + + if start_case is not None: + # convert integer types to floats since FAME does not have an integer type + s = start_case - zero_represents + e = end_case - zero_represents + + fame_data = fame_data[s:e+1] + fame_mask = fame_mask[s:e+1] + newType = mp.fametype_tonumpy(fame_type) + if fame_type >= 8: + # date type + fame_data = fame_data - mp.value_adjust[fame_data.freq] + elif newType != fame_data.dtype: + fame_data = fame_data.astype(newType) + + cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_case, end_case, fame_type, mp.HCASEX) + + + def write_scalar(self, name, scalar): + """write `scalar` to the database as `name` as a scalar object. If an +object already exists in the database named as `name` then it is +over-written, otherwise it is created. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `scalar` : one of the following: string, numpy scalar, int, float, + list of strings (for name lists), Date, boolean""" + + self.__check_writeable() + + fame_type = mp.fametype_fromdata(scalar) + + if isinstance(scalar, ts.Date): + fame_data = numpy.int32(scalar.value - mp.value_adjust[scalar.freq]) + elif hasattr(scalar, "dtype"): + if scalar.ndim != 0: raise ValueError("received non-scalar data") + newType = mp.fametype_tonumpy(fame_type) + if newType != scalar.dtype: fame_data = scalar.astype(newType) + else: fame_data = scalar + elif fame_type == mp.HSTRNG: + fame_data = scalar + elif fame_type == mp.HPRECN: + fame_data = numpy.float64(scalar) + elif fame_type == mp.HBOOLN: + fame_data = numpy.int32(scalar) + elif fame_type == mp.HNAMEL: + fame_data = "{" + ", ".join(scalar) + "}" + else: + raise ValueError("Unrecognized data type") + + if self.exists(name): self.remove(name) + cf_create(self.dbkey, name, mp.HSCALA, mp.HUNDFX, fame_type, mp.HBSUND, mp.HOBUND) + + # convert integer types to floats since FAME does not have an integer type + newType = mp.fametype_tonumpy(fame_type) + if hasattr(fame_data, 'dtype') and newType != fame_data.dtype: + fame_data = fame_data.astype(newType) + + if fame_type == mp.HNAMEL: + cf_write_namelist(self.dbkey, name, fame_data) + else: + cf_write_scalar(self.dbkey, name, fame_data, fame_type) + + + + def wildlist(self, exp, wildonly=False): + """performs a wildlist lookup on the database, using Fame syntax +("?" and "^"), returns a normal python list of strings""" + self.__check_readable() + res = cf_wildlist(self.dbkey, exp) + + if wildonly: + exp = exp.replace("?", "(.*)") + exp = exp.replace("^", "(.)") + exp = exp.replace("$","\$") + regex = re.compile(exp) + for i in range(len(res)): + res[i] = "".join(regex.match(res[i]).groups()) + return res + + def exists(self, objName): + return cf_exists(self.dbkey, objName) + + def close(self): + if self.dbIsOpen: + cf_close(self.dbkey) + self.dbIsOpen = False + + def __del__(self): + if self.dbIsOpen: + self.close() + + + def __check_writeable(self): + """Raises error if data base is not writeable""" + if not self.dbIsOpen: + raise DBError("Database is not open") + if self.mode == 'r': + raise DBError("Cannot write to a read-only database") + + def __check_readable(self): + """Raises error if data base is not readable""" + if not self.dbIsOpen: + raise DBError("Database is not open") + + + def remove(self, name, ignoreError=True): + """Deletes the given series from the database""" + if type(name) == type(""): name = [name] + + for x in name: + try: + cf_remove(self.dbkey, x) + except: + if not ignoreError: raise + + def get_freq(self, name): + """Finds the frequency of the object stored in the db as `name`""" + if not self.dbIsOpen: + raise DBError("Database is not open") + + result = cf_size(self.dbkey, name.upper()) + return result['freq'] + + + def whats(self, name): + """Preforms a fame "whats" command on the provided series""" + if type(name) == type(""): name = [name] + + result = {} + for dbname in name: + if not self.dbIsOpen: + raise DBError("Database is not open") + + result[dbname] = cf_whats(self.dbkey, dbname.upper()) + + if len(result) == 1: + return result.values()[0] + return result + + + + def restore(self): + """Discard any changes made to the database since it was last opened or posted.""" + return cf_restore(self.dbkey) + + +class cFameCall: + """wrapper for cfame functions that acquires and releases a resource log. +This is needed because the Fame C api is not thread safe.""" + + def __init__ (self, func): + self.f = func + self.__doc__ = getattr(func, "__doc__", str(func)) + self.__name__ = getattr(func, "__name__", str(func)) + # + def __call__ (self, *args, **kwargs): + "Execute the call behavior." + tmp = fameLock.acquire() + try: + result = self.f(*args, **kwargs) + fameLock.release() + except: + fameLock.release() + raise + + return result + +cf_open = cFameCall(cfame.open) +cf_close = cFameCall(cfame.close) +cf_restore = cFameCall(cfame.restore) +cf_size = cFameCall(cfame.size) +cf_whats = cFameCall(cfame.whats) +cf_remove = cFameCall(cfame.remove) +cf_create = cFameCall(cfame.create) +cf_read = cFameCall(cfame.read) +cf_write_scalar = cFameCall(cfame.write_scalar) +cf_write_series = cFameCall(cfame.write_series) +cf_write_namelist = cFameCall(cfame.write_namelist) +cf_wildlist = cFameCall(cfame.wildlist) +cf_exists = cFameCall(cfame.exists) \ No newline at end of file Added: trunk/Lib/sandbox/timeseries/io/fame/mapping.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/mapping.py 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/mapping.py 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,205 @@ + + +# --------------------------- +# For fametype mapping +import types +import numpy +from timeseries import TimeSeries, Date, DateArray, freq_fromstr + + +# --------------------------- +# Fame specific constants + +HRMODE = 1 # READ +HCMODE = 2 # CREATE +HOMODE = 3 # OVERWRITE +HUMODE = 4 # UPDATE +HSMODE = 5 # SHARED +HWMODE = 6 # WRITE +HDMODE = 7 # DIRECT WRITE + +#** FAME Data Object Classes ** + +HSERIE = 1 # SERIES +HSCALA = 2 # SCALAR +HFRMLA = 3 # FORMULA +HITEM = 4 # ITEM +HGLNAM = 5 # GLNAME +HGLFOR = 6 # GLFORMULA + +#** FAME Data Object Types ** + +HUNDFT = 0 # Undefined +HNUMRC = 1 # NUMERIC +HNAMEL = 2 # NAMELIST +HBOOLN = 3 # BOOLEAN +HSTRNG = 4 # STRING +HPRECN = 5 # PRECISION +HDATE = 6 # General DATE +HRECRD = 7 # RECORD + +#** FAME Frequencies ** + +HUNDFX = 0 # Undefined +HDAILY = 8 # DAILY +HBUSNS = 9 # BUSINESS +HWKSUN = 16 #WEEKLY (SUNDAY) +HMONTH = 129 # MONTHLY +HCASEX = 232 # CASE +HSEC = 226 # SECONDLY +HMIN = 227 # MINUTELY +HHOUR = 228 # HOURLY +HQTOCT = 160 # QUARTERLY (OCTOBER) +HQTNOV = 161 # QUARTERLY (NOVEMBER) +HQTDEC = 162 # QUARTERLY (DECEMBER) +HANJAN = 192 # ANNUAL (JANUARY) +HANFEB = 193 # ANNUAL (FEBRUARY) +HANMAR = 194 # ANNUAL (MARCH) +HANAPR = 195 # ANNUAL (APRIL) +HANMAY = 196 # ANNUAL (MAY) +HANJUN = 197 # ANNUAL (JUNE) +HANJUL = 198 # ANNUAL (JULY) +HANAUG = 199 # ANNUAL (AUGUST) +HANSEP = 200 # ANNUAL (SEPTEMBER) +HANOCT = 201 # ANNUAL (OCTOBER) +HANNOV = 202 # ANNUAL (NOVEMBER) +HANDEC = 203 # ANNUAL (DECEMBER) + +#** FAME BASIS Attribute Settings ** + +HBSUND = 0 # Undefined +HBSDAY = 1 # DAILY +HBSBUS = 2 # BUSINESS + +#** FAME OBSERVED Attribute Settings ** + +HOBUND = 0 # Undefined +HOBBEG = 1 # BEGINNING +HOBEND = 2 # ENDING +HOBAVG = 3 # AVERAGED +HOBSUM = 4 # SUMMED +HOBANN = 5 # ANNUALIZED +HOBFRM = 6 # FORMULA +HOBHI = 7 # HIGH +HOBLO = 8 # LOW + +def reverse_dict(d): + return dict([(y, x) for x, y in d.iteritems()]) + +basisMapping = { HBSUND:"UNDEFINED", + HBSDAY:"D", + HBSBUS:"B"} +basisReverseMapping = reverse_dict(basisMapping) + +observedMapping = { HOBUND:"UNDEFINED", + HOBBEG: "BEGINNING", + HOBEND: "ENDING", + HOBAVG: "AVERAGED", + HOBSUM: "SUMMED", + HOBANN: "ANNUALIZED", + HOBFRM: "FORMULA", + HOBHI: "MAXIMUM", + HOBLO: "MINIMUM" } + +observedReverseMapping = reverse_dict(observedMapping) + +freqMapping = { HDAILY:"D", + HBUSNS:"B", + HMONTH:"M", + HWKSUN:"W", + HSEC :"S", + HMIN :"T", + HHOUR :"H", + HQTOCT:"Q", + HQTNOV:"Q", + HQTDEC:"Q", + HANJAN:"A", + HANFEB:"A", + HANMAR:"A", + HANAPR:"A", + HANMAY:"A", + HANJUN:"A", + HANJUL:"A", + HANAUG:"A", + HANSEP:"A", + HANOCT:"A", + HANNOV:"A", + HANDEC:"A" } + +freqMapping = dict([(x, freq_fromstr(val)) for x, val in freqMapping.iteritems()]) + +freqReverseMapping = { "D" : HDAILY, + "B" : HBUSNS, + "M" : HMONTH, + "W" : HWKSUN, + "S" : HSEC, + "T" : HMIN, + "H" : HHOUR, + "Q" : HQTDEC, + "A" : HANDEC} + +freqReverseMapping = dict([(freq_fromstr(x), val) for x, val in freqReverseMapping.iteritems()]) + +value_adjust = { + 'A':1849, + 'Q':7396, + 'M':22188, + 'W':96477, + 'B':482381, + 'D':675333, + 'H':87648, + 'T':5258880, + 'S':315532800} + +value_adjust = dict([(freq_fromstr(x), val) for x, val in value_adjust.iteritems()]) + + +def fametype_fromdata(data): + """determine fame type code from a data object""" + + if isinstance(data, DateArray) or isinstance(data, Date): + return freqReverseMapping[data.freq] + elif hasattr(data, 'dtype'): + dtypeStr = str(data.dtype) + + if dtypeStr[:5] == "float": + if int(dtypeStr[5:]) > 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:3] == "int": + if int(dtypeStr[3:]) > 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:4] == "uint": + if int(dtypeStr[4:]) >= 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:2] == "|S" or dtypeStr == 'object': + return HSTRNG + elif dtypeStr == "bool": + return HBOOLN + else: + raise ValueError("Unsupported dtype for fame database: %s", dtypeStr) + + elif type(data) == types.StringType: + return HSTRNG + elif type(data) in (types.IntType, types.FloatType): + return HPRECN + elif type(data) == types.BooleanType: + return HBOOLN + elif type(data) == types.ListType: + return HNAMEL + else: + raise ValueError("Unrecognized data type") + +def fametype_tonumpy(fametype): + if fametype >= 8: + # date types + return numpy.int32 + elif fametype == HNAMEL: + return None + else: + typeMap = { + HNUMRC:numpy.float32, + HBOOLN:numpy.int32, + HSTRNG:numpy.object_, + HPRECN:numpy.float64} + return typeMap[fametype] + Added: trunk/Lib/sandbox/timeseries/io/fame/readme.txt =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/readme.txt 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/readme.txt 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,6 @@ +Requirements and warnings: + +1. Requires FAME version 9.2. Can be back-ported to version 9.0 with very + minor tweaking, but does not work with 9.0 out of the box. +2. Requires the timeseries module, and has all the same requirements and + warnings listed in that module's readme file. Added: trunk/Lib/sandbox/timeseries/io/fame/setup.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/setup.py 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/setup.py 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,53 @@ +__version__ = '1.0' +__revision__ = "$Revision: 37 $" +__date__ = '$Date: 2006-12-08 14:30:29 -0500 (Fri, 08 Dec 2006) $' + +import os, sys +from os.path import join + +def configuration(parent_package='',top_path=None): + from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs + nxheader = join(get_numpy_include_dirs()[0],'numpy',) + + famedir = os.getenv('FAME') + if famedir is None: + raise EnvironmentError("FAME environment variable not found") + + if sys.platform == 'win32': msvc_flags() + + fameheader = famedir + confgr = Configuration(parent_package=parent_package,top_path=top_path) + + sources = join('src', 'cfame.c') + libraries = "chli" + library_dirs = [famedir, join(famedir, "demo/hli")] + confgr.add_extension('cfame', + sources=[sources], + include_dirs=[nxheader, fameheader, library_dirs], + libraries = [libraries], + library_dirs = [library_dirs] + ) + return confgr + +def msvc_flags(): + """/DWIN32 flag is required on windows for compiling FAME +C-hli code""" + + from distutils.msvccompiler import MSVCCompiler + + # remember old initialize + old_MSVCCompiler_initialize = MSVCCompiler.initialize + + def fame_msvccompiler_initialize(self, *args, **kws): + apply(old_MSVCCompiler_initialize, (self,) + args, kws) + self.compile_options.extend(['/DWIN32']) + + # "Install" new initialize + MSVCCompiler.initialize = fame_msvccompiler_initialize + +if __name__ == "__main__": + + from numpy.distutils.core import setup + config = configuration().todict() + setup(**config) + \ No newline at end of file Added: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,998 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +//Constants +#define MAXOBJNAME 64 +#define MAXNLLENGTH 1000 + +#define CALLFAME(cmd) Py_BEGIN_ALLOW_THREADS; cmd; Py_END_ALLOW_THREADS; if (checkError(status)) return NULL + +#define ROUND(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) + +/**********************************************************************/ + +static float nmistt[3]; //Numeric +static double pmistt[3]; //Precision +static int bmistt[3]; //Boolean +static int dmistt[3]; //Date + +//Numeric +static float N_ND = 1.701419e+038; +static float N_NC = 1.701418e+038; +static float N_NA = 1.701417e+038; +// Precision +static double P_ND = 1.70141507979e+038; +static double P_NC = 1.70141507978e+038; +static double P_NA = 1.70141507977e+038; +// Boolean +static int B_ND = 127; +static int B_NC = 126; +static int B_NA = 125; +// Date +static int D_ND = -1; +static int D_NC = -2; +static int D_NA = -3; + +static char cfame_doc[] = "Module providing access to FAME functionality."; + +//if there was an error, we need to set the Python error status before returning +static checkError(int status) +{ + if (status != HSUCC && status != HTRUNC) + { + char message[1000]; + PyErr_SetString(PyExc_RuntimeError, getsta(status, message)); + cfmfin(&status); + return 1; + } + return 0; +} + +static int makeTranslationTables(void) +{ + //Set up translation tables for ND, NC, NA mappings + int status; + + cfmspm(&status, P_NC, P_ND, P_NA, pmistt); //Precision + cfmsnm(&status, N_NC, N_ND, N_NA, nmistt); //Numeric + cfmsbm(&status, B_NC, B_ND, B_NA, bmistt); //Boolean + cfmsdm(&status, D_NC, D_ND, D_NA, dmistt); //Date + return 0; +} +/////////////////////////////////////////////////////////////////////// + +static char cfame_open_doc[] = "open(database, access)\n\nOpens a FAME database and returns a FAME db idenifier."; +static PyObject * +cfame_open(PyObject *self, PyObject *args) +{ + int status; + int dbkey, access, large; + const char *dbname; + if (!PyArg_ParseTuple(args, "sii:open", &dbname, &access, &large)) return NULL; + + if (access == HOMODE || access == HCMODE) { + + if (large) { CALLFAME(cfmsopt(&status, "DBSIZE", "LARGE")); } + else { CALLFAME(cfmsopt(&status, "DBSIZE", "STANDARD")); } + } + + CALLFAME(cfmopdb (&status, &dbkey, dbname, access)); + + return PyInt_FromLong(dbkey); +} + +static char cfame_close_doc[] = "close(database_id)\n\nCloses an open FAME database."; +static PyObject * +cfame_close(PyObject *self, PyObject *args) +{ + int status; + int dbkey; + if (!PyArg_ParseTuple(args, "i:close", &dbkey)) return NULL; + + cfmcldb (&status, dbkey); + if (checkError(status)) return NULL; + + return PyInt_FromLong(0); +} + +static char cfame_wildlist_doc[] = "wildlist(dbkey, wildlist expression, wildonly)\n\nPerforms a wildlist."; +static PyObject * +cfame_wildlist(PyObject *self, PyObject *args) +{ + int status; + int dbkey; + const char *expression; + int class, type, freq; + char objnam[MAXOBJNAME+1]; + PyObject *result = PyList_New(0); + + if (!PyArg_ParseTuple(args, "is:wildlist(dbkey, expression)", &dbkey, &expression)) return NULL; + + // initialize wildlist + CALLFAME(cfminwc (&status, dbkey, expression)); + + // get first data object matching wildlist expression + cfmnxwc (&status, dbkey, objnam, &class, &type, &freq); + + if (status == HNOOBJ) + // no matching objects, return empty list + return result; + else + if (checkError(status)) return NULL; + + while (status != HNOOBJ) + { + // append objnam to list + if (PyList_Append(result, PyString_FromString(objnam))) return NULL; + + // get next item + cfmnxwc (&status, dbkey, objnam, &class, &type, &freq); + if (status != HNOOBJ && status != HSUCC) + if (checkError(status)) return NULL; + } + + return result; +} + +// Make appropriate boolean mask for data (based on special constants) +static PyObject *make_mask(void *data, int arraylen, int type) { + + PyArrayObject *mask; + int i; + int *mask_raw; + + if ((mask_raw = malloc(arraylen * sizeof(int))) == NULL) return PyErr_NoMemory(); + + switch(type) + { + + case HNUMRC: { // numeric + float *castdata = (float*)data; + float val; + + for (i = 0; i < arraylen; i++) { + val = castdata[i]; + if (val == N_ND || val == N_NC || val == N_NA) { + mask_raw[i] = 1; + } else { + mask_raw[i] = 0; + } + } + } break; + case HBOOLN: { // boolean + int *castdata = (int*)data; + int val; + + for (i = 0; i < arraylen; i++) { + val = castdata[i]; + if (val == B_ND || val == B_NC || val == B_NA) { + mask_raw[i] = 1; + } else { mask_raw[i] = 0;} + } + } break; + case HSTRNG: { // string + char **castdata = (char**)data; + char *val; + for (i = 0; i < arraylen; i++) { + val = castdata[i]; + if (val == "") { + mask_raw[i] = 1; + } else { mask_raw[i] = 0; } + } + } break; + case HPRECN: { // precision + double *castdata = (double*)data; + double val; + for (i = 0; i < arraylen; i++) { + val = castdata[i]; + if (val == P_ND || val == P_NC || val == P_NA) { + mask_raw[i] = 1; + } else { mask_raw[i] = 0; } + } + } break; + default: + if (type >= 8) { + int *castdata = (int*)data; + int val; + for (i = 0; i < arraylen; i++) { + val = castdata[i]; + if (val == D_ND || val == D_NC || val == D_NA) { + mask_raw[i] = 1; + } else { mask_raw[i] = 0; } + } + } else { + PyErr_SetString(PyExc_ValueError, "unsupported datatype"); + return NULL; + } + } + + mask = (PyArrayObject*)PyArray_SimpleNewFromData(1, &arraylen, PyArray_INT32, mask_raw); + mask->flags = (mask->flags) | NPY_OWNDATA; + + return (PyObject*)mask; +} + + +static char cfame_read_doc[] = "read(dbkey, data object name, startDate, endDate, dateSeriesFlag, longStr)\n\nReturns specified object."; +//startDate(endDate) must be the int value of the startDate(endDate) using the frequency of the underlying data +//dateSeriesFlag is 1 for date series 0 for case series +//longStr is 1 for string series with very long items 0 otherwise. Use 1 with care as it takes up alot of memory. +static PyObject * +cfame_read(PyObject *self, PyObject *args) +{ + int status, dbkey, i; + int dataFlag; + + const char *object_name; + + int first_point, last_point; //this defines the custom range to read (-1 for both means read all) + int longStr; //1 for case series with really long items + + int max_string_len; + + int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + int basis, observed, created_year, created_month, created_day, mod_year, mod_month, mod_day; //additional fields for cfmwhat + char desc[1], doc[1]; + PyObject * returnVal = NULL; + PyObject * values = NULL; + int numobjs, typeNum; + int range[3]; + void* dbValues; + + desc[0] = 0x0; + doc[0] = 0x0; + + // "isiii:get" means parse args for an int, a string and 4 more ints and use "get" as the function name in error messages + if (!PyArg_ParseTuple(args, "isiii:read", + &dbkey, + &object_name, + &first_point, + &last_point, + &max_string_len)) return NULL; //get params + + CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observed, &start_year, &start_period, &end_year, &end_period, &created_year, &created_month, &created_day, &mod_year, &mod_month, &mod_day, desc, doc)); + + returnVal = PyDict_New(); + + PyDict_SetItemString(returnVal, "type", PyInt_FromLong(type)); + PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); + PyDict_SetItemString(returnVal, "class", PyInt_FromLong(class)); + PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); + PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); + PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); + PyDict_SetItemString(returnVal, "observed", PyInt_FromLong(observed)); + PyDict_SetItemString(returnVal, "basis", PyInt_FromLong(basis)); + + if (type == HNAMEL) //namelists + { + int length; + char names[MAXOBJNAME*MAXNLLENGTH+1]; + + CALLFAME(cfmgtnl(&status, dbkey, object_name, HNLALL, names, MAXOBJNAME*MAXNLLENGTH, &length)); + PyDict_SetItemString(returnVal, "data", PyString_FromStringAndSize(names, length)); //just return the namelist as a comma delimited string + } + else + { + dataFlag = 1; + + switch (class) + { + case HSERIE: + //initialize custom range + + //if we are dealing with a date we need to convert + //'begin' and 'end' dates to year/period format + + if (first_point != -1) { + if (freq == HCASEX) { + start_year = 0; + start_period = first_point; + } else { + CALLFAME(cfmdatp(&status, freq, first_point, &start_year, &start_period)); + } + } else { + if (freq == HCASEX) { + /* for case series, if first_point not explicitly defined, always + read starting at index 1 (not the first data point like with + time series */ + start_year = 0; + start_period = 1; + } + } + + if (last_point != -1) { + if (freq == HCASEX) { + end_year = 0; + end_period = last_point; + } else { + CALLFAME(cfmdatp(&status, freq, last_point, &end_year, &end_period)); + } + } + + if (end_year < start_year || + (start_year == end_year && end_period < start_period) || + (start_period == -1)) { + dataFlag = 0; + break; + } + + numobjs = -1; + CALLFAME(cfmsrng(&status, freq, &start_year, &start_period, &end_year, &end_period, range, &numobjs)); //set the range of data to get + break; + + case HSCALA: + numobjs = 1; + break; + default: //This should never happen + PyErr_SetString(PyExc_RuntimeError, "Critical internal error #0 in CFAMEMODULE"); + return NULL; + } + + if (dataFlag) + { + switch (type) //initialize an array of the correct type to get the data from Fame + { + case HNUMRC: + if ((dbValues = malloc(numobjs * sizeof(float))) == NULL) return PyErr_NoMemory(); + typeNum = PyArray_FLOAT; + break; + case HPRECN: + if ((dbValues = malloc(numobjs * sizeof(double))) == NULL) return PyErr_NoMemory(); + typeNum = PyArray_DOUBLE; + break; + case HSTRNG: + typeNum = PyArray_OBJECT; + break; + default: + if ((dbValues = malloc(numobjs * sizeof(int))) == NULL) return PyErr_NoMemory(); + typeNum = PyArray_INT; + break; + } + if (type == HSTRNG) //additional initilization for getting strings + { + + if (class == HSERIE) + { + PyObject** temp; + PyArrayObject *mask; + //string series + int* missing; + int* outlen; + int inlen[1]; + int *mask_raw; + + if ( ((dbValues = malloc(numobjs * sizeof(char*))) == NULL) || + ((temp = malloc(numobjs * sizeof(PyObject*))) == NULL) || + ((mask_raw = malloc(numobjs * sizeof(int))) == NULL) || + ((missing = malloc(numobjs * sizeof(int))) == NULL) || + ((outlen = malloc(numobjs * sizeof(int))) == NULL) ) { + return PyErr_NoMemory(); + } + + for (i = 0; i < numobjs; i++) { + if ((((char**)dbValues)[i] = malloc((max_string_len+1) * sizeof(char))) == NULL) { + return PyErr_NoMemory(); + } + } + + inlen[0] = -max_string_len; + + //we need to know how big each string will be so that we can set up room for it + CALLFAME(cfmgtsts(&status, dbkey, object_name, range, dbValues, missing, inlen, outlen)); + for (i = 0; i < numobjs; i++) { + if (outlen[i] > max_string_len) { + PyErr_SetString(PyExc_RuntimeError, "FAME returned a string longer than the max_string_len. Adjust max_string_len parameter."); + return NULL; + } else { + + if (missing[i] != HNMVAL) { + if ((temp[i] = PyString_FromString("")) == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Failed to initialize missing string element."); + return NULL; + } + mask_raw[i] = 1; + } else { + if ((temp[i] = PyString_FromStringAndSize(((char**)dbValues)[i], outlen[i])) == NULL) { + return PyErr_NoMemory(); + } + mask_raw[i] = 0; + } + + free(((char**)dbValues)[i]); + } + } + + free(dbValues); + dbValues = temp; + + { + PyArrayObject* data = (PyArrayObject *)PyArray_SimpleNewFromData(1, &numobjs, typeNum, dbValues); + PyArrayObject* mask = (PyArrayObject*)PyArray_SimpleNewFromData(1, &numobjs, PyArray_INT32, mask_raw); + PyObject* startindex = PyInt_FromLong((long)range[1]); + + // transfer ownership of dbValues to the array + data->flags = (data->flags) | NPY_OWNDATA; + mask->flags = (mask->flags) | NPY_OWNDATA; + + PyDict_SetItemString(returnVal, "data", (PyObject*)data); + PyDict_SetItemString(returnVal, "mask", (PyObject*)mask); + PyDict_SetItemString(returnVal, "startindex", startindex); + + Py_DECREF(data); + Py_DECREF(mask); + Py_DECREF(startindex); + } + + free(missing); + free(outlen); + } + else + { + //get one string + int missing; + int length; + + if ((dbValues = malloc((max_string_len+1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + + CALLFAME(cfmgtstr(&status, dbkey, object_name, NULL, dbValues, &missing, max_string_len, &length)); + + if (length > max_string_len) { + PyErr_SetString(PyExc_RuntimeError, "FAME returned a string longer than the maxlength. Use extra long string parameter"); + return NULL; + } + + { + PyObject* data = PyString_FromString((char*)dbValues); + PyObject* mask; + PyObject* startindex = PyInt_FromLong(-1); + + if (missing != HNMVAL) { mask = PyBool_FromLong(1); } + else { mask = PyBool_FromLong(0); } + + PyDict_SetItemString(returnVal, "data", data); + PyDict_SetItemString(returnVal, "mask", mask); + PyDict_SetItemString(returnVal, "startindex", startindex); + + Py_DECREF(data); + Py_DECREF(mask); + Py_DECREF(startindex); + } + + } + } else { + switch(type) + { + + case HNUMRC: + CALLFAME(cfmrrng(&status, dbkey, object_name, range, dbValues, HTMIS, nmistt)); + break; + case HBOOLN: + CALLFAME(cfmrrng(&status, dbkey, object_name, range, dbValues, HTMIS, bmistt)); + break; + case HPRECN: + CALLFAME(cfmrrng(&status, dbkey, object_name, range, dbValues, HTMIS, pmistt)); + break; + default: + if (type >= 8) { + CALLFAME(cfmrrng(&status, dbkey, object_name, range, dbValues, HTMIS, dmistt)); + } else { + PyErr_SetString(PyExc_ValueError, "unsupported datatype"); + return NULL; + } + } + + { + PyArrayObject* data = (PyArrayObject *)PyArray_SimpleNewFromData(1, &numobjs, typeNum, dbValues); + PyObject* mask = make_mask(dbValues, numobjs, type); + PyObject* startindex = PyInt_FromLong((long)range[1]); + + // transfer ownership of dbValues to the array + data->flags = (data->flags) | NPY_OWNDATA; + + PyDict_SetItemString(returnVal, "data", (PyObject*)data); + PyDict_SetItemString(returnVal, "mask", mask); + PyDict_SetItemString(returnVal, "startindex", startindex); + + Py_DECREF(data); + Py_DECREF(mask); + Py_DECREF(startindex); + } + } + + + + } // (dataFlag) + + + //if dataFlag was set return an object with no data + if (!dataFlag) { + return returnVal; + } + } // (type == HNAMEL) //namelists + return returnVal; +} + + +// replace masked values with the ND constant +static PyArrayObject *replace_mask(PyObject *orig_data, PyObject *orig_mask, int type) { + + PyArrayObject *data_copy, *data, *mask; + PyObject *valMask, *fillVal; + int i; + + data_copy = (PyArrayObject *)PyArray_Copy((PyArrayObject *)orig_data); + data = PyArray_GETCONTIGUOUS(data_copy); + + // don't care if mask is contiguous or not + mask = (PyArrayObject *)orig_mask; + + switch(type) + { + + case HNUMRC: + fillVal = PyFloat_FromDouble(N_ND); + break; + case HBOOLN: + fillVal = PyInt_FromLong(B_ND); + break; + case HSTRNG: + fillVal = PyString_FromString(""); + break; + case HPRECN: + fillVal = PyFloat_FromDouble(P_ND); + break; + default: + if (type >= 8) { + fillVal = PyInt_FromLong(D_ND); + } else { + PyErr_SetString(PyExc_ValueError, "unsupported datatype"); + return NULL; + } + } + + for (i = 0; i < data->dimensions[0]; i++) { + valMask = PyArray_GETITEM(mask, PyArray_GetPtr(mask, &i)); + if (PyInt_AsLong(valMask)) { + PyArray_SETITEM(data, PyArray_GetPtr(data, &i), fillVal); + } + Py_DECREF(valMask); + } + return data; +} + +static char cfame_write_series_doc[] = "write_series(dbkey, name, data, mask, start_index, end_index, source_type, source_freq)\n\nWrites a series to the DB"; +static PyObject * +cfame_write_series(PyObject *self, PyObject *args) +{ + int status, dbkey; + PyObject *dataArrayTemp, *maskArrayTemp; + PyArrayObject *dataArray, *maskArray; + const char* name; + char errMsg[500]; + int class, start_index, end_index, numobjs, source_type, type, ppd, + source_freq, freq, start_year, start_period, end_year, end_period; + PyObject * returnVal = NULL; + int range[3]; + + if (!PyArg_ParseTuple(args, "isOOiiii:write_series", &dbkey, &name, &dataArrayTemp, &maskArrayTemp, &start_index, &end_index, &source_type, &source_freq)) return NULL; //get params + CALLFAME(cfmosiz(&status, dbkey, name, &class, &type, &freq, &start_year, &start_period, &end_year, &end_period)); //get object info + + if (source_type != type) { + PyErr_SetString(PyExc_RuntimeError, "received a non-matching type, cannot write"); + return NULL; + } + + if (source_freq != freq) { + PyErr_SetString(PyExc_RuntimeError, "received a non-matching frequency, cannot write"); + return NULL; + } + + numobjs = -1; + if (freq == HCASEX) { + start_year = 0; + end_year = 0; + start_period = start_index; + end_period = end_index; + } else if (freq >= 226) { // HOURLY, MINUTELY, or SECONDLY + CALLFAME(timeper(&status, freq, start_index, &start_year, &start_period)); + CALLFAME(timeper(&status, freq, end_index, &end_year, &end_period)); + } else { //convert int dates to fame period dates + CALLFAME(cfmdatp(&status, freq, start_index, &start_year, &start_period)); + CALLFAME(cfmdatp(&status, freq, end_index, &end_year, &end_period)); + } + //set the range that we will be writing to + CALLFAME(cfmsrng(&status, freq, &start_year, &start_period, &end_year, &end_period, range, &numobjs)); + if (!PyArray_Check(dataArrayTemp)) { + PyErr_SetString(PyExc_RuntimeError, "write_series was passed something other than an ndarray"); + return NULL; + } + + if (type == HSTRNG) { + + //setting strings requires a different function call + int* missing; + int* lengths; + char** values; + int i; + + PyObject *str, *mask; + + if (((missing = malloc(numobjs * sizeof(int))) == NULL) || + ((lengths = malloc(numobjs * sizeof(int))) == NULL) || + ((values = malloc(numobjs * sizeof(char*))) == NULL)) { + return PyErr_NoMemory(); + } + + dataArray = (PyArrayObject*)dataArrayTemp; + maskArray = (PyArrayObject*)maskArrayTemp; + + for (i = 0; i < numobjs; i++) { + //extract a string and add it to the array to be written + + str = PyArray_GETITEM(dataArray, PyArray_GetPtr(dataArray, &i)); + mask = PyArray_GETITEM(maskArray, PyArray_GetPtr(maskArray, &i)); + + lengths[i] = PyString_Size(str); + if ((values[i] = malloc((lengths[i]+1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + values[i] = PyString_AsString(str); + + if (PyInt_AsLong(mask)) { + missing[i] = HNDVAL; + } else { + missing[i] = HNMVAL; + } + } + //write all the strings to Fame + CALLFAME(cfmwsts(&status, dbkey, name, range, values, missing, lengths)); + + //clear the extra memory that the strings are using + for (i = 0; i < numobjs; i++) { + free(values[i]); + } + + free(missing); + free(lengths); + free(values); + + } else { + + // replace masked values with the ND constant + dataArray = replace_mask(dataArrayTemp, maskArrayTemp, type); + + switch (type) { + case HNUMRC: { // numeric + CALLFAME(cfmwrng(&status, dbkey, name, range, dataArray->data, HTMIS, nmistt)); + break; + } + case HPRECN: { // precision + CALLFAME(cfmwrng(&status, dbkey, name, range, dataArray->data, HTMIS, pmistt)); + break; + } + case HBOOLN: { // boolean + CALLFAME(cfmwrng(&status, dbkey, name, range, dataArray->data, HTMIS, bmistt)); + break; + } + default: + if(type >= 8) { // date type + CALLFAME(cfmwrng(&status, dbkey, name, range, dataArray->data, HTMIS, dmistt)); + } else { + Py_DECREF(dataArray); + sprintf(errMsg, "unsupported data type: %i", type); + PyErr_SetString(PyExc_RuntimeError, errMsg); + return NULL; + } + + } + + Py_DECREF(dataArray); + + } + + Py_RETURN_NONE; +} + + +static char cfame_write_scalar_doc[] = "write_scalar(dbkey, name, object, source_type)\n\nWrites a scalar to the DB"; +static PyObject * +cfame_write_scalar(PyObject *self, PyObject *args) +{ + int status, dbkey; + PyObject* object; + const char* name; + int class, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + PyObject * returnVal = NULL; + int source_type, type; + int range[3]; + + if (!PyArg_ParseTuple(args, "isOi:write_scalar", &dbkey, &name, &object, &source_type)) return NULL; //get params + CALLFAME(cfmosiz(&status, dbkey, name, &class, &type, &freq, &start_year, &start_period, &end_year, &end_period)); //get object info + + if (source_type != type) { + PyErr_SetString(PyExc_RuntimeError, "received a non-matching type, cannot write"); + return NULL; + } + + switch (type) { + case HSTRNG: { + char* value; + int length; + + length = PyString_Size(object); + value = malloc((length + 1) * sizeof(char)); + value = PyString_AsString(object); + + CALLFAME(cfmwstr(&status, dbkey, name, range, value, HNMVAL, length)); + free(value); + } break; + case HNUMRC: { + float values[1]; + values[0] = (float)PyFloat_AsDouble(object); + CALLFAME(cfmwrng(&status, dbkey, name, range, values, HNTMIS, NULL)); + } break; + case HPRECN: { + double values[1]; + values[0] = PyFloat_AsDouble(object); + CALLFAME(cfmwrng(&status, dbkey, name, range, values, HNTMIS, NULL)); + } break; + case HBOOLN: { + int values[1]; + values[0] = (int)PyInt_AsLong(object); + CALLFAME(cfmwrng(&status, dbkey, name, range, values, HNTMIS, NULL)); + } break; + default: + if (type >= 8) { + // date data type + int values[1]; + values[0] = (int)PyInt_AsLong(object); + CALLFAME(cfmwrng(&status, dbkey, name, range, values, HNTMIS, NULL)); + } else { + PyErr_SetString(PyExc_ValueError, "Unrecognized type, cannot write"); + return NULL; + } + } + Py_RETURN_NONE; +} + + +static char cfame_write_namelist_doc[] = "write_namelist(dbkey, name, namelist_string)\n\nWrites a namelist to the DB"; +static PyObject * +cfame_write_namelist(PyObject *self, PyObject *args) +{ + int status, dbkey; + const char* name; + const char* namelist; + + if (!PyArg_ParseTuple(args, "iss:writeNamelist", &dbkey, &name, &namelist)) return NULL; + + CALLFAME(cfmwtnl(&status, dbkey, name, HNLALL, namelist)); + + Py_RETURN_NONE; +} + +static char cfame_create_doc[] = "create(dbkey, object_name, class_arg, freq_arg, type_arg, basis_arg, observed_arg)\n\nCreates a fame object in the DB"; +static PyObject * +cfame_create(PyObject *self, PyObject *args) +{ + int status, dbkey; + const char* object_name; + int class_arg, freq_arg, type_arg, basis_arg, observed_arg; + + if (!PyArg_ParseTuple(args, "isiiiii:create", &dbkey, &object_name, &class_arg, &freq_arg, &type_arg, &basis_arg, &observed_arg)) return NULL; //get params + CALLFAME(cfmnwob(&status, dbkey, object_name, class_arg, freq_arg, type_arg, basis_arg, observed_arg)); + + Py_RETURN_NONE; +} + +static char cfame_remove_doc[] = "remove(dbkey, object_name)"; +static PyObject* +cfame_remove(PyObject* self, PyObject* args) +{ + int status, dbkey; + const char* object_name; + + if (!PyArg_ParseTuple(args, "is:remove", &dbkey, &object_name)) return NULL; //get params + CALLFAME(cfmdlob(&status, dbkey, object_name)); + + Py_RETURN_NONE; +} + +static char cfame_exists_doc[] = "exists(dbkey, object_name)"; +static PyObject* +cfame_exists(PyObject* self, PyObject* args) +{ + int status, dbkey; + const char* object_name; + int deslen, doclen; + + if (!PyArg_ParseTuple(args, "is:exists", &dbkey, &object_name)) return NULL; //get params + + cfmdlen (&status, dbkey, object_name, &deslen, &doclen); + if (status == HNOOBJ) + Py_RETURN_FALSE; + else + Py_RETURN_TRUE; +} + +static char cfame_updated_doc[] = "updated(dbkey, object_name)"; +static PyObject* +cfame_updated(PyObject* self, PyObject* args) +{ + int status, dbkey; + const char *object_name; + int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; + char desc[1], doc[1]; + PyObject * returnVal = NULL; + + desc[0] = 0x0; + doc[0] = 0x0; + + if (!PyArg_ParseTuple(args, "is:updated", &dbkey, &object_name)) return NULL; //get params + + CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observ, + &start_year, &start_period, &end_year, &end_period, + &created_year, &created_month, &created_day, + &mod_year, &mod_month, &mod_day, + desc, doc)); + + returnVal = PyDict_New(); + + PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); + PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); + PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); + + return returnVal; +} + +static char cfame_whats_doc[] = "whats(dbkey, data object name)\n\nReturns information about the specified object."; +static PyObject * +cfame_whats(PyObject *self, PyObject *args) +{ + //arguments + char *object_name; + int dbkey; + + //return val + PyObject *returnVal = NULL; + + int deslen, doclen; /* Length of desc and doc string return from cfmdlen */ + + int status; + void *tempdesc, *tempdoc; + char *desc, *doc; + int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; //additional fields for cfmwhat + + //get arguments. + //"is" means first one is integer second is string + //"whats" is what will appear in python error messages if this method crashes + if (!PyArg_ParseTuple(args, "is:whats", + &dbkey, + &object_name)) return NULL; + + /* Get the length of the desc and doc strings */ + CALLFAME(cfmdlen(&status, dbkey, object_name, &deslen, &doclen)); + + /* allocate the memory needed for the desc/doc strings */ + if ((tempdesc = malloc((deslen + 1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + if ((tempdoc = malloc((doclen + 1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + + /* set the memory to non-null chars to tell fame the length of string we will accept */ + memset(tempdesc, 'A', deslen); + memset(tempdoc, 'A', doclen); + + /* cast to char array */ + desc = (char*)tempdesc; + doc = (char*)tempdoc; + + /* terminate the string with a null */ + desc[deslen] = 0x0; + doc[doclen] = 0x0; + + CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observ, + &start_year, &start_period, &end_year, &end_period, + &created_year, &created_month, &created_day, + &mod_year, &mod_month, &mod_day, + desc, doc)); + + returnVal = PyDict_New(); + + PyDict_SetItemString(returnVal, "type", PyInt_FromLong(type)); + PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); + PyDict_SetItemString(returnVal, "class", PyInt_FromLong(class)); + PyDict_SetItemString(returnVal, "start_year", PyInt_FromLong(start_year)); + PyDict_SetItemString(returnVal, "start_period", PyInt_FromLong(start_period)); + PyDict_SetItemString(returnVal, "end_year", PyInt_FromLong(end_year)); + PyDict_SetItemString(returnVal, "end_period", PyInt_FromLong(end_period)); + PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); + PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); + PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); + PyDict_SetItemString(returnVal, "desc", PyString_FromString(desc)); + PyDict_SetItemString(returnVal, "doc", PyString_FromString(doc)); + + free((void*)desc); + free((void*)doc); + + return returnVal; +} + +static char cfame_size_doc[] = "size(dbkey, data object name)\n\nReturns limited about the specified object."; +static PyObject * +cfame_size(PyObject *self, PyObject *args) +{ + //arguments + char *object_name; + int dbkey; + + //return val + PyObject *returnVal = NULL; + + int status; + int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + + //get arguments. + //"is" means first one is integer second is string + //"size" is what will appear in python error messages if this method crashes + if (!PyArg_ParseTuple(args, "is:size", + &dbkey, + &object_name)) return NULL; + + CALLFAME(cfmosiz(&status, dbkey, object_name, &class, &type, &freq, &start_year, &start_period, &end_year, &end_period)); + + returnVal = PyDict_New(); + + PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); + //To return other fields add them here + //look up cfmosiz in fame help for other fields + + return returnVal; +} + +static char cfame_restore_doc[] = "restore(dbkey)\n\nDiscard any changes made to the database since it was last opened or posted.\nXXX: not sure what posted means, see FAME API"; +static PyObject * +cfame_restore(PyObject *self, PyObject *args) +{ + int status, dbkey; + + if (!PyArg_ParseTuple(args, "i:restore", &dbkey)) return NULL; + + CALLFAME(cfmrsdb(&status, dbkey)); + Py_RETURN_NONE; +} + +/////////////////////////////////////////////////////////////////////// + +static PyMethodDef cfame_methods[] = { + {"open", cfame_open, METH_VARARGS, cfame_open_doc}, + {"close", cfame_close, METH_VARARGS, cfame_close_doc}, + {"wildlist", cfame_wildlist, METH_VARARGS, cfame_wildlist_doc}, + {"read", cfame_read, METH_VARARGS, cfame_read_doc}, + {"whats", cfame_whats, METH_VARARGS, cfame_whats_doc}, + {"size", cfame_size, METH_VARARGS, cfame_size_doc}, + {"write_scalar", cfame_write_scalar, METH_VARARGS, cfame_write_scalar_doc}, + {"write_series", cfame_write_series, METH_VARARGS, cfame_write_series_doc}, + {"create", cfame_create, METH_VARARGS, cfame_create_doc}, + {"remove", cfame_remove, METH_VARARGS, cfame_remove_doc}, + {"exists", cfame_exists, METH_VARARGS, cfame_exists_doc}, + {"updated", cfame_updated, METH_VARARGS, cfame_updated_doc}, + {"write_namelist", cfame_write_namelist, METH_VARARGS, cfame_write_namelist_doc}, + {"restore", cfame_restore, METH_VARARGS, cfame_restore_doc}, + {NULL, NULL} +}; + +PyMODINIT_FUNC +initcfame(void) +{ + int status; + cfmini(&status); + Py_InitModule3("cfame", cfame_methods, cfame_doc); + import_array(); + + makeTranslationTables(); +} \ No newline at end of file Added: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 16:42:18 UTC (rev 2711) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 17:52:27 UTC (rev 2712) @@ -0,0 +1,390 @@ +"""Tests suite for fame io submodule. + +:author: Matt Knox +:contact: mattknox_ca_at_hotmail_dot_com +:version: $Id: test_fame.py 2578 2007-01-17 19:25:10Z mattknox_ca $ +""" +__author__ = "Matt Knox ($Author: mattknox_ca $)" +__version__ = '1.0' +__revision__ = "$Revision: 2578 $" +__date__ = '$Date: 2007-01-17 14:25:10 -0500 (Wed, 17 Jan 2007) $' + +import numpy as N +from numpy import bool_, complex_, float_, int_, object_ +import numpy.core.fromnumeric as fromnumeric +import numpy.core.numeric as numeric +from numpy.testing import NumpyTest, NumpyTestCase +from numpy.testing.utils import build_err_msg + +from timeseries.io import fame +from timeseries import Report +import timeseries as ts +import maskedarray as ma +import numpy as np + +import maskedarray +from maskedarray import masked_array, masked, nomask + +import maskedarray.testutils +from maskedarray.testutils import assert_equal, assert_array_equal, approx, assert_mask_equal + +# setup all the data to be used for reading and writing +data = {'dates':{}, 'darrays':{}, 'freqs':{}, 'cser':{}, 'tser':{}, 'scalars':{}} + +data['dates']['a'] = ts.Date(freq='A', year=2004) +data['dates']['q'] = ts.Date(freq='Q', year=2004, quarter=1) +data['dates']['m'] = ts.Date(freq='M', year=2004, month=1) +data['dates']['w'] = ts.Date(freq='W', year=2004, month=1, day=1) +data['dates']['b'] = ts.Date(freq='b', year=2004, month=1, day=1) +data['dates']['d'] = ts.Date(freq='d', year=2004, month=1, day=1) +data['dates']['h'] = ts.Date(freq='h', year=2004, month=1, day=1, hour=0) +data['dates']['t'] = ts.Date(freq='t', year=2004, month=1, day=1, hour=0, minute=0) +data['dates']['s'] = ts.Date(freq='s', year=2004, month=1, day=1, hour=0, minute=0, second=0) + +for freq in data['dates']: + data['darrays'][freq] = ts.date_array(start_date=data['dates'][freq], length=10) + data['cser']['date_'+freq] = data['darrays'][freq] + +data['cser']['bool'] = [True, False, True, False, True, True] +data['cser']['int32'] = np.arange(6).astype(np.int32) +data['cser']['int64'] = np.arange(6).astype(np.int64) +data['cser']['float32'] = np.arange(6).astype(np.float32) +data['cser']['float64'] = np.arange(6).astype(np.float64) +data['cser']['str'] = ["asdf", "aasssssssss", "zzzzzzzzzzzz", "", "blah"] + +for x in data['cser']: + data['cser'][x] = ma.masked_array(data['cser'][x]) + data['tser'][x] = ts.time_series(data['cser'][x], start_date=data['dates']['a']) + +for freq in data['dates']: + data['freqs'][freq] = ts.time_series(np.arange(20).astype(np.float32), start_date=data['dates'][freq]) + +# test writing for all data types as time series and as case series +for x in data['tser']: + data['tser'][x][1] = ma.masked + data['cser'][x][1] = ma.masked + +# series for testing appending data to an existing series +appendTSer = ts.time_series(np.arange(10, 15).astype(np.float32), freq='A', start_date=ts.Date(freq='A', year=2007)) +appendCSer = np.arange(10, 15).astype(np.float32) + +# series for testing writing over a specified range +rangeTSer = ts.time_series(np.arange(20).astype(np.float32), freq='A', start_date=ts.Date(freq='A', year=2004)) +rangeCSer = np.arange(20).astype(np.float32) + +data['scalars']['int32'] = np.int32(5) +data['scalars']['int64'] = np.int64(5) +data['scalars']['float32'] = np.float32(5) +data['scalars']['float64'] = np.float64(5) +data['scalars']['pyInt'] = 5 +data['scalars']['pyFloat'] = 5234.6323 +data['scalars']['string'] = "mystring" +data['scalars']['namelist'] = ["mystring", "$asdf","gggggggg"] +data['scalars']['boolean'] = True +for f in data['dates']: + data['scalars']['date_'+f] = data['dates'][f] + +class test_write(NumpyTestCase): + + def setUp(self): + self.db = fame.FameDb("testdb.db",'o') + + def test_main(self): + "execute all the tests. Order is important here" + + self._test_write_scalars() + self._test_read_scalars() + + self._test_dict_scalars() + + self._test_write_freqs_tser() + self._test_read_freqs_tser() + + self._test_write_dtypes_tser() + self._test_read_dtypes_tser() + + self._test_read_range_tser() + + self._test_write_append_tser() + self._test_read_append_tser() + + self._test_write_range_tser() + self._test_verify_write_range_tser() + + self._test_write_empty_tser() + self._test_read_empty_tser() + + self._test_overwrite_tser() + + self._test_assume_exists_tser() + + self._test_dict_tser() + + self._test_write_dtypes_cser() + self._test_read_dtypes_cser() + + self._test_read_range_cser() + + self._test_write_append_cser() + self._test_read_append_cser() + + self._test_write_range_cser() + self._test_verify_write_range_cser() + + self._test_write_empty_cser() + self._test_read_empty_cser() + + self._test_overwrite_cser() + + self._test_assume_exists_cser() + + self._test_dict_cser() + + def _test_write_scalars(self): + "test writing all types of scalar values" + for s in data['scalars']: + self.db.write_scalar('$scalar_'+s, data['scalars'][s]) + + def _test_dict_scalars(self): + "test writing multiple scalars at once using write_scalar_dict" + self.db.write_scalar_dict({'$scalar_1':data['scalars']['float32'], + '$scalar_2':data['scalars']['float32']}) + result = self.db.read(['$scalar_1', '$scalar_2']) + assert_equal(result['$scalar_1'], data['scalars']['float32']) + assert_equal(result['$scalar_2'], data['scalars']['float32']) + + def _test_read_scalars(self): + "read scalars of every data type" + for s in data['scalars']: + sclr = self.db.read('$scalar_'+s) + orig = data['scalars'][s] + + if s == 'int32': + assert_equal(sclr, orig.astype(np.float32)) + elif s in ('pyInt', 'pyFloat', 'int64'): + assert_equal(sclr, np.float64(orig)) + elif s == 'namelist': + assert_equal(sclr, [x.upper() for x in orig]) + else: + assert_equal(sclr, orig) + + def _test_write_freqs_tser(self): + "test writing time series for all frequencies" + for x in data['freqs']: + self.db.write_tser('$freq_'+x, data['freqs'][x]) + + def _test_read_freqs_tser(self): + """read series at every frequency and ensure they are the + same as what was written""" + for x in data['freqs']: + ser = self.db.read('$freq_'+x) + assert_mask_equal(ser.mask, data['freqs'][x].mask) + assert((ser == data['freqs'][x]).all()) + + def _test_write_dtypes_tser(self): + "test writing for all dtypes for time series" + for x in data['tser']: + self.db.write_tser('$tser_'+x, data['tser'][x]) + + def _test_read_dtypes_tser(self): + "read time series of every data type" + for x in data['tser']: + ser = self.db.read('$tser_'+x) + if str(ser.dtype)[:5] == 'float' and str(data['tser'][x].dtype)[:3] == 'int': + ser = ser.astype(data['tser'][x].dtype) + + assert_mask_equal(ser.mask, data['tser'][x].mask) + assert((ser == data['tser'][x]).all()) + + def _test_read_range_tser(self): + "test reading a time series over specified ranges" + src = data['tser']['float32'] + s1 = src.start_date+2 + s2 = src.start_date-2 + e1 = src.end_date+2 + e2 = src.end_date-2 + + dateList = [(s1, e1), + (s1, e2), + (s2, e1), + (s2, e2)] + + for s, e in dateList: + res = ts.adjust_endpoints(src, start_date=s, end_date=e) + ser = self.db.read('$tser_float32', start_date=s, end_date=e) + assert_array_equal(res, ser) + + + def _test_write_append_tser(self): + "test appending data to an existing time series" + self.db.write_tser('$appendTSer', data['tser']['float32']) + self.db.write_tser('$appendTSer', appendTSer) + + def _test_read_append_tser(self): + "test reading of appended time series" + result = ts.adjust_endpoints(data['tser']['float32'], + start_date=data['tser']['float32'].start_date, + end_date=appendTSer.end_date) + result[appendTSer.start_date:appendTSer.end_date+1] = appendTSer + + ser = self.db.read('$appendTSer') + + assert_array_equal(result, ser) + + + def _test_write_range_tser(self): + "test writing a time series over a specified range" + self.db.write_tser('$rangeTSer', rangeTSer, + start_date=ts.Date(freq='A', year=2008), + end_date=ts.Date(freq='A', year=2012)) + + def _test_verify_write_range_tser(self): + "verify that _test_write_range_write_tser worked as expected" + + ser = self.db.read('$rangeTSer') + + sDate = ts.Date(freq='A', year=2008) + eDate = ts.Date(freq='A', year=2012) + + assert_array_equal(ser, rangeTSer[sDate:eDate+1]) + + def _test_write_empty_tser(self): + "test writing a time series with no data" + emptySer = ts.time_series([], freq='A') + self.db.write_tser('$emptyTSer', emptySer) + + def _test_read_empty_tser(self): + "test reading a time series with no data" + ser = self.db.read('$emptyTSer') + assert(ser.start_date is None) + + def _test_overwrite_tser(self): + "test overwriting a time series" + self.db.write_tser('$tser_float32', data['tser']['bool'], overwrite=True) + ser = self.db.read('$tser_float32') + assert_array_equal(ser, data['tser']['bool']) + + def _test_assume_exists_tser(self): + "check to see if the assume_exists flag works for write_tser" + exception = False + try: + self.db.write_tser('$doesNotExist', appendTSer, assume_exists=True) + except fame.DBError: + exception = True + assert(exception) + + def _test_dict_tser(self): + "test writing multiple time series at once using write_tser_dict" + self.db.write_tser_dict({'$tser_1':data['tser']['float32'], + '$tser_2':data['tser']['float32']}) + result = self.db.read(['$tser_1', '$tser_2']) + assert_array_equal(result['$tser_1'], data['tser']['float32']) + assert_array_equal(result['$tser_2'], data['tser']['float32']) + + def _test_write_dtypes_cser(self): + "test writing for all dtypes for case series""" + for x in data['cser']: + self.db.write_cser('$cser_'+x, data['cser'][x]) + + def _test_read_dtypes_cser(self): + "read case series of every data type" + for x in data['cser']: + ser = self.db.read('$cser_'+x) + if str(ser.dtype)[:5] == 'float' and str(data['cser'][x].dtype)[:3] == 'int': + ser = ser.astype(data['cser'][x].dtype) + + assert_mask_equal(ser.mask, data['cser'][x].mask) + assert((ser == data['cser'][x]).all()) + + def _test_read_range_cser(self): + "test reading case series over specified ranges" + src = data['cser']['float32'] + s1 = 3 + s2 = 1 + e1 = 8 + e2 = 4 + + caseList = [(s1, e1), + (s1, e2), + (s2, e1), + (s2, e2)] + + for s, e in caseList: + size = (e - s + 1) + res = ma.array([0]*size , np.float32, mask=[1]*size ) + + if e < src.size: _e = size + else: _e = size - max(e-size, 0, size - src.size) + + res[0:_e] = src[s-1:min(e, src.size)] + ser = self.db.read('$cser_float32', start_case=s, end_case=e) + + assert_array_equal(res, ser) + + def _test_write_append_cser(self): + "test appending to an existing case series" + self.db.write_cser('$appendCSer', data['cser']['float32']) + self.db.write_cser('$appendCSer', appendCSer, zero_represents=4) + + def _test_read_append_cser(self): + "test reading of appended case series" + + result = ma.concatenate([data['cser']['float32'][:3], appendCSer]) + ser = self.db.read('$appendCSer') + assert_array_equal(result, ser) + + def _test_write_range_cser(self): + "test writing over a specified range" + self.db.write_cser('$rangeCSer', rangeCSer, + start_case=5, end_case=9) + + def _test_verify_write_range_cser(self): + "verify that _test_write_range_write_cser worked as expected" + + ser = self.db.read('$rangeCSer') + result = ma.arange(9).astype(np.float32) + result[:4] = ma.masked + + assert_array_equal(ser, result) + + def _test_write_empty_cser(self): + "test writing a case series with no data" + self.db.write_cser('$emptyCSer', ma.array([])) + + def _test_read_empty_cser(self): + "test reading a case series with no data" + ser = self.db.read('$emptyCSer') + assert_equal(ser.size, 0) + + def _test_overwrite_cser(self): + "test overwriting a case series" + self.db.write_cser('$cser_float32', data['cser']['bool'], overwrite=True) + ser = self.db.read('$cser_float32') + assert_array_equal(ser, data['cser']['bool']) + + def _test_assume_exists_cser(self): + "check to see if the assume_exists flag works for write_cser" + exception = False + try: + self.db.write_cser('$doesNotExist', appendCSer, assume_exists=True) + except fame.DBError: + exception = True + assert(exception) + + def _test_dict_cser(self): + "test writing multiple case series at once using write_cser_dict" + self.db.write_cser_dict({'$cser_1':data['cser']['float32'], + '$cser_2':data['cser']['float32']}) + result = self.db.read(['$cser_1', '$cser_2']) + assert_array_equal(result['$cser_1'], data['cser']['float32']) + assert_array_equal(result['$cser_2'], data['cser']['float32']) + + def tearDown(self): + self.db.close() + + + +############################################################################### +#------------------------------------------------------------------------------ +if __name__ == "__main__": + NumpyTest().run() From scipy-svn at scipy.org Thu Feb 15 13:35:08 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 12:35:08 -0600 (CST) Subject: [Scipy-svn] r2713 - trunk/Lib/sandbox/timeseries/io/fame/src Message-ID: <20070215183508.7E50E39C02F@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 12:35:05 -0600 (Thu, 15 Feb 2007) New Revision: 2713 Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c Log: - added set_option function as a wrapper to cfmsopt - removed large option to open function (use set_option independently to control this behaviour) Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 17:52:27 UTC (rev 2712) +++ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 18:35:05 UTC (rev 2713) @@ -65,23 +65,34 @@ cfmsdm(&status, D_NC, D_ND, D_NA, dmistt); //Date return 0; } -/////////////////////////////////////////////////////////////////////// +static char cfame_set_option_doc[] = "wrapper to cfmsopt"; +static PyObject * +cfame_set_option(PyObject *self, PyObject *args) +{ + int status; + char *name, *val; + if (!PyArg_ParseTuple(args, "ss:set_option", &name, &val)) return NULL; + + printf("%s\n", name); + printf("%s\n", val); + + CALLFAME(cfmsopt(&status, name, val)); + + Py_RETURN_NONE; +} + + + static char cfame_open_doc[] = "open(database, access)\n\nOpens a FAME database and returns a FAME db idenifier."; static PyObject * cfame_open(PyObject *self, PyObject *args) { int status; - int dbkey, access, large; + int dbkey, access; const char *dbname; - if (!PyArg_ParseTuple(args, "sii:open", &dbname, &access, &large)) return NULL; + if (!PyArg_ParseTuple(args, "si:open", &dbname, &access)) return NULL; - if (access == HOMODE || access == HCMODE) { - - if (large) { CALLFAME(cfmsopt(&status, "DBSIZE", "LARGE")); } - else { CALLFAME(cfmsopt(&status, "DBSIZE", "STANDARD")); } - } - CALLFAME(cfmopdb (&status, &dbkey, dbname, access)); return PyInt_FromLong(dbkey); @@ -975,6 +986,7 @@ {"read", cfame_read, METH_VARARGS, cfame_read_doc}, {"whats", cfame_whats, METH_VARARGS, cfame_whats_doc}, {"size", cfame_size, METH_VARARGS, cfame_size_doc}, + {"set_option", cfame_set_option, METH_VARARGS, cfame_set_option_doc}, {"write_scalar", cfame_write_scalar, METH_VARARGS, cfame_write_scalar_doc}, {"write_series", cfame_write_series, METH_VARARGS, cfame_write_series_doc}, {"create", cfame_create, METH_VARARGS, cfame_create_doc}, From scipy-svn at scipy.org Thu Feb 15 13:35:37 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 12:35:37 -0600 (CST) Subject: [Scipy-svn] r2714 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070215183537.355F439C02F@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 12:35:34 -0600 (Thu, 15 Feb 2007) New Revision: 2714 Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py Log: add set_option function Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 18:35:05 UTC (rev 2713) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 18:35:34 UTC (rev 2714) @@ -25,12 +25,11 @@ class DBError(Exception): pass - class FameDb(object): """Fame database object :Construction: - x = FameDb(conn_str, mode='r', large=True) + x = FameDb(conn_str, mode='r') :Paramaters: - `conn_str` (str) : valid connection string. Can be a physical path, @@ -43,12 +42,8 @@ 'c' => create 'u' => update 'w' => write - 'd' => direct - - `large` (boolean, *[True]*) : Applies only when `mode` is 'o' or 'c'. - If True, a large size database will be created. If False, a standard size - database will be created. -""" - def __init__(self, conn_str, mode='r', large=True): + 'd' => direct""" + def __init__(self, conn_str, mode='r'): mode = mode.lower() if mode == 'r': intmode = mp.HRMODE @@ -69,7 +64,7 @@ self.mode = mode try: - self.dbkey = cf_open(conn_str, intmode, int(large)) + self.dbkey = cf_open(conn_str, intmode) self.dbIsOpen = True except: self.dbIsOpen = False @@ -643,6 +638,7 @@ return result cf_open = cFameCall(cfame.open) +cf_set_option = cFameCall(cfame.set_option) cf_close = cFameCall(cfame.close) cf_restore = cFameCall(cfame.restore) cf_size = cFameCall(cfame.size) @@ -654,4 +650,17 @@ cf_write_series = cFameCall(cfame.write_series) cf_write_namelist = cFameCall(cfame.write_namelist) cf_wildlist = cFameCall(cfame.wildlist) -cf_exists = cFameCall(cfame.exists) \ No newline at end of file +cf_exists = cFameCall(cfame.exists) + +set_option = cf_set_option +set_option.__doc__ = \ +"""Set an option in the C HLI. See the FAME documentation for cfmsopt for a +listing of allowable option settings. + +:Parameters: + - option (str) : name of the option to set + - setting (str) : value of the option to set + +:Example: + set_option("DBSIZE", "LARGE") +""" From scipy-svn at scipy.org Thu Feb 15 16:29:17 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 15:29:17 -0600 (CST) Subject: [Scipy-svn] r2715 - trunk/Lib/sandbox/timeseries/io/fame/src Message-ID: <20070215212917.9E70839C1F7@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 15:29:13 -0600 (Thu, 15 Feb 2007) New Revision: 2715 Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c Log: fixed compile warnings Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 18:35:34 UTC (rev 2714) +++ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 21:29:13 UTC (rev 2715) @@ -23,9 +23,10 @@ static int dmistt[3]; //Date //Numeric -static float N_ND = 1.701419e+038; -static float N_NC = 1.701418e+038; -static float N_NA = 1.701417e+038; +static float N_ND = (float)1.701419e+038; +static float N_NC = (float)1.701418e+038; +static float N_NA = (float)1.701417e+038; + // Precision static double P_ND = 1.70141507979e+038; static double P_NC = 1.70141507978e+038; @@ -74,9 +75,6 @@ char *name, *val; if (!PyArg_ParseTuple(args, "ss:set_option", &name, &val)) return NULL; - printf("%s\n", name); - printf("%s\n", val); - CALLFAME(cfmsopt(&status, name, val)); Py_RETURN_NONE; @@ -84,13 +82,13 @@ -static char cfame_open_doc[] = "open(database, access)\n\nOpens a FAME database and returns a FAME db idenifier."; +static char cfame_open_doc[] = "C level open method. This is called from the __init__ method of FameDb in fame.py"; static PyObject * cfame_open(PyObject *self, PyObject *args) { int status; int dbkey, access; - const char *dbname; + char *dbname; if (!PyArg_ParseTuple(args, "si:open", &dbname, &access)) return NULL; CALLFAME(cfmopdb (&status, &dbkey, dbname, access)); @@ -98,7 +96,7 @@ return PyInt_FromLong(dbkey); } -static char cfame_close_doc[] = "close(database_id)\n\nCloses an open FAME database."; +static char cfame_close_doc[] = "C level portion of the close method."; static PyObject * cfame_close(PyObject *self, PyObject *args) { @@ -112,13 +110,13 @@ return PyInt_FromLong(0); } -static char cfame_wildlist_doc[] = "wildlist(dbkey, wildlist expression, wildonly)\n\nPerforms a wildlist."; +static char cfame_wildlist_doc[] = "C level portion of the wildlist method."; static PyObject * cfame_wildlist(PyObject *self, PyObject *args) { int status; int dbkey; - const char *expression; + char *expression; int class, type, freq; char objnam[MAXOBJNAME+1]; PyObject *result = PyList_New(0); @@ -230,20 +228,16 @@ } -static char cfame_read_doc[] = "read(dbkey, data object name, startDate, endDate, dateSeriesFlag, longStr)\n\nReturns specified object."; -//startDate(endDate) must be the int value of the startDate(endDate) using the frequency of the underlying data -//dateSeriesFlag is 1 for date series 0 for case series -//longStr is 1 for string series with very long items 0 otherwise. Use 1 with care as it takes up alot of memory. +static char cfame_read_doc[] = "C level portion of read method."; static PyObject * cfame_read(PyObject *self, PyObject *args) { int status, dbkey, i; int dataFlag; - const char *object_name; + char *object_name; int first_point, last_point; //this defines the custom range to read (-1 for both means read all) - int longStr; //1 for case series with really long items int max_string_len; @@ -259,7 +253,6 @@ desc[0] = 0x0; doc[0] = 0x0; - // "isiii:get" means parse args for an int, a string and 4 more ints and use "get" as the function name in error messages if (!PyArg_ParseTuple(args, "isiii:read", &dbkey, &object_name, @@ -371,7 +364,7 @@ if (class == HSERIE) { PyObject** temp; - PyArrayObject *mask; + //string series int* missing; int* outlen; @@ -577,30 +570,40 @@ return data; } -static char cfame_write_series_doc[] = "write_series(dbkey, name, data, mask, start_index, end_index, source_type, source_freq)\n\nWrites a series to the DB"; +static char cfame_write_series_doc[] = + "C level portion of code for write_tser and write_cser method."; static PyObject * cfame_write_series(PyObject *self, PyObject *args) { int status, dbkey; PyObject *dataArrayTemp, *maskArrayTemp; PyArrayObject *dataArray, *maskArray; - const char* name; + char* name; char errMsg[500]; - int class, start_index, end_index, numobjs, source_type, type, ppd, + int class, start_index, end_index, numobjs, source_type, type, source_freq, freq, start_year, start_period, end_year, end_period; PyObject * returnVal = NULL; int range[3]; - if (!PyArg_ParseTuple(args, "isOOiiii:write_series", &dbkey, &name, &dataArrayTemp, &maskArrayTemp, &start_index, &end_index, &source_type, &source_freq)) return NULL; //get params - CALLFAME(cfmosiz(&status, dbkey, name, &class, &type, &freq, &start_year, &start_period, &end_year, &end_period)); //get object info + if (!PyArg_ParseTuple(args, "isOOiiii:write_series", + &dbkey, &name, + &dataArrayTemp, &maskArrayTemp, + &start_index, &end_index, + &source_type, &source_freq)) return NULL; + CALLFAME(cfmosiz(&status, dbkey, name, + &class, &type, &freq, &start_year, + &start_period, &end_year, &end_period)); + if (source_type != type) { - PyErr_SetString(PyExc_RuntimeError, "received a non-matching type, cannot write"); + PyErr_SetString(PyExc_RuntimeError, + "received a non-matching type, cannot write"); return NULL; } if (source_freq != freq) { - PyErr_SetString(PyExc_RuntimeError, "received a non-matching frequency, cannot write"); + PyErr_SetString(PyExc_RuntimeError, + "received a non-matching frequency, cannot write"); return NULL; } @@ -620,7 +623,8 @@ //set the range that we will be writing to CALLFAME(cfmsrng(&status, freq, &start_year, &start_period, &end_year, &end_period, range, &numobjs)); if (!PyArray_Check(dataArrayTemp)) { - PyErr_SetString(PyExc_RuntimeError, "write_series was passed something other than an ndarray"); + PyErr_SetString(PyExc_RuntimeError, + "write_series was passed something other than an ndarray"); return NULL; } @@ -700,22 +704,20 @@ } } - Py_DECREF(dataArray); - } Py_RETURN_NONE; } -static char cfame_write_scalar_doc[] = "write_scalar(dbkey, name, object, source_type)\n\nWrites a scalar to the DB"; +static char cfame_write_scalar_doc[] = "C level portion of write_scalar method."; static PyObject * cfame_write_scalar(PyObject *self, PyObject *args) { int status, dbkey; PyObject* object; - const char* name; + char* name; int class, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz PyObject * returnVal = NULL; int source_type, type; @@ -771,13 +773,12 @@ } -static char cfame_write_namelist_doc[] = "write_namelist(dbkey, name, namelist_string)\n\nWrites a namelist to the DB"; +static char cfame_write_namelist_doc[] = "C level portion of code for writing namelists."; static PyObject * cfame_write_namelist(PyObject *self, PyObject *args) { int status, dbkey; - const char* name; - const char* namelist; + char *name, *namelist; if (!PyArg_ParseTuple(args, "iss:writeNamelist", &dbkey, &name, &namelist)) return NULL; @@ -786,42 +787,46 @@ Py_RETURN_NONE; } -static char cfame_create_doc[] = "create(dbkey, object_name, class_arg, freq_arg, type_arg, basis_arg, observed_arg)\n\nCreates a fame object in the DB"; +static char cfame_create_doc[] = "C level portion of code for creating objects."; static PyObject * cfame_create(PyObject *self, PyObject *args) { int status, dbkey; - const char* object_name; + char* object_name; int class_arg, freq_arg, type_arg, basis_arg, observed_arg; - if (!PyArg_ParseTuple(args, "isiiiii:create", &dbkey, &object_name, &class_arg, &freq_arg, &type_arg, &basis_arg, &observed_arg)) return NULL; //get params + if (!PyArg_ParseTuple(args, "isiiiii:create", + &dbkey, &object_name, &class_arg, + &freq_arg, &type_arg, &basis_arg, + &observed_arg)) return NULL; + CALLFAME(cfmnwob(&status, dbkey, object_name, class_arg, freq_arg, type_arg, basis_arg, observed_arg)); Py_RETURN_NONE; } -static char cfame_remove_doc[] = "remove(dbkey, object_name)"; +static char cfame_remove_doc[] = "C level portion of code for deleting objects."; static PyObject* cfame_remove(PyObject* self, PyObject* args) { int status, dbkey; - const char* object_name; + char* object_name; - if (!PyArg_ParseTuple(args, "is:remove", &dbkey, &object_name)) return NULL; //get params + if (!PyArg_ParseTuple(args, "is:remove", &dbkey, &object_name)) return NULL; CALLFAME(cfmdlob(&status, dbkey, object_name)); Py_RETURN_NONE; } -static char cfame_exists_doc[] = "exists(dbkey, object_name)"; +static char cfame_exists_doc[] = "C level portion of code for checking existence of object."; static PyObject* cfame_exists(PyObject* self, PyObject* args) { int status, dbkey; - const char* object_name; + char* object_name; int deslen, doclen; - if (!PyArg_ParseTuple(args, "is:exists", &dbkey, &object_name)) return NULL; //get params + if (!PyArg_ParseTuple(args, "is:exists", &dbkey, &object_name)) return NULL; cfmdlen (&status, dbkey, object_name, &deslen, &doclen); if (status == HNOOBJ) @@ -830,13 +835,13 @@ Py_RETURN_TRUE; } -static char cfame_updated_doc[] = "updated(dbkey, object_name)"; +static char cfame_updated_doc[] = "C level portion of updated function."; static PyObject* cfame_updated(PyObject* self, PyObject* args) { int status, dbkey; - const char *object_name; - int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz + char *object_name; + int class, type, freq, start_year, start_period, end_year, end_period; int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; char desc[1], doc[1]; PyObject * returnVal = NULL; @@ -844,7 +849,7 @@ desc[0] = 0x0; doc[0] = 0x0; - if (!PyArg_ParseTuple(args, "is:updated", &dbkey, &object_name)) return NULL; //get params + if (!PyArg_ParseTuple(args, "is:updated", &dbkey, &object_name)) return NULL; CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observ, &start_year, &start_period, &end_year, &end_period, @@ -861,7 +866,7 @@ return returnVal; } -static char cfame_whats_doc[] = "whats(dbkey, data object name)\n\nReturns information about the specified object."; +static char cfame_whats_doc[] = "C level portion of whats function."; static PyObject * cfame_whats(PyObject *self, PyObject *args) { @@ -880,9 +885,11 @@ int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; //additional fields for cfmwhat - //get arguments. - //"is" means first one is integer second is string - //"whats" is what will appear in python error messages if this method crashes + PyObject *py_class, *py_type, *py_freq, *py_basis, *py_observ, + *py_start_year, *py_start_period, *py_end_year, *py_end_period, + *py_mod_year, *py_mod_month, *py_mod_day, + *py_desc, *py_doc; + if (!PyArg_ParseTuple(args, "is:whats", &dbkey, &object_name)) return NULL; @@ -912,28 +919,63 @@ &mod_year, &mod_month, &mod_day, desc, doc)); + py_class = PyInt_FromLong(class); + py_type = PyInt_FromLong(type); + py_freq = PyInt_FromLong(freq); + py_basis = PyInt_FromLong(basis); + py_observ = PyInt_FromLong(observ); + py_start_year = PyInt_FromLong(start_year); + py_start_period = PyInt_FromLong(start_period); + py_end_year = PyInt_FromLong(end_year); + py_end_period = PyInt_FromLong(end_period); + py_mod_year = PyInt_FromLong(mod_year); + py_mod_month = PyInt_FromLong(mod_month); + py_mod_day = PyInt_FromLong(mod_day); + + py_desc = PyString_FromString(desc); + py_doc = PyString_FromString(doc); + returnVal = PyDict_New(); - PyDict_SetItemString(returnVal, "type", PyInt_FromLong(type)); - PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); - PyDict_SetItemString(returnVal, "class", PyInt_FromLong(class)); - PyDict_SetItemString(returnVal, "start_year", PyInt_FromLong(start_year)); - PyDict_SetItemString(returnVal, "start_period", PyInt_FromLong(start_period)); - PyDict_SetItemString(returnVal, "end_year", PyInt_FromLong(end_year)); - PyDict_SetItemString(returnVal, "end_period", PyInt_FromLong(end_period)); - PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); - PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); - PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); - PyDict_SetItemString(returnVal, "desc", PyString_FromString(desc)); - PyDict_SetItemString(returnVal, "doc", PyString_FromString(doc)); + PyDict_SetItemString(returnVal, "class", py_class); + PyDict_SetItemString(returnVal, "type", py_type); + PyDict_SetItemString(returnVal, "freq", py_freq); + PyDict_SetItemString(returnVal, "basis", py_basis); + PyDict_SetItemString(returnVal, "observ", py_observ); + PyDict_SetItemString(returnVal, "start_year", py_start_year); + PyDict_SetItemString(returnVal, "start_period", py_start_period); + PyDict_SetItemString(returnVal, "end_year", py_end_year); + PyDict_SetItemString(returnVal, "end_period", py_end_period); + PyDict_SetItemString(returnVal, "mod_year", py_mod_year); + PyDict_SetItemString(returnVal, "mod_month", py_mod_month); + PyDict_SetItemString(returnVal, "mod_day", py_mod_day); + PyDict_SetItemString(returnVal, "desc", py_desc); + PyDict_SetItemString(returnVal, "doc", py_doc); + free((void*)desc); free((void*)doc); + Py_DECREF(py_class); + Py_DECREF(py_type); + Py_DECREF(py_freq); + Py_DECREF(py_basis); + Py_DECREF(py_observ); + Py_DECREF(py_start_year); + Py_DECREF(py_start_period); + Py_DECREF(py_end_year); + Py_DECREF(py_end_period); + Py_DECREF(py_mod_year); + Py_DECREF(py_mod_month); + Py_DECREF(py_mod_day); + + Py_DECREF(py_desc); + Py_DECREF(py_doc); + return returnVal; } -static char cfame_size_doc[] = "size(dbkey, data object name)\n\nReturns limited about the specified object."; +static char cfame_size_doc[] = "C level portion of size method."; static PyObject * cfame_size(PyObject *self, PyObject *args) { @@ -944,28 +986,48 @@ //return val PyObject *returnVal = NULL; + PyObject *py_class, *py_type, *py_freq, *py_start_year, *py_start_period, + *py_end_year, *py_end_period; + int status; int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz - //get arguments. - //"is" means first one is integer second is string - //"size" is what will appear in python error messages if this method crashes if (!PyArg_ParseTuple(args, "is:size", &dbkey, &object_name)) return NULL; CALLFAME(cfmosiz(&status, dbkey, object_name, &class, &type, &freq, &start_year, &start_period, &end_year, &end_period)); + py_class = PyInt_FromLong(class); + py_type = PyInt_FromLong(type); + py_freq = PyInt_FromLong(freq); + py_start_year = PyInt_FromLong(start_year); + py_start_period = PyInt_FromLong(start_period); + py_end_year = PyInt_FromLong(end_year); + py_end_period = PyInt_FromLong(end_period); + returnVal = PyDict_New(); - PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); - //To return other fields add them here - //look up cfmosiz in fame help for other fields + PyDict_SetItemString(returnVal, "class", py_class); + PyDict_SetItemString(returnVal, "type", py_type); + PyDict_SetItemString(returnVal, "freq", py_freq); + PyDict_SetItemString(returnVal, "start_year", py_start_year); + PyDict_SetItemString(returnVal, "start_period", py_start_period); + PyDict_SetItemString(returnVal, "end_year", py_end_year); + PyDict_SetItemString(returnVal, "end_period", py_end_period); + Py_DECREF(py_class); + Py_DECREF(py_type); + Py_DECREF(py_freq); + Py_DECREF(py_start_year); + Py_DECREF(py_start_period); + Py_DECREF(py_end_year); + Py_DECREF(py_end_period); + return returnVal; } -static char cfame_restore_doc[] = "restore(dbkey)\n\nDiscard any changes made to the database since it was last opened or posted.\nXXX: not sure what posted means, see FAME API"; +static char cfame_restore_doc[] = "C level portion of restore method."; static PyObject * cfame_restore(PyObject *self, PyObject *args) { From scipy-svn at scipy.org Thu Feb 15 16:31:14 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 15:31:14 -0600 (CST) Subject: [Scipy-svn] r2716 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070215213114.C50C839C1F7@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 15:31:11 -0600 (Thu, 15 Feb 2007) New Revision: 2716 Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py Log: bug fixes Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 21:29:13 UTC (rev 2715) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-15 21:31:11 UTC (rev 2716) @@ -545,12 +545,12 @@ exp = exp.replace("^", "(.)") exp = exp.replace("$","\$") regex = re.compile(exp) - for i in range(len(res)): - res[i] = "".join(regex.match(res[i]).groups()) + res = ["".join(regex.match(res[i]).groups()) \ + for i in range(len(res))] return res - def exists(self, objName): - return cf_exists(self.dbkey, objName) + def exists(self, name): + return cf_exists(self.dbkey, name) def close(self): if self.dbIsOpen: @@ -575,15 +575,11 @@ raise DBError("Database is not open") - def remove(self, name, ignoreError=True): + def remove(self, name, must_exist=True): """Deletes the given series from the database""" - if type(name) == type(""): name = [name] + if isinstance(name, str): name = [name] + [cf_remove(self.dbkey, n) for n in name if must_exist or self.exists(n)] - for x in name: - try: - cf_remove(self.dbkey, x) - except: - if not ignoreError: raise def get_freq(self, name): """Finds the frequency of the object stored in the db as `name`""" @@ -595,18 +591,23 @@ def whats(self, name): - """Preforms a fame "whats" command on the provided series""" - if type(name) == type(""): name = [name] + """Preforms a fame "whats" command on the provided name(s)""" + if isinstance(name, str): + single_obj = True + name = [name] + else: + single_obj = False result = {} - for dbname in name: + for n in name: if not self.dbIsOpen: raise DBError("Database is not open") - result[dbname] = cf_whats(self.dbkey, dbname.upper()) + result[n] = cf_whats(self.dbkey, n.upper()) - if len(result) == 1: + if single_obj: return result.values()[0] + return result @@ -624,7 +625,7 @@ self.f = func self.__doc__ = getattr(func, "__doc__", str(func)) self.__name__ = getattr(func, "__name__", str(func)) - # + def __call__ (self, *args, **kwargs): "Execute the call behavior." tmp = fameLock.acquire() From scipy-svn at scipy.org Thu Feb 15 16:31:30 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 15:31:30 -0600 (CST) Subject: [Scipy-svn] r2717 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070215213130.CA09B39C1F7@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 15:31:27 -0600 (Thu, 15 Feb 2007) New Revision: 2717 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: more tests added Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:31:11 UTC (rev 2716) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:31:27 UTC (rev 2717) @@ -139,6 +139,16 @@ self._test_assume_exists_cser() self._test_dict_cser() + + self._test_whats() + + self._test_exists() + + self._test_remove() + + self._test_wildlist() + + def _test_write_scalars(self): "test writing all types of scalar values" @@ -378,6 +388,37 @@ result = self.db.read(['$cser_1', '$cser_2']) assert_array_equal(result['$cser_1'], data['cser']['float32']) assert_array_equal(result['$cser_2'], data['cser']['float32']) + + def _test_whats(self): + "test whats method" + + # just make sure it doesn't crash for now + what_dict = self.db.whats('$tser_float32') + + def _test_exists(self): + assert(self.db.exists('$cser_float32')) + assert(not self.db.exists('$fake_series')) + + def _test_remove(self): + assert(self.db.exists('$cser_1')) + assert(self.db.exists('$cser_2')) + self.db.remove(['$cser_1', '$cser_2']) + assert(not self.db.exists('$cser_1')) + assert(not self.db.exists('$cser_2')) + + self.db.remove('$cser_1', must_exist=False) + + + def _test_wildlist(self): + wl1 = self.db.wildlist("$cser_?") + wl2 = self.db.wildlist("$cser_?", wildonly=True) + + res1 = sorted(["$CSER_"+x.upper() for x in list(data['cser'])]) + res2 = sorted([x.upper() for x in list(data['cser'])]) + + assert_equal(wl1, res1) + assert_equal(wl2, res2) + def tearDown(self): self.db.close() From scipy-svn at scipy.org Thu Feb 15 16:54:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 15:54:04 -0600 (CST) Subject: [Scipy-svn] r2718 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070215215404.3D92F39C1F7@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 15:54:01 -0600 (Thu, 15 Feb 2007) New Revision: 2718 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: added test for restore method Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:31:27 UTC (rev 2717) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:54:01 UTC (rev 2718) @@ -147,8 +147,9 @@ self._test_remove() self._test_wildlist() + + self._test_restore() - def _test_write_scalars(self): "test writing all types of scalar values" @@ -396,10 +397,12 @@ what_dict = self.db.whats('$tser_float32') def _test_exists(self): + "test exists method" assert(self.db.exists('$cser_float32')) assert(not self.db.exists('$fake_series')) def _test_remove(self): + "test remove method" assert(self.db.exists('$cser_1')) assert(self.db.exists('$cser_2')) self.db.remove(['$cser_1', '$cser_2']) @@ -410,6 +413,7 @@ def _test_wildlist(self): + "test wildlist method" wl1 = self.db.wildlist("$cser_?") wl2 = self.db.wildlist("$cser_?", wildonly=True) @@ -418,6 +422,16 @@ assert_equal(wl1, res1) assert_equal(wl2, res2) + + def _test_restore(self): + "test restore method" + self.db.close() + self.db = fame.FameDb("testdb.db",'s') + + self.db.remove('$tser_float32') + assert(not self.db.exists('$tser_float32')) + self.db.restore() + assert(self.db.exists('$tser_float32')) def tearDown(self): From scipy-svn at scipy.org Thu Feb 15 16:54:38 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 15:54:38 -0600 (CST) Subject: [Scipy-svn] r2719 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070215215438.7B98D39C1F7@new.scipy.org> Author: mattknox_ca Date: 2007-02-15 15:54:35 -0600 (Thu, 15 Feb 2007) New Revision: 2719 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:54:01 UTC (rev 2718) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-15 21:54:35 UTC (rev 2719) @@ -392,7 +392,6 @@ def _test_whats(self): "test whats method" - # just make sure it doesn't crash for now what_dict = self.db.whats('$tser_float32') @@ -408,18 +407,14 @@ self.db.remove(['$cser_1', '$cser_2']) assert(not self.db.exists('$cser_1')) assert(not self.db.exists('$cser_2')) - self.db.remove('$cser_1', must_exist=False) - def _test_wildlist(self): "test wildlist method" wl1 = self.db.wildlist("$cser_?") wl2 = self.db.wildlist("$cser_?", wildonly=True) - res1 = sorted(["$CSER_"+x.upper() for x in list(data['cser'])]) res2 = sorted([x.upper() for x in list(data['cser'])]) - assert_equal(wl1, res1) assert_equal(wl2, res2) @@ -433,7 +428,6 @@ self.db.restore() assert(self.db.exists('$tser_float32')) - def tearDown(self): self.db.close() From scipy-svn at scipy.org Thu Feb 15 17:29:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 15 Feb 2007 16:29:55 -0600 (CST) Subject: [Scipy-svn] r2720 - trunk/Lib/sparse Message-ID: <20070215222955.3F4F439C0A0@new.scipy.org> Author: wnbell Date: 2007-02-15 16:29:50 -0600 (Thu, 15 Feb 2007) New Revision: 2720 Modified: trunk/Lib/sparse/sparse.py Log: Applied patch to spmatrix.dot(). Resolves ticket #374 Modified: trunk/Lib/sparse/sparse.py =================================================================== --- trunk/Lib/sparse/sparse.py 2007-02-15 21:54:35 UTC (rev 2719) +++ trunk/Lib/sparse/sparse.py 2007-02-15 22:29:50 UTC (rev 2720) @@ -315,7 +315,7 @@ if len(other.shape) == 1: result = self.matvec(other) - elif isdense(other) and asarray(other).squeeze().ndim == 1: + elif isdense(other) and asarray(other).squeeze().ndim <= 1: # If it's a row or column vector, return a DENSE result result = self.matvec(other) elif len(other.shape) == 2: From scipy-svn at scipy.org Fri Feb 16 16:14:12 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 16 Feb 2007 15:14:12 -0600 (CST) Subject: [Scipy-svn] r2721 - trunk/Lib/sandbox/timeseries/io/fame/src Message-ID: <20070216211412.4C02F39C0C8@new.scipy.org> Author: mattknox_ca Date: 2007-02-16 15:14:08 -0600 (Fri, 16 Feb 2007) New Revision: 2721 Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c Log: added new functions. Lots of little tweaks Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-15 22:29:50 UTC (rev 2720) +++ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-16 21:14:08 UTC (rev 2721) @@ -13,6 +13,9 @@ #define CALLFAME(cmd) Py_BEGIN_ALLOW_THREADS; cmd; Py_END_ALLOW_THREADS; if (checkError(status)) return NULL +//call fame without checking for errors +#define CALLFAME_NOCHECK(cmd) Py_BEGIN_ALLOW_THREADS; cmd; Py_END_ALLOW_THREADS; + #define ROUND(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) /**********************************************************************/ @@ -49,7 +52,6 @@ { char message[1000]; PyErr_SetString(PyExc_RuntimeError, getsta(status, message)); - cfmfin(&status); return 1; } return 0; @@ -76,7 +78,6 @@ if (!PyArg_ParseTuple(args, "ss:set_option", &name, &val)) return NULL; CALLFAME(cfmsopt(&status, name, val)); - Py_RETURN_NONE; } @@ -86,13 +87,11 @@ static PyObject * cfame_open(PyObject *self, PyObject *args) { - int status; - int dbkey, access; + int status, dbkey, access; char *dbname; if (!PyArg_ParseTuple(args, "si:open", &dbname, &access)) return NULL; CALLFAME(cfmopdb (&status, &dbkey, dbname, access)); - return PyInt_FromLong(dbkey); } @@ -100,16 +99,88 @@ static PyObject * cfame_close(PyObject *self, PyObject *args) { - int status; - int dbkey; + int status, dbkey; if (!PyArg_ParseTuple(args, "i:close", &dbkey)) return NULL; - cfmcldb (&status, dbkey); - if (checkError(status)) return NULL; + CALLFAME(cfmcldb (&status, dbkey)); + Py_RETURN_NONE; +} - return PyInt_FromLong(0); +static char cfame_get_db_attr_doc[] = "C level portion of the get_db_attr method."; +static PyObject * +cfame_get_db_attr(PyObject *self, PyObject *args) +{ + int status, dbkey; + int is_desc, is_doc, is_created, is_modified, is_isopen; + char *attr; + if (!PyArg_ParseTuple(args, "is:get_db_attr", &dbkey, &attr)) return NULL; + + is_desc = (strcmp(attr, "DESC") == 0); + is_doc = (strcmp(attr, "DOC") == 0); + is_created = (strcmp(attr, "CREATED") == 0); + is_modified = (strcmp(attr, "MODIFIED") == 0); + is_isopen = (strcmp(attr, "ISOPEN") == 0); + + if (is_desc || is_doc) { + int cyear, cmonth, cday, myear, mmonth, mday; + int deslen, doclen; + void *tempdesc, *tempdoc; + char *desc, *doc; + PyObject *result; + + CALLFAME(cfmglen(&status, dbkey, &deslen, &doclen)); + + if (is_desc) { doclen = 0; } + else { deslen = 0; } + + /* allocate the memory needed for the desc/doc strings */ + if ((tempdesc = malloc((deslen + 1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + if ((tempdoc = malloc((doclen + 1) * sizeof(char))) == NULL) return PyErr_NoMemory(); + + /* set the memory to non-null chars to tell fame the length of string we will accept */ + memset(tempdesc, 'A', deslen); + memset(tempdoc, 'A', doclen); + + /* cast to char array */ + desc = (char*)tempdesc; + doc = (char*)tempdoc; + + /* terminate the string with a null */ + desc[deslen] = 0x0; + doc[doclen] = 0x0; + + CALLFAME(cfmgdba(&status, dbkey, &cyear, &cmonth, &cday, + &myear, &mmonth, &mday, + desc, doc)); + + if (is_desc) { result = PyString_FromString(desc); } + else { result = PyString_FromString(doc); } + + free((void*)desc); + free((void*)doc); + + return result; + } else if (is_isopen) { + int deslen, doclen; + + CALLFAME_NOCHECK(cfmglen(&status, dbkey, &deslen, &doclen)); + + if (status == HBKEY) { Py_RETURN_FALSE; } + else if (checkError(status)) { return NULL; } + else { Py_RETURN_TRUE; } + } else if (is_created || is_modified) { + + int cdate, mdate; + CALLFAME(cfmgdbd(&status, dbkey, HSEC, &cdate, &mdate)); + if (is_modified) { return PyInt_FromLong(mdate); } + else { return PyInt_FromLong(cdate); } + + } else { + Py_RETURN_NONE; + } } + static char cfame_wildlist_doc[] = "C level portion of the wildlist method."; static PyObject * cfame_wildlist(PyObject *self, PyObject *args) @@ -137,8 +208,10 @@ while (status != HNOOBJ) { + PyObject *py_str = PyString_FromString(objnam); // append objnam to list - if (PyList_Append(result, PyString_FromString(objnam))) return NULL; + if (PyList_Append(result, py_str)) return NULL; + Py_DECREF(py_str); // get next item cfmnxwc (&status, dbkey, objnam, &class, &type, &freq); @@ -237,15 +310,17 @@ char *object_name; - int first_point, last_point; //this defines the custom range to read (-1 for both means read all) + int first_point, last_point; int max_string_len; - int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz - int basis, observed, created_year, created_month, created_day, mod_year, mod_month, mod_day; //additional fields for cfmwhat + int class, type, freq, start_year, start_period, end_year, end_period; + int basis, observed, created_year, created_month, created_day, + mod_year, mod_month, mod_day; char desc[1], doc[1]; PyObject * returnVal = NULL; PyObject * values = NULL; + PyObject *py_type, *py_freq, *py_class, *py_observed; int numobjs, typeNum; int range[3]; void* dbValues; @@ -260,26 +335,40 @@ &last_point, &max_string_len)) return NULL; //get params - CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observed, &start_year, &start_period, &end_year, &end_period, &created_year, &created_month, &created_day, &mod_year, &mod_month, &mod_day, desc, doc)); + CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, + &basis, &observed, + &start_year, &start_period, + &end_year, &end_period, + &created_year, &created_month, &created_day, + &mod_year, &mod_month, &mod_day, desc, doc)); returnVal = PyDict_New(); - PyDict_SetItemString(returnVal, "type", PyInt_FromLong(type)); - PyDict_SetItemString(returnVal, "freq", PyInt_FromLong(freq)); - PyDict_SetItemString(returnVal, "class", PyInt_FromLong(class)); - PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); - PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); - PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); - PyDict_SetItemString(returnVal, "observed", PyInt_FromLong(observed)); - PyDict_SetItemString(returnVal, "basis", PyInt_FromLong(basis)); + py_type = PyInt_FromLong(type); + py_freq = PyInt_FromLong(freq); + py_class = PyInt_FromLong(class); + py_observed = PyInt_FromLong(observed); + PyDict_SetItemString(returnVal, "type", py_type); + PyDict_SetItemString(returnVal, "freq", py_freq); + PyDict_SetItemString(returnVal, "class", py_class); + PyDict_SetItemString(returnVal, "observed", py_observed); + + Py_DECREF(py_type); + Py_DECREF(py_freq); + Py_DECREF(py_class); + Py_DECREF(py_observed); + if (type == HNAMEL) //namelists { + PyObject *py_nl; int length; char names[MAXOBJNAME*MAXNLLENGTH+1]; CALLFAME(cfmgtnl(&status, dbkey, object_name, HNLALL, names, MAXOBJNAME*MAXNLLENGTH, &length)); - PyDict_SetItemString(returnVal, "data", PyString_FromStringAndSize(names, length)); //just return the namelist as a comma delimited string + py_nl = PyString_FromStringAndSize(names, length); + PyDict_SetItemString(returnVal, "data", py_nl); + Py_DECREF(py_nl); } else { @@ -396,18 +485,19 @@ } else { if (missing[i] != HNMVAL) { - if ((temp[i] = PyString_FromString("")) == NULL) { + PyObject *zerolenstr = PyString_FromString(""); + if ((temp[i] = zerolenstr) == NULL) { PyErr_SetString(PyExc_RuntimeError, "Failed to initialize missing string element."); return NULL; } mask_raw[i] = 1; } else { - if ((temp[i] = PyString_FromStringAndSize(((char**)dbValues)[i], outlen[i])) == NULL) { + PyObject *currStr = PyString_FromStringAndSize(((char**)dbValues)[i], outlen[i]); + if ((temp[i] = currStr) == NULL) { return PyErr_NoMemory(); } mask_raw[i] = 0; } - free(((char**)dbValues)[i]); } } @@ -567,6 +657,7 @@ } Py_DECREF(valMask); } + Py_DECREF(fillVal); return data; } @@ -805,14 +896,16 @@ Py_RETURN_NONE; } -static char cfame_remove_doc[] = "C level portion of code for deleting objects."; + + +static char cfame_delete_doc[] = "C level portion of code for deleting objects."; static PyObject* -cfame_remove(PyObject* self, PyObject* args) +cfame_delete(PyObject* self, PyObject* args) { int status, dbkey; char* object_name; - if (!PyArg_ParseTuple(args, "is:remove", &dbkey, &object_name)) return NULL; + if (!PyArg_ParseTuple(args, "is:delete", &dbkey, &object_name)) return NULL; CALLFAME(cfmdlob(&status, dbkey, object_name)); Py_RETURN_NONE; @@ -835,37 +928,8 @@ Py_RETURN_TRUE; } -static char cfame_updated_doc[] = "C level portion of updated function."; -static PyObject* -cfame_updated(PyObject* self, PyObject* args) -{ - int status, dbkey; - char *object_name; - int class, type, freq, start_year, start_period, end_year, end_period; - int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; - char desc[1], doc[1]; - PyObject * returnVal = NULL; - desc[0] = 0x0; - doc[0] = 0x0; - if (!PyArg_ParseTuple(args, "is:updated", &dbkey, &object_name)) return NULL; - - CALLFAME(cfmwhat(&status, dbkey, object_name, &class, &type, &freq, &basis, &observ, - &start_year, &start_period, &end_year, &end_period, - &created_year, &created_month, &created_day, - &mod_year, &mod_month, &mod_day, - desc, doc)); - - returnVal = PyDict_New(); - - PyDict_SetItemString(returnVal, "mod_year", PyInt_FromLong(mod_year)); - PyDict_SetItemString(returnVal, "mod_month", PyInt_FromLong(mod_month)); - PyDict_SetItemString(returnVal, "mod_day", PyInt_FromLong(mod_day)); - - return returnVal; -} - static char cfame_whats_doc[] = "C level portion of whats function."; static PyObject * cfame_whats(PyObject *self, PyObject *args) @@ -882,8 +946,9 @@ int status; void *tempdesc, *tempdoc; char *desc, *doc; - int class, type, freq, start_year, start_period, end_year, end_period; // data fields returned by cfmosiz - int basis, observ, created_year, created_month, created_day, mod_year, mod_month, mod_day; //additional fields for cfmwhat + int class, type, freq, start_year, start_period, end_year, end_period, + basis, observ, created_year, created_month, created_day, + mod_year, mod_month, mod_day; PyObject *py_class, *py_type, *py_freq, *py_basis, *py_observ, *py_start_year, *py_start_period, *py_end_year, *py_end_period, @@ -975,9 +1040,9 @@ return returnVal; } -static char cfame_size_doc[] = "C level portion of size method."; +static char cfame_obj_size_doc[] = "C level portion of obj_size method."; static PyObject * -cfame_size(PyObject *self, PyObject *args) +cfame_obj_size(PyObject *self, PyObject *args) { //arguments char *object_name; @@ -1047,16 +1112,16 @@ {"wildlist", cfame_wildlist, METH_VARARGS, cfame_wildlist_doc}, {"read", cfame_read, METH_VARARGS, cfame_read_doc}, {"whats", cfame_whats, METH_VARARGS, cfame_whats_doc}, - {"size", cfame_size, METH_VARARGS, cfame_size_doc}, + {"obj_size", cfame_obj_size, METH_VARARGS, cfame_obj_size_doc}, {"set_option", cfame_set_option, METH_VARARGS, cfame_set_option_doc}, {"write_scalar", cfame_write_scalar, METH_VARARGS, cfame_write_scalar_doc}, {"write_series", cfame_write_series, METH_VARARGS, cfame_write_series_doc}, {"create", cfame_create, METH_VARARGS, cfame_create_doc}, - {"remove", cfame_remove, METH_VARARGS, cfame_remove_doc}, + {"delete", cfame_delete, METH_VARARGS, cfame_delete_doc}, {"exists", cfame_exists, METH_VARARGS, cfame_exists_doc}, - {"updated", cfame_updated, METH_VARARGS, cfame_updated_doc}, {"write_namelist", cfame_write_namelist, METH_VARARGS, cfame_write_namelist_doc}, {"restore", cfame_restore, METH_VARARGS, cfame_restore_doc}, + {"get_db_attr", cfame_get_db_attr, METH_VARARGS, cfame_get_db_attr_doc}, {NULL, NULL} }; From scipy-svn at scipy.org Fri Feb 16 16:14:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 16 Feb 2007 15:14:55 -0600 (CST) Subject: [Scipy-svn] r2722 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070216211455.AA37039C0C8@new.scipy.org> Author: mattknox_ca Date: 2007-02-16 15:14:51 -0600 (Fri, 16 Feb 2007) New Revision: 2722 Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py Log: lots of fixes and tweaks and additions Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-16 21:14:08 UTC (rev 2721) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-16 21:14:51 UTC (rev 2722) @@ -23,10 +23,32 @@ if hasattr(key, 'upper'): key = key.upper() super(CaseInsensitiveDict, self).__setitem__(key, item) + +def _single_or_multi_func(dbkey, name, func, *args, **kwargs): + if isinstance(name, str): + single_obj = True + name = [name] + else: + single_obj = False + + result = {} + for n in name: + result[n] = func(dbkey, n, *args, **kwargs) + + if single_obj: + return result.values()[0] + + return result + +def _famedate_to_tsdate(fame_date, freqstr): + "convert integer fame date to a timeseries Date" + value = fame_date + mp.value_adjust[ts.freq_fromstr(freqstr)] + return ts.Date(freq=freqstr, value=value) + class DBError(Exception): pass class FameDb(object): - """Fame database object + """Fame database object. :Construction: x = FameDb(conn_str, mode='r') @@ -42,7 +64,12 @@ 'c' => create 'u' => update 'w' => write - 'd' => direct""" + 'd' => direct + +Notes + - For changes to be commited, you must explictly use the "commit" or + "close" methods (changes are commited on close). Changes are NOT + committed when the database object is deleted.""" def __init__(self, conn_str, mode='r'): mode = mode.lower() if mode == 'r': @@ -63,12 +90,7 @@ raise ValueError, "Database access mode not supported." self.mode = mode - try: - self.dbkey = cf_open(conn_str, intmode) - self.dbIsOpen = True - except: - self.dbIsOpen = False - raise + self.dbkey = cf_open(conn_str, intmode) def read(self, name, @@ -104,10 +126,6 @@ if `name` is a single string: object from database that is stored as `name`""" - - if not self.dbIsOpen: - raise DBError("Database is not open") - isSingle = False if isinstance(name, types.StringType): names = [name] @@ -145,7 +163,7 @@ objName = objName.upper() if checkFreq: - objFreq = self.get_freq(objName) + objFreq = self.obj_size(objName)['freq'] if objFreq == range_freq: start_index, end_index = _start_date, _end_date @@ -209,7 +227,6 @@ pyObj = ma.array(data, mask=mask) else: observed = mp.observedMapping[result['observed']] - basis = mp.basisMapping[result['basis']] freq = mp.freqMapping[result['freq']] if 'data' in result: @@ -324,8 +341,6 @@ - `end_date` (Date, *[None]*) : If None, data will be written until the end of `tser`. If specified, only data points on or before end_date will be written. """ - - self.__check_writeable() if not isinstance(tser, ts.TimeSeries): raise ValueError("tser is not a valid time series") @@ -345,10 +360,7 @@ if create: - if hasattr(tser, "basis"): - fame_basis = mp.basisReverseMapping[tser.basis] - else: - fame_basis = mp.HBSDAY + fame_basis = mp.HBSDAY if hasattr(tser, "observed"): fame_observed = mp.observedReverseMapping[tser.observed] @@ -356,7 +368,7 @@ else: fame_observed = mp.HOBEND - if self.exists(name): self.remove(name) + if self.exists(name): self.delete(name) cf_create(self.dbkey, name, mp.HSERIE, fame_freq, fame_type, fame_basis, fame_observed) def get_boundary_date(bdate, attr): @@ -420,8 +432,6 @@ - `end_case` (int, *[None]*) : If None, data will be written until the end of `cser`. If specified, only data points on or before end_case will be written. """ - - self.__check_writeable() if not isinstance(cser, numpy.ndarray): raise ValueError("cser is not a valid ndarray") @@ -447,7 +457,7 @@ fame_type = mp.fametype_fromdata(fame_data) if create: - if self.exists(name): self.remove(name) + if self.exists(name): self.delete(name) cf_create(self.dbkey, name, mp.HSERIE, mp.HCASEX, fame_type, mp.HBSUND, mp.HOBUND) def get_boundary_case(bcase, attr): @@ -497,8 +507,6 @@ - `scalar` : one of the following: string, numpy scalar, int, float, list of strings (for name lists), Date, boolean""" - self.__check_writeable() - fame_type = mp.fametype_fromdata(scalar) if isinstance(scalar, ts.Date): @@ -519,7 +527,7 @@ else: raise ValueError("Unrecognized data type") - if self.exists(name): self.remove(name) + if self.exists(name): self.delete(name) cf_create(self.dbkey, name, mp.HSCALA, mp.HUNDFX, fame_type, mp.HBSUND, mp.HOBUND) # convert integer types to floats since FAME does not have an integer type @@ -533,11 +541,30 @@ cf_write_scalar(self.dbkey, name, fame_data, fame_type) + def desc(self): + "get 'description' attribute of database" + return cf_get_db_attr(self.dbkey, "DESC") + + def doc(self): + "get 'doc' attribute of database" + return cf_get_db_attr(self.dbkey, "DOC") + + def created(self): + "get 'created' attribute of database" + fame_date = cf_get_db_attr(self.dbkey, "CREATED") + return _famedate_to_tsdate(fame_date, 's') + + def modified(self): + "get 'modified' attribute of database" + fame_date = cf_get_db_attr(self.dbkey, "MODIFIED") + return _famedate_to_tsdate(fame_date, 's') + + def is_open(self): + return cf_get_db_attr(self.dbkey, "ISOPEN") def wildlist(self, exp, wildonly=False): """performs a wildlist lookup on the database, using Fame syntax ("?" and "^"), returns a normal python list of strings""" - self.__check_readable() res = cf_wildlist(self.dbkey, exp) if wildonly: @@ -553,72 +580,33 @@ return cf_exists(self.dbkey, name) def close(self): - if self.dbIsOpen: + """Closes the database. Changes will be posted.""" + if self.is_open(): cf_close(self.dbkey) - self.dbIsOpen = False + + def commit(self): + pass - def __del__(self): - if self.dbIsOpen: - self.close() - - - def __check_writeable(self): - """Raises error if data base is not writeable""" - if not self.dbIsOpen: - raise DBError("Database is not open") - if self.mode == 'r': - raise DBError("Cannot write to a read-only database") - - def __check_readable(self): - """Raises error if data base is not readable""" - if not self.dbIsOpen: - raise DBError("Database is not open") - - - def remove(self, name, must_exist=True): - """Deletes the given series from the database""" + def delete(self, name, must_exist=True): + """Deletes the specified object(s) from the database""" if isinstance(name, str): name = [name] - [cf_remove(self.dbkey, n) for n in name if must_exist or self.exists(n)] + [cf_delete(self.dbkey, n) for n in name if must_exist or self.exists(n)] + def obj_size(self, name): + """basic information about the size of an object(s) in a database""" + return _single_or_multi_func(self.dbkey, name, cf_obj_size) - def get_freq(self, name): - """Finds the frequency of the object stored in the db as `name`""" - if not self.dbIsOpen: - raise DBError("Database is not open") - - result = cf_size(self.dbkey, name.upper()) - return result['freq'] - - def whats(self, name): """Preforms a fame "whats" command on the provided name(s)""" - if isinstance(name, str): - single_obj = True - name = [name] - else: - single_obj = False + return _single_or_multi_func(self.dbkey, name, cf_whats) - result = {} - for n in name: - if not self.dbIsOpen: - raise DBError("Database is not open") - - result[n] = cf_whats(self.dbkey, n.upper()) - - if single_obj: - return result.values()[0] - - return result - - - def restore(self): """Discard any changes made to the database since it was last opened or posted.""" return cf_restore(self.dbkey) class cFameCall: - """wrapper for cfame functions that acquires and releases a resource log. + """wrapper for cfame functions that acquires and releases a resource lock. This is needed because the Fame C api is not thread safe.""" def __init__ (self, func): @@ -642,9 +630,9 @@ cf_set_option = cFameCall(cfame.set_option) cf_close = cFameCall(cfame.close) cf_restore = cFameCall(cfame.restore) -cf_size = cFameCall(cfame.size) +cf_obj_size = cFameCall(cfame.obj_size) cf_whats = cFameCall(cfame.whats) -cf_remove = cFameCall(cfame.remove) +cf_delete = cFameCall(cfame.delete) cf_create = cFameCall(cfame.create) cf_read = cFameCall(cfame.read) cf_write_scalar = cFameCall(cfame.write_scalar) @@ -652,6 +640,7 @@ cf_write_namelist = cFameCall(cfame.write_namelist) cf_wildlist = cFameCall(cfame.wildlist) cf_exists = cFameCall(cfame.exists) +cf_get_db_attr = cFameCall(cfame.get_db_attr) set_option = cf_set_option set_option.__doc__ = \ From scipy-svn at scipy.org Mon Feb 19 00:29:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 18 Feb 2007 23:29:04 -0600 (CST) Subject: [Scipy-svn] r2723 - in trunk/Lib/sandbox/maskedarray: . alternative_versions tests Message-ID: <20070219052904.08BC639C00C@new.scipy.org> Author: pierregm Date: 2007-02-18 23:28:52 -0600 (Sun, 18 Feb 2007) New Revision: 2723 Added: trunk/Lib/sandbox/maskedarray/alternative_versions/ trunk/Lib/sandbox/maskedarray/alternative_versions/core_alt.py trunk/Lib/sandbox/maskedarray/alternative_versions/core_ini.py Removed: trunk/Lib/sandbox/maskedarray/core_ini.py Modified: trunk/Lib/sandbox/maskedarray/CHANGELOG trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/mrecords.py trunk/Lib/sandbox/maskedarray/tests/test_core.py trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py trunk/Lib/sandbox/maskedarray/timer_comparison.py Log: core : generic cleanup mrecords : adapted to the new core module tests : added a new set of tests (test_subclassing) for basic subclassing of MaskedArray Modified: trunk/Lib/sandbox/maskedarray/CHANGELOG =================================================================== --- trunk/Lib/sandbox/maskedarray/CHANGELOG 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/CHANGELOG 2007-02-19 05:28:52 UTC (rev 2723) @@ -1,58 +1,106 @@ -#2007-01-22 : core : fixed a call to numpy.float128 on 32b machines -#2007-01-21 : core : fixed max/min_fill_values -# : : fixed sort (as method and function) -#2007-01-18 : core : fixed default filling values for unsigned int_. -#2007-01-16 : extras : new function : `mediff1d` -#2007-01-15 : mrecords: new method: `addfield` -# : core : Force the mask of a masked array `x` to be copied w/... -# ...masked_array(x,copy=True,mask=nomask,keep_mask=true) -#2007-01-14 : mrecords: Slices are now properly supported -# : mrecords: `filled` is now properly supported -#2007-01-12 : mrecords: Complete reorganization... -#2007-01-10 : mrecords: Defines a class of records that support masked arrays -#2007-01-08 : Core: -# : core : Force a reset of the class defaults after initialization -# : core : Modified __array_finallize__ to allow objects w/ _data... -# ... _mask fields to be recognized as MA -#2007-01-04 : core : Fixed a but in masked_all_like -#2007-01-02 : extras : Force apply_along_axis to output the proper fill_value -# : core : Can use dtypes for the definition of default_fill_value -#2006-12-30 : core : Cleaned up setitem/setslice w/ hard_mask=True -# : core : Fixed masked_unary/binary_operations... -# ...to work with subclasses of MaskedArray -#2006-12-22 : core : Optimized(?) default_/maximum_/minimum_fill_value -# : core : Force __new__ to not return a MaskedArray, in order to ... -# : ... optimize __array_finalize__ -# : core : Add the hard_mask flag to __new__ (*[False]*) -#2006-12-19 : core : Fixed a problem on _set_mask which prevented to set a mask to nomask -# : extras : Renamed compress2d to compress_rowcols -# : extras : Added dot -#2006-12-18 : extras : Added compress2d and mask_rowcols -# : extras : moved 'average' to 'extras' -#2006-12-13 : core : Fixed make_mask (forced filling to True) -# : core : Fixed ndim -# : core : Fixed error message in __new__ when wrong sizes -# : core : Fixed the reshape function. -# : extras : masked_all: set default dtype to float_ -# : extras : _fromnxfunctions: make sure that list are recognized -# : extras : added notmasked_edges, notmasked_contiguous -#2006-12-09 : - Code reorganization: define 2 modules, core and extras -#2006-11-25 : core : Disable copy by default -# core : Added keep_mask flag (to save mask when creating a ma from a ma) -# core : Fixed functions: empty_like -# core : Fixed methods: .any and .all -# core : New functions: masked_all, masked_all_like -# core : New methods: .squeeze -#2006-11-20 : core : fixed make_mask -# core : fixed nonzero method -#2006-11-16 : core : fixed .T -#2006-11-12 : core : add max, min as function (not only method...) -# core : repr returns a name like masked_xxx, where xxx is the subclass -#2006-10-31 : core : make sure that make_mask returns a pure ndarray. -#2006-10-30 : core : When converted to a float, a masked singleton is ... -# ...transformed to nan instead of raising an exception. -#21: Use __get__ method in _arraymethods, _arithmethods, _compamethods -#18: Updated put to match the definition of numpy 1.0, deleted putmask, changed resize -#2: prevent an extra kword being sent to make_mask_none + The maskedarray package went through some major updates recently. +Originally, a MaskedArray object had two major attributes: (i) _data, a +(subclass of) ndarray that stored the values, and (ii) _mask, a ndarray of +booleans storing whether the values were masekd or not. This structure was +directly derived from the original implementation of MaskedArray, available in +numpy.core.ma. +However, this approach wasn't very natural. For example, in order to access the +data of a MaskedArray instance, one had to query the _data attribute instead of +the instance itself. In addition, the previous implementation of MaskedArray as +a subclass of ndarray presented some problems. Thus, most of the attributes of +a MaskedArray object were defined as class defaults, which turned out to be +thread-unsafe. Moreover, subclassing MaskedArray wasn't straightforward, when +the subclass introduced new parameters in its __new__ method. + +The current implementation tries to alleviate these problems. The most +significant difference is that the _data attribute is now a view of the array +as a (subclass of) ndarray. The actual type is set by the _baseclass attribute, +defined at the creation of a new masked array as the class of the underlying +data. Thus, it is still possible to use a matrix object as the underlying data: +the new masked array will then use the matrix methods. Note that if the +underlying data has its own attributes, these latter are not propagated to the +MaskedArray instance, but are reinitialized at each access. In other terms, you +will lose specific values of these attributes during processing. You should +then consider defining a subclass of MaskedArray. Note also that because the +_data attribute is a view, any attempt to directly set it will likely fail. For +example, x._data = 5 will raise an AttributeError exception. You should then +use x._data[:] = 5 instead). + +The _mask attibute is left unchanged. Because it is a basic attribute, it can +be overwritten far too easily. If you want to specify a new value for the mask, +you should use the _setmask or __setmask__ methods: these methods ensure that +the new mask has the same shape as the data, and that the _hardmask condition +is respected. + +Following the suggestions of Reggie Dugard, the class defaults have been +suppressed. Unfortunately, that required to add some extra definitions in the +__array_finalize__ method, which tends to have a slight negative impact on +performances. Moreover, most methods now return a view of the masked array +instead of creating a new masked array from scratch. + + +The previous implementation of MaskedArray is called core_ini.py and it can be +found in the alternative_versions directory. This folder contains also yet +another implementation core_alt. This latter is left for documentation purposes, +and should serve as a template when we'll port the package to C. + +#............................................................................... +2007-01-22 : core : fixed a call to numpy.float128 on 32b machines +2007-01-21 : core : fixed max/min_fill_values + : : fixed sort (as method and function) +2007-01-18 : core : fixed default filling values for unsigned int_. +2007-01-16 : extras : new function : `mediff1d` +2007-01-15 : mrecords: new method: `addfield` + : core : Force the mask of a masked array `x` to be copied w/... + ...masked_array(x,copy=True,mask=nomask,keep_mask=true) +2007-01-14 : mrecords: Slices are now properly supported + : mrecords: `filled` is now properly supported +2007-01-12 : mrecords: Complete reorganization... +2007-01-10 : mrecords: Defines a class of records that support masked arrays +2007-01-08 : Core: + : core : Force a reset of the class defaults after initialization + : core : Modified __array_finallize__ to allow objects w/ _data... + ... _mask fields to be recognized as MA +2007-01-04 : core : Fixed a but in masked_all_like +2007-01-02 : extras : Force apply_along_axis to output the proper fill_value + : core : Can use dtypes for the definition of default_fill_value +2006-12-30 : core : Cleaned up setitem/setslice w/ hard_mask=True + : core : Fixed masked_unary/binary_operations... + ...to work with subclasses of MaskedArray +2006-12-22 : core : Optimized(?) default_/maximum_/minimum_fill_value + : core : Force __new__ to not return a MaskedArray, in order to ... + : ... optimize __array_finalize__ + : core : Add the hard_mask flag to __new__ (*[False]*) +2006-12-19 : core : Fixed a problem on _set_mask which prevented to set a mask to nomask + : extras : Renamed compress2d to compress_rowcols + : extras : Added dot +2006-12-18 : extras : Added compress2d and mask_rowcols + : extras : moved 'average' to 'extras' +2006-12-13 : core : Fixed make_mask (forced filling to True) + : core : Fixed ndim + : core : Fixed error message in __new__ when wrong sizes + : core : Fixed the reshape function. + : extras : masked_all: set default dtype to float_ + : extras : _fromnxfunctions: make sure that list are recognized + : extras : added notmasked_edges, notmasked_contiguous +2006-12-09 : - Code reorganization: define 2 modules, core and extras +2006-11-25 : core : Disable copy by default + core : Added keep_mask flag (to save mask when creating a ma from a ma) + core : Fixed functions: empty_like + core : Fixed methods: .any and .all + core : New functions: masked_all, masked_all_like + core : New methods: .squeeze +2006-11-20 : core : fixed make_mask + core : fixed nonzero method +2006-11-16 : core : fixed .T +2006-11-12 : core : add max, min as function (not only method...) + core : repr returns a name like masked_xxx, where xxx is the subclass +2006-10-31 : core : make sure that make_mask returns a pure ndarray. +2006-10-30 : core : When converted to a float, a masked singleton is ... + ...transformed to nan instead of raising an exception. +21: Use __get__ method in _arraymethods, _arithmethods, _compamethods +18: Updated put to match the definition of numpy 1.0, deleted putmask, changed resize +2: prevent an extra kword being sent to make_mask_none + #............................................................ \ No newline at end of file Added: trunk/Lib/sandbox/maskedarray/alternative_versions/core_alt.py =================================================================== --- trunk/Lib/sandbox/maskedarray/alternative_versions/core_alt.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/alternative_versions/core_alt.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -0,0 +1,2993 @@ +# pylint: disable-msg=E1002 +"""MA: a facility for dealing with missing observations +MA is generally used as a numpy.array look-alike. +by Paul F. Dubois. + +Copyright 1999, 2000, 2001 Regents of the University of California. +Released for unlimited redistribution. +Adapted for numpy_core 2005 by Travis Oliphant and +(mainly) Paul Dubois. + +Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant. +pgmdevlist_AT_gmail_DOT_com +Improvements suggested by Reggie Dugard (reggie_AT_merfinllc_DOT_com) + +:author: Pierre Gerard-Marchant +:contact: pierregm_at_uga_dot_edu +:version: $Id$ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author$)" +__version__ = '1.0' +__revision__ = "$Revision$" +__date__ = '$Date$' + +__all__ = ['MAError', 'MaskType', 'MaskedArray', + 'bool_', 'complex_', 'float_', 'int_', 'object_', + 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', + 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', + 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', + 'arctanh', 'argmax', 'argmin', 'argsort', 'around', + 'array', 'asarray', + 'bitwise_and', 'bitwise_or', 'bitwise_xor', + 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate', + 'cos', 'cosh', 'count', + 'diagonal', 'divide', 'dump', 'dumps', + 'empty', 'empty_like', 'equal', 'exp', + 'fabs', 'fmod', 'filled', 'floor', 'floor_divide', + 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot', + 'ids', 'inner', 'innerproduct', + 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', + 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', + 'logical_and', 'logical_not', 'logical_or', 'logical_xor', + 'make_mask', 'make_mask_none', 'mask_or', 'masked', + 'masked_array', 'masked_equal', 'masked_greater', + 'masked_greater_equal', 'masked_inside', 'masked_less', + 'masked_less_equal', 'masked_not_equal', 'masked_object', + 'masked_outside', 'masked_print_option', 'masked_singleton', + 'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min', + 'minimum', 'multiply', + 'negative', 'nomask', 'nonzero', 'not_equal', + 'ones', 'outer', 'outerproduct', + 'power', 'product', 'ptp', 'put', 'putmask', + 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', + 'right_shift', 'round_', + 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std', + 'subtract', 'sum', 'swapaxes', + 'take', 'tan', 'tanh', 'transpose', 'true_divide', + 'var', 'where', + 'zeros'] + +import sys +import types +import cPickle +# +import numpy +from numpy import bool_, complex_, float_, int_, object_, str_ + +import numpy.core.umath as umath +import numpy.core.fromnumeric as fromnumeric +from numpy.core.numeric import ndarray +from numpy.core.fromnumeric import amax, amin +import numpy.core.numerictypes as ntypes +from numpy.core.numerictypes import bool_, typecodes +from numpy.core.multiarray import dtype +import numpy.core.numeric as numeric +from numpy.lib.shape_base import expand_dims as n_expand_dims +import warnings + + +MaskType = bool_ +nomask = MaskType(0) + +divide_tolerance = 1.e-35 +numpy.seterr(all='ignore') + +# TODO: There's still a problem with N.add.reduce not working... +# TODO: ...neither does N.add.accumulate + +#####-------------------------------------------------------------------------- +#---- --- Exceptions --- +#####-------------------------------------------------------------------------- +class MAError(Exception): + "Class for MA related errors." + def __init__ (self, args=None): + "Creates an exception." + Exception.__init__(self,args) + self.args = args + def __str__(self): + "Calculates the string representation." + return str(self.args) + __repr__ = __str__ + +#####-------------------------------------------------------------------------- +#---- --- Filling options --- +#####-------------------------------------------------------------------------- +# b: boolean - c: complex - f: floats - i: integer - O: object - S: string +default_filler = {'b': True, + 'c' : 1.e20 + 0.0j, + 'f' : 1.e20, + 'i' : 999999, + 'O' : '?', + 'S' : 'N/A', + 'u' : 999999, + 'V' : '???', + } +max_filler = ntypes._minvals +max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]]) +min_filler = ntypes._maxvals +min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]]) +if 'float128' in ntypes.typeDict: + max_filler.update([(numpy.float128,-numeric.inf)]) + min_filler.update([(numpy.float128, numeric.inf)]) + + +def default_fill_value(obj): + "Calculates the default fill value for an object `obj`." + if hasattr(obj,'dtype'): + defval = default_filler[obj.dtype.kind] + elif isinstance(obj, numeric.dtype): + defval = default_filler[obj.kind] + elif isinstance(obj, float): + defval = default_filler['f'] + elif isinstance(obj, int) or isinstance(obj, long): + defval = default_filler['i'] + elif isinstance(obj, str): + defval = default_filler['S'] + elif isinstance(obj, complex): + defval = default_filler['c'] + else: + defval = default_filler['O'] + return defval + +def minimum_fill_value(obj): + "Calculates the default fill value suitable for taking the minimum of `obj`." + if hasattr(obj, 'dtype'): + objtype = obj.dtype + filler = min_filler[objtype] + if filler is None: + raise TypeError, 'Unsuitable type for calculating minimum.' + return filler + elif isinstance(obj, float): + return min_filler[ntypes.typeDict['float_']] + elif isinstance(obj, int): + return min_filler[ntypes.typeDict['int_']] + elif isinstance(obj, long): + return min_filler[ntypes.typeDict['uint']] + elif isinstance(obj, numeric.dtype): + return min_filler[obj] + else: + raise TypeError, 'Unsuitable type for calculating minimum.' + +def maximum_fill_value(obj): + "Calculates the default fill value suitable for taking the maximum of `obj`." + if hasattr(obj, 'dtype'): + objtype = obj.dtype + filler = max_filler[objtype] + if filler is None: + raise TypeError, 'Unsuitable type for calculating minimum.' + return filler + elif isinstance(obj, float): + return max_filler[ntypes.typeDict['float_']] + elif isinstance(obj, int): + return max_filler[ntypes.typeDict['int_']] + elif isinstance(obj, long): + return max_filler[ntypes.typeDict['uint']] + elif isinstance(obj, numeric.dtype): + return max_filler[obj] + else: + raise TypeError, 'Unsuitable type for calculating minimum.' + +def set_fill_value(a, fill_value): + "Sets the fill value of `a` if it is a masked array." + if isinstance(a, MaskedArray): + a.set_fill_value(fill_value) + +def get_fill_value(a): + """Returns the fill value of `a`, if any. + Otherwise, returns the default fill value for that type. + """ + if isinstance(a, MaskedArray): + result = a.fill_value + else: + result = default_fill_value(a) + return result + +def common_fill_value(a, b): + "Returns the common fill_value of `a` and `b`, if any, or `None`." + t1 = get_fill_value(a) + t2 = get_fill_value(b) + if t1 == t2: + return t1 + return None + +#................................................ +def filled(a, value = None): + """Returns `a` as an array with masked data replaced by `value`. +If `value` is `None` or the special element `masked`, `get_fill_value(a)` +is used instead. + +If `a` is already a contiguous numeric array, `a` itself is returned. + +`filled(a)` can be used to be sure that the result is numeric when passing +an object a to other software ignorant of MA, in particular to numpy itself. + """ + if hasattr(a, 'filled'): + return a.filled(value) + elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']: + return a + elif isinstance(a, dict): + return numeric.array(a, 'O') + else: + return numeric.array(a) + +def get_masked_subclass(*arrays): + """Returns the youngest subclass of MaskedArray from a list of arrays, + or MaskedArray. In case of siblings, the first takes over.""" + if len(arrays) == 1: + arr = arrays[0] + if isinstance(arr, MaskedArray): + rcls = type(arr) + else: + rcls = MaskedArray + else: + arrcls = [type(a) for a in arrays] + rcls = arrcls[0] + if not issubclass(rcls, MaskedArray): + rcls = MaskedArray + for cls in arrcls[1:]: + if issubclass(cls, rcls): + rcls = cls + return rcls + +#####-------------------------------------------------------------------------- +#---- --- Ufuncs --- +#####-------------------------------------------------------------------------- +ufunc_domain = {} +ufunc_fills = {} + +class domain_check_interval: + """Defines a valid interval, +so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`.""" + def __init__(self, a, b): + "domain_check_interval(a,b)(x) = true where x < a or y > b" + if (a > b): + (a, b) = (b, a) + self.a = a + self.b = b + + def __call__ (self, x): + "Execute the call behavior." + return umath.logical_or(umath.greater (x, self.b), + umath.less(x, self.a)) +#............................ +class domain_tan: + """Defines a valid interval for the `tan` function, +so that `domain_tan(eps) = True where `abs(cos(x)) < eps`""" + def __init__(self, eps): + "domain_tan(eps) = true where abs(cos(x)) < eps)" + self.eps = eps + def __call__ (self, x): + "Execute the call behavior." + return umath.less(umath.absolute(umath.cos(x)), self.eps) +#............................ +class domain_safe_divide: + """defines a domain for safe division.""" + def __init__ (self, tolerance=divide_tolerance): + self.tolerance = tolerance + def __call__ (self, a, b): + return umath.absolute(a) * self.tolerance >= umath.absolute(b) +#............................ +class domain_greater: + "domain_greater(v)(x) = true where x <= v" + def __init__(self, critical_value): + "domain_greater(v)(x) = true where x <= v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less_equal(x, self.critical_value) +#............................ +class domain_greater_equal: + "domain_greater_equal(v)(x) = true where x < v" + def __init__(self, critical_value): + "domain_greater_equal(v)(x) = true where x < v" + self.critical_value = critical_value + + def __call__ (self, x): + "Execute the call behavior." + return umath.less(x, self.critical_value) +#.............................................................................. +class masked_unary_operation: + """Defines masked version of unary operations, +where invalid values are pre-masked. + +:IVariables: + - `f` : function. + - `fill` : Default filling value *[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, mufunc, fill=0, domain=None): + """ masked_unary_operation(aufunc, fill=0, domain=None) + aufunc(fill) must be defined + self(x) returns aufunc(x) + with masked values where domain(x) is true or getmask(x) is true. + """ + self.f = mufunc + self.fill = fill + self.domain = domain + self.__doc__ = getattr(mufunc, "__doc__", str(mufunc)) + self.__name__ = getattr(mufunc, "__name__", str(mufunc)) + ufunc_domain[mufunc] = domain + ufunc_fills[mufunc] = fill + # + def __call__ (self, a, *args, **kwargs): + "Execute the call behavior." +# numeric tries to return scalars rather than arrays when given scalars. + m = getmask(a) + d1 = filled(a, self.fill) + if self.domain is not None: + m = mask_or(m, numeric.asarray(self.domain(d1))) + # Take care of the masked singletong first ... + if m.ndim == 0 and m: + return masked + # Get the result.... + if isinstance(a, MaskedArray): + result = self.f(d1, *args, **kwargs).view(type(a)) + else: + result = self.f(d1, *args, **kwargs).view(MaskedArray) + # Fix the mask if we don't have a scalar + if result.ndim > 0: + result._mask = m + return result + # + def __str__ (self): + return "Masked version of %s. [Invalid values are masked]" % str(self.f) +#.............................................................................. +class masked_binary_operation: + """Defines masked version of binary operations, +where invalid values are pre-masked. + +:IVariables: + - `f` : function. + - `fillx` : Default filling value for first array*[0]*. + - `filly` : Default filling value for second array*[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, mbfunc, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = mbfunc + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc)) + self.__name__ = getattr(mbfunc, "__name__", str(mbfunc)) + ufunc_domain[mbfunc] = None + ufunc_fills[mbfunc] = (fillx, filly) + # + def __call__ (self, a, b, *args, **kwargs): + "Execute the call behavior." + m = mask_or(getmask(a), getmask(b)) + if (not m.ndim) and m: + return masked + result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b)) + if result.ndim > 0: + result.unshare_mask() + result._masklayer |= m + return result + # + def reduce (self, target, axis=0, dtype=None): + """Reduces `target` along the given `axis`.""" + if isinstance(target, MaskedArray): + tclass = type(target) + else: + tclass = MaskedArray + m = getmask(target) + t = filled(target, self.filly) + if t.shape == (): + t = t.reshape(1) + if m is not nomask: + m = make_mask(m, copy=1) + m.shape = (1,) + if m is nomask: + return self.f.reduce(t, axis).view(tclass) + t = t.view(tclass) + t._mask = m + # XXX: "or t.dtype" below is a workaround for what appears + # XXX: to be a bug in reduce. + tr = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype) + mr = umath.logical_and.reduce(m, axis) + tr = tr.view(tclass) + if mr.ndim > 0: + tr._mask = mr + return tr + elif mr: + return masked + return tr + + def outer (self, a, b): + "Returns the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + m = nomask + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = umath.logical_or.outer(ma, mb) + if (not m.ndim) and m: + return masked + rcls = get_masked_subclass(a,b) + d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)).view(rcls) + if d.ndim > 0: + d._mask = m + return d + + def accumulate (self, target, axis=0): + """Accumulates `target` along `axis` after filling with y fill value.""" + if isinstance(target, MaskedArray): + tclass = type(target) + else: + tclass = masked_array + t = filled(target, self.filly) + return self.f.accumulate(t, axis).view(tclass) + + def __str__ (self): + return "Masked version of " + str(self.f) +#.............................................................................. +class domained_binary_operation: + """Defines binary operations that have a domain, like divide. + +These are complicated so they are a separate class. +They have no reduce, outer or accumulate. + +:IVariables: + - `f` : function. + - `fillx` : Default filling value for first array*[0]*. + - `filly` : Default filling value for second array*[0]*. + - `domain` : Default domain *[None]*. + """ + def __init__ (self, dbfunc, domain, fillx=0, filly=0): + """abfunc(fillx, filly) must be defined. + abfunc(x, filly) = x for all x to enable reduce. + """ + self.f = dbfunc + self.domain = domain + self.fillx = fillx + self.filly = filly + self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc)) + self.__name__ = getattr(dbfunc, "__name__", str(dbfunc)) + ufunc_domain[dbfunc] = domain + ufunc_fills[dbfunc] = (fillx, filly) + + def __call__(self, a, b): + "Execute the call behavior." + ma = getmask(a) + mb = getmask(b) + d1 = filled(a, self.fillx) + d2 = filled(b, self.filly) + t = numeric.asarray(self.domain(d1, d2)) + + if fromnumeric.sometrue(t, None): + d2 = numeric.where(t, self.filly, d2) + mb = mask_or(mb, t) + m = mask_or(ma, mb) + if (not m.ndim) and m: + return masked + result = self.f(d1, d2).view(get_masked_subclass(a,b)) + if result.ndim > 0: + result._mask = m + return result + + def __str__ (self): + return "Masked version of " + str(self.f) + +#.............................................................................. +# Unary ufuncs +exp = masked_unary_operation(umath.exp) +conjugate = masked_unary_operation(umath.conjugate) +sin = masked_unary_operation(umath.sin) +cos = masked_unary_operation(umath.cos) +tan = masked_unary_operation(umath.tan) +arctan = masked_unary_operation(umath.arctan) +arcsinh = masked_unary_operation(umath.arcsinh) +sinh = masked_unary_operation(umath.sinh) +cosh = masked_unary_operation(umath.cosh) +tanh = masked_unary_operation(umath.tanh) +abs = absolute = masked_unary_operation(umath.absolute) +fabs = masked_unary_operation(umath.fabs) +negative = masked_unary_operation(umath.negative) +floor = masked_unary_operation(umath.floor) +ceil = masked_unary_operation(umath.ceil) +around = masked_unary_operation(fromnumeric.round_) +logical_not = masked_unary_operation(umath.logical_not) +# Domained unary ufuncs +sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0)) +log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0)) +log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0)) +tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35)) +arcsin = masked_unary_operation(umath.arcsin, 0.0, + domain_check_interval(-1.0, 1.0)) +arccos = masked_unary_operation(umath.arccos, 0.0, + domain_check_interval(-1.0, 1.0)) +arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0)) +arctanh = masked_unary_operation(umath.arctanh, 0.0, + domain_check_interval(-1.0+1e-15, 1.0-1e-15)) +# Binary ufuncs +add = masked_binary_operation(umath.add) +subtract = masked_binary_operation(umath.subtract) +multiply = masked_binary_operation(umath.multiply, 1, 1) +arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0) +equal = masked_binary_operation(umath.equal) +equal.reduce = None +not_equal = masked_binary_operation(umath.not_equal) +not_equal.reduce = None +less_equal = masked_binary_operation(umath.less_equal) +less_equal.reduce = None +greater_equal = masked_binary_operation(umath.greater_equal) +greater_equal.reduce = None +less = masked_binary_operation(umath.less) +less.reduce = None +greater = masked_binary_operation(umath.greater) +greater.reduce = None +logical_and = masked_binary_operation(umath.logical_and) +alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce +logical_or = masked_binary_operation(umath.logical_or) +sometrue = logical_or.reduce +logical_xor = masked_binary_operation(umath.logical_xor) +bitwise_and = masked_binary_operation(umath.bitwise_and) +bitwise_or = masked_binary_operation(umath.bitwise_or) +bitwise_xor = masked_binary_operation(umath.bitwise_xor) +hypot = masked_binary_operation(umath.hypot) +# Domained binary ufuncs +divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1) +true_divide = domained_binary_operation(umath.true_divide, + domain_safe_divide(), 0, 1) +floor_divide = domained_binary_operation(umath.floor_divide, + domain_safe_divide(), 0, 1) +remainder = domained_binary_operation(umath.remainder, + domain_safe_divide(), 0, 1) +fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1) + + +#####-------------------------------------------------------------------------- +#---- --- Mask creation functions --- +#####-------------------------------------------------------------------------- +def getmask(a): + """Returns the mask of `a`, if any, or `nomask`. +Returns `nomask` if `a` is not a masked array. +To get an array for sure use getmaskarray.""" + if hasattr(a, "_mask"): + return a._mask + else: + return nomask + +def getmaskarray(a): + """Returns the mask of `a`, if any. +Otherwise, returns an array of `False`, with the same shape as `a`. + """ + m = getmask(a) + if m is nomask: + return make_mask_none(fromnumeric.shape(a)) + else: + return m + +def is_mask(m): + """Returns `True` if `m` is a legal mask. +Does not check contents, only type. + """ + try: + return m.dtype.type is MaskType + except AttributeError: + return False +# +def make_mask(m, copy=False, small_mask=True, flag=None): + """make_mask(m, copy=0, small_mask=0) +Returns `m` as a mask, creating a copy if necessary or requested. +The function can accept any sequence of integers or `nomask`. +Does not check that contents must be 0s and 1s. +If `small_mask=True`, returns `nomask` if `m` contains no true elements. + +:Parameters: + - `m` (ndarray) : Mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. + """ + if flag is not None: + warnings.warn("The flag 'flag' is now called 'small_mask'!", + DeprecationWarning) + small_mask = flag + if m is nomask: + return nomask + elif isinstance(m, ndarray): + m = filled(m, True) + if m.dtype.type is MaskType: + if copy: + result = numeric.array(m, dtype=MaskType, copy=copy) + else: + result = m + else: + result = numeric.array(m, dtype=MaskType) + else: + result = numeric.array(filled(m, True), dtype=MaskType) + # Bas les masques ! + if small_mask and not result.any(): + return nomask + else: + return result + +def make_mask_none(s): + "Returns a mask of shape `s`, filled with `False`." + result = numeric.zeros(s, dtype=MaskType) + return result + +def mask_or (m1, m2, copy=False, small_mask=True): + """Returns the combination of two masks `m1` and `m2`. +The masks are combined with the `logical_or` operator, treating `nomask` as false. +The result may equal m1 or m2 if the other is nomask. + +:Parameters: + - `m` (ndarray) : Mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. + """ + if m1 is nomask: + return make_mask(m2, copy=copy, small_mask=small_mask) + if m2 is nomask: + return make_mask(m1, copy=copy, small_mask=small_mask) + if m1 is m2 and is_mask(m1): + return m1 + return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask) + +#####-------------------------------------------------------------------------- +#--- --- Masking functions --- +#####-------------------------------------------------------------------------- +def masked_where(condition, x, copy=True): + """Returns `x` as an array masked where `condition` is true. +Masked values of `x` or `condition` are kept. + +:Parameters: + - `condition` (ndarray) : Masking condition. + - `x` (ndarray) : Array to mask. + - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. + """ + cm = filled(condition,1) + if isinstance(x,MaskedArray): + m = mask_or(x._mask, cm) + return x.__class__(x._data, mask=m, copy=copy) + else: + return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm) + +def masked_greater(x, value, copy=1): + "Shortcut to `masked_where`, with ``condition = (x > value)``." + return masked_where(greater(x, value), x, copy=copy) + +def masked_greater_equal(x, value, copy=1): + "Shortcut to `masked_where`, with ``condition = (x >= value)``." + return masked_where(greater_equal(x, value), x, copy=copy) + +def masked_less(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x < value)``." + return masked_where(less(x, value), x, copy=copy) + +def masked_less_equal(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x <= value)``." + return masked_where(less_equal(x, value), x, copy=copy) + +def masked_not_equal(x, value, copy=True): + "Shortcut to `masked_where`, with ``condition = (x != value)``." + return masked_where((x != value), x, copy=copy) + +# +def masked_equal(x, value, copy=True): + """Shortcut to `masked_where`, with ``condition = (x == value)``. +For floating point, consider `masked_values(x, value)` instead. + """ + return masked_where((x == value), x, copy=copy) +# d = filled(x, 0) +# c = umath.equal(d, value) +# m = mask_or(c, getmask(x)) +# return array(d, mask=m, copy=copy) + +def masked_inside(x, v1, v2, copy=True): + """Shortcut to `masked_where`, where `condition` is True for x inside +the interval `[v1,v2]` ``(v1 <= x <= v2)``. +The boundaries `v1` and `v2` can be given in either order. + """ + if v2 < v1: + (v1, v2) = (v2, v1) + xf = filled(x) + condition = (xf >= v1) & (xf <= v2) + return masked_where(condition, x, copy=copy) + +def masked_outside(x, v1, v2, copy=True): + """Shortcut to `masked_where`, where `condition` is True for x outside +the interval `[v1,v2]` ``(x < v1)|(x > v2)``. +The boundaries `v1` and `v2` can be given in either order. + """ + if v2 < v1: + (v1, v2) = (v2, v1) + xf = filled(x) + condition = (xf < v1) | (xf > v2) + return masked_where(condition, x, copy=copy) + +# +def masked_object(x, value, copy=True): + """Masks the array `x` where the data are exactly equal to `value`. +This function is suitable only for `object` arrays: for floating point, +please use `masked_values` instead. +The mask is set to `nomask` if posible. + +:parameter copy (Boolean, *[True]*): Returns a copy of `x` if true. """ + if isMaskedArray(x): + condition = umath.equal(x._data, value) + mask = x._mask + else: + condition = umath.equal(fromnumeric.asarray(x), value) + mask = nomask + mask = mask_or(mask, make_mask(condition, small_mask=True)) + return masked_array(x, mask=mask, copy=copy, fill_value=value) + +def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True): + """Masks the array `x` where the data are approximately equal to `value` +(that is, ``abs(x - value) <= atol+rtol*abs(value)``). +Suitable only for floating points. For integers, please use `masked_equal`. +The mask is set to `nomask` if posible. + +:Parameters: + - `rtol` (Float, *[1e-5]*): Tolerance parameter. + - `atol` (Float, *[1e-8]*): Tolerance parameter. + - `copy` (boolean, *[False]*) : Returns a copy of `x` if True. + """ + abs = umath.absolute + xnew = filled(x, value) + if issubclass(xnew.dtype.type, numeric.floating): + condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value)) + try: + mask = x._mask + except AttributeError: + mask = nomask + else: + condition = umath.equal(xnew, value) + mask = nomask + mask = mask_or(mask, make_mask(condition, small_mask=True)) + return masked_array(xnew, mask=mask, copy=copy, fill_value=value) + +#####-------------------------------------------------------------------------- +#---- --- Printing options --- +#####-------------------------------------------------------------------------- +class _MaskedPrintOption: + """Handles the string used to represent missing data in a masked array.""" + def __init__ (self, display): + "Creates the masked_print_option object." + self._display = display + self._enabled = True + + def display(self): + "Displays the string to print for masked values." + return self._display + + def set_display (self, s): + "Sets the string to print for masked values." + self._display = s + + def enabled(self): + "Is the use of the display value enabled?" + return self._enabled + + def enable(self, small_mask=1): + "Set the enabling small_mask to `small_mask`." + self._enabled = small_mask + + def __str__ (self): + return str(self._display) + + __repr__ = __str__ + +#if you single index into a masked location you get this object. +masked_print_option = _MaskedPrintOption('--') + +#####-------------------------------------------------------------------------- +#---- --- MaskedArray class --- +#####-------------------------------------------------------------------------- +#def _getoptions(a_out, a_in): +# "Copies standards options of a_in to a_out." +# for att in ['] +class _mathmethod(object): + """Defines a wrapper for arithmetic methods. +Instead of directly calling a ufunc, the corresponding method of the `array._data` +object is called instead. + """ + def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): + """ +:Parameters: + - `methodname` (String) : Method name. + - `fill_self` (Float *[0]*) : Fill value for the instance. + - `fill_other` (Float *[0]*) : Fill value for the target. + - `domain` (Domain object *[None]*) : Domain of non-validity. + """ + self.methodname = methodname + self.fill_self = fill_self + self.fill_other = fill_other + self.domain = domain + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self.methodname).__doc__ + except: + return getattr(ndarray, self.methodname).__doc__ + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, other, *args): + "Execute the call behavior." + instance = self.obj + m_self = instance._mask + m_other = getmask(other) + base = instance.filled(self.fill_self) + target = filled(other, self.fill_other) + if self.domain is not None: + # We need to force the domain to a ndarray only. + if self.fill_other > self.fill_self: + domain = self.domain(base, target) + else: + domain = self.domain(target, base) + if domain.any(): + #If `other` is a subclass of ndarray, `filled` must have the + # same subclass, else we'll lose some info. + #The easiest then is to fill `target` instead of creating + # a pure ndarray. + #Oh, and we better make a copy! + if isinstance(other, ndarray): + # We don't want to modify other: let's copy target, then + target = target.copy() + target[fromnumeric.asarray(domain)] = self.fill_other + else: + target = numeric.where(fromnumeric.asarray(domain), + self.fill_other, target) + m_other = mask_or(m_other, domain) + m = mask_or(m_self, m_other) + method = getattr(base, self.methodname) + result = method(target, *args).view(type(instance)) + try: + result._mask = m + except AttributeError: + if m: + result = masked + return result +#............................................................................... +class _arraymethod(object): + """Defines a wrapper for basic array methods. +Upon call, returns a masked array, where the new `_data` array is the output +of the corresponding method called on the original `_data`. + +If `onmask` is True, the new mask is the output of the method calld on the initial mask. +If `onmask` is False, the new mask is just a reference to the initial mask. + +:Parameters: + `funcname` : String + Name of the function to apply on data. + `onmask` : Boolean *[True]* + Whether the mask must be processed also (True) or left alone (False). + """ + def __init__(self, funcname, onmask=True): + self._name = funcname + self._onmask = onmask + self.obj = None + self.__doc__ = self.getdoc() + # + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + methdoc = getattr(ndarray, self._name, None) + methdoc = getattr(numpy, self._name, methdoc) +# methdoc = getattr(MaskedArray, self._name, methdoc) + if methdoc is not None: + return methdoc.__doc__ +# try: +# return getattr(MaskedArray, self._name).__doc__ +# except: +# try: +# return getattr(numpy, self._name).__doc__ +# except: +# return getattr(ndarray, self._name).__doc + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__(self, *args, **params): + methodname = self._name + data = self.obj._data + mask = self.obj._mask + cls = type(self.obj) + result = getattr(data, methodname)(*args, **params).view(cls) + if result.ndim: + if not self._onmask: + result._mask = mask + elif mask is not nomask: + result.__setmask__(getattr(mask, methodname)(*args, **params)) + return result +#.......................................................... + +class flatiter(object): + "Defines an interator." + def __init__(self, ma): + self.ma = ma + self.ma_iter = numpy.asarray(ma).flat + + if ma._mask is nomask: + self.maskiter = None + else: + self.maskiter = ma._mask.flat + + def __iter__(self): + return self + + ### This won't work is ravel makes a copy + def __setitem__(self, index, value): + a = self.ma.ravel() + a[index] = value + + def next(self): + d = self.ma_iter.next() + if self.maskiter is not None and self.maskiter.next(): + d = masked + return d + + +class MaskedArray(numeric.ndarray): + """Arrays with possibly masked values. +Masked values of True exclude the corresponding element from any computation. + +Construction: + x = array(data, dtype=None, copy=True, order=False, + mask = nomask, fill_value=None, small_mask=True) + +If copy=False, every effort is made not to copy the data: +If `data` is a MaskedArray, and argument mask=nomask, then the candidate data +is `data._data` and the mask used is `data._mask`. +If `data` is a numeric array, it is used as the candidate raw data. +If `dtype` is not None and is different from data.dtype.char then a data copy is required. +Otherwise, the candidate is used. + +If a data copy is required, the raw (unmasked) data stored is the result of: +numeric.array(data, dtype=dtype.char, copy=copy) + +If `mask` is `nomask` there are no masked values. +Otherwise mask must be convertible to an array of booleans with the same shape as x. +If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`. +Otherwise, the mask is not compressed. + +fill_value is used to fill in masked values when necessary, such as when +printing and in method/function filled(). +The fill_value is not used for computation within this module. + """ + __array_priority__ = 10.1 + _defaultmask = nomask + _defaulthardmask = False + _baseclass = numeric.ndarray + def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None, + keep_mask=True, small_mask=True, hard_mask=False, flag=None, + **options): + """array(data, dtype=None, copy=True, mask=nomask, fill_value=None) + +If `data` is already a ndarray, its dtype becomes the default value of dtype. + """ + if flag is not None: + warnings.warn("The flag 'flag' is now called 'small_mask'!", + DeprecationWarning) + small_mask = flag + # Process data............ + _data = numeric.array(data, dtype=dtype, copy=copy, subok=True) + _baseclass = getattr(_data, '_baseclass', type(_data)) + _data = _data.view(cls) + # Pricess mask ........... + # Backwards compat + if hasattr(data,'_mask') and not isinstance(data, ndarray): + _data._mask = data._mask + _data._sharedmask = True + if mask is nomask: + if _data._hasmasked: + if copy: + _data._masklayer = data._masklayer.copy() + _data._sharedmask = False + if not keep_mask: + _data._masklayer.flat = False + _data._hasmasked = False +# else; +# _data._mask = data._mask + else: + mask = numeric.array(mask, dtype=MaskType, copy=copy) + if mask.shape != _data.shape: + (nd, nm) = (_data.size, mask.size) + if nm == 1: + mask = numeric.resize(mask, _data.shape) + elif nm == nd: + mask = fromnumeric.reshape(mask, _data.shape) + else: + msg = "Mask and data not compatible: data size is %i, "+\ + "mask size is %i." + raise MAError, msg % (nd, nm) + if not (_data._hasmasked and keep_mask): + _data._masklayer = mask + _data._hasmasked = True + _data._sharedmask = True + else: + _data._masklayer = _data._masklayer.copy() + _data._mask.__ior__(mask) + _data._sharedmask = False + _data._hasmasked = True + # Process extra options .. + # Update fille_value + _data._fill_value = getattr(data, '_fill_value', fill_value) + if _data._fill_value is None: + _data._fill_value = default_fill_value(_data) + _data._hardmask = hard_mask + _data._smallmask = small_mask + _data._baseclass = _baseclass + return _data + #........................ + def __array_finalize__(self,obj): + """Finalizes the masked array. + """ + # Finalize mask ............... + self._masklayer = getattr(obj, '_masklayer', nomask) + self._hasmasked = getattr(obj, '_hasmasked', False) + if self._masklayer is nomask: + self._masklayer = numeric.zeros(obj.shape, dtype=MaskType) + self._hasmasked = False +# if not self.shape: +# self._mask.shape = obj.shape +# else: +# self._mask.shape = self.shape + if self._hasmasked: + self._masklayer.shape = self.shape +# except ValueError: +# self._mask = nomask + # Get the remaining options ... + self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask) + self._smallmask = getattr(obj, '_smallmask', True) + self._sharedmask = True + self._baseclass = getattr(obj, '_baseclass', type(obj)) + self._fill_value = getattr(obj, '_fill_value', None) + return + #.................................. + def __array_wrap__(self, obj, context=None): + """Special hook for ufuncs. +Wraps the numpy array and sets the mask according to context. + """ + result = obj.view(type(self)) + result._masklayer = self._masklayer.copy() + result._hasmasked = self._hasmasked +# result._hasmasked = result._masklayer.any() + #.......... + if context is not None: + (func, args, out_index) = context + m = reduce(mask_or, [getmask(arg) for arg in args]) + # Get domain mask + domain = ufunc_domain.get(func, None) + if domain is not None: + if len(args) > 2: + d = reduce(domain, args) + else: + d = domain(*args) + if m is nomask: + if d is not nomask: + result._masklayer.flat = d + result._hasmasked = True + else: + result._masklayer |= d + result._hasmasked |= d.any() + else: + result._masklayer.__ior__(m) + result._hasmasked = m.any() + if result._masklayer.size == 1 and result._masklayer.item(): + return masked + #.... + result._fill_value = self._fill_value + result._hardmask = self._hardmask + result._smallmask = self._smallmask + result._baseclass = self._baseclass + return result + #............................................. + def __getitem__(self, indx): + """x.__getitem__(y) <==> x[y] +Returns the item described by i. Not a copy as in previous versions. + """ + # This test is useful, but we should keep things light... +# if getmask(indx) is not nomask: +# msg = "Masked arrays must be filled before they can be used as indices!" +# raise IndexError, msg + # super() can't work here if the underlying data is a matrix... + result = (self._data).__getitem__(indx) + m = self._masklayer[indx] + if hasattr(result, 'shape') and len(result.shape) > 0: + # Not a scalar: make sure that dout is a MA + result = result.view(type(self)) + result.__setmask__(m) + elif not m.shape and m: + return masked + return result + #........................ + def __setitem__(self, indx, value): + """x.__setitem__(i, y) <==> x[i]=y +Sets item described by index. If value is masked, masks those locations. + """ + if self is masked: + raise MAError, 'Cannot alter the masked element.' +# if getmask(indx) is not nomask: +# msg = "Masked arrays must be filled before they can be used as indices!" +# raise IndexError, msg + #.... + if value is masked: +# if self._sharedmask: +# slef.unshare_mask() + self._masklayer[indx] = True + self._hasmasked = True + return + #.... + dval = numeric.asarray(value).astype(self.dtype) + valmask = getmask(value) + + if not self._hasmasked: + if valmask is not nomask: + self._masklayer[indx] = valmask + self._hasmasked = True + elif not self._hardmask: + self.unshare_mask() + if valmask is nomask: + self._masklayer[indx] = False + self._hasmasked = self._masklayer.any() + else: + self._masklayer[indx] = valmask + self._hasmasked = True + elif hasattr(indx, 'dtype') and (indx.dtype==bool_): + indx = indx * umath.logical_not(self._masklayer) +# elif isinstance(index, int): + else: + mindx = mask_or(self._masklayer[indx], valmask, copy=True) + dindx = self._data[indx] + if dindx.size > 1: + dindx[~mindx] = dval + elif mindx is nomask: + dindx = dval + dval = dindx + self._masklayer[indx] = mindx + # Set data .......... + #dval = filled(value).astype(self.dtype) + ndarray.__setitem__(self._data,indx,dval) +# #..... +# if m is nomask or not m.any(): +# self._mask = nomask +# else: +# self._mask = m + #............................................ + def __getslice__(self, i, j): + """x.__getslice__(i, j) <==> x[i:j] +Returns the slice described by i, j. +The use of negative indices is not supported.""" + return self.__getitem__(slice(i,j)) + #........................ + def __setslice__(self, i, j, value): + """x.__setslice__(i, j, value) <==> x[i:j]=value +Sets a slice i:j to `value`. +If `value` is masked, masks those locations.""" + self.__setitem__(slice(i,j), value) + #............................................ + def __setmask__(self, mask): + newmask = make_mask(mask, copy=False, small_mask=self._smallmask) +# self.unshare_mask() + if self._hardmask: + if newmask is not nomask: + self.unshare_mask() + self._masklayer.__ior__(newmask) + self._hasmasked = True + else: +# if self._hasmasked and (mask is nomask): +# self.unshare_mask() + self._masklayer.flat = newmask + self._hasmasked = newmask.any() + if self._masklayer.shape: + self._masklayer.shape = self.shape + _set_mask = __setmask__ + + def _get_mask(self): + """Returns the current mask.""" + if not self._hasmasked and self._smallmask: + return nomask + return self._masklayer + +# def _set_mask(self, mask): +# """Sets the mask to `mask`.""" +# mask = make_mask(mask, copy=False, small_mask=self._smallmask) +# if mask is not nomask: +# if mask.size != self.size: +# raise ValueError, "Inconsistent shape between data and mask!" +# if mask.shape != self.shape: +# mask.shape = self.shape +# self._mask = mask +# else: +# self._mask = nomask + mask = _mask = property(fget=_get_mask, fset=__setmask__, doc="Mask") + #............................................ + def harden_mask(self): + "Forces the mask to hard." + self._hardmask = True + + def soften_mask(self): + "Forces the mask to soft." + self._hardmask = False + + def unshare_mask(self): + if self._sharedmask: + self._masklayer = self._masklayer.copy() + self._sharedmask = False + + #............................................ + def _get_data(self): + "Returns the current data (as a view of the original underlying data)>" + return self.view(self._baseclass) + _data = property(fget=_get_data) + #............................................ + def _get_flat(self): + """Calculates the flat value. + """ + return flatiter(self) + # + def _set_flat (self, value): + "x.flat = value" + y = self.ravel() + y[:] = value + # + flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") + #............................................ + def get_fill_value(self): + "Returns the filling value." + if self._fill_value is None: + self._fill_value = default_fill_value(self) + return self._fill_value + + def set_fill_value(self, value=None): + """Sets the filling value to `value`. +If None, uses the default, based on the data type.""" + if value is None: + value = default_fill_value(self) + self._fill_value = value + + fill_value = property(fget=get_fill_value, fset=set_fill_value, + doc="Filling value") + + def filled(self, fill_value=None): + """Returns an array of the same class as `_data`, + with masked values filled with `fill_value`. +Subclassing is preserved. + +If `fill_value` is None, uses self.fill_value. + """ + m = self._mask + if m is nomask: + return self._data + # + if fill_value is None: + fill_value = self.fill_value + # + if self is masked_singleton: + result = numeric.asanyarray(fill_value) + else: + result = self._data.copy() + try: + result[m] = fill_value + except (TypeError, AttributeError): + fill_value = numeric.array(fill_value, dtype=object) + d = result.astype(object) + result = fromnumeric.choose(m, (d, fill_value)) + except IndexError: + #ok, if scalar + if self._data.shape: + raise + elif m: + result = numeric.array(fill_value, dtype=self.dtype) + else: + result = self._data + return result + + def compressed(self): + "A 1-D array of all the non-masked data." + d = self.ravel() + if self._mask is nomask: + return d + else: + return d[numeric.logical_not(d._mask)] + #............................................ + def __str__(self): + """x.__str__() <==> str(x) +Calculates the string representation, using masked for fill if it is enabled. +Otherwise, fills with fill value. + """ + if masked_print_option.enabled(): + f = masked_print_option + if self is masked: + return str(f) + m = self._mask + if m is nomask: + res = self._data + else: + if m.shape == () and m: + return str(f) + # convert to object array to make filled work +#CHECK: the two lines below seem more robust than the self._data.astype +# res = numeric.empty(self._data.shape, object_) +# numeric.putmask(res,~m,self._data) + res = self._data.astype("|O8") + res[m] = f + else: + res = self.filled(self.fill_value) + return str(res) + + def __repr__(self): + """x.__repr__() <==> repr(x) +Calculates the repr representation, using masked for fill if it is enabled. +Otherwise fill with fill value. + """ + with_mask = """\ +masked_%(name)s(data = + %(data)s, + mask = + %(mask)s, + fill_value=%(fill)s) +""" + with_mask1 = """\ +masked_%(name)s(data = %(data)s, + mask = %(mask)s, + fill_value=%(fill)s) +""" + n = len(self.shape) + name = repr(self._data).split('(')[0] + if n <= 1: + return with_mask1 % { + 'name': name, + 'data': str(self), + 'mask': str(self._mask), + 'fill': str(self.fill_value), + } + return with_mask % { + 'name': name, + 'data': str(self), + 'mask': str(self._mask), + 'fill': str(self.fill_value), + } + #............................................ + def __iadd__(self, other): + "Adds other to self in place." + ndarray.__iadd__(self._data,other) + m = getmask(other) + if m is not nomask: + self._masklayer += m + self._hasmasked = True +# if m is not nomask: +# self._masklayer |= m +# self._hasmasked = True + return self + #.... + def __isub__(self, other): + "Subtracts other from self in place." + ndarray.__isub__(self._data,other) + m = getmask(other) +# if not self._hasmasked: +# self._masklayer.flat = m +# elif m is not nomask: +# self._masklayer += m + if m is not nomask: + self._masklayer += m + self._hasmasked = True + return self + #.... + def __imul__(self, other): + "Multiplies self by other in place." + ndarray.__imul__(self._data,other) + m = getmask(other) +# if not self._hasmasked: +# self._masklayer.flat = m +# elif m is not nomask: +# self._masklayer += m + if m is not nomask: + self._masklayer += m + self._hasmasked = True + return self + #.... + def __idiv__(self, other): + "Divides self by other in place." + dom_mask = domain_safe_divide().__call__(self, filled(other,1)) + other_mask = getmask(other) + new_mask = mask_or(other_mask, dom_mask) + ndarray.__idiv__(self._data, other) + self._mask = mask_or(self._mask, new_mask) + return self + #.... + __add__ = _mathmethod('__add__') + __radd__ = _mathmethod('__add__') + __sub__ = _mathmethod('__sub__') + __rsub__ = _mathmethod('__rsub__') + __pow__ = _mathmethod('__pow__') + __mul__ = _mathmethod('__mul__', 1, 1) + __rmul__ = _mathmethod('__mul__', 1, 1) + __div__ = _mathmethod('__div__', 0, 1, domain_safe_divide()) + __rdiv__ = _mathmethod('__rdiv__', 1, 0, domain_safe_divide()) + __truediv__ = _mathmethod('__truediv__', 0, 1, domain_safe_divide()) + __rtruediv__ = _mathmethod('__rtruediv__', 1, 0, domain_safe_divide()) + __floordiv__ = _mathmethod('__floordiv__', 0, 1, domain_safe_divide()) + __rfloordiv__ = _mathmethod('__rfloordiv__', 1, 0, domain_safe_divide()) + __eq__ = _mathmethod('__eq__') + __ne__ = _mathmethod('__ne__') + __le__ = _mathmethod('__le__') + __lt__ = _mathmethod('__lt__') + __ge__ = _mathmethod('__ge__') + __gt__ = _mathmethod('__gt__') + + #............................................ + def __float__(self): + "Converts self to float." + if self._mask is not nomask: + warnings.warn("Warning: converting a masked element to nan.") + return numpy.nan + #raise MAError, 'Cannot convert masked element to a Python float.' + return float(self.item()) + + def __int__(self): + "Converts self to int." + if self._mask is not nomask: + raise MAError, 'Cannot convert masked element to a Python int.' + return int(self.item()) + #............................................ + def count(self, axis=None): + """Counts the non-masked elements of the array along a given axis, +and returns a masked array where the mask is True where all data are masked. +If `axis` is None, counts all the non-masked elements, and returns either a +scalar or the masked singleton.""" + m = self._mask + s = self.shape + ls = len(s) + if m is nomask: + if ls == 0: + return 1 + if ls == 1: + return s[0] + if axis is None: + return self.size + else: + n = s[axis] + t = list(s) + del t[axis] + return numeric.ones(t) * n + n1 = fromnumeric.size(m, axis) + n2 = m.astype(int_).sum(axis) + if axis is None: + return (n1-n2) + else: + return masked_array(n1 - n2) + #............................................ +# def _get_shape(self): +# "Returns the current shape." +# return self._data.shape +# # +# def _set_shape (self, newshape): +# "Sets the array's shape." +# self._data.shape = newshape +# if self._mask is not nomask: +# #self._mask = self._mask.copy() +# self._mask.shape = newshape +# # +# shape = property(fget=_get_shape, fset=_set_shape, +# doc="Shape of the array, as a tuple.") + # + def reshape (self, *s): + """Reshapes the array to shape s. +Returns a new masked array. +If you want to modify the shape in place, please use `a.shape = s`""" + # TODO: Do we keep super, or reshape _data and take a view ? + result = super(MaskedArray, self).reshape(*s) + result._masklayer = self._masklayer.reshape(*s) + return result + # + repeat = _arraymethod('repeat') + # + def resize(self, newshape, refcheck=True, order=False): + """Attempts to modify size and shape of self inplace. + The array must own its own memory and not be referenced by other arrays. + Returns None. + """ + try: + self._data.resize(newshape, refcheck, order) + if self.mask is not nomask: + self._mask.resize(newshape, refcheck, order) + except ValueError: + raise ValueError("Cannot resize an array that has been referenced " + "or is referencing another array in this way.\n" + "Use the resize function.") + return None + # + flatten = _arraymethod('flatten') + # + def put(self, indices, values, mode='raise'): + """Sets storage-indexed locations to corresponding values. +a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices. +`values` can be scalar or an array shorter than indices, and it will be repeated, +if necessary. +If `values` has some masked values, the initial mask is updated in consequence, +else the corresponding values are unmasked. + """ + m = self._mask + # Hard mask: Get rid of the values/indices that fall on masked data + if self._hardmask and self._mask is not nomask: + mask = self._mask[indices] + indices = numeric.asarray(indices) + values = numeric.asanyarray(values) + values.resize(indices.shape) + indices = indices[~mask] + values = values[~mask] + #.... + self._data.put(indices, values, mode=mode) + #.... + if m is nomask: + m = getmask(values) + else: + m = m.copy() + if getmask(values) is nomask: + m.put(indices, False, mode=mode) + else: + m.put(indices, values._mask, mode=mode) + m = make_mask(m, copy=False, small_mask=True) + self._mask = m + #............................................ + def ids (self): + """Return the address of the data and mask areas.""" + return (self.ctypes.data, self._mask.ctypes.data) + #............................................ + def all(self, axis=None, out=None): + """a.all(axis) returns True if all entries along the axis are True. + Returns False otherwise. If axis is None, uses the flatten array. + Masked data are considered as True during computation. + Outputs a masked array, where the mask is True if all data are masked along the axis. + Note: the out argument is not really operational... + """ + d = self.filled(True).all(axis=axis, out=out).view(type(self)) + if d.ndim > 0: + d.__setmask__(self._mask.all(axis)) + return d + + def any(self, axis=None, out=None): + """a.any(axis) returns True if some or all entries along the axis are True. + Returns False otherwise. If axis is None, uses the flatten array. + Masked data are considered as False during computation. + Outputs a masked array, where the mask is True if all data are masked along the axis. + Note: the out argument is not really operational... + """ + d = self.filled(False).any(axis=axis, out=out).view(type(self)) + if d.ndim > 0: + d.__setmask__(self._mask.all(axis)) + return d + + def nonzero(self): + """a.nonzero() returns a tuple of arrays + + Returns a tuple of arrays, one for each dimension of a, + containing the indices of the non-zero elements in that + dimension. The corresponding non-zero values can be obtained + with + a[a.nonzero()]. + + To group the indices by element, rather than dimension, use + transpose(a.nonzero()) + instead. The result of this is always a 2d array, with a row for + each non-zero element.""" + return numeric.asarray(self.filled(0)).nonzero() + #............................................ + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): + """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None) +Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`. + """ + # TODO: What are we doing with `out`? + m = self._mask + if m is nomask: + result = super(MaskedArray, self).trace(offset=offset, axis1=axis1, + axis2=axis2, out=out) + return result.astype(dtype) + else: + D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2) + return D.astype(dtype).sum(axis=None) + #............................................ + def sum(self, axis=None, dtype=None): + """a.sum(axis=None, dtype=None) +Sums the array `a` over the given axis `axis`. +Masked values are set to 0. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: + mask = nomask + else: + mask = self._mask.all(axis) + if (not mask.ndim) and mask: + return masked + result = self.filled(0).sum(axis, dtype=dtype).view(type(self)) + if result.ndim > 0: + result.__setmask__(mask) + return result + + def cumsum(self, axis=None, dtype=None): + """a.cumprod(axis=None, dtype=None) +Returns the cumulative sum of the elements of array `a` along the given axis `axis`. +Masked values are set to 0. +If `axis` is None, applies to a flattened version of the array. + """ + result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self)) + result.__setmask__(self.mask) + return result + + def prod(self, axis=None, dtype=None): + """a.prod(axis=None, dtype=None) +Returns the product of the elements of array `a` along the given axis `axis`. +Masked elements are set to 1. +If `axis` is None, applies to a flattened version of the array. + """ + if self._mask is nomask: + mask = nomask + else: + mask = self._mask.all(axis) + if (not mask.ndim) and mask: + return masked + result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self)) + if result.ndim: + result.__setmask__(mask) + return result + product = prod + + def cumprod(self, axis=None, dtype=None): + """a.cumprod(axis=None, dtype=None) +Returns the cumulative product of ethe lements of array `a` along the given axis `axis`. +Masked values are set to 1. +If `axis` is None, applies to a flattened version of the array. + """ + result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self)) + result.__setmask__(self.mask) + return result + + def mean(self, axis=None, dtype=None): + """a.mean(axis=None, dtype=None) + + Averages the array over the given axis. If the axis is None, + averages over all dimensions of the array. Equivalent to + + a.sum(axis, dtype) / size(a, axis). + + The optional dtype argument is the data type for intermediate + calculations in the sum. + + Returns a masked array, of the same class as a. + """ + if self._mask is nomask: + return super(MaskedArray, self).mean(axis=axis, dtype=dtype) + else: + dsum = self.sum(axis=axis, dtype=dtype) + cnt = self.count(axis=axis) + return dsum*1./cnt + + def anom(self, axis=None, dtype=None): + """a.anom(axis=None, dtype=None) + Returns the anomalies, or deviation from the average. + """ + m = self.mean(axis, dtype) + if not axis: + return (self - m) + else: + return (self - expand_dims(m,axis)) + + def var(self, axis=None, dtype=None): + """a.var(axis=None, dtype=None) +Returns the variance, a measure of the spread of a distribution. + +The variance is the average of the squared deviations from the mean, +i.e. var = mean((x - x.mean())**2). + """ + if self._mask is nomask: + # TODO: Do we keep super, or var _data and take a view ? + return super(MaskedArray, self).var(axis=axis, dtype=dtype) + else: + cnt = self.count(axis=axis) + danom = self.anom(axis=axis, dtype=dtype) + danom *= danom + dvar = danom.sum(axis) / cnt +# dvar /= cnt + if axis is not None: + dvar._mask = mask_or(self._mask.all(axis), (cnt==1)) + return dvar + + def std(self, axis=None, dtype=None): + """a.std(axis=None, dtype=None) +Returns the standard deviation, a measure of the spread of a distribution. + +The standard deviation is the square root of the average of the squared +deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)). + """ + dvar = self.var(axis,dtype) + if axis is not None or dvar is not masked: + dvar = sqrt(dvar) + return dvar + #............................................ + def argsort(self, axis=None, fill_value=None, kind='quicksort', + order=None): + """Returns an array of indices that sort 'a' along the specified axis. + Masked values are filled beforehand to `fill_value`. + If `fill_value` is None, uses the default for the data type. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `kind` : String *['quicksort']* + Sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort' + + Returns: array of indices that sort 'a' along the specified axis. + + This method executes an indirect sort along the given axis using the + algorithm specified by the kind keyword. It returns an array of indices of + the same shape as 'a' that index data along the given axis in sorted order. + + The various sorts are characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order. The three + available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is not + along the last axis. Consequently, sorts along the last axis are faster and use + less space than sorts along other axis. + """ + if fill_value is None: + fill_value = default_fill_value(self) + d = self.filled(fill_value) + return d.argsort(axis=axis, kind=kind, order=order) + #........................ + def argmin(self, axis=None, fill_value=None): + """Returns a ndarray of indices for the minimum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = minimum_fill_value(self) + d = self.filled(fill_value) + return d.argmin(axis) + #........................ + def argmax(self, axis=None, fill_value=None): + """Returns the array of indices for the maximum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = maximum_fill_value(self._data) + d = self.filled(fill_value) + return d.argmax(axis) + + def sort(self, axis=-1, kind='quicksort', order=None, + endwith=True, fill_value=None): + """ + Sort a along the given axis. + + Keyword arguments: + + axis -- axis to be sorted (default -1) + kind -- sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort'. + order -- If a has fields defined, then the order keyword can be the + field name to sort on or a list (or tuple) of field names + to indicate the order that fields should be used to define + the sort. + endwith--Boolean flag indicating whether missing values (if any) should + be forced in the upper indices (at the end of the array) or + lower indices (at the beginning). + + Returns: None. + + This method sorts 'a' in place along the given axis using the algorithm + specified by the kind keyword. + + The various sorts may characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order and is most + useful when used with argsort where the key might differ from the items + being sorted. The three available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is + not along the last axis. Consequently, sorts along the last axis are faster + and use less space than sorts along other axis. + + """ + if fill_value is None: + if endwith: + filler = minimum_fill_value(self) + else: + filler = maximum_fill_value(self) + else: + filler = fill_value + indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order) + self[:] = self[indx] + return + #............................................ + def min(self, axis=None, fill_value=None): + """Returns the minimum/a along the given axis. +If `axis` is None, applies to the flattened array. Masked values are filled +with `fill_value` during processing. If `fill_value is None, it is set to the +maximum_fill_value corresponding to the data type.""" + mask = self._mask + # Check all/nothing case ...... + if mask is nomask: + return super(MaskedArray, self).min(axis=axis) + elif (not mask.ndim) and mask: + return masked + # Get the mask ................ + if axis is None: + mask = umath.logical_and.reduce(mask.flat) + else: + mask = umath.logical_and.reduce(mask, axis=axis) + # Get the fil value ........... + if fill_value is None: + fill_value = minimum_fill_value(self) + # Get the data ................ + result = self.filled(fill_value).min(axis=axis).view(type(self)) + if result.ndim > 0: + result._mask = mask + return result + #........................ + def max(self, axis=None, fill_value=None): + """Returns the maximum/a along the given axis. +If `axis` is None, applies to the flattened array. Masked values are filled +with `fill_value` during processing. If `fill_value is None, it is set to the +maximum_fill_value corresponding to the data type.""" + mask = self._mask + # Check all/nothing case ...... + if mask is nomask: + return super(MaskedArray, self).max(axis=axis) + elif (not mask.ndim) and mask: + return masked + # Check teh mask .............. + if axis is None: + mask = umath.logical_and.reduce(mask.flat) + else: + mask = umath.logical_and.reduce(mask, axis=axis) + # Get the fill value .......... + if fill_value is None: + fill_value = maximum_fill_value(self) + # Get the data ................ + result = self.filled(fill_value).max(axis=axis).view(type(self)) + if result.ndim > 0: + result._mask = mask + return result + #........................ + def ptp(self, axis=None, fill_value=None): + """Returns the visible data range (max-min) along the given axis. +If the axis is `None`, applies on a flattened array. Masked values are filled +with `fill_value` for processing. If `fill_value` is None, the maximum is uses +the maximum default, the minimum uses the minimum default.""" + return self.max(axis, fill_value) - self.min(axis, fill_value) + + # Array methods --------------------------------------- + conj = conjugate = _arraymethod('conjugate') + copy = _arraymethod('copy') + diagonal = _arraymethod('diagonal') + take = _arraymethod('take') + ravel = _arraymethod('ravel') + transpose = _arraymethod('transpose') + T = property(fget=lambda self:self.transpose()) + swapaxes = _arraymethod('swapaxes') + clip = _arraymethod('clip', onmask=False) + compress = _arraymethod('compress') + copy = _arraymethod('copy') + squeeze = _arraymethod('squeeze') + #-------------------------------------------- + def tolist(self, fill_value=None): + """Copies the data portion of the array to a hierarchical python list and + returns that list. Data items are converted to the nearest compatible Python + type. Masked values are filled with `fill_value`""" + return self.filled(fill_value).tolist() + #........................ + def tostring(self, fill_value=None): + """a.tostring(order='C', fill_value=None) -> raw copy of array data as a Python string. + + Keyword arguments: + order : order of the data item in the copy {"C","F","A"} (default "C") + fill_value : value used in lieu of missing data + + Construct a Python string containing the raw bytes in the array. The order + of the data in arrays with ndim > 1 is specified by the 'order' keyword and + this keyword overrides the order of the array. The + choices are: + + "C" -- C order (row major) + "Fortran" -- Fortran order (column major) + "Any" -- Current order of array. + None -- Same as "Any" + + Masked data are filled with fill_value. If fill_value is None, the data-type- + dependent default is used.""" + return self.filled(fill_value).tostring() + #-------------------------------------------- + # Backwards Compatibility. Heck... + @property + def data(self): + """Returns the `_data` part of the MaskedArray. +You should really use `_data` instead...""" + return self._data + def raw_data(self): + """Returns the `_data` part of the MaskedArray. +You should really use `_data` instead...""" + return self._data + +##.............................................................................. + + + +#class _arithmethods: +# """Defines a wrapper for arithmetic methods. +#Instead of directly calling a ufunc, the corresponding method of the `array._data` +#object is called instead. +# """ +# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): +# """ +#:Parameters: +# - `methodname` (String) : Method name. +# - `fill_self` (Float *[0]*) : Fill value for the instance. +# - `fill_other` (Float *[0]*) : Fill value for the target. +# - `domain` (Domain object *[None]*) : Domain of non-validity. +# """ +# self.methodname = methodname +# self.fill_self = fill_self +# self.fill_other = fill_other +# self.domain = domain +# # +# def __call__ (self, instance, other, *args): +# "Execute the call behavior." +# m_self = instance._mask +# m_other = getmask(other) +# base = filled(instance,self.fill_self) +# target = filled(other, self.fill_other) +# if self.domain is not None: +# # We need to force the domain to a ndarray only. +# if self.fill_other > self.fill_self: +# domain = self.domain(base, target) +# else: +# domain = self.domain(target, base) +# if domain.any(): +# #If `other` is a subclass of ndarray, `filled` must have the +# # same subclass, else we'll lose some info. +# #The easiest then is to fill `target` instead of creating +# # a pure ndarray. +# #Oh, and we better make a copy! +# if isinstance(other, ndarray): +# if target is other: +# # We don't want to modify other: let's copy target, then +# target = target.copy() +# target[:] = numeric.where(fromnumeric.asarray(domain), +# self.fill_other, target) +# else: +# target = numeric.where(fromnumeric.asarray(domain), +# self.fill_other, target) +# m_other = mask_or(m_other, domain) +# m = mask_or(m_self, m_other) +# method = getattr(base, self.methodname) +# return instance.__class__(method(target, *args), mask=m) +# # +# def patch(self): +# """Applies the method `func` from class `method` to MaskedArray""" +# return types.MethodType(self,None,MaskedArray) +#.............................................................................. + +#####-------------------------------------------------------------------------- +#---- --- Shortcuts --- +#####--------------------------------------------------------------------------- +def isMaskedArray(x): + "Is x a masked array, that is, an instance of MaskedArray?" + return isinstance(x, MaskedArray) +isarray = isMaskedArray +isMA = isMaskedArray #backward compatibility +#masked = MaskedArray(0, int, mask=1) +masked_singleton = MaskedArray(0, dtype=int_, mask=True) +masked = masked_singleton + +masked_array = MaskedArray +def array(data, dtype=None, copy=False, order=False, mask=nomask, + keep_mask=True, small_mask=True, hard_mask=None, fill_value=None): + """array(data, dtype=None, copy=True, order=False, mask=nomask, + keep_mask=True, small_mask=True, fill_value=None) +Acts as shortcut to MaskedArray, with options in a different order for convenience. +And backwards compatibility... + """ + #TODO: we should try to put 'order' somwehere + return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, + keep_mask=keep_mask, small_mask=small_mask, + hard_mask=hard_mask, fill_value=fill_value) + +def is_masked(x): + """Returns whether x has some masked values.""" + m = getmask(x) + if m is nomask: + return False + elif m.any(): + return True + return False + + +#####-------------------------------------------------------------------------- +#---- --- Patch methods --- +#####-------------------------------------------------------------------------- +#class _arraymethod: +# """Defines a wrapper for basic array methods. +#Upon call, returns a masked array, where the new `_data` array is the output +#of the corresponding method called on the original `_data`. +# +#If `onmask` is True, the new mask is the output of the method calld on the initial mask. +#If `onmask` is False, the new mask is just a reference to the initial mask. +# +#:Parameters: +# `funcname` : String +# Name of the function to apply on data. +# `onmask` : Boolean *[True]* +# Whether the mask must be processed also (True) or left alone (False). +# """ +# def __init__(self, funcname, onmask=True): +# self._name = funcname +# self._onmask = onmask +# self.__doc__ = getattr(ndarray, self._name).__doc__ +# def __call__(self, instance, *args, **params): +# methodname = self._name +# (d,m) = (instance._data, instance._mask) +# C = instance.__class__ +# if m is nomask: +# return C(getattr(d,methodname).__call__(*args, **params)) +# elif self._onmask: +# return C(getattr(d,methodname).__call__(*args, **params), +# mask=getattr(m,methodname)(*args, **params) ) +# else: +# return C(getattr(d,methodname).__call__(*args, **params), mask=m) +# +# def patch(self): +# "Adds the new method to MaskedArray." +# return types.MethodType(self, None, MaskedArray) +##...................................... +#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() +#MaskedArray.diagonal = _arraymethod('diagonal').patch() +#MaskedArray.take = _arraymethod('take').patch() +#MaskedArray.ravel = _arraymethod('ravel').patch() +#MaskedArray.transpose = _arraymethod('transpose').patch() +#MaskedArray.T = _arraymethod('transpose').patch() +#MaskedArray.swapaxes = _arraymethod('swapaxes').patch() +#MaskedArray.clip = _arraymethod('clip', onmask=False).patch() +#MaskedArray.compress = _arraymethod('compress').patch() +#MaskedArray.resize = _arraymethod('resize').patch() +#MaskedArray.copy = _arraymethod('copy').patch() + + + +#####--------------------------------------------------------------------------- +#---- --- Extrema functions --- +#####--------------------------------------------------------------------------- +class _extrema_operation(object): + def __call__(self, a, b=None): + "Executes the call behavior." + if b is None: + return self.reduce(a) + return where(self.compare(a, b), a, b) + #......... + def reduce(self, target, axis=None): + """Reduces target along the given axis.""" + m = getmask(target) + if axis is not None: + kargs = { 'axis' : axis } + else: + kargs = {} + + if m is nomask: + t = self.ufunc.reduce(target, **kargs) + else: + target = target.filled(self.fill_value_func(target)).view(type(target)) + t = self.ufunc.reduce(target, **kargs) + m = umath.logical_and.reduce(m, **kargs) + if hasattr(t, '_mask'): + t._mask = m + elif m: + t = masked + return t + #......... + def outer (self, a, b): + "Returns the function applied to the outer product of a and b." + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + m = nomask + else: + ma = getmaskarray(a) + mb = getmaskarray(b) + m = logical_or.outer(ma, mb) + result = self.ufunc.outer(filled(a), filled(b)) + result._mask = m + return result +#............................ +class _minimum_operation(_extrema_operation): + "Object to calculate minima" + def __init__ (self): + """minimum(a, b) or minimum(a) +In one argument case, returns the scalar minimum. + """ + self.ufunc = umath.minimum + self.afunc = amin + self.compare = less + self.fill_value_func = minimum_fill_value +#............................ +class _maximum_operation(_extrema_operation): + "Object to calculate maxima" + def __init__ (self): + """maximum(a, b) or maximum(a) + In one argument case returns the scalar maximum. + """ + self.ufunc = umath.maximum + self.afunc = amax + self.compare = greater + self.fill_value_func = maximum_fill_value +#.......................................................... +def min(array, axis=None, out=None): + """Returns the minima along the given axis. +If `axis` is None, applies to the flattened array.""" + if out is not None: + raise TypeError("Output arrays Unsupported for masked arrays") + if axis is None: + return minimum(array) + else: + return minimum.reduce(array, axis) +#............................ +def max(obj, axis=None, out=None): + """Returns the maxima along the given axis. +If `axis` is None, applies to the flattened array.""" + if out is not None: + raise TypeError("Output arrays Unsupported for masked arrays") + if axis is None: + return maximum(obj) + else: + return maximum.reduce(obj, axis) +#............................. +def ptp(obj, axis=None): + """a.ptp(axis=None) = a.max(axis)-a.min(axis)""" + try: + return obj.max(axis)-obj.min(axis) + except AttributeError: + return max(obj, axis=axis) - min(obj, axis=axis) + + +#####--------------------------------------------------------------------------- +#---- --- Definition of functions from the corresponding methods --- +#####--------------------------------------------------------------------------- +class _frommethod: + """Defines functions from existing MaskedArray methods. +:ivar _methodname (String): Name of the method to transform. + """ + def __init__(self, methodname): + self._methodname = methodname + self.__doc__ = self.getdoc() + def getdoc(self): + "Returns the doc of the function (from the doc of the method)." + try: + return getattr(MaskedArray, self._methodname).__doc__ + except: + return getattr(numpy, self._methodname).__doc__ + def __call__(self, a, *args, **params): + if isinstance(a, MaskedArray): + return getattr(a, self._methodname).__call__(*args, **params) + #FIXME ---- + #As x is not a MaskedArray, we transform it to a ndarray with asarray + #... and call the corresponding method. + #Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2))) + #we end up with a "SystemError: NULL result without error in PyObject_Call" + #A dirty trick is then to call the initial numpy function... + method = getattr(fromnumeric.asarray(a), self._methodname) + try: + return method(*args, **params) + except SystemError: + return getattr(numpy,self._methodname).__call__(a, *args, **params) + +all = _frommethod('all') +anomalies = anom = _frommethod('anom') +any = _frommethod('any') +conjugate = _frommethod('conjugate') +ids = _frommethod('ids') +nonzero = _frommethod('nonzero') +diagonal = _frommethod('diagonal') +maximum = _maximum_operation() +mean = _frommethod('mean') +minimum = _minimum_operation () +product = _frommethod('prod') +ptp = _frommethod('ptp') +ravel = _frommethod('ravel') +repeat = _frommethod('repeat') +std = _frommethod('std') +sum = _frommethod('sum') +swapaxes = _frommethod('swapaxes') +take = _frommethod('take') +var = _frommethod('var') + +#.............................................................................. +def power(a, b, third=None): + """Computes a**b elementwise. + Masked values are set to 1.""" + if third is not None: + raise MAError, "3-argument power not supported." + ma = getmask(a) + mb = getmask(b) + m = mask_or(ma, mb) + fa = filled(a, 1) + fb = filled(b, 1) + if fb.dtype.char in typecodes["Integer"]: + return masked_array(umath.power(fa, fb), m) + md = make_mask((fa < 0), small_mask=1) + m = mask_or(m, md) + if m is nomask: + return masked_array(umath.power(fa, fb)) + else: + fa[m] = 1 + return masked_array(umath.power(fa, fb), m) + +#.............................................................................. +def argsort(a, axis=None, kind='quicksort', order=None, fill_value=None): + """Returns an array of indices that sort 'a' along the specified axis. + Masked values are filled beforehand to `fill_value`. + If `fill_value` is None, uses the default for the data type. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `kind` : String *['quicksort']* + Sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort' + + Returns: array of indices that sort 'a' along the specified axis. + + This method executes an indirect sort along the given axis using the + algorithm specified by the kind keyword. It returns an array of indices of + the same shape as 'a' that index data along the given axis in sorted order. + + The various sorts are characterized by average speed, worst case + performance, need for work space, and whether they are stable. A stable + sort keeps items with the same key in the same relative order. The three + available algorithms have the following properties: + + |------------------------------------------------------| + | kind | speed | worst case | work space | stable| + |------------------------------------------------------| + |'quicksort'| 1 | O(n^2) | 0 | no | + |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | + |'heapsort' | 3 | O(n*log(n)) | 0 | no | + |------------------------------------------------------| + + All the sort algorithms make temporary copies of the data when the sort is not + along the last axis. Consequently, sorts along the last axis are faster and use + less space than sorts along other axis. + """ + if fill_value is None: + fill_value = default_fill_value(a) + d = filled(a, fill_value) + if axis is None: + return d.argsort(kind=kind, order=order) + return d.argsort(axis, kind=kind, order=order) + +def argmin(a, axis=None, fill_value=None): + """Returns the array of indices for the minimum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(a) + d = filled(a, fill_value) + return d.argmin(axis=axis) + +def argmax(a, axis=None, fill_value=None): + """Returns the array of indices for the maximum values of `a` along the + specified axis. + Masked values are treated as if they had the value `fill_value`. + If `fill_value` is None, the default for the data type is used. + Returns a numpy array. + +:Keywords: + `axis` : Integer *[None]* + Axis to be indirectly sorted (default -1) + `fill_value` : var *[None]* + Default filling value. If None, uses the data type default. + """ + if fill_value is None: + fill_value = default_fill_value(a) + try: + fill_value = - fill_value + except: + pass + d = filled(a, fill_value) + return d.argmax(axis=axis) + +def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): + """ + Sort a along the given axis. + +Keyword arguments: + +axis -- axis to be sorted (default -1) +kind -- sorting algorithm (default 'quicksort') + Possible values: 'quicksort', 'mergesort', or 'heapsort'. +order -- If a has fields defined, then the order keyword can be the + field name to sort on or a list (or tuple) of field names + to indicate the order that fields should be used to define + the sort. +endwith--Boolean flag indicating whether missing values (if any) should + be forced in the upper indices (at the end of the array) or + lower indices (at the beginning). + +Returns: None. + +This method sorts 'a' in place along the given axis using the algorithm +specified by the kind keyword. + +The various sorts may characterized by average speed, worst case +performance, need for work space, and whether they are stable. A stable +sort keeps items with the same key in the same relative order and is most +useful when used with argsort where the key might differ from the items +being sorted. The three available algorithms have the following properties: + +|------------------------------------------------------| +| kind | speed | worst case | work space | stable| +|------------------------------------------------------| +|'quicksort'| 1 | O(n^2) | 0 | no | +|'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | +|'heapsort' | 3 | O(n*log(n)) | 0 | no | +|------------------------------------------------------| + +All the sort algorithms make temporary copies of the data when the sort is +not along the last axis. Consequently, sorts along the last axis are faster +and use less space than sorts along other axis. + +""" + a = numeric.asanyarray(a) + if fill_value is None: + if endwith: + filler = minimum_fill_value(a) + else: + filler = maximum_fill_value(a) + else: + filler = fill_value +# return + indx = numpy.indices(a.shape).tolist() + indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order) + return a[indx] + +def compressed(x): + """Returns a compressed version of a masked array (or just the array if it + wasn't masked first).""" + if getmask(x) is None: + return x + else: + return x.compressed() + +def count(a, axis = None): + "Count of the non-masked elements in a, or along a certain axis." + a = masked_array(a) + return a.count(axis) + +def concatenate(arrays, axis=0): + "Concatenates the arrays along the given axis" + d = numeric.concatenate([filled(a) for a in arrays], axis) + rcls = get_masked_subclass(*arrays) + data = d.view(rcls) + for x in arrays: + if getmask(x) is not nomask: + break + else: + return data + dm = numeric.concatenate([getmaskarray(a) for a in arrays], axis) + dm = make_mask(dm, copy=False, small_mask=True) + data._mask = dm + return data + +def expand_dims(x,axis): + """Expand the shape of a by including newaxis before given axis.""" + result = n_expand_dims(x,axis) + if isinstance(x, MaskedArray): + new_shape = result.shape + result = x.view() + result.shape = new_shape + if result._mask is not nomask: + result._mask.shape = new_shape + return result + +#...................................... +def left_shift (a, n): + "Left shift n bits" + m = getmask(a) + if m is nomask: + d = umath.left_shift(filled(a), n) + return masked_array(d) + else: + d = umath.left_shift(filled(a, 0), n) + return masked_array(d, mask=m) + +def right_shift (a, n): + "Right shift n bits" + m = getmask(a) + if m is nomask: + d = umath.right_shift(filled(a), n) + return masked_array(d) + else: + d = umath.right_shift(filled(a, 0), n) + return masked_array(d, mask=m) +#...................................... +def put(a, indices, values, mode='raise'): + """Sets storage-indexed locations to corresponding values. + Values and indices are filled if necessary.""" + # We can't use 'frommethod', the order of arguments is different + try: + return a.put(indices, values, mode=mode) + except AttributeError: + return fromnumeric.asarray(a).put(indices, values, mode=mode) + +def putmask(a, mask, values): #, mode='raise'): + """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true. +If `v` is shorter than `mask`, it will be repeated as necessary. +In particular `v` can be a scalar or length 1 array.""" + # We can't use 'frommethod', the order of arguments is different + try: + return a.putmask(values, mask) + except AttributeError: + return fromnumeric.asarray(a).putmask(values, mask) + +def transpose(a,axes=None): + """Returns a view of the array with dimensions permuted according to axes. +If `axes` is None (default), returns array with dimensions reversed. + """ + #We can't use 'frommethod', as 'transpose' doesn't take keywords + try: + return a.transpose(axes) + except AttributeError: + return fromnumeric.asarray(a).transpose(axes) + +def reshape(a, new_shape): + """Changes the shape of the array `a` to `new_shape`.""" + #We can't use 'frommethod', it whine about some parameters. Dmmit. + try: + return a.reshape(new_shape) + except AttributeError: + return fromnumeric.asarray(a).reshape(new_shape) + +def resize(x, new_shape): + """resize(a,new_shape) returns a new array with the specified shape. + The total size of the original array can be any size. + The new array is filled with repeated copies of a. If a was masked, the new + array will be masked, and the new mask will be a repetition of the old one. + """ + # We can't use _frommethods here, as N.resize is notoriously whiny. + m = getmask(x) + if m is not nomask: + m = fromnumeric.resize(m, new_shape) + result = fromnumeric.resize(x, new_shape).view(get_masked_subclass(x)) + if result.ndim: + result._mask = m + return result + + +#................................................ +def rank(obj): + """Gets the rank of sequence a (the number of dimensions, not a matrix rank) +The rank of a scalar is zero.""" + return fromnumeric.rank(filled(obj)) +# +def shape(obj): + """Returns the shape of `a` (as a function call which also works on nested sequences). + """ + return fromnumeric.shape(filled(obj)) +# +def size(obj, axis=None): + """Returns the number of elements in the array along the given axis, +or in the sequence if `axis` is None. + """ + return fromnumeric.size(filled(obj), axis) +#................................................ + +#####-------------------------------------------------------------------------- +#---- --- Extra functions --- +#####-------------------------------------------------------------------------- +def where (condition, x, y): + """where(condition, x, y) is x where condition is nonzero, y otherwise. + condition must be convertible to an integer array. + Answer is always the shape of condition. + The type depends on x and y. It is integer if both x and y are + the value masked. + """ + fc = filled(not_equal(condition, 0), 0) + xv = filled(x) + xm = getmask(x) + yv = filled(y) + ym = getmask(y) + d = numeric.choose(fc, (yv, xv)) + md = numeric.choose(fc, (ym, xm)) + m = getmask(condition) + m = make_mask(mask_or(m, md), copy=False, small_mask=True) + return masked_array(d, mask=m) + +def choose (indices, t, out=None, mode='raise'): + "Returns array shaped like indices with elements chosen from t" + #TODO: implement options `out` and `mode`, if possible. + def fmask (x): + "Returns the filled array, or True if ``masked``." + if x is masked: + return 1 + return filled(x) + def nmask (x): + "Returns the mask, True if ``masked``, False if ``nomask``." + if x is masked: + return 1 + m = getmask(x) + if m is nomask: + return 0 + return m + c = filled(indices, 0) + masks = [nmask(x) for x in t] + a = [fmask(x) for x in t] + d = numeric.choose(c, a) + m = numeric.choose(c, masks) + m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1) + return masked_array(d, mask=m) + +def round_(a, decimals=0, out=None): + """Returns reference to result. Copies a and rounds to 'decimals' places. + + Keyword arguments: + decimals -- number of decimals to round to (default 0). May be negative. + out -- existing array to use for output (default copy of a). + + Return: + Reference to out, where None specifies a copy of the original array a. + + Round to the specified number of decimals. When 'decimals' is negative it + specifies the number of positions to the left of the decimal point. The + real and imaginary parts of complex numbers are rounded separately. + Nothing is done if the array is not of float type and 'decimals' is greater + than or equal to 0.""" + result = fromnumeric.round_(filled(a), decimals, out) + if isinstance(a,MaskedArray): + result = result.view(type(a)) + result._mask = a._mask + else: + result = result.view(MaskedArray) + return result + +def arange(start, stop=None, step=1, dtype=None): + """Just like range() except it returns a array whose type can be specified + by the keyword argument dtype. + """ + return array(numeric.arange(start, stop, step, dtype),mask=nomask) + +def inner(a, b): + """inner(a,b) returns the dot product of two arrays, which has + shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the + product of the elements from the last dimensions of a and b. + Masked elements are replace by zeros. + """ + fa = filled(a, 0) + fb = filled(b, 0) + if len(fa.shape) == 0: + fa.shape = (1,) + if len(fb.shape) == 0: + fb.shape = (1,) + return masked_array(numeric.inner(fa, fb)) +innerproduct = inner + +def outer(a, b): + """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))""" + fa = filled(a, 0).ravel() + fb = filled(b, 0).ravel() + d = numeric.outer(fa, fb) + ma = getmask(a) + mb = getmask(b) + if ma is nomask and mb is nomask: + return masked_array(d) + ma = getmaskarray(a) + mb = getmaskarray(b) + m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0) + return masked_array(d, mask=m) +outerproduct = outer + +def allequal (a, b, fill_value=True): + """ +Returns `True` if all entries of a and b are equal, using +fill_value as a truth value where either or both are masked. + """ + m = mask_or(getmask(a), getmask(b)) + if m is nomask: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + return d.all() + elif fill_value: + x = filled(a) + y = filled(b) + d = umath.equal(x, y) + dm = array(d, mask=m, copy=False) + return dm.filled(True).all(None) + else: + return False + +def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8): + """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances. +If `fill_value` is True, masked values are considered equal. +If `fill_value` is False, masked values considered unequal. +The relative error rtol should be positive and << 1.0 +The absolute error `atol` comes into play for those elements of `b` + that are very small or zero; it says how small `a` must be also. + """ + m = mask_or(getmask(a), getmask(b)) + d1 = filled(a) + d2 = filled(b) + x = filled(array(d1, copy=0, mask=m), fill_value).astype(float) + y = filled(array(d2, copy=0, mask=m), 1).astype(float) + d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y)) + return fromnumeric.alltrue(fromnumeric.ravel(d)) + +#.............................................................................. +def asarray(a, dtype=None): + """asarray(data, dtype) = array(data, dtype, copy=0) +Returns `a` as an masked array. +No copy is performed if `a` is already an array. +Subclasses are converted to base class MaskedArray. + """ + return masked_array(a, dtype=dtype, copy=False, keep_mask=True) + +def empty(new_shape, dtype=float): + """empty((d1,...,dn),dtype=float,order='C') +Returns a new array of shape (d1,...,dn) and given type with all its +entries uninitialized. This can be faster than zeros.""" + return numeric.empty(new_shape, dtype).view(MaskedArray) + +def empty_like(a): + """empty_like(a) +Returns an empty (uninitialized) array of the shape and typecode of a. +Note that this does NOT initialize the returned array. +If you require your array to be initialized, you should use zeros_like().""" + return numeric.empty_like(a).view(MaskedArray) + +def ones(new_shape, dtype=float): + """ones(shape, dtype=None) +Returns an array of the given dimensions, initialized to all ones.""" + return numeric.ones(new_shape, dtype).view(MaskedArray) + +def zeros(new_shape, dtype=float): + """zeros(new_shape, dtype=None) +Returns an array of the given dimensions, initialized to all zeros.""" + return numeric.zeros(new_shape, dtype).view(MaskedArray) + +#####-------------------------------------------------------------------------- +#---- --- Pickling --- +#####-------------------------------------------------------------------------- +#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data +def _mareconstruct(subtype, baseshape, basetype,): + """Internal function that builds a new MaskedArray from the information stored +in a pickle.""" + _data = ndarray.__new__(ndarray, baseshape, basetype) + _mask = ndarray.__new__(ndarray, baseshape, basetype) + return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False) + +def _getstate(a): + "Returns the internal state of the masked array, for pickling purposes." + state = (1, + a.shape, + a.dtype, + a.flags.fnc, + a.tostring(), + getmaskarray(a).tostring()) + return state + +def _setstate(a, state): + """Restores the internal state of the masked array, for pickling purposes. +`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: + + - class name + - a tuple giving the shape of the data + - a typecode for the data + - a binary string for the data + - a binary string for the mask. + """ + (ver, shp, typ, isf, raw, msk) = state + super(MaskedArray, a).__setstate__((shp, typ, isf, raw)) + (a._mask).__setstate__((shp, dtype('|b1'), isf, msk)) + +def _reduce(a): + """Returns a 3-tuple for pickling a MaskedArray.""" + return (_mareconstruct, + (a.__class__, (0,), 'b', ), + a.__getstate__()) + +def dump(a,F): + """Pickles the MaskedArray `a` to the file `F`. +`F` can either be the handle of an exiting file, or a string representing a file name. + """ + if not hasattr(F,'readline'): + F = open(F,'w') + return cPickle.dump(a,F) + +def dumps(a): + """Returns a string corresponding to the pickling of the MaskedArray.""" + return cPickle.dumps(a) + +def load(F): + """Wrapper around ``cPickle.load`` which accepts either a file-like object or + a filename.""" + if not hasattr(F, 'readline'): + F = open(F,'r') + return cPickle.load(F) + +def loads(strg): + "Loads a pickle from the current string.""" + return cPickle.loads(strg) + +MaskedArray.__getstate__ = _getstate +MaskedArray.__setstate__ = _setstate +MaskedArray.__reduce__ = _reduce +MaskedArray.__dump__ = dump +MaskedArray.__dumps__ = dumps + +################################################################################ + +if __name__ == '__main__': + import numpy as N + from maskedarray.testutils import assert_equal, assert_array_equal, assert_mask_equal + pi = N.pi +# coremodule = __main__ + + a = array([1,2,3,4,5],mask=[0,1,0,0,0]) + x = add.reduce(a) + assert_equal(add.reduce(a), 13) + + if 0: + x = masked_array([1,2,3], mask=[1,0,0]) + mx = masked_array(x) + assert_equal(mx.mask, x.mask) + mx = masked_array(x, mask=[0,1,0], keep_mask=False) + assert_equal(mx.mask, [0,1,0]) + mx = masked_array(x, mask=[0,1,0], keep_mask=True) + assert_equal(mx.mask, [1,1,0]) + #We default to true + mx = masked_array(x, mask=[0,1,0]) + assert_equal(mx.mask, [1,1,0]) + if 0: + import numpy.core.ma as nma + x = nma.arange(5) + x[2] = nma.masked + X = masked_array(x, mask=x._mask) + assert_equal(X._mask, x.mask) + assert_equal(X._data, x._data) + X = masked_array(x) + assert_equal(X._data, x._data) + assert_equal(X._mask, x.mask) + assert_equal(getmask(x), [0,0,1,0,0]) + if 0: + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + x = array(d, mask = m) + assert( x[3] is masked) + assert( x[4] is masked) + x[[1,4]] = [10,40] + assert( x.mask is not m) + assert( x[3] is masked) + assert( x[4] is not masked) + assert_equal(x, [0,10,2,-1,40]) + if 0: + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + xh = array(d, mask = m, hard_mask=True) + # We need to copy, to avoid updating d in xh! + xs = array(d, mask = m, hard_mask=False, copy=True) + xh[[1,4]] = [10,40] + xs[[1,4]] = [10,40] + assert_equal(xh._data, [0,10,2,3,4]) + assert_equal(xs._data, [0,10,2,3,40]) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + assert_equal(xs.mask, [0,0,0,1,0]) + assert(xh._hardmask) + assert(not xs._hardmask) + xh[1:4] = [10,20,30] + xs[1:4] = [10,20,30] + assert_equal(xh._data, [0,10,20,3,4]) + assert_equal(xs._data, [0,10,20,30,40]) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + assert_equal(xs.mask, nomask) + xh[0] = masked + xs[0] = masked + assert_equal(xh.mask, [1,0,0,1,1]) + assert_equal(xs.mask, [1,0,0,0,0]) + xh[:] = 1 + xs[:] = 1 + assert_equal(xh._data, [0,1,1,3,4]) + assert_equal(xs._data, [1,1,1,1,1]) + assert_equal(xh.mask, [1,0,0,1,1]) + assert_equal(xs.mask, nomask) + # Switch to soft mask + xh.soften_mask() + xh[:] = arange(5) + assert_equal(xh._data, [0,1,2,3,4]) + assert_equal(xh.mask, nomask) + # Switch back to hard mask + xh.harden_mask() + xh[xh<3] = masked + assert_equal(xh._data, [0,1,2,3,4]) + assert_equal(xh._mask, [1,1,1,0,0]) + xh[filled(xh>1,False)] = 5 + assert_equal(xh._data, [0,1,2,5,5]) + assert_equal(xh._mask, [1,1,1,0,0]) + # + xh = array([[1,2],[3,4]], mask = [[1,0],[0,0]], hard_mask=True) + xh[0] = 0 + assert_equal(xh._data, [[1,0],[3,4]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + xh[-1,-1] = 5 + assert_equal(xh._data, [[1,0],[3,5]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + xh[filled(xh<5,False)] = 2 + assert_equal(xh._data, [[1,2],[2,5]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + # + "Another test of hardmask" + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + xh = array(d, mask = m, hard_mask=True) + xh[4:5] = 999 + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + xh[0:1] = 999 + assert_equal(xh._data,[999,1,2,3,4]) + if 0: + x = N.array([[ 0.13, 0.26, 0.90], + [ 0.28, 0.33, 0.63], + [ 0.31, 0.87, 0.70]]) + m = N.array([[ True, False, False], + [False, False, False], + [True, True, False]], dtype=N.bool_) + mx = masked_array(x, mask=m) + xbig = N.array([[False, False, True], + [False, False, True], + [False, True, True]], dtype=N.bool_) + mxbig = (mx > 0.5) + mxsmall = (mx < 0.5) + # + assert (mxbig.all()==False) + assert (mxbig.any()==True) + y = mxbig.all(0) + assert_equal(mxbig.all(0),[False, False, True]) + assert_equal(mxbig.all(1), [False, False, True]) + assert_equal(mxbig.any(0),[False, False, True]) + assert_equal(mxbig.any(1), [True, True, True]) + + if 1: + "Tests put." + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + x = array(d, mask = m) + assert( x[3] is masked) + assert( x[4] is masked) + x[[1,4]] = [10,40] + assert( x.mask is not m) + assert( x[3] is masked) + assert( x[4] is not masked) + assert_equal(x, [0,10,2,-1,40]) + # + x = masked_array(arange(10), mask=[1,0,0,0,0]*2) + i = [0,2,4,6] + x.put(i, [6,4,2,0]) + assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) + assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) + x.put(i, masked_array([0,2,4,6],[1,0,1,0])) + assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) + assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) + # + x = masked_array(arange(10), mask=[1,0,0,0,0]*2) + put(x, i, [6,4,2,0]) + assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) + assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) + put(x, i, masked_array([0,2,4,6],[1,0,1,0])) + assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) + assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) + + if 1: + x = arange(20) + x = x.reshape(4,5) + x.flat[5] = 12 + assert x[1,0] == 12 + z = x + 10j * x + assert_equal(z.real, x) + assert_equal(z.imag, 10*x) + assert_equal((z*conjugate(z)).real, 101*x*x) + z.imag[...] = 0.0 + if 1: + x = array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6) + y = array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6) + d = (x,y) + + z = N.add(x, y) + + for f in ['sqrt', 'log', 'log10', 'exp', 'conjugate', + 'sin', 'cos', 'tan', + 'arcsin', 'arccos', 'arctan', + 'sinh', 'cosh', 'tanh', + 'arcsinh', + 'arccosh', + 'arctanh', + 'absolute', 'fabs', 'negative', + # 'nonzero', 'around', + 'floor', 'ceil', + # 'sometrue', 'alltrue', + 'logical_not', + 'add', 'subtract', 'multiply', + 'divide', 'true_divide', 'floor_divide', + 'remainder', 'fmod', 'hypot', 'arctan2', + 'equal', 'not_equal', 'less_equal', 'greater_equal', + 'less', 'greater', + 'logical_and', 'logical_or', 'logical_xor', + ]: + print f + try: + uf = getattr(umath, f) + except AttributeError: + uf = getattr(fromnumeric, f) + mf = eval(f) #getattr(coremodule, f) + args = d[:uf.nin] + ur = uf(*args) + mr = mf(*args) + assert_equal(ur.filled(0), mr.filled(0), f) + assert_mask_equal(ur.mask, mr.mask) Property changes on: trunk/Lib/sandbox/maskedarray/alternative_versions/core_alt.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Copied: trunk/Lib/sandbox/maskedarray/alternative_versions/core_ini.py (from rev 2700, trunk/Lib/sandbox/maskedarray/core_ini.py) Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -75,48 +75,15 @@ from numpy.lib.shape_base import expand_dims as n_expand_dims import warnings -import logging -logging.basicConfig(level=logging.DEBUG, format='%(name)-15s %(levelname)s %(message)s',) - MaskType = bool_ nomask = MaskType(0) divide_tolerance = 1.e-35 numpy.seterr(all='ignore') -######-------------------------------------------------------------------------- -##---- --- Helper functions --- -######-------------------------------------------------------------------------- -#def convert_typecode(f,dtchar): -# """Converts the type of `f` to a type compatible with `dtchar`, for inline operations.""" -# ftype = f.dtype.char -# if dtchar == ftype: -# return f -# elif dtchar in typecodes['Integer']: -# if ftype in typecodes['Integer']: -# f = f.astype(dtchar) -# else: -# raise TypeError, 'Incorrect type for in-place operation.' -# elif dtchar in typecodes['Float']: -# if ftype in typecodes['Integer']: -# f = f.astype(dtchar) -# elif ftype in typecodes['Float']: -# f = f.astype(dtchar) -# else: -# raise TypeError, 'Incorrect type for in-place operation.' -# elif dtchar in typecodes['Complex']: -# if ftype in typecodes['Integer']: -# f = f.astype(dtchar) -# elif ftype in typecodes['Float']: -# f = f.astype(dtchar) -# elif ftype in typecodes['Complex']: -# f = f.astype(dtchar) -# else: -# raise TypeError, 'Incorrect type for in-place operation.' -# else: -# raise TypeError, 'Incorrect type for in-place operation.' -# return f +# TODO: There's still a problem with N.add.reduce not working... +# TODO: ...neither does N.add.accumulate #####-------------------------------------------------------------------------- #---- --- Exceptions --- @@ -824,77 +791,76 @@ #####-------------------------------------------------------------------------- #---- --- MaskedArray class --- #####-------------------------------------------------------------------------- -#def _getoptions(a_out, a_in): -# "Copies standards options of a_in to a_out." -# for att in ['] -class _mathmethod(object): - """Defines a wrapper for arithmetic methods. -Instead of directly calling a ufunc, the corresponding method of the `array._data` -object is called instead. - """ - def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): - """ -:Parameters: - - `methodname` (String) : Method name. - - `fill_self` (Float *[0]*) : Fill value for the instance. - - `fill_other` (Float *[0]*) : Fill value for the target. - - `domain` (Domain object *[None]*) : Domain of non-validity. - """ - self.methodname = methodname - self.fill_self = fill_self - self.fill_other = fill_other - self.domain = domain - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.methodname).__doc__ - except: - return getattr(ndarray, self.methodname).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." -# logging.debug("_mathmethod : %s" % self.methodname) - instance = self.obj - m_self = instance._mask - m_other = getmask(other) - base = instance.filled(self.fill_self) - target = filled(other, self.fill_other) - if self.domain is not None: - # We need to force the domain to a ndarray only. - if self.fill_other > self.fill_self: - domain = self.domain(base, target) - else: - domain = self.domain(target, base) - if domain.any(): - #If `other` is a subclass of ndarray, `filled` must have the - # same subclass, else we'll lose some info. - #The easiest then is to fill `target` instead of creating - # a pure ndarray. - #Oh, and we better make a copy! - if isinstance(other, ndarray): - # We don't want to modify other: let's copy target, then - target = target.copy() - target[fromnumeric.asarray(domain)] = self.fill_other - else: - target = numeric.where(fromnumeric.asarray(domain), - self.fill_other, target) - m_other = mask_or(m_other, domain) - m = mask_or(m_self, m_other) - method = getattr(base, self.methodname) - result = method(target, *args).view(type(instance)) - try: - result._mask = m - except AttributeError: - if m: - result = masked - return result +##def _getoptions(a_out, a_in): +## "Copies standards options of a_in to a_out." +## for att in ['] +#class _mathmethod(object): +# """Defines a wrapper for arithmetic methods. +#Instead of directly calling a ufunc, the corresponding method of the `array._data` +#object is called instead. +# """ +# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): +# """ +#:Parameters: +# - `methodname` (String) : Method name. +# - `fill_self` (Float *[0]*) : Fill value for the instance. +# - `fill_other` (Float *[0]*) : Fill value for the target. +# - `domain` (Domain object *[None]*) : Domain of non-validity. +# """ +# self.methodname = methodname +# self.fill_self = fill_self +# self.fill_other = fill_other +# self.domain = domain +# self.obj = None +# self.__doc__ = self.getdoc() +# # +# def getdoc(self): +# "Returns the doc of the function (from the doc of the method)." +# try: +# return getattr(MaskedArray, self.methodname).__doc__ +# except: +# return getattr(ndarray, self.methodname).__doc__ +# # +# def __get__(self, obj, objtype=None): +# self.obj = obj +# return self +# # +# def __call__ (self, other, *args): +# "Execute the call behavior." +# instance = self.obj +# m_self = instance._mask +# m_other = getmask(other) +# base = instance.filled(self.fill_self) +# target = filled(other, self.fill_other) +# if self.domain is not None: +# # We need to force the domain to a ndarray only. +# if self.fill_other > self.fill_self: +# domain = self.domain(base, target) +# else: +# domain = self.domain(target, base) +# if domain.any(): +# #If `other` is a subclass of ndarray, `filled` must have the +# # same subclass, else we'll lose some info. +# #The easiest then is to fill `target` instead of creating +# # a pure ndarray. +# #Oh, and we better make a copy! +# if isinstance(other, ndarray): +# # We don't want to modify other: let's copy target, then +# target = target.copy() +# target[fromnumeric.asarray(domain)] = self.fill_other +# else: +# target = numeric.where(fromnumeric.asarray(domain), +# self.fill_other, target) +# m_other = mask_or(m_other, domain) +# m = mask_or(m_self, m_other) +# method = getattr(base, self.methodname) +# result = method(target, *args).view(type(instance)) +# try: +# result._mask = m +# except AttributeError: +# if m: +# result = masked +# return result #............................................................................... class _arraymethod(object): """Defines a wrapper for basic array methods. @@ -945,7 +911,7 @@ if not self._onmask: result._mask = mask elif mask is not nomask: - result._set_mask(getattr(mask, methodname)(*args, **params)) + result.__setmask__(getattr(mask, methodname)(*args, **params)) return result #.......................................................... @@ -1013,52 +979,48 @@ If `data` is already a ndarray, its dtype becomes the default value of dtype. """ -# logging.debug("__new__ received %s" % type(data)) if flag is not None: warnings.warn("The flag 'flag' is now called 'small_mask'!", DeprecationWarning) small_mask = flag - # Process data........................... + # Process data............ _data = numeric.array(data, dtype=dtype, copy=copy, subok=True) _baseclass = getattr(_data, '_baseclass', type(_data)) -# _data = numeric.ndarray.__new__(cls, shape=_data.shape, -# dtype=_data.dtype, buffer=_data) _data = _data.view(cls) - # Process mask .......................... - if hasattr(data,"_mask"): - if data is masked: - return masked.view(cls) - # Keep the original mask if needed .. - if keep_mask: - if mask is nomask: - if copy: - mask = data._mask.copy() - else: - mask = data._mask - else: - mask = mask_or(data._mask, mask, - copy=copy, small_mask=small_mask) - # Create a new mask from scratch .... - else: - mask = make_mask(mask, copy=copy, small_mask=small_mask) + # Pricess mask ........... + # Backwards compat + if hasattr(data,'_mask') and not isinstance(data, ndarray): + _data._mask = data._mask + _data._sharedmask = True + if mask is nomask: + if _data._mask is not nomask: + if copy: + _data._mask = data._mask.copy() + _data._sharedmask = False + if not keep_mask: + _data._mask = nomask +# else; +# _data._mask = data._mask else: - mask = make_mask(mask, copy=copy, small_mask=small_mask) - # Check shape compatibility between mask and data - if mask is not nomask: - (nd, nm) = (_data.size, mask.size) - if (nm != nd): - # We need to resize w/ a function, in case _data is only a reference + mask = numeric.array(mask, dtype=MaskType, copy=copy) + if mask.shape != _data.shape: + (nd, nm) = (_data.size, mask.size) if nm == 1: - mask = fromnumeric.resize(mask, _data.shape) - elif nd == 1: - _data = fromnumeric.resize(_data, mask.shape) + mask = numeric.resize(mask, _data.shape) + elif nm == nd: + mask = fromnumeric.reshape(mask, _data.shape) else: msg = "Mask and data not compatible: data size is %i, "+\ "mask size is %i." raise MAError, msg % (nd, nm) - elif (mask.shape != _data.shape): - mask = mask.reshape(_data.shape) - + if _data._mask is nomask or not keep_mask: + _data._mask = mask + _data._sharedmask = True + else: + _data._mask = _data._mask.copy() + _data._mask.__ior__(mask) + _data._sharedmask = False + # Process extra options .. # Update fille_value _data._fill_value = getattr(data, '_fill_value', fill_value) if _data._fill_value is None: @@ -1066,21 +1028,37 @@ _data._hardmask = hard_mask _data._smallmask = small_mask _data._baseclass = _baseclass - _data._mask = mask return _data + #........................ + def __array_finalize__(self,obj): + """Finalizes the masked array. + """ + # Finalize mask ............... + self._mask = getattr(obj, '_mask', nomask) +# if not self.shape: +# self._mask.shape = obj.shape +# else: +# self._mask.shape = self.shape + if self._mask is not nomask: + self._mask.shape = self.shape +# except ValueError: +# self._mask = nomask + # Get the remaining options ... + self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask) + self._smallmask = getattr(obj, '_smallmask', True) + self._sharedmask = True + self._baseclass = getattr(obj, '_baseclass', type(obj)) + self._fill_value = getattr(obj, '_fill_value', None) + return #.................................. def __array_wrap__(self, obj, context=None): """Special hook for ufuncs. Wraps the numpy array and sets the mask according to context. """ -# logging.debug("wrap from : %s" % obj) result = obj.view(type(self)) -# logging.debug("wrap result: %s" % result) #.......... - if context is None: - m = self._mask - #.......... - else: + if context is not None: + result._mask = result._mask.copy() (func, args, out_index) = context m = reduce(mask_or, [getmask(arg) for arg in args]) # Get domain mask @@ -1090,45 +1068,22 @@ d = reduce(domain, args) else: d = domain(*args) - m = mask_or(m, d) - # Update mask - if m is not nomask and hasattr(obj,'shape'): - if m.shape != obj.shape: - m = reduce(mask_or, [getmaskarray(arg) for arg in args]) + if m is nomask: + if d is not nomask: + m = d + else: + m |= d + result._mask = m + if (not m.ndim) and m: + return masked #.... -# result = obj.view(type(self)) result._mask = m result._fill_value = self._fill_value result._hardmask = self._hardmask result._smallmask = self._smallmask result._baseclass = self._baseclass return result - #........................ - def __array_finalize__(self,obj): - """Finalizes the masked array. - """ - # Finalize mask ............... - self._mask = getattr(obj, '_mask', self._defaultmask) - if self._mask is not nomask: - self._mask.shape = self.shape -# if mask is not nomask and mask.shape != self.shape: -# # try and set the shape if we're coming from a assignment to shape -# # __array_interface__['data'] is REALLY faster than ctypes.data -# if self.__array_interface__['data'] == obj.__array_interface__['data']: -# try: -# mask.shape = self.shape -# except: -# mask = nomask -# else: -# mask = nomask -# self._mask = mask - # Get the remaining options ... - self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask) - self._smallmask = getattr(obj, '_smallmask', True) - self._baseclass = getattr(obj, '_baseclass', type(obj)) - self._fill_value = getattr(obj, '_fill_value', None) - return - #............................................ + #............................................. def __getitem__(self, indx): """x.__getitem__(y) <==> x[y] Returns the item described by i. Not a copy as in previous versions. @@ -1145,71 +1100,60 @@ dout = dout.view(type(self)) if m is not nomask: # use _set_mask to take care of the shape - dout._set_mask(m[indx]) + dout.__setmask__(m[indx]) elif m is not nomask and m[indx]: - dout = masked + return masked return dout #........................ def __setitem__(self, indx, value): """x.__setitem__(i, y) <==> x[i]=y Sets item described by index. If value is masked, masks those locations. """ - #logging.debug("__setitem__ %s to %s" % (index,value)) if self is masked: raise MAError, 'Cannot alter the masked element.' # if getmask(indx) is not nomask: # msg = "Masked arrays must be filled before they can be used as indices!" # raise IndexError, msg #.... - m = self._mask - #.... if value is masked: - if m is nomask: - m = make_mask_none(self.shape) + if self._mask is nomask: + self._mask = make_mask_none(self.shape) else: - m = m.copy() - m[indx] = True - self._mask = m + self._mask = self._mask.copy() + self._mask[indx] = True return #.... dval = numeric.asarray(value).astype(self.dtype) valmask = getmask(value) - if m is nomask: + if self._mask is nomask: if valmask is not nomask: - m = make_mask_none(self.shape) - m[indx] = valmask + self._mask = make_mask_none(self.shape) + self._mask[indx] = valmask elif not self._hardmask: - m = m.copy() + self._mask = self._mask.copy() if valmask is nomask: - m[indx] = False + self._mask[indx] = False else: - m[indx] = valmask + self._mask[indx] = valmask elif hasattr(indx, 'dtype') and (indx.dtype==bool_): - indx = indx * ~m -# elif isinstance(index, int): + indx = indx * umath.logical_not(self._mask) else: - mindx = mask_or(m[indx], valmask, copy=True) + mindx = mask_or(self._mask[indx], valmask, copy=True) dindx = self._data[indx] if dindx.size > 1: dindx[~mindx] = dval elif mindx is nomask: dindx = dval dval = dindx - m[indx] = mindx + self._mask[indx] = mindx # Set data .......... #dval = filled(value).astype(self.dtype) ndarray.__setitem__(self._data,indx,dval) - #..... - if m is nomask or not m.any(): - self._mask = nomask - else: - self._mask = m #............................................ def __getslice__(self, i, j): """x.__getslice__(i, j) <==> x[i:j] Returns the slice described by i, j. The use of negative indices is not supported.""" - #logging.debug("__getslice__ (%s,%s)" % (i,j)) return self.__getitem__(slice(i,j)) #........................ def __setslice__(self, i, j, value): @@ -1218,21 +1162,127 @@ If `value` is masked, masks those locations.""" self.__setitem__(slice(i,j), value) #............................................ - # If we don't want to crash the performance, we better leave __getattribute__ alone... -# def __getattribute__(self, name): -# """x.__getattribute__('name') = x.name -#Returns the chosen attribute. -#If the attribute cannot be directly accessed, checks the _data section. -# """ -# try: -# return ndarray.__getattribute__(self, name) -# except AttributeError: -# pass -# try: -# return self._data.__getattribute__(name) -# except AttributeError: -# raise AttributeError #............................................ + def __setmask__(self, mask): + newmask = make_mask(mask, copy=False, small_mask=self._smallmask) +# self.unshare_mask() + if self._mask is nomask: + self._mask = newmask + elif self._hardmask: + if newmask is not nomask: + self._mask.__ior__(newmask) + else: + self._mask.flat = newmask + if self._mask.shape: + self._mask.shape = self.shape + _setmask = __setmask__ + + def _get_mask(self): + """Returns the current mask.""" + return self._mask + +# def _set_mask(self, mask): +# """Sets the mask to `mask`.""" +# mask = make_mask(mask, copy=False, small_mask=self._smallmask) +# if mask is not nomask: +# if mask.size != self.size: +# raise ValueError, "Inconsistent shape between data and mask!" +# if mask.shape != self.shape: +# mask.shape = self.shape +# self._mask = mask +# else: +# self._mask = nomask + mask = property(fget=_get_mask, fset=__setmask__, doc="Mask") + #............................................ + def harden_mask(self): + "Forces the mask to hard." + self._hardmask = True + + def soften_mask(self): + "Forces the mask to soft." + self._hardmask = False + + def unshare_mask(self): + if self._sharedmask: + self._mask = self._mask.copy() + self._sharedmask = False + + #............................................ + def _get_data(self): + "Returns the current data (as a view of the original underlying data)>" + return self.view(self._baseclass) + _data = property(fget=_get_data) + #............................................ + def _get_flat(self): + """Calculates the flat value. + """ + return flatiter(self) + # + def _set_flat (self, value): + "x.flat = value" + y = self.ravel() + y[:] = value + # + flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") + #............................................ + def get_fill_value(self): + "Returns the filling value." + if self._fill_value is None: + self._fill_value = default_fill_value(self) + return self._fill_value + + def set_fill_value(self, value=None): + """Sets the filling value to `value`. +If None, uses the default, based on the data type.""" + if value is None: + value = default_fill_value(self) + self._fill_value = value + + fill_value = property(fget=get_fill_value, fset=set_fill_value, + doc="Filling value") + + def filled(self, fill_value=None): + """Returns an array of the same class as `_data`, + with masked values filled with `fill_value`. +Subclassing is preserved. + +If `fill_value` is None, uses self.fill_value. + """ + m = self._mask + if m is nomask: + return self._data + # + if fill_value is None: + fill_value = self.fill_value + # + if self is masked_singleton: + result = numeric.asanyarray(fill_value) + else: + result = self._data.copy() + try: + result[m] = fill_value + except (TypeError, AttributeError): + fill_value = numeric.array(fill_value, dtype=object) + d = result.astype(object) + result = fromnumeric.choose(m, (d, fill_value)) + except IndexError: + #ok, if scalar + if self._data.shape: + raise + elif m: + result = numeric.array(fill_value, dtype=self.dtype) + else: + result = self._data + return result + + def compressed(self): + "A 1-D array of all the non-masked data." + d = self.ravel() + if self._mask is nomask: + return d + else: + return d[numeric.logical_not(d._mask)] + #............................................ def __str__(self): """x.__str__() <==> str(x) Calculates the string representation, using masked for fill if it is enabled. @@ -1325,32 +1375,10 @@ "Divides self by other in place." dom_mask = domain_safe_divide().__call__(self, filled(other,1)) other_mask = getmask(other) -# logging.debug("dom_mask: %s" % dom_mask) new_mask = mask_or(other_mask, dom_mask) ndarray.__idiv__(self._data, other) self._mask = mask_or(self._mask, new_mask) return self - #.... - __add__ = _mathmethod('__add__') - __radd__ = _mathmethod('__add__') - __sub__ = _mathmethod('__sub__') - __rsub__ = _mathmethod('__rsub__') - __pow__ = _mathmethod('__pow__') - __mul__ = _mathmethod('__mul__', 1, 1) - __rmul__ = _mathmethod('__mul__', 1, 1) - __div__ = _mathmethod('__div__', 0, 1, domain_safe_divide()) - __rdiv__ = _mathmethod('__rdiv__', 1, 0, domain_safe_divide()) - __truediv__ = _mathmethod('__truediv__', 0, 1, domain_safe_divide()) - __rtruediv__ = _mathmethod('__rtruediv__', 1, 0, domain_safe_divide()) - __floordiv__ = _mathmethod('__floordiv__', 0, 1, domain_safe_divide()) - __rfloordiv__ = _mathmethod('__rfloordiv__', 1, 0, domain_safe_divide()) - __eq__ = _mathmethod('__eq__') - __ne__ = _mathmethod('__ne__') - __le__ = _mathmethod('__le__') - __lt__ = _mathmethod('__lt__') - __ge__ = _mathmethod('__ge__') - __gt__ = _mathmethod('__gt__') - #............................................ def __float__(self): "Converts self to float." @@ -1366,105 +1394,6 @@ raise MAError, 'Cannot convert masked element to a Python int.' return int(self.item()) #............................................ - 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 _get_flat(self): - """Calculates the flat value. - """ - return flatiter(self) - # - def _set_flat (self, value): - "x.flat = value" - y = self.ravel() - y[:] = value - # - flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") - #............................................ - def _get_mask(self): - """Returns the current mask.""" - return self._mask - def _set_mask(self, mask): - """Sets the mask to `mask`.""" - mask = make_mask(mask, copy=False, small_mask=self._smallmask) - if mask is not nomask: - if mask.size != self.size: - raise ValueError, "Inconsistent shape between data and mask!" - if mask.shape != self.shape: - mask.shape = self.shape - self._mask = mask - else: - self._mask = nomask - mask = property(fget=_get_mask, fset=_set_mask, doc="Mask") - #............................................ - def _get_data(self): - "Returns the current data (as a view of the original underlying data)>" - return self.view(self._baseclass) - _data = property(fget=_get_data) - #............................................ - def get_fill_value(self): - "Returns the filling value." - if self._fill_value is None: - self._fill_value = default_fill_value(self) - return self._fill_value - - def set_fill_value(self, value=None): - """Sets the filling value to `value`. -If None, uses the default, based on the data type.""" - if value is None: - value = default_fill_value(self) - self._fill_value = value - - fill_value = property(fget=get_fill_value, fset=set_fill_value, - doc="Filling value") - - def filled(self, fill_value=None): - """Returns an array of the same class as `_data`, - with masked values filled with `fill_value`. -Subclassing is preserved. - -If `fill_value` is None, uses self.fill_value. - """ - m = self._mask - if m is nomask: - return self._data - # - if fill_value is None: - fill_value = self.fill_value -# logging.debug("filled use %s instead of %s" % (self.fill_value, fill_value)) - # - if self is masked_singleton: - result = numeric.asanyarray(fill_value) - else: - result = self._data.copy() - try: - result[m] = fill_value - except (TypeError, AttributeError): - fill_value = numeric.array(fill_value, dtype=object) - d = result.astype(object) - result = fromnumeric.choose(m, (d, fill_value)) - except IndexError: - #ok, if scalar - if self._data.shape: - raise - elif m: - result = numeric.array(fill_value, dtype=self.dtype) - else: - result = self._data - return result - - def compressed(self): - "A 1-D array of all the non-masked data." - d = self.ravel() - if self._mask is nomask: - return d - else: - return d[numeric.logical_not(d._mask)] - #............................................ def count(self, axis=None): """Counts the non-masked elements of the array along a given axis, and returns a masked array where the mask is True where all data are masked. @@ -1579,7 +1508,7 @@ """ d = self.filled(True).all(axis=axis, out=out).view(type(self)) if d.ndim > 0: - d._set_mask(self._mask.all(axis)) + d.__setmask__(self._mask.all(axis)) return d def any(self, axis=None, out=None): @@ -1591,7 +1520,7 @@ """ d = self.filled(False).any(axis=axis, out=out).view(type(self)) if d.ndim > 0: - d._set_mask(self._mask.all(axis)) + d.__setmask__(self._mask.all(axis)) return d def nonzero(self): @@ -1637,7 +1566,7 @@ return masked result = self.filled(0).sum(axis, dtype=dtype).view(type(self)) if result.ndim > 0: - result._set_mask(mask) + result.__setmask__(mask) return result def cumsum(self, axis=None, dtype=None): @@ -1647,7 +1576,7 @@ If `axis` is None, applies to a flattened version of the array. """ result = self.filled(0).cumsum(axis=axis, dtype=dtype).view(type(self)) - result._set_mask(self.mask) + result.__setmask__(self.mask) return result def prod(self, axis=None, dtype=None): @@ -1664,7 +1593,7 @@ return masked result = self.filled(1).prod(axis=axis, dtype=dtype).view(type(self)) if result.ndim: - result._set_mask(mask) + result.__setmask__(mask) return result product = prod @@ -1675,7 +1604,7 @@ If `axis` is None, applies to a flattened version of the array. """ result = self.filled(1).cumprod(axis=axis, dtype=dtype).view(type(self)) - result._set_mask(self.mask) + result.__setmask__(self.mask) return result def mean(self, axis=None, dtype=None): @@ -1967,7 +1896,7 @@ Masked data are filled with fill_value. If fill_value is None, the data-type- dependent default is used.""" - return self.filled(fill_value).tostring() + return self.filled(fill_value).tostring() #-------------------------------------------- # Backwards Compatibility. Heck... @property @@ -2808,3 +2737,159 @@ import numpy as N from maskedarray.testutils import assert_equal, assert_array_equal pi = N.pi + + a = array([1,2,3,4,5],mask=[0,1,0,0,0]) + x = add.reduce(a) + assert_equal(add.reduce(a), 13) + + if 0: + x = masked_array([1,2,3], mask=[1,0,0]) + mx = masked_array(x) + assert_equal(mx.mask, x.mask) + mx = masked_array(x, mask=[0,1,0], keep_mask=False) + assert_equal(mx.mask, [0,1,0]) + mx = masked_array(x, mask=[0,1,0], keep_mask=True) + assert_equal(mx.mask, [1,1,0]) + #We default to true + mx = masked_array(x, mask=[0,1,0]) + assert_equal(mx.mask, [1,1,0]) + if 0: + import numpy.core.ma as nma + x = nma.arange(5) + x[2] = nma.masked + X = masked_array(x, mask=x._mask) + assert_equal(X._mask, x.mask) + assert_equal(X._data, x._data) + X = masked_array(x) + assert_equal(X._data, x._data) + assert_equal(X._mask, x.mask) + assert_equal(getmask(x), [0,0,1,0,0]) + if 0: + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + x = array(d, mask = m) + assert( x[3] is masked) + assert( x[4] is masked) + x[[1,4]] = [10,40] + assert( x.mask is not m) + assert( x[3] is masked) + assert( x[4] is not masked) + assert_equal(x, [0,10,2,-1,40]) + if 0: + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + xh = array(d, mask = m, hard_mask=True) + # We need to copy, to avoid updating d in xh! + xs = array(d, mask = m, hard_mask=False, copy=True) + xh[[1,4]] = [10,40] + xs[[1,4]] = [10,40] + assert_equal(xh._data, [0,10,2,3,4]) + assert_equal(xs._data, [0,10,2,3,40]) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + assert_equal(xs.mask, [0,0,0,1,0]) + assert(xh._hardmask) + assert(not xs._hardmask) + xh[1:4] = [10,20,30] + xs[1:4] = [10,20,30] + assert_equal(xh._data, [0,10,20,3,4]) + assert_equal(xs._data, [0,10,20,30,40]) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + assert_equal(xs.mask, nomask) + xh[0] = masked + xs[0] = masked + assert_equal(xh.mask, [1,0,0,1,1]) + assert_equal(xs.mask, [1,0,0,0,0]) + xh[:] = 1 + xs[:] = 1 + assert_equal(xh._data, [0,1,1,3,4]) + assert_equal(xs._data, [1,1,1,1,1]) + assert_equal(xh.mask, [1,0,0,1,1]) + assert_equal(xs.mask, nomask) + # Switch to soft mask + xh.soften_mask() + xh[:] = arange(5) + assert_equal(xh._data, [0,1,2,3,4]) + assert_equal(xh.mask, nomask) + # Switch back to hard mask + xh.harden_mask() + xh[xh<3] = masked + assert_equal(xh._data, [0,1,2,3,4]) + assert_equal(xh._mask, [1,1,1,0,0]) + xh[filled(xh>1,False)] = 5 + assert_equal(xh._data, [0,1,2,5,5]) + assert_equal(xh._mask, [1,1,1,0,0]) + # + xh = array([[1,2],[3,4]], mask = [[1,0],[0,0]], hard_mask=True) + xh[0] = 0 + assert_equal(xh._data, [[1,0],[3,4]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + xh[-1,-1] = 5 + assert_equal(xh._data, [[1,0],[3,5]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + xh[filled(xh<5,False)] = 2 + assert_equal(xh._data, [[1,2],[2,5]]) + assert_equal(xh._mask, [[1,0],[0,0]]) + # + "Another test of hardmask" + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + xh = array(d, mask = m, hard_mask=True) + xh[4:5] = 999 + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + xh[0:1] = 999 + assert_equal(xh._data,[999,1,2,3,4]) + if 0: + x = N.array([[ 0.13, 0.26, 0.90], + [ 0.28, 0.33, 0.63], + [ 0.31, 0.87, 0.70]]) + m = N.array([[ True, False, False], + [False, False, False], + [True, True, False]], dtype=N.bool_) + mx = masked_array(x, mask=m) + xbig = N.array([[False, False, True], + [False, False, True], + [False, True, True]], dtype=N.bool_) + mxbig = (mx > 0.5) + mxsmall = (mx < 0.5) + # + assert (mxbig.all()==False) + assert (mxbig.any()==True) + y = mxbig.all(0) + assert_equal(mxbig.all(0),[False, False, True]) + assert_equal(mxbig.all(1), [False, False, True]) + assert_equal(mxbig.any(0),[False, False, True]) + assert_equal(mxbig.any(1), [True, True, True]) + + if 1: + "Tests put." + d = arange(5) + n = [0,0,0,1,1] + m = make_mask(n) + x = array(d, mask = m) + assert( x[3] is masked) + assert( x[4] is masked) + x[[1,4]] = [10,40] + assert( x.mask is not m) + assert( x[3] is masked) + assert( x[4] is not masked) + assert_equal(x, [0,10,2,-1,40]) + # + x = masked_array(arange(10), mask=[1,0,0,0,0]*2) + i = [0,2,4,6] + x.put(i, [6,4,2,0]) + assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) + assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) + x.put(i, masked_array([0,2,4,6],[1,0,1,0])) + assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) + assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) + # + x = masked_array(arange(10), mask=[1,0,0,0,0]*2) + put(x, i, [6,4,2,0]) + assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) + assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) + put(x, i, masked_array([0,2,4,6],[1,0,1,0])) + assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) + assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) Deleted: trunk/Lib/sandbox/maskedarray/core_ini.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core_ini.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/core_ini.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -1,3033 +0,0 @@ -"""MA: a facility for dealing with missing observations -MA is generally used as a numpy.array look-alike. -by Paul F. Dubois. - -Copyright 1999, 2000, 2001 Regents of the University of California. -Released for unlimited redistribution. -Adapted for numpy_core 2005 by Travis Oliphant and -(mainly) Paul Dubois. - -Subclassing of the base ndarray 2006 by Pierre Gerard-Marchant. -pgmdevlist_at_gmail_dot_com - -:author: Pierre Gerard-Marchant -:contact: pierregm_at_uga_dot_edu -:version: $Id$ -""" -__author__ = "Pierre GF Gerard-Marchant ($Author$)" -__version__ = '1.0' -__revision__ = "$Revision$" -__date__ = '$Date$' - -__all__ = ['MAError', 'MaskType', 'MaskedArray', - 'bool_', 'complex_', 'float_', 'int_', 'object_', - 'abs', 'absolute', 'add', 'all', 'allclose', 'allequal', 'alltrue', - 'amax', 'amin', 'anom', 'anomalies', 'any', 'arange', - 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctan2', - 'arctanh', 'argmax', 'argmin', 'argsort', 'around', - 'array', 'asarray', - 'bitwise_and', 'bitwise_or', 'bitwise_xor', - 'ceil', 'choose', 'compressed', 'concatenate', 'conjugate', - 'cos', 'cosh', 'count', - 'diagonal', 'divide', 'dump', 'dumps', - 'empty', 'empty_like', 'equal', 'exp', - 'fabs', 'fmod', 'filled', 'floor', 'floor_divide', - 'getmask', 'getmaskarray', 'greater', 'greater_equal', 'hypot', - 'ids', 'inner', 'innerproduct', - 'isMA', 'isMaskedArray', 'is_mask', 'is_masked', 'isarray', - 'left_shift', 'less', 'less_equal', 'load', 'loads', 'log', 'log10', - 'logical_and', 'logical_not', 'logical_or', 'logical_xor', - 'make_mask', 'make_mask_none', 'mask_or', 'masked', - 'masked_array', 'masked_equal', 'masked_greater', - 'masked_greater_equal', 'masked_inside', 'masked_less', - 'masked_less_equal', 'masked_not_equal', 'masked_object', - 'masked_outside', 'masked_print_option', 'masked_singleton', - 'masked_values', 'masked_where', 'max', 'maximum', 'mean', 'min', - 'minimum', 'multiply', - 'negative', 'nomask', 'nonzero', 'not_equal', - 'ones', 'outer', 'outerproduct', - 'power', 'product', 'ptp', 'put', 'putmask', - 'rank', 'ravel', 'remainder', 'repeat', 'reshape', 'resize', - 'right_shift', 'round_', - 'shape', 'sin', 'sinh', 'size', 'sometrue', 'sort', 'sqrt', 'std', - 'subtract', 'sum', 'swapaxes', - 'take', 'tan', 'tanh', 'transpose', 'true_divide', - 'var', 'where', - 'zeros'] - -import sys -import types -import cPickle -# -import numpy -from numpy import bool_, complex_, float_, int_, object_, str_ - -import numpy.core.umath as umath -import numpy.core.fromnumeric as fromnumeric -from numpy.core.numeric import ndarray -from numpy.core.fromnumeric import amax, amin -import numpy.core.numerictypes as ntypes -from numpy.core.numerictypes import bool_, typecodes -from numpy.core.multiarray import dtype -import numpy.core.numeric as numeric -from numpy.lib.shape_base import expand_dims as n_expand_dims -import warnings - - - -MaskType = bool_ -nomask = MaskType(0) - -divide_tolerance = 1.e-35 -numpy.seterr(all='ignore') - -#####-------------------------------------------------------------------------- -#---- --- Helper functions --- -#####-------------------------------------------------------------------------- -def convert_typecode(f,dtchar): - """Converts the type of `f` to a type compatible with `dtchar`, for inline operations.""" - ftype = f.dtype.char - if dtchar == ftype: - return f - elif dtchar in typecodes['Integer']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - elif dtchar in typecodes['Float']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - elif ftype in typecodes['Float']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - elif dtchar in typecodes['Complex']: - if ftype in typecodes['Integer']: - f = f.astype(dtchar) - elif ftype in typecodes['Float']: - f = f.astype(dtchar) - elif ftype in typecodes['Complex']: - f = f.astype(dtchar) - else: - raise TypeError, 'Incorrect type for in-place operation.' - else: - raise TypeError, 'Incorrect type for in-place operation.' - return f - -#####-------------------------------------------------------------------------- -#---- --- Exceptions --- -#####-------------------------------------------------------------------------- -class MAError(Exception): - "Class for MA related errors." - def __init__ (self, args=None): - "Creates an exception." - Exception.__init__(self,args) - self.args = args - def __str__(self): - "Calculates the string representation." - return str(self.args) - __repr__ = __str__ - -#####-------------------------------------------------------------------------- -#---- --- Filling options --- -#####-------------------------------------------------------------------------- -# b: boolean - c: complex - f: floats - i: integer - O: object - S: string -default_filler = {'b': True, - 'c' : 1.e20 + 0.0j, - 'f' : 1.e20, - 'i' : 999999, - 'O' : '?', - 'S' : 'N/A', - 'u' : 999999, - 'V' : '???', - } -#{0: , -# 1: , -# 2: , -# 3: , -# 4: , -# 5: , -# 6: , -# 7: , -# 8: , -# 9: , -# 10: , -# 11: , -# 12: , -# 13: , -# 14: , -# 15: , -# 16: , -# 17: , -# 18: , -# 19: , -# 20: , -max_filler = ntypes._minvals -max_filler.update([(k,-numeric.inf) for k in [numpy.float32, numpy.float64]]) -min_filler = ntypes._maxvals -min_filler.update([(k,numeric.inf) for k in [numpy.float32, numpy.float64]]) -if 'float128' in ntypes.typeDict: - max_filler.update([(numpy.float128,-numeric.inf)]) - min_filler.update([(numpy.float128, numeric.inf)]) - - -def default_fill_value (obj): - "Calculates the default fill value for an object `obj`." - if hasattr(obj,'dtype'): - return default_filler[obj.dtype.kind] - elif isinstance(obj, float): - return default_filler['f'] - elif isinstance(obj, int) or isinstance(obj, long): - return default_filler['i'] - elif isinstance(obj, str): - return default_filler['S'] - elif isinstance(obj, complex): - return default_filler['c'] - elif isinstance(obj, numeric.dtype): - return default_filler[obj.kind] - else: - return default_filler['O'] - -def minimum_fill_value (obj): - "Calculates the default fill value suitable for taking the minimum of `obj`." - if hasattr(obj, 'dtype'): - objtype = obj.dtype - filler = min_filler[objtype] - if filler is None: - raise TypeError, 'Unsuitable type for calculating minimum.' - return filler - elif isinstance(obj, float): - return min_filler[ntypes.typeDict['float_']] - elif isinstance(obj, int): - return min_filler[ntypes.typeDict['int_']] - elif isinstance(obj, long): - return min_filler[ntypes.typeDict['uint']] - elif isinstance(obj, numeric.dtype): - return min_filler[obj] - else: - raise TypeError, 'Unsuitable type for calculating minimum.' - -def maximum_fill_value (obj): - "Calculates the default fill value suitable for taking the maximum of `obj`." - if hasattr(obj, 'dtype'): - objtype = obj.dtype - filler = max_filler[objtype] - if filler is None: - raise TypeError, 'Unsuitable type for calculating minimum.' - return filler - elif isinstance(obj, float): - return max_filler[ntypes.typeDict['float_']] - elif isinstance(obj, int): - return max_filler[ntypes.typeDict['int_']] - elif isinstance(obj, long): - return max_filler[ntypes.typeDict['uint']] - elif isinstance(obj, numeric.dtype): - return max_filler[obj] - else: - raise TypeError, 'Unsuitable type for calculating minimum.' - -def set_fill_value (a, fill_value): - "Sets the fill value of `a` if it is a masked array." - if isinstance(a, MaskedArray): - a.set_fill_value(fill_value) - -def get_fill_value (a): - """Returns the fill value of `a`, if any. - Otherwise, returns the default fill value for that type. - """ - if isinstance(a, MaskedArray): - result = a.fill_value - else: - result = default_fill_value(a) - return result - -def common_fill_value (a, b): - "Returns the common fill_value of `a` and `b`, if any, or `None`." - t1 = get_fill_value(a) - t2 = get_fill_value(b) - if t1 == t2: - return t1 - return None - -#................................................ -def filled(a, value = None): - """Returns `a` as an array with masked data replaced by `value`. -If `value` is `None` or the special element `masked`, `get_fill_value(a)` -is used instead. - -If `a` is already a contiguous numeric array, `a` itself is returned. - -`filled(a)` can be used to be sure that the result is numeric when passing -an object a to other software ignorant of MA, in particular to numpy itself. - """ - if hasattr(a, 'filled'): - return a.filled(value) - elif isinstance(a, ndarray): # and a.flags['CONTIGUOUS']: - return a - elif isinstance(a, types.DictType): - return numeric.array(a, 'O') - else: - return numeric.array(a) - -#####-------------------------------------------------------------------------- -#---- --- Ufuncs --- -#####-------------------------------------------------------------------------- -ufunc_domain = {} -ufunc_fills = {} - -class domain_check_interval: - """Defines a valid interval, -so that `domain_check_interval(a,b)(x) = true` where `x < a` or `x > b`.""" - def __init__(self, a, b): - "domain_check_interval(a,b)(x) = true where x < a or y > b" - if (a > b): - (a, b) = (b, a) - self.a = a - self.b = b - - def __call__ (self, x): - "Execute the call behavior." - return umath.logical_or(umath.greater (x, self.b), - umath.less(x, self.a)) -#............................ -class domain_tan: - """Defines a valid interval for the `tan` function, -so that `domain_tan(eps) = True where `abs(cos(x)) < eps`""" - def __init__(self, eps): - "domain_tan(eps) = true where abs(cos(x)) < eps)" - self.eps = eps - def __call__ (self, x): - "Execute the call behavior." - return umath.less(umath.absolute(umath.cos(x)), self.eps) -#............................ -class domain_safe_divide: - """defines a domain for safe division.""" - def __init__ (self, tolerance=divide_tolerance): - self.tolerance = tolerance - def __call__ (self, a, b): - return umath.absolute(a) * self.tolerance >= umath.absolute(b) -#............................ -class domain_greater: - "domain_greater(v)(x) = true where x <= v" - def __init__(self, critical_value): - "domain_greater(v)(x) = true where x <= v" - self.critical_value = critical_value - - def __call__ (self, x): - "Execute the call behavior." - return umath.less_equal(x, self.critical_value) -#............................ -class domain_greater_equal: - "domain_greater_equal(v)(x) = true where x < v" - def __init__(self, critical_value): - "domain_greater_equal(v)(x) = true where x < v" - self.critical_value = critical_value - - def __call__ (self, x): - "Execute the call behavior." - return umath.less(x, self.critical_value) -#.............................................................................. -class masked_unary_operation: - """Defines masked version of unary operations, -where invalid values are pre-masked. - -:IVariables: - - `f` : function. - - `fill` : Default filling value *[0]*. - - `domain` : Default domain *[None]*. - """ - def __init__ (self, mufunc, fill=0, domain=None): - """ masked_unary_operation(aufunc, fill=0, domain=None) - aufunc(fill) must be defined - self(x) returns aufunc(x) - with masked values where domain(x) is true or getmask(x) is true. - """ - self.f = mufunc - self.fill = fill - self.domain = domain - self.__doc__ = getattr(mufunc, "__doc__", str(mufunc)) - self.__name__ = getattr(mufunc, "__name__", str(mufunc)) - ufunc_domain[mufunc] = domain - ufunc_fills[mufunc] = fill - # - def __call__ (self, a, *args, **kwargs): - "Execute the call behavior." -# numeric tries to return scalars rather than arrays when given scalars. - m = getmask(a) - d1 = filled(a, self.fill) - if self.domain is not None: - m = mask_or(m, numeric.asarray(self.domain(d1))) - result = self.f(d1, *args, **kwargs) - # - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) - # - def __str__ (self): - return "Masked version of %s. [Invalid values are masked]" % str(self.f) -#.............................................................................. -class masked_binary_operation: - """Defines masked version of binary operations, -where invalid values are pre-masked. - -:IVariables: - - `f` : function. - - `fillx` : Default filling value for first array*[0]*. - - `filly` : Default filling value for second array*[0]*. - - `domain` : Default domain *[None]*. - """ - def __init__ (self, mbfunc, fillx=0, filly=0): - """abfunc(fillx, filly) must be defined. - abfunc(x, filly) = x for all x to enable reduce. - """ - self.f = mbfunc - self.fillx = fillx - self.filly = filly - self.__doc__ = getattr(mbfunc, "__doc__", str(mbfunc)) - self.__name__ = getattr(mbfunc, "__name__", str(mbfunc)) - ufunc_domain[mbfunc] = None - ufunc_fills[mbfunc] = (fillx, filly) - # - def __call__ (self, a, b, *args, **kwargs): - "Execute the call behavior." - m = mask_or(getmask(a), getmask(b)) - d1 = filled(a, self.fillx) - d2 = filled(b, self.filly) - result = self.f(d1, d2, *args, **kwargs) -# if isinstance(result, ndarray) \ -# and m.ndim != 0 \ -# and m.shape != result.shape: -# m = mask_or(getmaskarray(a), getmaskarray(b)) - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) - # - def reduce (self, target, axis=0, dtype=None): - """Reduces `target` along the given `axis`.""" - if isinstance(target, MaskedArray): - tclass = target.__class__ - else: - tclass = MaskedArray - m = getmask(target) - t = filled(target, self.filly) - if t.shape == (): - t = t.reshape(1) - if m is not nomask: - m = make_mask(m, copy=1) - m.shape = (1,) - if m is nomask: - return tclass(self.f.reduce (t, axis)) - else: - t = tclass(t, mask=m) - # XXX: "or t.dtype" below is a workaround for what appears - # XXX: to be a bug in reduce. - t = self.f.reduce(filled(t, self.filly), axis, dtype=dtype or t.dtype) - m = umath.logical_and.reduce(m, axis) - if isinstance(t, ndarray): - return tclass(t, mask=m, fill_value=get_fill_value(target)) - elif m: - return masked - else: - return t - - def outer (self, a, b): - "Returns the function applied to the outer product of a and b." - ma = getmask(a) - mb = getmask(b) - if ma is nomask and mb is nomask: - m = nomask - else: - ma = getmaskarray(a) - mb = getmaskarray(b) - m = umath.logical_or.outer(ma, mb) - d = self.f.outer(filled(a, self.fillx), filled(b, self.filly)) - if isinstance(d, MaskedArray): - return d.__class__(d, mask=m) - return masked_array(d, mask=m) - - def accumulate (self, target, axis=0): - """Accumulates `target` along `axis` after filling with y fill value.""" - if isinstance(target, MaskedArray): - tclass = target.__class__ - else: - tclass = masked_array - t = filled(target, self.filly) - return tclass(self.f.accumulate(t, axis)) - - def __str__ (self): - return "Masked version of " + str(self.f) -#.............................................................................. -class domained_binary_operation: - """Defines binary operations that have a domain, like divide. - -These are complicated so they are a separate class. -They have no reduce, outer or accumulate. - -:IVariables: - - `f` : function. - - `fillx` : Default filling value for first array*[0]*. - - `filly` : Default filling value for second array*[0]*. - - `domain` : Default domain *[None]*. - """ - def __init__ (self, dbfunc, domain, fillx=0, filly=0): - """abfunc(fillx, filly) must be defined. - abfunc(x, filly) = x for all x to enable reduce. - """ - self.f = dbfunc - self.domain = domain - self.fillx = fillx - self.filly = filly - self.__doc__ = getattr(dbfunc, "__doc__", str(dbfunc)) - self.__name__ = getattr(dbfunc, "__name__", str(dbfunc)) - ufunc_domain[dbfunc] = domain - ufunc_fills[dbfunc] = (fillx, filly) - - def __call__(self, a, b): - "Execute the call behavior." - ma = getmask(a) - mb = getmask(b) - d1 = filled(a, self.fillx) - d2 = filled(b, self.filly) - t = numeric.asarray(self.domain(d1, d2)) - if fromnumeric.sometrue(t, None): - d2 = numeric.where(t, self.filly, d2) - mb = mask_or(mb, t) - m = mask_or(ma, mb) - result = self.f(d1, d2) - if isinstance(result, MaskedArray): - return result.__class__(result, mask=m) - return masked_array(result, mask=m) - - def __str__ (self): - return "Masked version of " + str(self.f) - -#.............................................................................. -# Unary ufuncs -exp = masked_unary_operation(umath.exp) -conjugate = masked_unary_operation(umath.conjugate) -sin = masked_unary_operation(umath.sin) -cos = masked_unary_operation(umath.cos) -tan = masked_unary_operation(umath.tan) -arctan = masked_unary_operation(umath.arctan) -arcsinh = masked_unary_operation(umath.arcsinh) -sinh = masked_unary_operation(umath.sinh) -cosh = masked_unary_operation(umath.cosh) -tanh = masked_unary_operation(umath.tanh) -abs = absolute = masked_unary_operation(umath.absolute) -fabs = masked_unary_operation(umath.fabs) -negative = masked_unary_operation(umath.negative) -floor = masked_unary_operation(umath.floor) -ceil = masked_unary_operation(umath.ceil) -around = masked_unary_operation(fromnumeric.round_) -logical_not = masked_unary_operation(umath.logical_not) -# Domained unary ufuncs -sqrt = masked_unary_operation(umath.sqrt, 0.0, domain_greater_equal(0.0)) -log = masked_unary_operation(umath.log, 1.0, domain_greater(0.0)) -log10 = masked_unary_operation(umath.log10, 1.0, domain_greater(0.0)) -tan = masked_unary_operation(umath.tan, 0.0, domain_tan(1.e-35)) -arcsin = masked_unary_operation(umath.arcsin, 0.0, - domain_check_interval(-1.0, 1.0)) -arccos = masked_unary_operation(umath.arccos, 0.0, - domain_check_interval(-1.0, 1.0)) -arccosh = masked_unary_operation(umath.arccosh, 1.0, domain_greater_equal(1.0)) -arctanh = masked_unary_operation(umath.arctanh, 0.0, - domain_check_interval(-1.0+1e-15, 1.0-1e-15)) -# Binary ufuncs -add = masked_binary_operation(umath.add) -subtract = masked_binary_operation(umath.subtract) -multiply = masked_binary_operation(umath.multiply, 1, 1) -arctan2 = masked_binary_operation(umath.arctan2, 0.0, 1.0) -equal = masked_binary_operation(umath.equal) -equal.reduce = None -not_equal = masked_binary_operation(umath.not_equal) -not_equal.reduce = None -less_equal = masked_binary_operation(umath.less_equal) -less_equal.reduce = None -greater_equal = masked_binary_operation(umath.greater_equal) -greater_equal.reduce = None -less = masked_binary_operation(umath.less) -less.reduce = None -greater = masked_binary_operation(umath.greater) -greater.reduce = None -logical_and = masked_binary_operation(umath.logical_and) -alltrue = masked_binary_operation(umath.logical_and, 1, 1).reduce -logical_or = masked_binary_operation(umath.logical_or) -sometrue = logical_or.reduce -logical_xor = masked_binary_operation(umath.logical_xor) -bitwise_and = masked_binary_operation(umath.bitwise_and) -bitwise_or = masked_binary_operation(umath.bitwise_or) -bitwise_xor = masked_binary_operation(umath.bitwise_xor) -hypot = masked_binary_operation(umath.hypot) -# Domained binary ufuncs -divide = domained_binary_operation(umath.divide, domain_safe_divide(), 0, 1) -true_divide = domained_binary_operation(umath.true_divide, - domain_safe_divide(), 0, 1) -floor_divide = domained_binary_operation(umath.floor_divide, - domain_safe_divide(), 0, 1) -remainder = domained_binary_operation(umath.remainder, - domain_safe_divide(), 0, 1) -fmod = domained_binary_operation(umath.fmod, domain_safe_divide(), 0, 1) - - -#####-------------------------------------------------------------------------- -#---- --- Mask creation functions --- -#####-------------------------------------------------------------------------- -def getmask(a): - """Returns the mask of `a`, if any, or `nomask`. -Returns `nomask` if `a` is not a masked array. -To get an array for sure use getmaskarray.""" - if hasattr(a, "_mask"): - return a._mask - else: - return nomask - -def getmaskarray(a): - """Returns the mask of `a`, if any. -Otherwise, returns an array of `False`, with the same shape as `a`. - """ - m = getmask(a) - if m is nomask: - return make_mask_none(fromnumeric.shape(a)) - else: - return m - -def is_mask(m): - """Returns `True` if `m` is a legal mask. -Does not check contents, only type. - """ - try: - return m.dtype.type is MaskType - except AttributeError: - return False -# -def make_mask(m, copy=False, small_mask=True, flag=None): - """make_mask(m, copy=0, small_mask=0) -Returns `m` as a mask, creating a copy if necessary or requested. -The function can accept any sequence of integers or `nomask`. -Does not check that contents must be 0s and 1s. -If `small_mask=True`, returns `nomask` if `m` contains no true elements. - -:Parameters: - - `m` (ndarray) : Mask. - - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. - - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. - """ - if flag is not None: - warnings.warn("The flag 'flag' is now called 'small_mask'!", - DeprecationWarning) - small_mask = flag - if m is nomask: - return nomask - elif isinstance(m, ndarray): - m = filled(m, True) - if m.dtype.type is MaskType: - if copy: - result = numeric.array(m, dtype=MaskType, copy=copy) - else: - result = m - else: - result = numeric.array(m, dtype=MaskType) - else: - result = numeric.array(filled(m, True), dtype=MaskType) - # Bas les masques ! - if small_mask and not result.any(): - return nomask - else: - return result - -def make_mask_none(s): - "Returns a mask of shape `s`, filled with `False`." - result = numeric.zeros(s, dtype=MaskType) - return result - -def mask_or (m1, m2, copy=False, small_mask=True): - """Returns the combination of two masks `m1` and `m2`. -The masks are combined with the `logical_or` operator, treating `nomask` as false. -The result may equal m1 or m2 if the other is nomask. - -:Parameters: - - `m` (ndarray) : Mask. - - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. - - `small_mask` (boolean, *[False]*): Flattens mask to `nomask` if `m` is all false. - """ - if m1 is nomask: - return make_mask(m2, copy=copy, small_mask=small_mask) - if m2 is nomask: - return make_mask(m1, copy=copy, small_mask=small_mask) - if m1 is m2 and is_mask(m1): - return m1 - return make_mask(umath.logical_or(m1, m2), copy=copy, small_mask=small_mask) - -#####-------------------------------------------------------------------------- -#--- --- Masking functions --- -#####-------------------------------------------------------------------------- -def masked_where(condition, x, copy=True): - """Returns `x` as an array masked where `condition` is true. -Masked values of `x` or `condition` are kept. - -:Parameters: - - `condition` (ndarray) : Masking condition. - - `x` (ndarray) : Array to mask. - - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. - """ - cm = filled(condition,1) - if isinstance(x,MaskedArray): - m = mask_or(x._mask, cm) - return x.__class__(x._data, mask=m, copy=copy) - else: - return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm) - -def masked_greater(x, value, copy=1): - "Shortcut to `masked_where`, with ``condition = (x > value)``." - return masked_where(greater(x, value), x, copy=copy) - -def masked_greater_equal(x, value, copy=1): - "Shortcut to `masked_where`, with ``condition = (x >= value)``." - return masked_where(greater_equal(x, value), x, copy=copy) - -def masked_less(x, value, copy=True): - "Shortcut to `masked_where`, with ``condition = (x < value)``." - return masked_where(less(x, value), x, copy=copy) - -def masked_less_equal(x, value, copy=True): - "Shortcut to `masked_where`, with ``condition = (x <= value)``." - return masked_where(less_equal(x, value), x, copy=copy) - -def masked_not_equal(x, value, copy=True): - "Shortcut to `masked_where`, with ``condition = (x != value)``." - return masked_where((x != value), x, copy=copy) - -# -def masked_equal(x, value, copy=True): - """Shortcut to `masked_where`, with ``condition = (x == value)``. -For floating point, consider `masked_values(x, value)` instead. - """ - return masked_where((x == value), x, copy=copy) -# d = filled(x, 0) -# c = umath.equal(d, value) -# m = mask_or(c, getmask(x)) -# return array(d, mask=m, copy=copy) - -def masked_inside(x, v1, v2, copy=True): - """Shortcut to `masked_where`, where `condition` is True for x inside -the interval `[v1,v2]` ``(v1 <= x <= v2)``. -The boundaries `v1` and `v2` can be given in either order. - """ - if v2 < v1: - (v1, v2) = (v2, v1) - xf = filled(x) - condition = (xf >= v1) & (xf <= v2) - return masked_where(condition, x, copy=copy) - -def masked_outside(x, v1, v2, copy=True): - """Shortcut to `masked_where`, where `condition` is True for x outside -the interval `[v1,v2]` ``(x < v1)|(x > v2)``. -The boundaries `v1` and `v2` can be given in either order. - """ - if v2 < v1: - (v1, v2) = (v2, v1) - xf = filled(x) - condition = (xf < v1) | (xf > v2) - return masked_where(condition, x, copy=copy) - -# -def masked_object(x, value, copy=True): - """Masks the array `x` where the data are exactly equal to `value`. -This function is suitable only for `object` arrays: for floating point, -please use `masked_values` instead. -The mask is set to `nomask` if posible. - -:parameter copy (Boolean, *[True]*): Returns a copy of `x` if true. """ - if isMaskedArray(x): - condition = umath.equal(x._data, value) - mask = x._mask - else: - condition = umath.equal(fromnumeric.asarray(x), value) - mask = nomask - mask = mask_or(mask, make_mask(condition, small_mask=True)) - return masked_array(x, mask=mask, copy=copy, fill_value=value) - -def masked_values(x, value, rtol=1.e-5, atol=1.e-8, copy=True): - """Masks the array `x` where the data are approximately equal to `value` -(that is, ``abs(x - value) <= atol+rtol*abs(value)``). -Suitable only for floating points. For integers, please use `masked_equal`. -The mask is set to `nomask` if posible. - -:Parameters: - - `rtol` (Float, *[1e-5]*): Tolerance parameter. - - `atol` (Float, *[1e-8]*): Tolerance parameter. - - `copy` (boolean, *[False]*) : Returns a copy of `x` if True. - """ - abs = umath.absolute - xnew = filled(x, value) - if issubclass(xnew.dtype.type, numeric.floating): - condition = umath.less_equal(abs(xnew-value), atol+rtol*abs(value)) - try: - mask = x._mask - except AttributeError: - mask = nomask - else: - condition = umath.equal(xnew, value) - mask = nomask - mask = mask_or(mask, make_mask(condition, small_mask=True)) - return masked_array(xnew, mask=mask, copy=copy, fill_value=value) - -#####-------------------------------------------------------------------------- -#---- --- Printing options --- -#####-------------------------------------------------------------------------- -class _MaskedPrintOption: - """Handles the string used to represent missing data in a masked array.""" - def __init__ (self, display): - "Creates the masked_print_option object." - self._display = display - self._enabled = True - - def display(self): - "Displays the string to print for masked values." - return self._display - - def set_display (self, s): - "Sets the string to print for masked values." - self._display = s - - def enabled(self): - "Is the use of the display value enabled?" - return self._enabled - - def enable(self, small_mask=1): - "Set the enabling small_mask to `small_mask`." - self._enabled = small_mask - - def __str__ (self): - return str(self._display) - - __repr__ = __str__ - -#if you single index into a masked location you get this object. -masked_print_option = _MaskedPrintOption('--') - -#####-------------------------------------------------------------------------- -#---- --- MaskedArray class --- -#####-------------------------------------------------------------------------- -class MaskedArray(numeric.ndarray): - """Arrays with possibly masked values. -Masked values of True exclude the corresponding element from any computation. - -Construction: - x = array(data, dtype=None, copy=True, order=False, - mask = nomask, fill_value=None, small_mask=True) - -If copy=False, every effort is made not to copy the data: -If `data` is a MaskedArray, and argument mask=nomask, then the candidate data -is `data._data` and the mask used is `data._mask`. -If `data` is a numeric array, it is used as the candidate raw data. -If `dtype` is not None and is different from data.dtype.char then a data copy is required. -Otherwise, the candidate is used. - -If a data copy is required, the raw (unmasked) data stored is the result of: -numeric.array(data, dtype=dtype.char, copy=copy) - -If `mask` is `nomask` there are no masked values. -Otherwise mask must be convertible to an array of booleans with the same shape as x. -If `small_mask` is True, a mask consisting of zeros (False) only is compressed to `nomask`. -Otherwise, the mask is not compressed. - -fill_value is used to fill in masked values when necessary, such as when -printing and in method/function filled(). -The fill_value is not used for computation within this module. - """ - __array_priority__ = 10.1 - _defaultmask = nomask - _defaulthardmask = False - #TODO: There some reorganization to do round here - def __new__(cls, data=None, mask=nomask, dtype=None, copy=False, fill_value=None, - keep_mask=True, small_mask=True, hard_mask=False, flag=None, - **options): - """array(data, dtype=None, copy=True, mask=nomask, fill_value=None) - -If `data` is already a ndarray, its dtype becomes the default value of dtype. - """ - if flag is not None: - warnings.warn("The flag 'flag' is now called 'small_mask'!", - DeprecationWarning) - small_mask = flag - # 1. Argument is MA ........... - if isinstance(data, MaskedArray) or\ - (hasattr(data,"_mask") and hasattr(data,"_data")) : - if data is masked: - return masked.view(cls) - if keep_mask: - if mask is nomask: - if copy: - cls._defaultmask = data._mask.copy() - else: - cls._defaultmask = data._mask - else: - cls._defaultmask = mask_or(data._mask, mask, - copy=copy, small_mask=small_mask) - else: - cls._defaultmask = make_mask(mask, copy=copy, small_mask=small_mask) - # Update fille_value - if fill_value is None: - cls._fill_value = data._fill_value - else: - cls._fill_value = fill_value - cls._defaulthardmask = hard_mask - _data = data._data - if dtype is not None and _data.dtype != numeric.dtype(dtype): - return _data.astype(dtype).view(cls) - elif copy: - return _data.copy().view(cls) - else: - return _data.view(cls) - # 2. Argument is not MA ....... - if isinstance(data, ndarray): - if dtype is not None and data.dtype != numeric.dtype(dtype): - _data = data.astype(dtype) - elif copy: - _data = data.copy() - else: - _data = data - else: - _data = numeric.array(data, dtype=dtype, copy=copy) -# try: -# _data = numeric.array(data, dtype=dtype, copy=copy) -# except TypeError: -# _data = empty(len(data), dtype=dtype) -# for (k,v) in enumerate(data): -# _data[k] = v -# if mask is nomask: -# cls.__defaultmask = getmask(_data) -# return _data.view(cls) - # Define mask ................. - mask = make_mask(mask, copy=copy, small_mask=small_mask) - #....Check shapes compatibility - if mask is not nomask: - (nd, nm) = (_data.size, mask.size) - if (nm != nd): - # We need to resize w/ a function, in case _data is only a reference - if nm == 1: - mask = fromnumeric.resize(mask, _data.shape) - elif nd == 1: - _data = fromnumeric.resize(_data, mask.shape) - else: - msg = "Mask and data not compatible: data size is %i, "+\ - "mask size is %i." - raise MAError, msg % (nd, nm) - elif (mask.shape != _data.shape): - mask = mask.reshape(_data.shape) -# mask = _data.shape - #.... - cls._fill_value = fill_value - cls._defaulthardmask = hard_mask - cls._defaultmask = mask - cls._defaultoptions = options - return numeric.asanyarray(_data).view(cls) - #.................................. - def __array_wrap__(self, obj, context=None): - """Special hook for ufuncs. -Wraps the numpy array and sets the mask according to context. - """ -# mclass = self.__class__ - #.......... - if context is None: -# return mclass(obj, mask=self._mask, copy=False) - return MaskedArray(obj, mask=self._mask, copy=False, - dtype=obj.dtype, - fill_value=self.fill_value, ) - #.......... - (func, args) = context[:2] - m = reduce(mask_or, [getmask(arg) for arg in args]) - # Get domain mask - domain = ufunc_domain.get(func, None) - if domain is not None: - m = mask_or(m, domain(*[getattr(arg, '_data', arg) for arg in args])) - # Update mask - if m is not nomask: - try: - dshape = obj.shape - except AttributeError: - pass - else: - if m.shape != dshape: - m = reduce(mask_or, [getmaskarray(arg) for arg in args]) -# return mclass(obj, copy=False, mask=m) - return MaskedArray(obj, copy=False, mask=m,) -# dtype=obj.dtype, fill_value=self._fill_value) - #........................ - #TODO: there should be some reorganization to do round here. - def __array_finalize__(self,obj): - """Finalizes the masked array. - """ - # - if isinstance(obj, MaskedArray): - # We came here from a MaskedArray - self._data = obj._data - self._mask = obj._mask - self._hardmask = obj._hardmask - self._fill_value = obj._fill_value - self.options = obj.options - else: - # We came here from a .view() - if hasattr(obj,'_data') and hasattr(obj, '_mask'): - # obj is an old masked array or a smart record - self._data = obj._data - self._mask = obj._mask - else: - # obj is anything but... - self._data = obj - self._mask = self._defaultmask - # Set the instance default - self._hardmask = self._defaulthardmask - self.fill_value = self._fill_value - self.options = self._defaultoptions - # Reset the class default - MaskedArray._defaultmask = nomask - MaskedArray._defaulthardmask = False - MaskedArray._fill_value = None -# # - return - #............................................ - def __getitem__(self, indx): - """x.__getitem__(y) <==> x[y] -Returns the item described by i. Not a copy as in previous versions. - """ - if getmask(indx) is not nomask: - msg = "Masked arrays must be filled before they can be used as indices!" - raise IndexError, msg - dout = self._data[indx] - m = self._mask - scalardout = (len(numeric.shape(dout))==0) - # - if m is nomask: - if scalardout: - return dout - else: - return self.__class__(dout, mask=nomask, keep_mask=True, - fill_value=self._fill_value, - **self.options) - #.... - mi = m[indx] - if mi.size == 1: - if mi: - return masked - return dout - else: - return self.__class__(dout, mask=mi, fill_value=self._fill_value, - **self.options) - #........................ - def __setitem__(self, index, value): - """x.__setitem__(i, y) <==> x[i]=y -Sets item described by index. If value is masked, masks those locations. - """ - if self is masked: - raise MAError, 'Cannot alter the masked element.' - if getmask(index) is not nomask: - msg = "Masked arrays must be filled before they can be used as indices!" - raise IndexError, msg - #.... - (d, m) = (self._data, self._mask) - #.... - if value is masked: - if m is nomask: - m = make_mask_none(d.shape) - else: - m = m.copy() - m[index] = True - self._mask = m - return - #.... - if m is nomask: - d[index] = filled(value) - valmask = getmask(value) - if valmask is not nomask: - m = make_mask_none(d.shape) - m[index] = valmask - elif not self._hardmask: - d[index] = filled(value) - valmask = getmask(value) - m = m.copy() - if valmask is nomask: - m[index] = False - else: - m[index] = valmask - elif hasattr(index, 'dtype') and (index.dtype==bool_): - index *= ~m - d[index] = filled(value) -# elif isinstance(index, int): - else: - mindx = m[index] - value = masked_array(value, mask=mindx, keep_mask=True) - valdata = filled(value) - valmask = getmask(value) - if valmask is nomask: - d[index] = valdata - elif valmask.size > 1: - dindx = d[index] - numeric.putmask(dindx, ~valmask, valdata) - d[index] = dindx - numeric.putmask(mindx, valmask, True) - m[index] = mindx - #..... - if not m.any(): - self._mask = nomask - else: - self._mask = m - #............................................ - def __getslice__(self, i, j): - """x.__getslice__(i, j) <==> x[i:j] -Returns the slice described by i, j. -The use of negative indices is not supported.""" - m = self._mask - dout = self._data[i:j] - if m is nomask: - return self.__class__(dout, fill_value=self._fill_value, - **self.options) - else: - return self.__class__(dout, mask=m[i:j], fill_value=self._fill_value, - **self.options) - #........................ - def __setslice__(self, i, j, value): - """x.__setslice__(i, j, value) <==> x[i:j]=value -Sets a slice i:j to `value`. -If `value` is masked, masks those locations.""" - if self is masked: - #TODO: Well, maybe we could/should - raise MAError, "Cannot alter the 'masked' object." - #.... - (d, m) = (self._data, self._mask) - #.... - if value is masked: - if m is nomask: - m = make_mask_none(d.shape) - m[i:j] = True - self._mask = m - return - #.... - if m is nomask: - valmask = getmask(value) - valdata = filled(value) - d[i:j] = valdata - if valmask is not nomask: - m = make_mask_none(d.shape) - m[i:j] = valmask - elif not self._hardmask: - valmask = getmask(value) - valdata = filled(value) - d[i:j] = valdata - if valmask is nomask: - m[i:j] = False - else: - m[i:j] = valmask - else: - mindx = m[i:j] - value = masked_array(value, mask=mindx, keep_mask=True) - valmask = value._mask - if valmask is nomask: - d[i:j][~mindx] = filled(value) - elif valmask.size > 1: - d[i:j][~mindx] = value[~valmask] - m[i:j][valmask] = True - #..... - if not m.any(): - self._mask = nomask - else: - self._mask = m - #............................................ - # If we don't want to crash the performance, we better leave __getattribute__ alone... -# def __getattribute__(self, name): -# """x.__getattribute__('name') = x.name -#Returns the chosen attribute. -#If the attribute cannot be directly accessed, checks the _data section. -# """ -# try: -# return ndarray.__getattribute__(self, name) -# except AttributeError: -# pass -# try: -# return self._data.__getattribute__(name) -# except AttributeError: -# raise AttributeError - #............................................ - def __str__(self): - """x.__str__() <==> str(x) -Calculates the string representation, using masked for fill if it is enabled. -Otherwise, fills with fill value. - """ - if masked_print_option.enabled(): - f = masked_print_option - if self is masked: - return str(f) - m = self._mask - if m is nomask: - res = self._data - else: - if m.shape == () and m: - return str(f) - # convert to object array to make filled work -#CHECK: the two lines below seem more robust than the self._data.astype -# res = numeric.empty(self._data.shape, object_) -# numeric.putmask(res,~m,self._data) - res = self._data.astype("|O8") - res[self._mask] = f - else: - res = self.filled(self.fill_value) - return str(res) - - def __repr__(self): - """x.__repr__() <==> repr(x) -Calculates the repr representation, using masked for fill if it is enabled. -Otherwise fill with fill value. - """ - with_mask = """\ -masked_%(name)s(data = - %(data)s, - mask = - %(mask)s, - fill_value=%(fill)s) -""" - with_mask1 = """\ -masked_%(name)s(data = %(data)s, - mask = %(mask)s, - fill_value=%(fill)s) -""" - n = len(self.shape) - name = repr(self._data).split('(')[0] - if n <= 1: - return with_mask1 % { - 'name': name, - 'data': str(self), - 'mask': str(self._mask), - 'fill': str(self.fill_value), - } - return with_mask % { - 'name': name, - 'data': str(self), - 'mask': str(self._mask), - 'fill': str(self.fill_value), - } - #............................................ - def __abs__(self): - """x.__abs__() <==> abs(x) -Returns a masked array of the current subclass, with the new `_data` -the absolute of the inital `_data`. - """ - return self.__class__(self._data.__abs__(), mask=self._mask, - fill_value = self._fill_value, **self.options) - # - def __neg__(self): - """x.__abs__() <==> neg(x) -Returns a masked array of the current subclass, with the new `_data` -the negative of the inital `_data`.""" - try: - return self.__class__(self._data.__neg__(), mask=self._mask, - fill_value = self._fill_value, **self.options) - except MAError: - return negative(self) - # - def __iadd__(self, other): - "Adds other to self in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) - m = getmask(other) - self._data += f - if self._mask is nomask: - self._mask = m - elif m is not nomask: - self._mask += m - return self - # - def __isub__(self, other): - "Subtracts other from self in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) - m = getmask(other) - self._data -= f - if self._mask is nomask: - self._mask = m - elif m is not nomask: - self._mask += m - return self - # - def __imul__(self, other): - "Multiplies self by other in place." - f = convert_typecode(filled(other, 1), self._data.dtype.char) - m = getmask(other) - self._data *= f - if self._mask is nomask: - self._mask = m - elif m is not nomask: - self._mask += m - return self - # - def __idiv__(self, other): - "Divides self by other in place." - f = convert_typecode(filled(other, 0), self._data.dtype.char) - mo = getmask(other) - result = divide(self, masked_array(f, mask=mo)) - self._data = result._data - dm = result._mask - if dm is not self._mask: - self._mask = dm - return self - -# # -# def __eq__(self, other): -# return equal(self,other) -# -# def __ne__(self, other): -# return not_equal(self,other) -# -# def __lt__(self, other): -# return less(self,other) -# -# def __le__(self, other): -# return less_equal(self,other) -# -# def __gt__(self, other): -# return greater(self,other) -# -# def __ge__(self, other): -# return greater_equal(self,other) - - #............................................ - def __float__(self): - "Converts self to float." - if self._mask is not nomask: - warnings.warn("Warning: converting a masked element to nan.") - return numpy.nan - #raise MAError, 'Cannot convert masked element to a Python float.' - return float(self._data.item()) - - def __int__(self): - "Converts self to int." - if self._mask is not nomask: - raise MAError, 'Cannot convert masked element to a Python int.' - return int(self._data.item()) - - @property - def dtype(self): - """returns the data type of `_data`.""" - return self._data.dtype - - def astype (self, tc): - """Returns self as an array of given type. -Subclassing is preserved.""" - if tc == self._data.dtype: - return self - try: - return self.__class__(self, mask=self._mask, dtype=tc, copy=True, - **self.options) - except: -# d = self._data.astype(tc) - return self.__class__(self._data.astype(tc), mask=self._mask, - dtype=tc, **self.options) -# -# - #............................................ - def harden_mask(self): - "Forces the mask to hard" - self._hardmask = True - def soften_mask(self): - "Forces the mask to soft" - self._hardmask = False - #............................................ - #TODO: FIX THAT: THAT"S NOT A REAL FLATITER - def _get_flat(self): - """Calculates the flat value. - """ - if self._mask is nomask: - return masked_array(self._data.ravel(), mask=nomask, copy=False, - fill_value = self._fill_value, - **self.options) - else: - return masked_array(self._data.ravel(), mask=self._mask.ravel(), - copy=False, fill_value = self._fill_value, - **self.options) - # - def _set_flat (self, value): - "x.flat = value" - y = self.ravel() - y[:] = value - # - flat = property(fget=_get_flat, fset=_set_flat, doc="Flat version") - # - #............................................ - def _get_real(self): - "Returns the real part of a complex array." - return self.__class__(self._data.real, mask=self.mask, - fill_value = self._fill_value, **self.options) - def _set_real (self, value): - "Sets the real part of a complex array to `value`." - y = self.real - y[...] = value - - real = property(fget=_get_real, fset=_set_real, doc="Get it real!") - - def _get_imaginary(self): - "Returns the imaginary part of a complex array." - return self.__class__(self._data.imag, mask=self.mask, - fill_value = self._fill_value, **self.options) - - def _set_imaginary (self, value): - "Sets the imaginary part of a complex array to `value`." - y = self.imaginary - y[...] = value - - imag = property(fget=_get_imaginary, fset=_set_imaginary, - doc="Imaginary part.") - imaginary = imag - #............................................ - def _get_mask(self): - """Returns the current mask.""" - return self._mask - def _set_mask(self, mask): - """Sets the mask to `mask`.""" - mask = make_mask(mask, copy=False, small_mask=True) - if mask is not nomask: - if mask.size != self._data.size: - raise ValueError, "Inconsistent shape between data and mask!" - if mask.shape != self._data.shape: - mask.shape = self._data.shape - self._mask = mask - else: - self._mask = nomask - mask = property(fget=_get_mask, fset=_set_mask, doc="Mask") - #............................................ - def get_fill_value(self): - "Returns the filling value." - return self._fill_value - - def set_fill_value(self, value=None): - """Sets the filling value to `value`. -If None, uses the default, based on the data type.""" - if value is None: - value = default_fill_value(self._data) - self._fill_value = value - - fill_value = property(fget=get_fill_value, fset=set_fill_value, - doc="Filling value") - - def filled(self, fill_value=None): - """Returns an array of the same class as `_data`, - with masked values filled with `fill_value`. -Subclassing is preserved. - -If `fill_value` is None, uses self.fill_value. - """ - d = self._data - m = self._mask - if m is nomask: - return d - # - if fill_value is None: - value = self.fill_value - else: - value = fill_value - # - if self is masked_singleton: - result = numeric.asanyarray(value) - else: - result = d.copy() - try: - result[m] = value - except (TypeError, AttributeError): - value = numeric.array(value, dtype=object) - d = d.astype(object) - result = fromnumeric.choose(m, (d, value)) - except IndexError: - #ok, if scalar - if d.shape: - raise - elif m: - result = numeric.array(value, dtype=d.dtype) - else: - result = d - return result - - def compressed(self): - "A 1-D array of all the non-masked data." - d = self._data.ravel() - if self._mask is nomask: - return d - else: - return d[~self._mask.ravel()] - #............................................ - def count(self, axis=None): - """Counts the non-masked elements of the array along a given axis, -and returns a masked array where the mask is True where all data are masked. -If `axis` is None, counts all the non-masked elements, and returns either a -scalar or the masked singleton.""" - m = self._mask - s = self._data.shape - ls = len(s) - if m is nomask: - if ls == 0: - return 1 - if ls == 1: - return s[0] - if axis is None: - return self._data.size - else: - n = s[axis] - t = list(s) - del t[axis] - return numeric.ones(t) * n - n1 = fromnumeric.size(m, axis) - n2 = m.astype(int_).sum(axis) - if axis is None: - return (n1-n2) - else: - return masked_array(n1 - n2) - #............................................ - def _get_shape(self): - "Returns the current shape." - return self._data.shape - # - def _set_shape (self, newshape): - "Sets the array's shape." - self._data.shape = newshape - if self._mask is not nomask: - #self._mask = self._mask.copy() - self._mask.shape = newshape - # - shape = property(fget=_get_shape, fset=_set_shape, - doc="Shape of the array, as a tuple.") - # - def _get_size(self): - "Returns the current size." - return self._data.size - size = property(fget=_get_size, - doc="Size (number of elements) of the array.") - # - def _get_ndim(self): - "Returns the number of dimensions." - return self._data.ndim - ndim = property(fget=_get_ndim, - doc="Number of dimensions of the array.") - # - def reshape (self, *s): - """Reshapes the array to shape s. -Returns a new masked array. -If you want to modify the shape in place, please use `a.shape = s`""" - if self._mask is not nomask: - return self.__class__(self._data.reshape(*s), - mask=self._mask.reshape(*s), - fill_value=self.fill_value, **self.options) - else: - return self.__class__(self._data.reshape(*s), - fill_value=self.fill_value, **self.options) - # - def repeat(self, repeats, axis=None): - """Repeat elements of `a` `repeats` times along `axis`. -`repeats` is a sequence of length `a.shape[axis]` telling how many times -each element should be repeated. -The mask is repeated accordingly. - """ - f = self.filled() - if isinstance(repeats, types.IntType): - if axis is None: - num = f.size - else: - num = f.shape[axis] - repeats = tuple([repeats]*num) - - m = self._mask - if m is not nomask: - m = fromnumeric.repeat(m, repeats, axis) - d = fromnumeric.repeat(f, repeats, axis) - return self.__class__(d, mask=m, fill_value=self.fill_value, - **self.options) - # - def resize(self, newshape, refcheck=True, order=False): - """Attempts to modify size and shape of self inplace. - The array must own its own memory and not be referenced by other arrays. - Returns None. - """ - try: - self._data.resize(newshape,) - if self.mask is not nomask: - self._mask.resize(newshape,) - except ValueError: - msg = "Cannot resize an array that has been referenced or "+\ - "is referencing another array in this way.\n"+\ - "Use the resize function." - raise ValueError, msg - return None - # - def flatten(self): - """Flattens the array in place. - """ - flatsize = self.size - self._data.resize((flatsize,)) - if self.mask is not nomask: - self._mask.resize((flatsize,)) - return self - - # - def put(self, indices, values, mode='raise'): - """Sets storage-indexed locations to corresponding values. -a.put(values, indices, mode) sets a.flat[n] = values[n] for each n in indices. -`values` can be scalar or an array shorter than indices, and it will be repeat, -if necessary. -If `values` has some masked values, the initial mask is updated in consequence, -else the corresponding values are unmasked. - """ - #TODO: Check that - (d, m) = (self._data, self._mask) - ind = filled(indices) - v = filled(values) - d.put(ind, v, mode=mode) - if m is not nomask: - if getmask(values) is not nomask: - m.put(ind, values._mask, mode=mode) - else: - m.put(ind, False, mode=mode) - self._mask = make_mask(m, copy=False, small_mask=True) - #............................................ - def ids (self): - """Return the ids of the data and mask areas.""" - return (id(self._data), id(self._mask)) - #............................................ - def all(self, axis=None): - """a.all(axis) returns True if all entries along the axis are True. - Returns False otherwise. If axis is None, uses the flatten array. - Masked data are considered as True during computation. - Outputs a masked array, where the mask is True if all data are masked along the axis. - """ - d = filled(self, True).all(axis) - m = self._mask.all(axis) - return self.__class__(d, mask=m, dtype=bool_, - fill_value=self._fill_value, **self.options) - def any(self, axis=None): - """a.any(axis) returns True if some or all entries along the axis are True. - Returns False otherwise. If axis is None, uses the flatten array. - Masked data are considered as False during computation. - Outputs a masked array, where the mask is True if all data are masked along the axis. - """ - d = filled(self, False).any(axis) - m = self._mask.all(axis) - return self.__class__(d, mask=m, dtype=bool_, - fill_value=self._fill_value, **self.options) - def nonzero(self): - """a.nonzero() returns a tuple of arrays - - Returns a tuple of arrays, one for each dimension of a, - containing the indices of the non-zero elements in that - dimension. The corresponding non-zero values can be obtained - with - a[a.nonzero()]. - - To group the indices by element, rather than dimension, use - transpose(a.nonzero()) - instead. The result of this is always a 2d array, with a row for - each non-zero element.""" - return numeric.asarray(self.filled(0)).nonzero() - #............................................ - def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None): - """a.trace(offset=0, axis1=0, axis2=1, dtype=None, out=None) -Returns the sum along the offset diagonal of the array's indicated `axis1` and `axis2`. - """ - #TODO: What are we doing with `out`? - (d,m) = (self._data, self._mask) - if m is nomask: - return d.trace(offset=offset, axis1=axis1, axis2=axis2, - out=out).astype(dtype) - else: - D = self.diagonal(offset=offset, axis1=axis1, axis2=axis2, - ).astype(dtype) - return D.sum(axis=None) - #............................................ - def sum(self, axis=None, dtype=None): - """a.sum(axis=None, dtype=None) -Sums the array `a` over the given axis `axis`. -Masked values are set to 0. -If `axis` is None, applies to a flattened version of the array. - """ - if self._mask is nomask: -# if axis is None: -# return self._data.sum(None, dtype=dtype) - return self.__class__(self._data.sum(axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) - else: -# if axis is None: -# return self.filled(0).sum(None, dtype=dtype) - return self.__class__(self.filled(0).sum(axis, dtype=dtype), - mask=self._mask.all(axis), - fill_value=self.fill_value, **self.options) - - def cumsum(self, axis=None, dtype=None): - """a.cumprod(axis=None, dtype=None) -Returns the cumulative sum of the elements of array `a` along the given axis `axis`. -Masked values are set to 0. -If `axis` is None, applies to a flattened version of the array. - """ - if self._mask is nomask: -# if axis is None: -# return self._data.cumsum(None, dtype=dtype) - return self.__class__(self._data.cumsum(axis=axis, dtype=dtype), - fill_value=self.fill_value, **self.options) - else: -# if axis is None: -# return self.filled(0).cumsum(None, dtype=dtype) - return self.__class__(self.filled(0).cumsum(axis=axis, dtype=dtype), - mask=self._mask, fill_value=self.fill_value, - **self.options) - - def prod(self, axis=None, dtype=None): - """a.prod(axis=None, dtype=None) -Returns the product of the elements of array `a` along the given axis `axis`. -Masked elements are set to 1. -If `axis` is None, applies to a flattened version of the array. - """ - if self._mask is nomask: -# if axis is None: -# return self._data.prod(None, dtype=dtype) - return self.__class__(self._data.prod(axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) -# return self.__class__(self._data.prod(axis=axis, dtype=dtype)) - else: -# if axis is None: -# return self.filled(1).prod(None, dtype=dtype) - return self.__class__(self.filled(1).prod(axis=axis, dtype=dtype), - mask=self._mask.all(axis), - fill_value=self.fill_value, - **self.options) - product = prod - - def cumprod(self, axis=None, dtype=None): - """a.cumprod(axis=None, dtype=None) -Returns the cumulative product of ethe lements of array `a` along the given axis `axis`. -Masked values are set to 1. -If `axis` is None, applies to a flattened version of the array. - """ - if self._mask is nomask: -# if axis is None: -# return self._data.cumprod(None, dtype=dtype) - return self.__class__(self._data.cumprod(axis=axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) - else: -# if axis is None: -# return self.filled(1).cumprod(None, dtype=dtype) - return self.__class__(self.filled(1).cumprod(axis=axis, dtype=dtype), - mask=self._mask, - fill_value=self.fill_value, **self.options) - - def mean(self, axis=None, dtype=None): - """a.mean(axis=None, dtype=None) - - Averages the array over the given axis. If the axis is None, - averages over all dimensions of the array. Equivalent to - - a.sum(axis, dtype) / size(a, axis). - - The optional dtype argument is the data type for intermediate - calculations in the sum. - - Returns a masked array, of the same class as a. - """ - if self._mask is nomask: -# if axis is None: -# return self._data.mean(axis=None, dtype=dtype) - return self.__class__(self._data.mean(axis=axis, dtype=dtype), - mask=nomask, fill_value=self.fill_value, - **self.options) - else: - dsum = fromnumeric.sum(self.filled(0), axis=axis, dtype=dtype) - cnt = self.count(axis=axis) - mask = self._mask.all(axis) - if axis is None and mask: - return masked - return self.__class__(dsum*1./cnt, mask=mask, - fill_value=self.fill_value, **self.options) - - def anom(self, axis=None, dtype=None): - """a.anom(axis=None, dtype=None) - Returns the anomalies, or deviation from the average. - """ - m = self.mean(axis, dtype) - if not axis: - return (self - m) - else: - return (self - expand_dims(m,axis)) - - def var(self, axis=None, dtype=None): - """a.var(axis=None, dtype=None) -Returns the variance, a measure of the spread of a distribution. - -The variance is the average of the squared deviations from the mean, -i.e. var = mean((x - x.mean())**2). - """ - if self._mask is nomask: -# if axis is None: -# return self._data.var(axis=None, dtype=dtype) - return self.__class__(self._data.var(axis=axis, dtype=dtype), - mask=nomask, - fill_value=self.fill_value, **self.options) - else: - cnt = self.count(axis=axis) - danom = self.anom(axis=axis, dtype=dtype) - danom *= danom - dvar = danom.sum(axis) / cnt -# dvar /= cnt - if axis is None: - return dvar - return self.__class__(dvar, - mask=mask_or(self._mask.all(axis), (cnt==1)), - fill_value=self.fill_value, **self.options) - - def std(self, axis=None, dtype=None): - """a.std(axis=None, dtype=None) -Returns the standard deviation, a measure of the spread of a distribution. - -The standard deviation is the square root of the average of the squared -deviations from the mean, i.e. std = sqrt(mean((x - x.mean())**2)). - """ - dvar = self.var(axis,dtype) - if axis is None: - if dvar is masked: - return masked - else: - # Should we use umath.sqrt instead ? - return sqrt(dvar) - return self.__class__(sqrt(dvar._data), mask=dvar._mask, - dtype = self.dtype, - fill_value=self.fill_value, **self.options) - #............................................ - def argsort(self, axis=None, fill_value=None, kind='quicksort', - order=None): - """Returns an array of indices that sort 'a' along the specified axis. - Masked values are filled beforehand to `fill_value`. - If `fill_value` is None, uses the default for the data type. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `kind` : String *['quicksort']* - Sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort' - - Returns: array of indices that sort 'a' along the specified axis. - - This method executes an indirect sort along the given axis using the - algorithm specified by the kind keyword. It returns an array of indices of - the same shape as 'a' that index data along the given axis in sorted order. - - The various sorts are characterized by average speed, worst case - performance, need for work space, and whether they are stable. A stable - sort keeps items with the same key in the same relative order. The three - available algorithms have the following properties: - - |------------------------------------------------------| - | kind | speed | worst case | work space | stable| - |------------------------------------------------------| - |'quicksort'| 1 | O(n^2) | 0 | no | - |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | - |'heapsort' | 3 | O(n*log(n)) | 0 | no | - |------------------------------------------------------| - - All the sort algorithms make temporary copies of the data when the sort is not - along the last axis. Consequently, sorts along the last axis are faster and use - less space than sorts along other axis. - """ - if fill_value is None: - fill_value = default_fill_value(self._data) - d = self.filled(fill_value) - if axis is None: - return d.argsort(kind=kind, order=order) - return d.argsort(axis, kind=kind, order=order) - - def argmin(self, axis=None, fill_value=None): - """Returns the array of indices for the minimum values of `a` along the - specified axis. - Masked values are treated as if they had the value `fill_value`. - If `fill_value` is None, the default for the data type is used. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `fill_value` : var *[None]* - Default filling value. If None, uses the data type default. - """ - if fill_value is None: - fill_value = default_fill_value(self._data) - d = self.filled(fill_value) - if axis is None: - return d.argmin() - return d.argmin(axis) - - def argmax(self, axis=None, fill_value=None): - """Returns the array of indices for the maximum values of `a` along the - specified axis. - Masked values are treated as if they had the value `fill_value`. - If `fill_value` is None, the default for the data type is used. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `fill_value` : var *[None]* - Default filling value. If None, uses the data type default. - """ - if fill_value is None: - fill_value = default_fill_value(self._data) - try: - fill_value = - fill_value - except: - pass - d = self.filled(fill_value) - if axis is None: - return d.argmax() - return d.argmax(axis) - - def sort(self, axis=-1, kind='quicksort', order=None, endwith=True): - """ - Sort a along the given axis. - - Keyword arguments: - - axis -- axis to be sorted (default -1) - kind -- sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort'. - order -- If a has fields defined, then the order keyword can be the - field name to sort on or a list (or tuple) of field names - to indicate the order that fields should be used to define - the sort. - endwith--Boolean flag indicating whether missing values (if any) should - be forced in the upper indices (at the end of the array) or - lower indices (at the beginning). - - Returns: None. - - This method sorts 'a' in place along the given axis using the algorithm - specified by the kind keyword. - - The various sorts may characterized by average speed, worst case - performance, need for work space, and whether they are stable. A stable - sort keeps items with the same key in the same relative order and is most - useful when used with argsort where the key might differ from the items - being sorted. The three available algorithms have the following properties: - - |------------------------------------------------------| - | kind | speed | worst case | work space | stable| - |------------------------------------------------------| - |'quicksort'| 1 | O(n^2) | 0 | no | - |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | - |'heapsort' | 3 | O(n*log(n)) | 0 | no | - |------------------------------------------------------| - - All the sort algorithms make temporary copies of the data when the sort is - not along the last axis. Consequently, sorts along the last axis are faster - and use less space than sorts along other axis. - - """ - - if endwith: - filler = minimum_fill_value(self.dtype) - else: - filler = maximum_fill_value(self.dtype) - indx = self.filled(filler).argsort(axis=axis,kind=kind,order=order) - self._data = self._data[indx] - m = self._mask - if m is not nomask: - self._mask = m[indx] - return - #............................................ - # Backwards Compatibility. Heck... - @property - def data(self): - """Returns the `_data` part of the MaskedArray. -You should really use `_data` instead...""" - return self._data - def raw_data(self): - """Returns the `_data` part of the MaskedArray. -You should really use `_data` instead...""" - return self._data - -##.............................................................................. - - - -#class _arithmethods: -# """Defines a wrapper for arithmetic methods. -#Instead of directly calling a ufunc, the corresponding method of the `array._data` -#object is called instead. -# """ -# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): -# """ -#:Parameters: -# - `methodname` (String) : Method name. -# - `fill_self` (Float *[0]*) : Fill value for the instance. -# - `fill_other` (Float *[0]*) : Fill value for the target. -# - `domain` (Domain object *[None]*) : Domain of non-validity. -# """ -# self.methodname = methodname -# self.fill_self = fill_self -# self.fill_other = fill_other -# self.domain = domain -# # -# def __call__ (self, instance, other, *args): -# "Execute the call behavior." -# m_self = instance._mask -# m_other = getmask(other) -# base = filled(instance,self.fill_self) -# target = filled(other, self.fill_other) -# if self.domain is not None: -# # We need to force the domain to a ndarray only. -# if self.fill_other > self.fill_self: -# domain = self.domain(base, target) -# else: -# domain = self.domain(target, base) -# if domain.any(): -# #If `other` is a subclass of ndarray, `filled` must have the -# # same subclass, else we'll lose some info. -# #The easiest then is to fill `target` instead of creating -# # a pure ndarray. -# #Oh, and we better make a copy! -# if isinstance(other, ndarray): -# if target is other: -# # We don't want to modify other: let's copy target, then -# target = target.copy() -# target[:] = numeric.where(fromnumeric.asarray(domain), -# self.fill_other, target) -# else: -# target = numeric.where(fromnumeric.asarray(domain), -# self.fill_other, target) -# m_other = mask_or(m_other, domain) -# m = mask_or(m_self, m_other) -# method = getattr(base, self.methodname) -# return instance.__class__(method(target, *args), mask=m) -# # -# def patch(self): -# """Applies the method `func` from class `method` to MaskedArray""" -# return types.MethodType(self,None,MaskedArray) -#.............................................................................. -class _arithmethods(object): - """Defines a wrapper for arithmetic methods. -Instead of directly calling a ufunc, the corresponding method of the `array._data` -object is called instead. - """ - def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): - """ -:Parameters: - - `methodname` (String) : Method name. - - `fill_self` (Float *[0]*) : Fill value for the instance. - - `fill_other` (Float *[0]*) : Fill value for the target. - - `domain` (Domain object *[None]*) : Domain of non-validity. - """ - self.methodname = methodname - self.fill_self = fill_self - self.fill_other = fill_other - self.domain = domain - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.methodname).__doc__ - except: - return getattr(numpy, self.methodname).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - m_self = instance._mask - m_other = getmask(other) - base = filled(instance,self.fill_self) - target = filled(other, self.fill_other) - if self.domain is not None: - # We need to force the domain to a ndarray only. - if self.fill_other > self.fill_self: - domain = self.domain(base, target) - else: - domain = self.domain(target, base) - if domain.any(): - #If `other` is a subclass of ndarray, `filled` must have the - # same subclass, else we'll lose some info. - #The easiest then is to fill `target` instead of creating - # a pure ndarray. - #Oh, and we better make a copy! - if isinstance(other, ndarray): - if target is other or target is base: - # We don't want to modify other: let's copy target, then - # Same if target us base, instead... - target = target.copy() - target[:] = numeric.where(fromnumeric.asarray(domain), - self.fill_other, target) - else: - target = numeric.where(fromnumeric.asarray(domain), - self.fill_other, target) - m_other = mask_or(m_other, domain) - m = mask_or(m_self, m_other) - method = getattr(base, self.methodname) - return instance.__class__(method(target, *args), mask=m, - fill_value=instance.fill_value, - **instance.options) -#...................................... -class _compamethods(object): - """Defines comparison methods (eq, ge, gt...). -Instead of calling a ufunc, the method of the masked object is called. - """ - def __init__ (self, methodname, fill_self=0, fill_other=0): - """ -:Parameters: - - `methodname` (String) : Method name. - - `fill_self` (Float *[0]*) : Fill value for the instance. - - `fill_other` (Float *[0]*) : Fill value for the target. - - `domain` (Domain object *[None]*) : Domain of non-validity. - """ - self.methodname = methodname - self.fill_self = fill_self - self.fill_other = fill_other - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self.methodname).__doc__ - except: - return getattr(numpy, self.methodname).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - m = mask_or(instance._mask, getmask(other), small_mask=False) - base = instance.filled(self.fill_self) - target = filled(other, self.fill_other) - method = getattr(base, self.methodname) - return instance.__class__(method(target, *args), mask=m, - **instance.options) -#.......................................................... -MaskedArray.__add__ = _arithmethods('__add__') -MaskedArray.__radd__ = _arithmethods('__add__') -MaskedArray.__sub__ = _arithmethods('__sub__') -MaskedArray.__rsub__ = _arithmethods('__rsub__') -MaskedArray.__pow__ = _arithmethods('__pow__') -MaskedArray.__mul__ = _arithmethods('__mul__', 1, 1) -MaskedArray.__rmul__ = _arithmethods('__mul__', 1, 1) -MaskedArray.__div__ = _arithmethods('__div__', 0, 1, - domain_safe_divide()) -MaskedArray.__rdiv__ = _arithmethods('__rdiv__', 1, 0, - domain_safe_divide()) -MaskedArray.__truediv__ = _arithmethods('__truediv__', 0, 1, - domain_safe_divide()) -MaskedArray.__rtruediv__ = _arithmethods('__rtruediv__', 1, 0, - domain_safe_divide()) -MaskedArray.__floordiv__ = _arithmethods('__floordiv__', 0, 1, - domain_safe_divide()) -MaskedArray.__rfloordiv__ = _arithmethods('__rfloordiv__', 1, 0, - domain_safe_divide()) -MaskedArray.__eq__ = _compamethods('__eq__') -MaskedArray.__ne__ = _compamethods('__ne__') -MaskedArray.__le__ = _compamethods('__le__') -MaskedArray.__lt__ = _compamethods('__lt__') -MaskedArray.__ge__ = _compamethods('__ge__') -MaskedArray.__gt__ = _compamethods('__gt__') -#####-------------------------------------------------------------------------- -#---- --- Shortcuts --- -#####--------------------------------------------------------------------------- -def isMaskedArray (x): - "Is x a masked array, that is, an instance of MaskedArray?" - return isinstance(x, MaskedArray) -isarray = isMaskedArray -isMA = isMaskedArray #backward compatibility -#masked = MaskedArray(0, int, mask=1) -masked_singleton = MaskedArray(0, dtype=int_, mask=True) -masked = masked_singleton - -masked_array = MaskedArray -def array(data, dtype=None, copy=False, order=False, mask=nomask, - keep_mask=True, small_mask=True, hard_mask=None, fill_value=None): - """array(data, dtype=None, copy=True, order=False, mask=nomask, - keep_mask=True, small_mask=True, fill_value=None) -Acts as shortcut to MaskedArray, with options in a different order for convenience. -And backwards compatibility... - """ - return MaskedArray(data, mask=mask, dtype=dtype, copy=copy, - keep_mask=keep_mask, small_mask=small_mask, - hard_mask=hard_mask, fill_value=fill_value) - -def is_masked(x): - """Returns whether x has some masked values.""" - m = getmask(x) - if m is nomask: - return False - elif m.any(): - return True - return False - - -#####-------------------------------------------------------------------------- -#---- --- Patch methods --- -#####-------------------------------------------------------------------------- -#class _arraymethod: -# """Defines a wrapper for basic array methods. -#Upon call, returns a masked array, where the new `_data` array is the output -#of the corresponding method called on the original `_data`. -# -#If `onmask` is True, the new mask is the output of the method calld on the initial mask. -#If `onmask` is False, the new mask is just a reference to the initial mask. -# -#:Parameters: -# `funcname` : String -# Name of the function to apply on data. -# `onmask` : Boolean *[True]* -# Whether the mask must be processed also (True) or left alone (False). -# """ -# def __init__(self, funcname, onmask=True): -# self._name = funcname -# self._onmask = onmask -# self.__doc__ = getattr(ndarray, self._name).__doc__ -# def __call__(self, instance, *args, **params): -# methodname = self._name -# (d,m) = (instance._data, instance._mask) -# C = instance.__class__ -# if m is nomask: -# return C(getattr(d,methodname).__call__(*args, **params)) -# elif self._onmask: -# return C(getattr(d,methodname).__call__(*args, **params), -# mask=getattr(m,methodname)(*args, **params) ) -# else: -# return C(getattr(d,methodname).__call__(*args, **params), mask=m) -# -# def patch(self): -# "Adds the new method to MaskedArray." -# return types.MethodType(self, None, MaskedArray) -##...................................... -#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() -#MaskedArray.diagonal = _arraymethod('diagonal').patch() -#MaskedArray.take = _arraymethod('take').patch() -#MaskedArray.ravel = _arraymethod('ravel').patch() -#MaskedArray.transpose = _arraymethod('transpose').patch() -#MaskedArray.T = _arraymethod('transpose').patch() -#MaskedArray.swapaxes = _arraymethod('swapaxes').patch() -#MaskedArray.clip = _arraymethod('clip', onmask=False).patch() -#MaskedArray.compress = _arraymethod('compress').patch() -#MaskedArray.resize = _arraymethod('resize').patch() -#MaskedArray.copy = _arraymethod('copy').patch() - -class _arraymethod(object): - """Defines a wrapper for basic array methods. -Upon call, returns a masked array, where the new `_data` array is the output -of the corresponding method called on the original `_data`. - -If `onmask` is True, the new mask is the output of the method calld on the initial mask. -If `onmask` is False, the new mask is just a reference to the initial mask. - -:Parameters: - `funcname` : String - Name of the function to apply on data. - `onmask` : Boolean *[True]* - Whether the mask must be processed also (True) or left alone (False). - """ - def __init__(self, funcname, onmask=True): - self._name = funcname - self._onmask = onmask - self.obj = None - self.__doc__ = self.getdoc() - # - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self._name).__doc__ - except: - return getattr(numpy, self._name).__doc__ - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__(self, *args, **params): - methodname = self._name - obj = self.obj - (d, m) = (obj._data, obj._mask) - (t, f) = (obj.dtype, obj._fill_value) - C = self.obj.__class__ - if m is nomask: - return C(getattr(d,methodname).__call__(*args, **params), - dtype=t, fill_value=f) - elif self._onmask: - return C(getattr(d,methodname).__call__(*args, **params), - mask=getattr(m,methodname)(*args, **params), - dtype=t, fill_value=f, **obj.options) - else: - return C(getattr(d,methodname).__call__(*args, **params), mask=m, - dtype=t, fill_value=f, **obj.options) -#...................................... -MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate') -MaskedArray.copy = _arraymethod('copy') -MaskedArray.diagonal = _arraymethod('diagonal') -MaskedArray.take = _arraymethod('take') -MaskedArray.ravel = _arraymethod('ravel') -MaskedArray.transpose = _arraymethod('transpose') -MaskedArray.T = property(fget=lambda self:self.transpose()) -MaskedArray.swapaxes = _arraymethod('swapaxes') -MaskedArray.clip = _arraymethod('clip', onmask=False) -MaskedArray.compress = _arraymethod('compress') -MaskedArray.copy = _arraymethod('copy') -MaskedArray.squeeze = _arraymethod('squeeze') - -#####-------------------------------------------------------------------------- -#---- --- Extrema functions --- -#####-------------------------------------------------------------------------- -class _minimum_operation: - "Object to calculate minima" - def __init__ (self): - """minimum(a, b) or minimum(a) -In one argument case, returns the scalar minimum. - """ - pass - #......... - def __call__ (self, a, b=None): - "Execute the call behavior." - if b is None: - m = getmask(a) - if m is nomask: - d = amin(filled(a).ravel()) - return d - ac = a.compressed() - if len(ac) == 0: - return masked - else: - return amin(ac) - else: - return where(less(a, b), a, b) - #......... - def reduce(self, target, axis=0): - """Reduces `target` along the given `axis`.""" - m = getmask(target) - if m is nomask: - t = filled(target) - return masked_array (umath.minimum.reduce (t, axis)) - else: - t = umath.minimum.reduce(filled(target, minimum_fill_value(target)), - axis) - m = umath.logical_and.reduce(m, axis) -# return masked_array(t, mask=m, fill_value=get_fill_value(target)) - try: - return target.__class__(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - except AttributeError: - return masked_array(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - #......... - def outer(self, a, b): - "Returns the function applied to the outer product of a and b." - ma = getmask(a) - mb = getmask(b) - if ma is nomask and mb is nomask: - m = nomask - else: - ma = getmaskarray(a) - mb = getmaskarray(b) - m = logical_or.outer(ma, mb) - d = umath.minimum.outer(filled(a), filled(b)) - return masked_array(d, mask=m) - -def min(array, axis=None, out=None): - """Returns the minima along the given axis. -If `axis` is None, applies to the flattened array.""" - if out is not None: - raise TypeError("Output arrays Unsupported for masked arrays") - if axis is None: - return minimum(array) - else: - return minimum.reduce(array, axis) -#................................................ -class _maximum_operation: - "Object to calculate maxima" - def __init__ (self): - """maximum(a, b) or maximum(a) - In one argument case returns the scalar maximum. - """ - pass - #......... - def __call__ (self, a, b=None): - "Executes the call behavior." - if b is None: - m = getmask(a) - if m is nomask: - d = amax(filled(a).ravel()) - return d - ac = a.compressed() - if len(ac) == 0: - return masked - else: - return amax(ac) - else: - return where(greater(a, b), a, b) - #......... - def reduce (self, target, axis=0): - """Reduces target along the given axis.""" - m = getmask(target) - if m is nomask: - t = filled(target) - return masked_array(umath.maximum.reduce (t, axis)) - else: - t = umath.maximum.reduce(filled(target, maximum_fill_value(target)), - axis) - m = umath.logical_and.reduce(m, axis) - try: - return target.__class__(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - except AttributeError: - return masked_array(t, mask=m, dtype=t.dtype, - fill_value=get_fill_value(target)) - #......... - def outer (self, a, b): - "Returns the function applied to the outer product of a and b." - ma = getmask(a) - mb = getmask(b) - if ma is nomask and mb is nomask: - m = nomask - else: - ma = getmaskarray(a) - mb = getmaskarray(b) - m = logical_or.outer(ma, mb) - d = umath.maximum.outer(filled(a), filled(b)) - return masked_array(d, mask=m) - -def max(obj, axis=None, out=None): - """Returns the maxima along the given axis. -If `axis` is None, applies to the flattened array.""" - if out is not None: - raise TypeError("Output arrays Unsupported for masked arrays") - if axis is None: - return maximum(obj) - else: - return maximum.reduce(obj, axis) -#................................................ -def ptp(obj, axis=None): - """a.ptp(axis=None) = a.max(axis)-a.min(axis)""" - try: - return obj.max(axis)-obj.min(axis) - except AttributeError: - return max(obj, axis=axis) - min(obj, axis=axis) -#................................................ -MaskedArray.min = min -MaskedArray.max = max -MaskedArray.ptp = ptp - -#####--------------------------------------------------------------------------- -#---- --- Definition of functions from the corresponding methods --- -#####--------------------------------------------------------------------------- -class _frommethod: - """Defines functions from existing MaskedArray methods. -:ivar _methodname (String): Name of the method to transform. - """ - def __init__(self, methodname): - self._methodname = methodname - self.__doc__ = self.getdoc() - def getdoc(self): - "Returns the doc of the function (from the doc of the method)." - try: - return getattr(MaskedArray, self._methodname).__doc__ - except: - return getattr(numpy, self._methodname).__doc__ - def __call__(self, x, *args, **params): - if isinstance(x, MaskedArray): - return getattr(x, self._methodname).__call__(*args, **params) - #FIXME: As x is not a MaskedArray, we transform it to a ndarray with asarray - #FIXME: ... and call the corresponding method. - #FIXME: Except that sometimes it doesn't work (try reshape([1,2,3,4],(2,2))) - #FIXME: we end up with a "SystemError: NULL result without error in PyObject_Call" - #FIXME: A dirty trick is then to call the initial numpy function... - method = getattr(fromnumeric.asarray(x), self._methodname) - try: - return method(*args, **params) - except SystemError: - return getattr(numpy,self._methodname).__call__(x, *args, **params) - -all = _frommethod('all') -anomalies = anom = _frommethod('anom') -any = _frommethod('any') -conjugate = _frommethod('conjugate') -ids = _frommethod('ids') -nonzero = _frommethod('nonzero') -diagonal = _frommethod('diagonal') -maximum = _maximum_operation() -mean = _frommethod('mean') -minimum = _minimum_operation () -product = _frommethod('prod') -ptp = _frommethod('ptp') -ravel = _frommethod('ravel') -repeat = _frommethod('repeat') -std = _frommethod('std') -sum = _frommethod('sum') -swapaxes = _frommethod('swapaxes') -take = _frommethod('take') -var = _frommethod('var') - -#.............................................................................. -def power(a, b, third=None): - """Computes a**b elementwise. - Masked values are set to 1.""" - if third is not None: - raise MAError, "3-argument power not supported." - ma = getmask(a) - mb = getmask(b) - m = mask_or(ma, mb) - fa = filled(a, 1) - fb = filled(b, 1) - if fb.dtype.char in typecodes["Integer"]: - return masked_array(umath.power(fa, fb), m) - md = make_mask((fa < 0), small_mask=1) - m = mask_or(m, md) - if m is nomask: - return masked_array(umath.power(fa, fb)) - else: - fa[m] = 1 - return masked_array(umath.power(fa, fb), m) - -#.............................................................................. -def argsort(a, axis=None, kind='quicksort', fill_value=None): - """Returns an array of indices that sort 'a' along the specified axis. - Masked values are filled beforehand to `fill_value`. - If `fill_value` is None, uses the default for the data type. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `kind` : String *['quicksort']* - Sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort' - - Returns: array of indices that sort 'a' along the specified axis. - - This method executes an indirect sort along the given axis using the - algorithm specified by the kind keyword. It returns an array of indices of - the same shape as 'a' that index data along the given axis in sorted order. - - The various sorts are characterized by average speed, worst case - performance, need for work space, and whether they are stable. A stable - sort keeps items with the same key in the same relative order. The three - available algorithms have the following properties: - - |------------------------------------------------------| - | kind | speed | worst case | work space | stable| - |------------------------------------------------------| - |'quicksort'| 1 | O(n^2) | 0 | no | - |'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | - |'heapsort' | 3 | O(n*log(n)) | 0 | no | - |------------------------------------------------------| - - All the sort algorithms make temporary copies of the data when the sort is not - along the last axis. Consequently, sorts along the last axis are faster and use - less space than sorts along other axis. - """ - if fill_value is None: - fill_value = default_fill_value(a) - d = filled(a, fill_value) - if axis is None: - return d.argsort(kind=kind) - return d.argsort(axis, kind) - -def argmin(a, axis=None, fill_value=None): - """Returns the array of indices for the minimum values of `a` along the - specified axis. - Masked values are treated as if they had the value `fill_value`. - If `fill_value` is None, the default for the data type is used. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `fill_value` : var *[None]* - Default filling value. If None, uses the data type default. - """ - if fill_value is None: - fill_value = default_fill_value(a) - d = filled(a, fill_value) - if axis is None: - return d.argmin(axis=None) - return d.argmin(axis=axis) - -def argmax(a, axis=None, fill_value=None): - """Returns the array of indices for the maximum values of `a` along the - specified axis. - Masked values are treated as if they had the value `fill_value`. - If `fill_value` is None, the default for the data type is used. - Returns a numpy array. - -:Keywords: - `axis` : Integer *[None]* - Axis to be indirectly sorted (default -1) - `fill_value` : var *[None]* - Default filling value. If None, uses the data type default. - """ - if fill_value is None: - fill_value = default_fill_value(a) - try: - fill_value = - fill_value - except: - pass - d = filled(a, fill_value) - if axis is None: - return d.argmax(axis=None) - return d.argmax(axis=axis) - -def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None): - """ - Sort a along the given axis. - -Keyword arguments: - -axis -- axis to be sorted (default -1) -kind -- sorting algorithm (default 'quicksort') - Possible values: 'quicksort', 'mergesort', or 'heapsort'. -order -- If a has fields defined, then the order keyword can be the - field name to sort on or a list (or tuple) of field names - to indicate the order that fields should be used to define - the sort. -endwith--Boolean flag indicating whether missing values (if any) should - be forced in the upper indices (at the end of the array) or - lower indices (at the beginning). - -Returns: None. - -This method sorts 'a' in place along the given axis using the algorithm -specified by the kind keyword. - -The various sorts may characterized by average speed, worst case -performance, need for work space, and whether they are stable. A stable -sort keeps items with the same key in the same relative order and is most -useful when used with argsort where the key might differ from the items -being sorted. The three available algorithms have the following properties: - -|------------------------------------------------------| -| kind | speed | worst case | work space | stable| -|------------------------------------------------------| -|'quicksort'| 1 | O(n^2) | 0 | no | -|'mergesort'| 2 | O(n*log(n)) | ~n/2 | yes | -|'heapsort' | 3 | O(n*log(n)) | 0 | no | -|------------------------------------------------------| - -All the sort algorithms make temporary copies of the data when the sort is -not along the last axis. Consequently, sorts along the last axis are faster -and use less space than sorts along other axis. - -""" - a = numeric.asanyarray(a) - if fill_value is None: - if endwith: - filler = minimum_fill_value(a) - else: - filler = maximum_fill_value(a) - else: - filler = fill_value -# return - indx = numpy.indices(a.shape).tolist() - indx[axis] = filled(a,filler).argsort(axis=axis,kind=kind,order=order) - return a[indx] - -def compressed(x): - """Returns a compressed version of a masked array (or just the array if it - wasn't masked first).""" - if getmask(x) is None: - return x - else: - return x.compressed() - -def count(a, axis = None): - "Count of the non-masked elements in a, or along a certain axis." - a = masked_array(a) - return a.count(axis) - -def concatenate(arrays, axis=0): - "Concatenates the arrays along the given axis" - #TODO: We lose the subclass, here! We should keep track of the classes... - #TODO: ...and find the max ? the lowest according to MRO? - d = [] - for x in arrays: - d.append(filled(x)) - d = numeric.concatenate(d, axis) - for x in arrays: - if getmask(x) is not nomask: - break - else: - return masked_array(d) - dm = [] - for x in arrays: - dm.append(getmaskarray(x)) - dm = make_mask(numeric.concatenate(dm, axis), copy=False, small_mask=True) - return masked_array(d, mask=dm) - -def expand_dims(x,axis): - """Expand the shape of a by including newaxis before given axis.""" - if isinstance(x, MaskedArray): - (d,m) = (x._data, x._mask) - if m is nomask: - return masked_array(n_expand_dims(d,axis), - dtype=d.dtype, fill_value=x._fill_value) - else: - return masked_array(n_expand_dims(d,axis), - mask=n_expand_dims(m,axis), - dtype=d.dtype, fill_value=x._fill_value) - else: - return n_expand_dims(x,axis) - -#...................................... -def left_shift (a, n): - "Left shift n bits" - m = getmask(a) - if m is nomask: - d = umath.left_shift(filled(a), n) - return masked_array(d) - else: - d = umath.left_shift(filled(a, 0), n) - return masked_array(d, mask=m) - -def right_shift (a, n): - "Right shift n bits" - m = getmask(a) - if m is nomask: - d = umath.right_shift(filled(a), n) - return masked_array(d) - else: - d = umath.right_shift(filled(a, 0), n) - return masked_array(d, mask=m) -#...................................... -def put(a, indices, values, mode='raise'): - """Sets storage-indexed locations to corresponding values. - Values and indices are filled if necessary.""" - # We can't use 'frommethod', the order of arguments is different - try: - return a.put(indices, values, mode=mode) - except AttributeError: - return fromnumeric.asarray(a).put(indices, values, mode=mode) - -def putmask(a, mask, values): #, mode='raise'): - """`putmask(a, mask, v)` results in `a = v` for all places where `mask` is true. -If `v` is shorter than `mask`, it will be repeated as necessary. -In particular `v` can be a scalar or length 1 array.""" - # We can't use 'frommethod', the order of arguments is different - try: - return a.putmask(values, mask) - except AttributeError: - return fromnumeric.asarray(a).putmask(values, mask) - -def transpose(a,axes=None): - """Returns a view of the array with dimensions permuted according to axes. -If `axes` is None (default), returns array with dimensions reversed. - """ - #We can't use 'frommethod', as 'transpose' doesn't take keywords - try: - return a.transpose(axes) - except AttributeError: - return fromnumeric.asarray(a).transpose(axes) - -def reshape(a, new_shape): - """Changes the shape of the array `a` to `new_shape`.""" - #We can't use 'frommethod', it whine about some parameters. Dmmit. - try: - return a.reshape(new_shape) - except AttributeError: - return fromnumeric.asarray(a).reshape(new_shape) - -def resize(x, new_shape): - """resize(a,new_shape) returns a new array with the specified shape. - The total size of the original array can be any size. - The new array is filled with repeated copies of a. If a was masked, the new - array will be masked, and the new mask will be a repetition of the old one. - """ - # We can't use _frommethods here, as N.resize is notoriously whiny. - m = getmask(x) - if m is not nomask: - m = fromnumeric.resize(m, new_shape) - if isinstance(x, MaskedArray): - result = x.__class__(fromnumeric.resize(filled(x), new_shape), mask=m) - else: - result = masked_array(fromnumeric.resize(filled(x), new_shape), mask=m) - result.set_fill_value(get_fill_value(x)) - return result - - -#................................................ -def rank(obj): - """Gets the rank of sequence a (the number of dimensions, not a matrix rank) -The rank of a scalar is zero.""" - return fromnumeric.rank(filled(obj)) -# -def shape(obj): - """Returns the shape of `a` (as a function call which also works on nested sequences). - """ - return fromnumeric.shape(filled(obj)) -# -def size(obj, axis=None): - """Returns the number of elements in the array along the given axis, -or in the sequence if `axis` is None. - """ - return fromnumeric.size(filled(obj), axis) -#................................................ - -#####-------------------------------------------------------------------------- -#---- --- Extra functions --- -#####-------------------------------------------------------------------------- -def where (condition, x, y): - """where(condition, x, y) is x where condition is nonzero, y otherwise. - condition must be convertible to an integer array. - Answer is always the shape of condition. - The type depends on x and y. It is integer if both x and y are - the value masked. - """ - fc = filled(not_equal(condition, 0), 0) - xv = filled(x) - xm = getmask(x) - yv = filled(y) - ym = getmask(y) - d = numeric.choose(fc, (yv, xv)) - md = numeric.choose(fc, (ym, xm)) - m = getmask(condition) - m = make_mask(mask_or(m, md), copy=False, small_mask=True) - return masked_array(d, mask=m) - -def choose (indices, t, out=None, mode='raise'): - "Returns array shaped like indices with elements chosen from t" - #TODO: implement options `out` and `mode`, if possible. - def fmask (x): - "Returns the filled array, or True if ``masked``." - if x is masked: - return 1 - return filled(x) - def nmask (x): - "Returns the mask, True if ``masked``, False if ``nomask``." - if x is masked: - return 1 - m = getmask(x) - if m is nomask: - return 0 - return m - c = filled(indices, 0) - masks = [nmask(x) for x in t] - a = [fmask(x) for x in t] - d = numeric.choose(c, a) - m = numeric.choose(c, masks) - m = make_mask(mask_or(m, getmask(indices)), copy=0, small_mask=1) - return masked_array(d, mask=m) - -def round_(a, decimals=0, out=None): - """Returns reference to result. Copies a and rounds to 'decimals' places. - - Keyword arguments: - decimals -- number of decimals to round to (default 0). May be negative. - out -- existing array to use for output (default copy of a). - - Return: - Reference to out, where None specifies a copy of the original array a. - - Round to the specified number of decimals. When 'decimals' is negative it - specifies the number of positions to the left of the decimal point. The - real and imaginary parts of complex numbers are rounded separately. - Nothing is done if the array is not of float type and 'decimals' is greater - than or equal to 0.""" - if not hasattr(a, "_mask"): - mask = nomask - else: - mask = a._mask - if out is None: - return a.__class__(fromnumeric.round_(a, decimals, None), mask=mask) - else: - out = a.__class__(fromnumeric.round_(a, decimals, out), mask=mask) - return out - -def arange(start, stop=None, step=1, dtype=None): - """Just like range() except it returns a array whose type can be specified - by the keyword argument dtype. - """ - return array(numeric.arange(start, stop, step, dtype), mask=nomask) - -def inner(a, b): - """inner(a,b) returns the dot product of two arrays, which has - shape a.shape[:-1] + b.shape[:-1] with elements computed by summing the - product of the elements from the last dimensions of a and b. - Masked elements are replace by zeros. - """ - fa = filled(a, 0) - fb = filled(b, 0) - if len(fa.shape) == 0: - fa.shape = (1,) - if len(fb.shape) == 0: - fb.shape = (1,) - return masked_array(numeric.inner(fa, fb)) -innerproduct = inner - -def outer(a, b): - """outer(a,b) = {a[i]*b[j]}, has shape (len(a),len(b))""" - fa = filled(a, 0).ravel() - fb = filled(b, 0).ravel() - d = numeric.outer(fa, fb) - ma = getmask(a) - mb = getmask(b) - if ma is nomask and mb is nomask: - return masked_array(d) - ma = getmaskarray(a) - mb = getmaskarray(b) - m = make_mask(1-numeric.outer(1-ma, 1-mb), copy=0) - return masked_array(d, mask=m) -outerproduct = outer - -def allequal (a, b, fill_value=True): - """ -Returns `True` if all entries of a and b are equal, using -fill_value as a truth value where either or both are masked. - """ - m = mask_or(getmask(a), getmask(b)) - if m is nomask: - x = filled(a) - y = filled(b) - d = umath.equal(x, y) - return d.all() - elif fill_value: - x = filled(a) - y = filled(b) - d = umath.equal(x, y) - dm = array(d, mask=m, copy=False) - return dm.filled(True).all(None) - else: - return False - -def allclose (a, b, fill_value=True, rtol=1.e-5, atol=1.e-8): - """ Returns `True` if all elements of `a` and `b` are equal subject to given tolerances. -If `fill_value` is True, masked values are considered equal. -If `fill_value` is False, masked values considered unequal. -The relative error rtol should be positive and << 1.0 -The absolute error `atol` comes into play for those elements of `b` - that are very small or zero; it says how small `a` must be also. - """ - m = mask_or(getmask(a), getmask(b)) - d1 = filled(a) - d2 = filled(b) - x = filled(array(d1, copy=0, mask=m), fill_value).astype(float) - y = filled(array(d2, copy=0, mask=m), 1).astype(float) - d = umath.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y)) - return fromnumeric.alltrue(fromnumeric.ravel(d)) - -#.............................................................................. -def asarray(a, dtype=None): - """asarray(data, dtype) = array(data, dtype, copy=0) -Returns `a` as an masked array. -No copy is performed if `a` is already an array. -Subclasses are converted to base class MaskedArray. - """ - return masked_array(a, dtype=dtype, copy=False, keep_mask=True) - -def empty(new_shape, dtype=float): - """empty((d1,...,dn),dtype=float,order='C') -Returns a new array of shape (d1,...,dn) and given type with all its -entries uninitialized. This can be faster than zeros.""" - return masked_array(numeric.empty(new_shape, dtype), mask=nomask) - -def empty_like(a): - """empty_like(a) -Returns an empty (uninitialized) array of the shape and typecode of a. -Note that this does NOT initialize the returned array. -If you require your array to be initialized, you should use zeros_like().""" - return masked_array(numeric.empty_like(a), mask=nomask) - -def ones(new_shape, dtype=float): - """ones(shape, dtype=None) -Returns an array of the given dimensions, initialized to all ones.""" - return masked_array(numeric.ones(new_shape, dtype), mask=nomask) - -def zeros(new_shape, dtype=float): - """zeros(new_shape, dtype=None) -Returns an array of the given dimensions, initialized to all zeros.""" - return masked_array(numeric.zeros(new_shape, dtype), mask=nomask) - -#####-------------------------------------------------------------------------- -#---- --- Pickling --- -#####-------------------------------------------------------------------------- -#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data -def _mareconstruct(subtype, baseshape, basetype,): - """Internal function that builds a new MaskedArray from the information stored -in a pickle.""" - _data = ndarray.__new__(ndarray, baseshape, basetype) - _mask = ndarray.__new__(ndarray, baseshape, basetype) - return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False) - -def _getstate(a): - "Returns the internal state of the masked array, for pickling purposes." - state = (1, - a.shape, - a.dtype, - a.flags.fnc, - (a._data).__reduce__()[-1][-1], - getmaskarray(a).__reduce__()[-1][-1]) - return state - -def _setstate(a, state): - """Restores the internal state of the masked array, for pickling purposes. -`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: - - - class name - - a tuple giving the shape of the data - - a typecode for the data - - a binary string for the data - - a binary string for the mask. - """ - (ver, shp, typ, isf, raw, msk) = state - (a._data).__setstate__((shp, typ, isf, raw)) - (a._mask).__setstate__((shp, dtype('|b1'), isf, msk)) - -def _reduce(a): - """Returns a 3-tuple for pickling a MaskedArray.""" - return (_mareconstruct, - (a.__class__, (0,), 'b', ), - a.__getstate__()) - -def dump(a,F): - """Pickles the MaskedArray `a` to the file `F`. -`F` can either be the handle of an exiting file, or a string representing a file name. - """ - if not hasattr(F,'readline'): - F = open(F,'w') - return cPickle.dump(a,F) - -def dumps(a): - """Returns a string corresponding to the pickling of the MaskedArray.""" - return cPickle.dumps(a) - -def load(F): - """Wrapper around ``cPickle.load`` which accepts either a file-like object or - a filename.""" - if not hasattr(F, 'readline'): - F = open(F,'r') - return cPickle.load(F) - -def loads(strg): - "Loads a pickle from the current string.""" - return cPickle.loads(strg) - -MaskedArray.__getstate__ = _getstate -MaskedArray.__setstate__ = _setstate -MaskedArray.__reduce__ = _reduce -MaskedArray.__dump__ = dump -MaskedArray.__dumps__ = dumps - -################################################################################ - -if __name__ == '__main__': - import numpy as N - if 1: - x = N.array([[ 0.13, 0.26, 0.90], - [ 0.28, 0.33, 0.63], - [ 0.31, 0.87, 0.70]]) - m = N.array([[ True, False, False], - [False, False, False], - [True, True, False]], dtype=N.bool_) - X = N.asmatrix(x) - mX = masked_array(X, mask=m) \ No newline at end of file Modified: trunk/Lib/sandbox/maskedarray/mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -28,7 +28,7 @@ _typestr = ntypes._typestr import maskedarray as MA -reload(MA) +#reload(MA) from maskedarray import masked, nomask, mask_or, filled, getmask, getmaskarray, \ masked_array, make_mask from maskedarray import MaskedArray @@ -105,12 +105,6 @@ # offset=0, strides=None, formats=None, names=None, titles=None, byteorder=None, aligned=False): - # - if isinstance(data, MaskedRecords): - cls._defaultfieldmask = data._fieldmask - cls._defaulthardmask = data._hardmask | hard_mask - cls._fill_value = data._fill_value - return data._data.view(cls) # Get the new descriptor ................ if dtype is not None: descr = numeric.dtype(dtype) @@ -131,6 +125,9 @@ 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 _fieldmask = mask @@ -140,43 +137,34 @@ for (n,v) in zip(_names, data): _data[n] = numeric.asarray(v).view(ndarray) _fieldmask[n] = getmaskarray(v) - - # Set filling value ..................... + #........................................ + _data = _data.view(cls) + _data._fieldmask = _fieldmask + _data._hardmask = hard_mask if fill_value is None: - cls._fill_value = [default_fill_value(numeric.dtype(d[1])) - for d in descr.descr] + _data._fill_value = [default_fill_value(numeric.dtype(d[1])) + for d in descr.descr] else: - cls._fill_value = fill_value - # Set class defaults .................... - cls._defaultfieldmask = _fieldmask - cls._defaulthardmask = hard_mask - # - return _data.view(cls) + _data._fill_value = fill_value + return _data def __array_finalize__(self,obj): if isinstance(obj, MaskedRecords): - self.__dict__.update(_data=obj._data, - _fieldmask=obj._fieldmask, + self.__dict__.update(_fieldmask=obj._fieldmask, _hardmask=obj._hardmask, _fill_value=obj._fill_value ) -# self._data = obj._data -# self._fieldmask = obj._fieldmask -# self._hardmask = obj._series._hardmask -# self._fill_value = obj._fill_value else: -# self._data = obj.view(recarray) -# self._fieldmask = self._defaultfieldmask -# self._hardmask = self._defaulthardmask -# self.fill_value = self._fill_value - self.__dict__.update(_data = obj.view(recarray), - _fieldmask = self._defaultfieldmask, - _hardmask = self._defaulthardmask, - fill_value = self._fill_value + self.__dict__.update(_fieldmask = nomask, + _hardmask = False, + fill_value = None ) - MaskedRecords._defaultfieldmask = nomask - MaskedRecords._defaulthardmask = False return + + def _getdata(self): + return self.view(recarray) + _data = property(fget=_getdata) + #...................................................... def __getattribute__(self, attr): try: @@ -187,17 +175,12 @@ pass # Get the list of fields ...... _names = self.dtype.names - _local = self.__dict__ - _mask = _local['_fieldmask'] if attr in _names: - _data = _local['_data'] + _data = self._data + _mask = self._fieldmask obj = numeric.asarray(_data.__getattribute__(attr)).view(MaskedArray) obj._mask = make_mask(_mask.__getattribute__(attr)) return obj - elif attr == '_mask': - if self.size > 1: - return _mask.view((bool_, len(self.dtype))).all(1) - return _mask.view((bool_, len(self.dtype))) raise AttributeError,"No attribute '%s' !" % attr def __setattr__(self, attr, val): @@ -232,43 +215,36 @@ base_fmask.__setattr__(attr, mval) return elif attr == '_mask': - if self._hardmask: - if val is not nomask: - mval = getmaskarray(val) - for k in _names: - m = mask_or(mval, base_fmask.__getattr__(k)) - base_fmask.__setattr__(k, m) - else: - mval = getmaskarray(val) - for k in _names: - base_fmask.__setattr__(k, mval) + self.__setmask__(val) return #............................................ def __getitem__(self, indx): """Returns all the fields sharing the same fieldname base. The fieldname base is either `_data` or `_mask`.""" _localdict = self.__dict__ + _data = self._data # We want a field ........ if isinstance(indx, str): - obj = _localdict['_data'][indx].view(MaskedArray) - obj._mask = make_mask(_localdict['_fieldmask'][indx]) + obj = _data[indx].view(MaskedArray) + obj._setmask(_localdict['_fieldmask'][indx]) return obj # We want some elements .. - return MaskedRecords(_localdict['_data'][indx], - mask=_localdict['_fieldmask'][indx], - dtype=self.dtype) + obj = ndarray.__getitem__(self, indx).view(type(self)) + obj._fieldmask = _localdict['_fieldmask'][indx] + return obj - def __getslice__(self, i, j): - """Returns the slice described by [i,j].""" - _localdict = self.__dict__ - return MaskedRecords(_localdict['_data'][i:j], - mask=_localdict['_fieldmask'][i:j], - dtype=self.dtype) - +# def __getslice__(self, i, j): +# """Returns the slice described by [i,j].""" +# _localdict = self.__dict__ +# return MaskedRecords(_localdict['_data'][i:j], +# mask=_localdict['_fieldmask'][i:j], +# dtype=self.dtype) +# def __setslice__(self, i, j, value): """Sets the slice described by [i,j] to `value`.""" _localdict = self.__dict__ - d = _localdict['_data'] + + d = self._data m = _localdict['_fieldmask'] names = self.dtype.names if value is masked: @@ -282,20 +258,42 @@ m[n][i:j] = mval else: mindx = getmaskarray(self)[i:j] - val = masked_array(value, mask=mindx, keep_mask=True) + dval = numeric.asarray(value) +# val = masked_array(value, mask=mindx, keep_mask=True) valmask = getmask(value) if valmask is nomask: for n in names: mval = mask_or(m[n][i:j], valmask) - d[n][i:j][~mval] = filled(value) + d[n][i:j][~mval] = value elif valmask.size > 1: for n in names: mval = mask_or(m[n][i:j], valmask) - d[n][i:j][~mval] = fval[~mval] + d[n][i:j][~mval] = dval[~mval] m[n][i:j] = mask_or(m[n][i:j], mval) - - return MaskedRecords(d, mask=m, dtype=self.dtype) +# self._data[i:j] = d + self._fieldmask = m + #..................................................... + def __setmask__(self, mask): + names = self.dtype.names + fmask = self.__dict__['_fieldmask'] + newmask = make_mask(mask, copy=False) +# self.unshare_mask() + if self._hardmask: + for n in names: + fmask[n].__ior__(newmask) + else: + for n in names: + fmask[n].flat = newmask + + + def _getmask(self): + if self.size > 1: + return self._fieldmask.view((bool_, len(self.dtype))).all(1) + + _setmask = __setmask__ + _mask = property(fget=_getmask, fset=_setmask) + #...................................................... def __str__(self): """x.__str__() <==> str(x) @@ -375,39 +373,10 @@ def copy(self): """Returns a copy of the masked record.""" _localdict = self.__dict__ - return MaskedRecords(_localdict['_data'].copy(), + return MaskedRecords(self._data.copy(), mask=_localdict['_fieldmask'].copy(), dtype=self.dtype) #............................................. - def addfield(self, newfield, newfieldname=None): - """Adds a new field to the masked record array, using `newfield` as data - and `newfieldname` as name. If `newfieldname` is None, the new field name is - set to 'fi', where `i` is the number of existing fields. - """ - _localdict = self.__dict__ - _data = _localdict['_data'] - _mask = _localdict['_fieldmask'] - if newfieldname is None or newfieldname in reserved_fields: - newfieldname = 'f%i' % len(_data.dtype) - newfield = MA.asarray(newfield) - # Get the enw data ............ - newdtype = numeric.dtype(_data.dtype.descr + \ - [(newfieldname, newfield.dtype)]) - newdata = recarray(_data.shape, newdtype) - [newdata.setfield(_data.getfield(*f),*f) - for f in _data.dtype.fields.values()] - newdata.setfield(newfield.filled(), *newdata.dtype.fields[newfieldname]) - # Get the new mask ............. - newmdtype = numeric.dtype([(n,N.bool_) for n in newdtype.names]) - newmask = recarray(_data.shape, newmdtype) - [newmask.setfield(_mask.getfield(*f),*f) - for f in _mask.dtype.fields.values()] - newmask.setfield(getmaskarray(newfield), - *newmask.dtype.fields[newfieldname]) - self.__dict__.update(_data=newdata, - _fieldmask=newmask) -# return MaskedRecords(newdata, mask=newmask, dtype=newdtype) - #####--------------------------------------------------------------------------- @@ -638,7 +607,45 @@ _datalist = [masked_array(a,mask=m,dtype=t) for (a,m,t) in zip(_variables.T, _mask, vartypes)] return MaskedRecords(_datalist, dtype=mdescr) - + +#.................................................................... +def addfield(mrecord, newfield, newfieldname=None): + """Adds a new field to the masked record array, using `newfield` as data +and `newfieldname` as name. If `newfieldname` is None, the new field name is +set to 'fi', where `i` is the number of existing fields. + """ + _data = mrecord._data + _mask = mrecord._fieldmask + if newfieldname is None or newfieldname in reserved_fields: + newfieldname = 'f%i' % len(_data.dtype) + newfield = MA.asarray(newfield) + # Get the new data ............ + # Create a new empty recarray + newdtype = numeric.dtype(_data.dtype.descr + \ + [(newfieldname, newfield.dtype)]) + newdata = recarray(_data.shape, newdtype) + # Add the exisintg field + [newdata.setfield(_data.getfield(*f),*f) + for f in _data.dtype.fields.values()] + # Add the new field + newdata.setfield(newfield._data, *newdata.dtype.fields[newfieldname]) + newdata = newdata.view(MaskedRecords) + # Get the new mask ............. + # Create a new empty recarray + newmdtype = numeric.dtype([(n,N.bool_) for n in newdtype.names]) + newmask = recarray(_data.shape, newmdtype) + # Add the old masks + [newmask.setfield(_mask.getfield(*f),*f) + for f in _mask.dtype.fields.values()] + # Add the mask of the new field + newmask.setfield(getmaskarray(newfield), + *newmask.dtype.fields[newfieldname]) + newdata._fieldmask = newmask + return newdata + + + + ################################################################################ if 1: from maskedarray.testutils import assert_equal @@ -653,6 +660,117 @@ base = MA.array(base_d, mask=base_m) mrecord = fromarrays(base.T,) self_data = [d, m, mrecord] + +# def test_get(self): +# "Tests fields retrieval" + [d, m, mrec] = self_data + mrec = mrec.copy() + assert_equal(mrec.f0, MA.array(d,mask=m)) + assert_equal(mrec.f1, MA.array(d[::-1],mask=m[::-1])) + assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]])).all()) + assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0)) + assert_equal(mrec.f0[1], mrec[1].f0) + # + assert(isinstance(mrec[:2], MaskedRecords)) + assert_equal(mrec[:2]['f0'], d[:2]) +# def test_set(self): +# "Tests setting fields/attributes." + if 0: + [d, m, mrecord] = self_data + mrecord.f0._data[:] = 5 + assert_equal(mrecord['f0']._data, [5,5,5,5,5]) + mrecord.f0 = 1 + assert_equal(mrecord['f0']._data, [1]*5) + assert_equal(getmaskarray(mrecord['f0']), [0]*5) + mrecord.f1 = MA.masked + assert_equal(mrecord.f1.mask, [1]*5) + assert_equal(getmaskarray(mrecord['f1']), [1]*5) + mrecord._mask = MA.masked + assert_equal(getmaskarray(mrecord['f1']), [1]*5) + assert_equal(mrecord['f0']._mask, mrecord['f1']._mask) + mrecord._mask = MA.nomask + assert_equal(getmaskarray(mrecord['f1']), [0]*5) + assert_equal(mrecord['f0']._mask, mrecord['f1']._mask) + # +# def test_setslices(self): +# "Tests setting slices." + if 0: + [d, m, mrec] = self_data + mrec[:2] = 5 + assert_equal(mrec.f0._data, [5,5,2,3,4]) + assert_equal(mrec.f1._data, [5,5,2,1,0]) + assert_equal(mrec.f0._mask, [0,0,0,1,1]) + assert_equal(mrec.f1._mask, [0,0,0,0,1]) + mrec.harden_mask() + mrec[-2:] = 5 + assert_equal(mrec.f0._data, [5,5,2,3,4]) + assert_equal(mrec.f1._data, [5,5,2,5,0]) + assert_equal(mrec.f0._mask, [0,0,0,1,1]) + assert_equal(mrec.f1._mask, [0,0,0,0,1]) + +# def test_hardmask(self): +# "Test hardmask" + if 0: + [d, m, mrec] = self_data + mrec = mrec.copy() + mrec.harden_mask() + assert(mrec._hardmask) + mrec._setmask(nomask) + assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0)) + mrec.soften_mask() + assert(not mrec._hardmask) + mrec._setmask(nomask) + assert(mrec['f1']._mask is nomask) + assert_equal(mrec['f0']._mask,mrec['f1']._mask) - \ No newline at end of file +# def test_fromrecords(self): +# "Test from recarray." + if 0: + [d, m, mrec] = self_data + nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]]) + mrecfr = fromrecords(nrec.tolist()) + assert_equal(mrecfr.f0, mrec.f0) + assert_equal(mrecfr.dtype, mrec.dtype) + #.................... + mrecfr = fromrecords(nrec) + assert_equal(mrecfr.f0, mrec.f0) + assert_equal(mrecfr.dtype, mrec.dtype) + #.................... + tmp = mrec[::-1] #.tolist() + mrecfr = fromrecords(tmp) + assert_equal(mrecfr.f0, mrec.f0[::-1]) +# +## def test_fromtextfile(self): +## "Tests reading from a text file." +## fcontent = """# +##'One (S)','Two (I)','Three (F)','Four (M)','Five (-)','Six (C)' +##'strings',1,1.0,'mixed column',,1 +##'with embedded "double quotes"',2,2.0,1.0,,1 +##'strings',3,3.0E5,3,,1 +##'strings',4,-1e-10,,,1 +##""" +## import os +## from datetime import datetime +## fname = 'tmp%s' % datetime.now().strftime("%y%m%d%H%M%S%s") +## f = open(fname, 'w') +## f.write(fcontent) +## f.close() +## mrectxt = fromtextfile(fname,delimitor=',',varnames='ABCDEFG') +## os.unlink(fname) +## # +## assert(isinstance(mrectxt, MaskedRecords)) +## assert_equal(mrectxt.F, [1,1,1,1]) +## assert_equal(mrectxt.E._mask, [1,1,1,1]) +## assert_equal(mrectxt.C, [1,2,3.e+5,-1e-10]) +# +# def test_addfield(self): +# "Tests addfield" + if 1: + [d, m, mrec] = self_data + newfield = masked_array(d+10, mask=m[::-1]) + mrec = addfield(mrec, newfield) + assert_equal(mrec.f2, d+10) + assert_equal(mrec.f2._mask, m[::-1]) +# +# \ No newline at end of file Modified: trunk/Lib/sandbox/maskedarray/tests/test_core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -1012,7 +1012,7 @@ xs[[1,4]] = [10,40] assert_equal(xh._data, [0,10,2,3,4]) assert_equal(xs._data, [0,10,2,3,40]) - assert(xh.mask is m) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) assert_equal(xs.mask, [0,0,0,1,0]) assert(xh._hardmask) assert(not xs._hardmask) @@ -1020,7 +1020,7 @@ xs[1:4] = [10,20,30] assert_equal(xh._data, [0,10,20,3,4]) assert_equal(xs._data, [0,10,20,30,40]) - assert(xh.mask is m) + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) assert_equal(xs.mask, nomask) xh[0] = masked xs[0] = masked @@ -1063,9 +1063,8 @@ m = make_mask(n) xh = array(d, mask = m, hard_mask=True) xh[4:5] = 999 - assert(xh.mask is m) - xh[0:1] = 999 - + #assert_equal(xh.mask.ctypes.data, m.ctypes.data) + xh[0:1] = 999 assert_equal(xh._data,[999,1,2,3,4]) def check_sort(self): Modified: trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -28,7 +28,8 @@ #from maskedarray.mrecords import mrecarray, fromarrays, fromtextfile, fromrecords import maskedarray.mrecords reload(maskedarray.mrecords) -from maskedarray.mrecords import MaskedRecords, fromarrays, fromtextfile, fromrecords +from maskedarray.mrecords import MaskedRecords, \ + fromarrays, fromtextfile, fromrecords, addfield #.............................................................................. class test_mrecords(NumpyTestCase): @@ -149,7 +150,7 @@ def test_addfield(self): "Tests addfield" [d, m, mrec] = self.data - mrec.addfield(masked_array(d+10, mask=m[::-1])) + mrec = addfield(mrec, masked_array(d+10, mask=m[::-1])) assert_equal(mrec.f2, d+10) assert_equal(mrec.f2._mask, m[::-1]) Modified: trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -1,3 +1,15 @@ +# pylint: disable-msg=W0611, W0612, W0511,R0201 +"""Tests suite for MaskedArray & subclassing. + +:author: Pierre Gerard-Marchant +:contact: pierregm_at_uga_dot_edu +:version: $Id$ +""" +__author__ = "Pierre GF Gerard-Marchant ($Author$)" +__version__ = '1.0' +__revision__ = "$Revision$" +__date__ = '$Date$' + import numpy as N import numpy.core.numeric as numeric @@ -4,15 +16,14 @@ from numpy.testing import NumpyTest, NumpyTestCase import maskedarray.testutils -reload(maskedarray.testutils) +#reload(maskedarray.testutils) from maskedarray.testutils import * import maskedarray.core as coremodule -reload(coremodule) +#reload(coremodule) from maskedarray.core import * - class SubArray(N.ndarray): """Defines a generic N.ndarray subclass, that stores some metadata in the dictionary `info`.""" @@ -37,8 +48,11 @@ return _data def __array_finalize__(self,obj): SubArray.__array_finalize__(self, obj) - MaskedArray.__array_finalize__(self,obj) + MaskedArray.__array_finalize__(self,obj) return + def _get_series(self): + return self.view(MaskedArray) + _series = property(fget=_get_series) msubarray = MSubArray class MMatrix(MaskedArray, N.matrix,): @@ -50,6 +64,9 @@ N.matrix.__array_finalize__(self, obj) MaskedArray.__array_finalize__(self,obj) return + def _get_series(self): + return self.view(MaskedArray) + _series = property(fget=_get_series) mmatrix = MMatrix @@ -98,18 +115,15 @@ assert isinstance(z, MSubArray) assert isinstance(z._data, SubArray) assert z._data.info['added'] > 0 + # + ym._setmask([1,0,0,0,1]) + assert_equal(ym._mask, [1,0,0,0,1]) + ym._series._setmask([0,0,0,0,1]) + assert_equal(ym._mask, [0,0,0,0,1]) + ################################################################################ if __name__ == '__main__': NumpyTest().run() - if 1: - x = N.arange(5) - mx = mmatrix(x,mask=[0,1,0,0,0]) - - c = mx.ravel() - - a = add(mx,x) - b = mx+x - assert_equal(a,b) Modified: trunk/Lib/sandbox/maskedarray/timer_comparison.py =================================================================== --- trunk/Lib/sandbox/maskedarray/timer_comparison.py 2007-02-16 21:14:51 UTC (rev 2722) +++ trunk/Lib/sandbox/maskedarray/timer_comparison.py 2007-02-19 05:28:52 UTC (rev 2723) @@ -25,6 +25,7 @@ self.equal = module.equal self.filled = module.filled self.getmask = module.getmask + self.getmaskarray = module.getmaskarray self.id = id self.inner = module.inner self.make_mask = module.make_mask @@ -233,7 +234,7 @@ assert t[2] == 3 #---------------------------------- def test_5(self): - "Tests inplace" + "Tests inplace w/ scalar" x = self.arange(10) y = self.arange(10) @@ -284,7 +285,12 @@ #assert id1 == self.id(x.raw_data()) assert self.allequal(x, y+1.) + + def test_6(self): + "Tests inplace w/ array" + x = self.arange(10, dtype=float_) + y = self.arange(10) xm = self.arange(10, dtype=float_) xm[2] = self.masked m = xm.mask @@ -328,8 +334,9 @@ a[-1] = self.masked x /= a xm /= a + #---------------------------------- - def test_6(self): + def test_7(self): "Tests ufunc" d = (self.array([1.0, 0, -1, pi/2]*2, mask=[0,1]+[0]*6), self.array([1.0, 0, -1, pi/2]*2, mask=[1,0]+[0]*6),) @@ -363,6 +370,7 @@ mr = mf(*args) self.assert_array_equal(ur.filled(0), mr.filled(0), f) self.assert_array_equal(ur._mask, mr._mask) + #---------------------------------- def test_99(self): # test average @@ -419,29 +427,37 @@ ################################################################################ if __name__ == '__main__': - setup_base = "from __main__ import moduletester\ntester = moduletester(module)" + + setup_base = "from __main__ import moduletester \n"\ + "import numpy\n" \ + "tester = moduletester(module)\n" setup_old = "import numpy.core.ma as module\n"+setup_base setup_new = "import maskedarray.core_ini as module\n"+setup_base - setup_alt = "import maskedarray.core as module\n"+setup_base + setup_cur = "import maskedarray.core as module\n"+setup_base +# setup_alt = "import maskedarray.core_alt as module\n"+setup_base +# setup_tmp = "import maskedarray.core_tmp as module\n"+setup_base - nrepeat = 2 - nloop = 3 - - - for i in range(1,7): - func = 'tester.test_%i()' % i - old = timeit.Timer(func, setup_old).repeat(nrepeat, nloop*10) - new = timeit.Timer(func, setup_new).repeat(nrepeat, nloop*10) - alt = timeit.Timer(func, setup_alt).repeat(nrepeat, nloop*10) - old = numpy.sort(old) - new = numpy.sort(new) - alt = numpy.sort(alt) - print "#%i" % i +50*'.' - print eval("moduletester.test_%i.__doc__" % i) - print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) - print "core_ini : %.3f - %.3f" % (new[0], new[1]) - print "core_alt : %.3f - %.3f" % (alt[0], alt[1]) -# import maskedarray.core_alt as module -# tester = moduletester(module) -# tester.test_3() -# print "OK" \ No newline at end of file + (nrepeat, nloop) = (10, 10) + + if 1: + for i in range(1,8): + func = 'tester.test_%i()' % i + old = timeit.Timer(func, setup_old).repeat(nrepeat, nloop*10) + new = timeit.Timer(func, setup_new).repeat(nrepeat, nloop*10) + cur = timeit.Timer(func, setup_cur).repeat(nrepeat, nloop*10) +# alt = timeit.Timer(func, setup_alt).repeat(nrepeat, nloop*10) +# tmp = timeit.Timer(func, setup_tmp).repeat(nrepeat, nloop*10) + old = numpy.sort(old) + new = numpy.sort(new) + cur = numpy.sort(cur) +# alt = numpy.sort(alt) +# tmp = numpy.sort(tmp) + print "#%i" % i +50*'.' + print eval("moduletester.test_%i.__doc__" % i) + print "numpy.core.ma: %.3f - %.3f" % (old[0], old[1]) + print "core_ini : %.3f - %.3f" % (new[0], new[1]) + print "core_current : %.3f - %.3f" % (cur[0], cur[1]) +# print "core_alt : %.3f - %.3f" % (alt[0], alt[1]) +# print "core_tmp : %.3f - %.3f" % (tmp[0], tmp[1]) + + \ No newline at end of file From scipy-svn at scipy.org Mon Feb 19 00:30:07 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 18 Feb 2007 23:30:07 -0600 (CST) Subject: [Scipy-svn] r2724 - trunk/Lib/sandbox/maskedarray Message-ID: <20070219053007.B14BF39C042@new.scipy.org> Author: pierregm Date: 2007-02-18 23:30:05 -0600 (Sun, 18 Feb 2007) New Revision: 2724 Modified: trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/mrecords.py Log: generic cleanup Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-19 05:28:52 UTC (rev 2723) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-19 05:30:05 UTC (rev 2724) @@ -987,7 +987,7 @@ _data = numeric.array(data, dtype=dtype, copy=copy, subok=True) _baseclass = getattr(_data, '_baseclass', type(_data)) _data = _data.view(cls) - # Pricess mask ........... + # Process mask ........... # Backwards compat if hasattr(data,'_mask') and not isinstance(data, ndarray): _data._mask = data._mask @@ -999,8 +999,6 @@ _data._sharedmask = False if not keep_mask: _data._mask = nomask -# else; -# _data._mask = data._mask else: mask = numeric.array(mask, dtype=MaskType, copy=copy) if mask.shape != _data.shape: @@ -1035,14 +1033,8 @@ """ # Finalize mask ............... self._mask = getattr(obj, '_mask', nomask) -# if not self.shape: -# self._mask.shape = obj.shape -# else: -# self._mask.shape = self.shape if self._mask is not nomask: self._mask.shape = self.shape -# except ValueError: -# self._mask = nomask # Get the remaining options ... self._hardmask = getattr(obj, '_hardmask', self._defaulthardmask) self._smallmask = getattr(obj, '_smallmask', True) @@ -1059,7 +1051,7 @@ #.......... if context is not None: result._mask = result._mask.copy() - (func, args, out_index) = context + (func, args, _) = context m = reduce(mask_or, [getmask(arg) for arg in args]) # Get domain mask domain = ufunc_domain.get(func, None) @@ -1162,7 +1154,6 @@ If `value` is masked, masks those locations.""" self.__setitem__(slice(i,j), value) #............................................ - #............................................ def __setmask__(self, mask): newmask = make_mask(mask, copy=False, small_mask=self._smallmask) # self.unshare_mask() @@ -1180,18 +1171,7 @@ def _get_mask(self): """Returns the current mask.""" return self._mask - -# def _set_mask(self, mask): -# """Sets the mask to `mask`.""" -# mask = make_mask(mask, copy=False, small_mask=self._smallmask) -# if mask is not nomask: -# if mask.size != self.size: -# raise ValueError, "Inconsistent shape between data and mask!" -# if mask.shape != self.shape: -# mask.shape = self.shape -# self._mask = mask -# else: -# self._mask = nomask + mask = property(fget=_get_mask, fset=__setmask__, doc="Mask") #............................................ def harden_mask(self): @@ -1421,20 +1401,6 @@ else: return masked_array(n1 - n2) #............................................ -# def _get_shape(self): -# "Returns the current shape." -# return self._data.shape -# # -# def _set_shape (self, newshape): -# "Sets the array's shape." -# self._data.shape = newshape -# if self._mask is not nomask: -# #self._mask = self._mask.copy() -# self._mask.shape = newshape -# # -# shape = property(fget=_get_shape, fset=_set_shape, -# doc="Shape of the array, as a tuple.") - # def reshape (self, *s): """Reshapes the array to shape s. Returns a new masked array. @@ -1652,7 +1618,6 @@ danom = self.anom(axis=axis, dtype=dtype) danom *= danom dvar = danom.sum(axis) / cnt -# dvar /= cnt if axis is not None: dvar._mask = mask_or(self._mask.all(axis), (cnt==1)) return dvar @@ -1836,7 +1801,7 @@ return super(MaskedArray, self).max(axis=axis) elif (not mask.ndim) and mask: return masked - # Check teh mask .............. + # Check the mask .............. if axis is None: mask = umath.logical_and.reduce(mask.flat) else: @@ -1901,73 +1866,13 @@ # Backwards Compatibility. Heck... @property def data(self): - """Returns the `_data` part of the MaskedArray. -You should really use `_data` instead...""" + """Returns the `_data` part of the MaskedArray.""" return self._data def raw_data(self): """Returns the `_data` part of the MaskedArray. -You should really use `_data` instead...""" +You should really use `data` instead...""" return self._data -##.............................................................................. - - - -#class _arithmethods: -# """Defines a wrapper for arithmetic methods. -#Instead of directly calling a ufunc, the corresponding method of the `array._data` -#object is called instead. -# """ -# def __init__ (self, methodname, fill_self=0, fill_other=0, domain=None): -# """ -#:Parameters: -# - `methodname` (String) : Method name. -# - `fill_self` (Float *[0]*) : Fill value for the instance. -# - `fill_other` (Float *[0]*) : Fill value for the target. -# - `domain` (Domain object *[None]*) : Domain of non-validity. -# """ -# self.methodname = methodname -# self.fill_self = fill_self -# self.fill_other = fill_other -# self.domain = domain -# # -# def __call__ (self, instance, other, *args): -# "Execute the call behavior." -# m_self = instance._mask -# m_other = getmask(other) -# base = filled(instance,self.fill_self) -# target = filled(other, self.fill_other) -# if self.domain is not None: -# # We need to force the domain to a ndarray only. -# if self.fill_other > self.fill_self: -# domain = self.domain(base, target) -# else: -# domain = self.domain(target, base) -# if domain.any(): -# #If `other` is a subclass of ndarray, `filled` must have the -# # same subclass, else we'll lose some info. -# #The easiest then is to fill `target` instead of creating -# # a pure ndarray. -# #Oh, and we better make a copy! -# if isinstance(other, ndarray): -# if target is other: -# # We don't want to modify other: let's copy target, then -# target = target.copy() -# target[:] = numeric.where(fromnumeric.asarray(domain), -# self.fill_other, target) -# else: -# target = numeric.where(fromnumeric.asarray(domain), -# self.fill_other, target) -# m_other = mask_or(m_other, domain) -# m = mask_or(m_self, m_other) -# method = getattr(base, self.methodname) -# return instance.__class__(method(target, *args), mask=m) -# # -# def patch(self): -# """Applies the method `func` from class `method` to MaskedArray""" -# return types.MethodType(self,None,MaskedArray) -#.............................................................................. - #####-------------------------------------------------------------------------- #---- --- Shortcuts --- #####--------------------------------------------------------------------------- @@ -2003,61 +1908,11 @@ return False -#####-------------------------------------------------------------------------- -#---- --- Patch methods --- -#####-------------------------------------------------------------------------- -#class _arraymethod: -# """Defines a wrapper for basic array methods. -#Upon call, returns a masked array, where the new `_data` array is the output -#of the corresponding method called on the original `_data`. -# -#If `onmask` is True, the new mask is the output of the method calld on the initial mask. -#If `onmask` is False, the new mask is just a reference to the initial mask. -# -#:Parameters: -# `funcname` : String -# Name of the function to apply on data. -# `onmask` : Boolean *[True]* -# Whether the mask must be processed also (True) or left alone (False). -# """ -# def __init__(self, funcname, onmask=True): -# self._name = funcname -# self._onmask = onmask -# self.__doc__ = getattr(ndarray, self._name).__doc__ -# def __call__(self, instance, *args, **params): -# methodname = self._name -# (d,m) = (instance._data, instance._mask) -# C = instance.__class__ -# if m is nomask: -# return C(getattr(d,methodname).__call__(*args, **params)) -# elif self._onmask: -# return C(getattr(d,methodname).__call__(*args, **params), -# mask=getattr(m,methodname)(*args, **params) ) -# else: -# return C(getattr(d,methodname).__call__(*args, **params), mask=m) -# -# def patch(self): -# "Adds the new method to MaskedArray." -# return types.MethodType(self, None, MaskedArray) -##...................................... -#MaskedArray.conj = MaskedArray.conjugate = _arraymethod('conjugate').patch() -#MaskedArray.diagonal = _arraymethod('diagonal').patch() -#MaskedArray.take = _arraymethod('take').patch() -#MaskedArray.ravel = _arraymethod('ravel').patch() -#MaskedArray.transpose = _arraymethod('transpose').patch() -#MaskedArray.T = _arraymethod('transpose').patch() -#MaskedArray.swapaxes = _arraymethod('swapaxes').patch() -#MaskedArray.clip = _arraymethod('clip', onmask=False).patch() -#MaskedArray.compress = _arraymethod('compress').patch() -#MaskedArray.resize = _arraymethod('resize').patch() -#MaskedArray.copy = _arraymethod('copy').patch() - - - #####--------------------------------------------------------------------------- #---- --- Extrema functions --- #####--------------------------------------------------------------------------- class _extrema_operation(object): + "Generic class for maximum/minimum functions." def __call__(self, a, b=None): "Executes the call behavior." if b is None: @@ -2733,163 +2588,7 @@ ################################################################################ -if __name__ == '__main__': - import numpy as N - from maskedarray.testutils import assert_equal, assert_array_equal - pi = N.pi - - a = array([1,2,3,4,5],mask=[0,1,0,0,0]) - x = add.reduce(a) - assert_equal(add.reduce(a), 13) - - if 0: - x = masked_array([1,2,3], mask=[1,0,0]) - mx = masked_array(x) - assert_equal(mx.mask, x.mask) - mx = masked_array(x, mask=[0,1,0], keep_mask=False) - assert_equal(mx.mask, [0,1,0]) - mx = masked_array(x, mask=[0,1,0], keep_mask=True) - assert_equal(mx.mask, [1,1,0]) - #We default to true - mx = masked_array(x, mask=[0,1,0]) - assert_equal(mx.mask, [1,1,0]) - if 0: - import numpy.core.ma as nma - x = nma.arange(5) - x[2] = nma.masked - X = masked_array(x, mask=x._mask) - assert_equal(X._mask, x.mask) - assert_equal(X._data, x._data) - X = masked_array(x) - assert_equal(X._data, x._data) - assert_equal(X._mask, x.mask) - assert_equal(getmask(x), [0,0,1,0,0]) - if 0: - d = arange(5) - n = [0,0,0,1,1] - m = make_mask(n) - x = array(d, mask = m) - assert( x[3] is masked) - assert( x[4] is masked) - x[[1,4]] = [10,40] - assert( x.mask is not m) - assert( x[3] is masked) - assert( x[4] is not masked) - assert_equal(x, [0,10,2,-1,40]) - if 0: - d = arange(5) - n = [0,0,0,1,1] - m = make_mask(n) - xh = array(d, mask = m, hard_mask=True) - # We need to copy, to avoid updating d in xh! - xs = array(d, mask = m, hard_mask=False, copy=True) - xh[[1,4]] = [10,40] - xs[[1,4]] = [10,40] - assert_equal(xh._data, [0,10,2,3,4]) - assert_equal(xs._data, [0,10,2,3,40]) - #assert_equal(xh.mask.ctypes.data, m.ctypes.data) - assert_equal(xs.mask, [0,0,0,1,0]) - assert(xh._hardmask) - assert(not xs._hardmask) - xh[1:4] = [10,20,30] - xs[1:4] = [10,20,30] - assert_equal(xh._data, [0,10,20,3,4]) - assert_equal(xs._data, [0,10,20,30,40]) - #assert_equal(xh.mask.ctypes.data, m.ctypes.data) - assert_equal(xs.mask, nomask) - xh[0] = masked - xs[0] = masked - assert_equal(xh.mask, [1,0,0,1,1]) - assert_equal(xs.mask, [1,0,0,0,0]) - xh[:] = 1 - xs[:] = 1 - assert_equal(xh._data, [0,1,1,3,4]) - assert_equal(xs._data, [1,1,1,1,1]) - assert_equal(xh.mask, [1,0,0,1,1]) - assert_equal(xs.mask, nomask) - # Switch to soft mask - xh.soften_mask() - xh[:] = arange(5) - assert_equal(xh._data, [0,1,2,3,4]) - assert_equal(xh.mask, nomask) - # Switch back to hard mask - xh.harden_mask() - xh[xh<3] = masked - assert_equal(xh._data, [0,1,2,3,4]) - assert_equal(xh._mask, [1,1,1,0,0]) - xh[filled(xh>1,False)] = 5 - assert_equal(xh._data, [0,1,2,5,5]) - assert_equal(xh._mask, [1,1,1,0,0]) - # - xh = array([[1,2],[3,4]], mask = [[1,0],[0,0]], hard_mask=True) - xh[0] = 0 - assert_equal(xh._data, [[1,0],[3,4]]) - assert_equal(xh._mask, [[1,0],[0,0]]) - xh[-1,-1] = 5 - assert_equal(xh._data, [[1,0],[3,5]]) - assert_equal(xh._mask, [[1,0],[0,0]]) - xh[filled(xh<5,False)] = 2 - assert_equal(xh._data, [[1,2],[2,5]]) - assert_equal(xh._mask, [[1,0],[0,0]]) - # - "Another test of hardmask" - d = arange(5) - n = [0,0,0,1,1] - m = make_mask(n) - xh = array(d, mask = m, hard_mask=True) - xh[4:5] = 999 - #assert_equal(xh.mask.ctypes.data, m.ctypes.data) - xh[0:1] = 999 - assert_equal(xh._data,[999,1,2,3,4]) - if 0: - x = N.array([[ 0.13, 0.26, 0.90], - [ 0.28, 0.33, 0.63], - [ 0.31, 0.87, 0.70]]) - m = N.array([[ True, False, False], - [False, False, False], - [True, True, False]], dtype=N.bool_) - mx = masked_array(x, mask=m) - xbig = N.array([[False, False, True], - [False, False, True], - [False, True, True]], dtype=N.bool_) - mxbig = (mx > 0.5) - mxsmall = (mx < 0.5) - # - assert (mxbig.all()==False) - assert (mxbig.any()==True) - y = mxbig.all(0) - assert_equal(mxbig.all(0),[False, False, True]) - assert_equal(mxbig.all(1), [False, False, True]) - assert_equal(mxbig.any(0),[False, False, True]) - assert_equal(mxbig.any(1), [True, True, True]) - - if 1: - "Tests put." - d = arange(5) - n = [0,0,0,1,1] - m = make_mask(n) - x = array(d, mask = m) - assert( x[3] is masked) - assert( x[4] is masked) - x[[1,4]] = [10,40] - assert( x.mask is not m) - assert( x[3] is masked) - assert( x[4] is not masked) - assert_equal(x, [0,10,2,-1,40]) - # - x = masked_array(arange(10), mask=[1,0,0,0,0]*2) - i = [0,2,4,6] - x.put(i, [6,4,2,0]) - assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) - assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) - x.put(i, masked_array([0,2,4,6],[1,0,1,0])) - assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) - assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) - # - x = masked_array(arange(10), mask=[1,0,0,0,0]*2) - put(x, i, [6,4,2,0]) - assert_equal(x, asarray([6,1,4,3,2,5,0,7,8,9,])) - assert_equal(x.mask, [0,0,0,0,0,1,0,0,0,0]) - put(x, i, masked_array([0,2,4,6],[1,0,1,0])) - assert_array_equal(x, [0,1,2,3,4,5,6,7,8,9,]) - assert_equal(x.mask, [1,0,0,0,1,1,0,0,0,0]) +#if __name__ == '__main__': +# import numpy as N +# from maskedarray.testutils import assert_equal, assert_array_equal +# pi = N.pi Modified: trunk/Lib/sandbox/maskedarray/mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-19 05:28:52 UTC (rev 2723) +++ trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-19 05:30:05 UTC (rev 2724) @@ -162,6 +162,7 @@ return def _getdata(self): + "Returns the data as a recarray." return self.view(recarray) _data = property(fget=_getdata) @@ -259,7 +260,6 @@ else: mindx = getmaskarray(self)[i:j] dval = numeric.asarray(value) -# val = masked_array(value, mask=mindx, keep_mask=True) valmask = getmask(value) if valmask is nomask: for n in names: @@ -270,7 +270,6 @@ mval = mask_or(m[n][i:j], valmask) d[n][i:j][~mval] = dval[~mval] m[n][i:j] = mask_or(m[n][i:j], mval) -# self._data[i:j] = d self._fieldmask = m #..................................................... @@ -286,8 +285,9 @@ for n in names: fmask[n].flat = newmask - def _getmask(self): + """Returns the mask of the mrecord: a record is masked when all the fields +are masked.""" if self.size > 1: return self._fieldmask.view((bool_, len(self.dtype))).all(1) @@ -588,7 +588,7 @@ # Get the data .............................. _variables = MA.asarray([line.strip().split(delimitor) for line in f if line[0] != commentchar and len(line) > 1]) - (nvars, nfields) = _variables.shape + (_, nfields) = _variables.shape # Try to guess the dtype .................... if vartypes is None: vartypes = _guessvartypes(_variables[0]) @@ -632,7 +632,7 @@ newdata = newdata.view(MaskedRecords) # Get the new mask ............. # Create a new empty recarray - newmdtype = numeric.dtype([(n,N.bool_) for n in newdtype.names]) + newmdtype = numeric.dtype([(n,bool_) for n in newdtype.names]) newmask = recarray(_data.shape, newmdtype) # Add the old masks [newmask.setfield(_mask.getfield(*f),*f) @@ -643,134 +643,5 @@ newdata._fieldmask = newmask return newdata - - - ################################################################################ -if 1: - from maskedarray.testutils import assert_equal - import numpy as N - if 1: -# def setup(self): -# "Generic setup" - d = N.arange(5) - m = MA.make_mask([1,0,0,1,1]) - base_d = N.r_[d,d[::-1]].reshape(2,-1).T - base_m = N.r_[[m, m[::-1]]].T - base = MA.array(base_d, mask=base_m) - mrecord = fromarrays(base.T,) - self_data = [d, m, mrecord] - -# def test_get(self): -# "Tests fields retrieval" - [d, m, mrec] = self_data - mrec = mrec.copy() - assert_equal(mrec.f0, MA.array(d,mask=m)) - assert_equal(mrec.f1, MA.array(d[::-1],mask=m[::-1])) - assert((mrec._fieldmask == N.core.records.fromarrays([m, m[::-1]])).all()) - assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0)) - assert_equal(mrec.f0[1], mrec[1].f0) - # - assert(isinstance(mrec[:2], MaskedRecords)) - assert_equal(mrec[:2]['f0'], d[:2]) - -# def test_set(self): -# "Tests setting fields/attributes." - if 0: - [d, m, mrecord] = self_data - mrecord.f0._data[:] = 5 - assert_equal(mrecord['f0']._data, [5,5,5,5,5]) - mrecord.f0 = 1 - assert_equal(mrecord['f0']._data, [1]*5) - assert_equal(getmaskarray(mrecord['f0']), [0]*5) - mrecord.f1 = MA.masked - assert_equal(mrecord.f1.mask, [1]*5) - assert_equal(getmaskarray(mrecord['f1']), [1]*5) - mrecord._mask = MA.masked - assert_equal(getmaskarray(mrecord['f1']), [1]*5) - assert_equal(mrecord['f0']._mask, mrecord['f1']._mask) - mrecord._mask = MA.nomask - assert_equal(getmaskarray(mrecord['f1']), [0]*5) - assert_equal(mrecord['f0']._mask, mrecord['f1']._mask) - # -# def test_setslices(self): -# "Tests setting slices." - if 0: - [d, m, mrec] = self_data - mrec[:2] = 5 - assert_equal(mrec.f0._data, [5,5,2,3,4]) - assert_equal(mrec.f1._data, [5,5,2,1,0]) - assert_equal(mrec.f0._mask, [0,0,0,1,1]) - assert_equal(mrec.f1._mask, [0,0,0,0,1]) - mrec.harden_mask() - mrec[-2:] = 5 - assert_equal(mrec.f0._data, [5,5,2,3,4]) - assert_equal(mrec.f1._data, [5,5,2,5,0]) - assert_equal(mrec.f0._mask, [0,0,0,1,1]) - assert_equal(mrec.f1._mask, [0,0,0,0,1]) - -# def test_hardmask(self): -# "Test hardmask" - if 0: - [d, m, mrec] = self_data - mrec = mrec.copy() - mrec.harden_mask() - assert(mrec._hardmask) - mrec._setmask(nomask) - assert_equal(mrec._mask, N.r_[[m,m[::-1]]].all(0)) - mrec.soften_mask() - assert(not mrec._hardmask) - mrec._setmask(nomask) - assert(mrec['f1']._mask is nomask) - assert_equal(mrec['f0']._mask,mrec['f1']._mask) - -# def test_fromrecords(self): -# "Test from recarray." - if 0: - [d, m, mrec] = self_data - nrec = N.core.records.fromarrays(N.r_[[d,d[::-1]]]) - mrecfr = fromrecords(nrec.tolist()) - assert_equal(mrecfr.f0, mrec.f0) - assert_equal(mrecfr.dtype, mrec.dtype) - #.................... - mrecfr = fromrecords(nrec) - assert_equal(mrecfr.f0, mrec.f0) - assert_equal(mrecfr.dtype, mrec.dtype) - #.................... - tmp = mrec[::-1] #.tolist() - mrecfr = fromrecords(tmp) - assert_equal(mrecfr.f0, mrec.f0[::-1]) -# -## def test_fromtextfile(self): -## "Tests reading from a text file." -## fcontent = """# -##'One (S)','Two (I)','Three (F)','Four (M)','Five (-)','Six (C)' -##'strings',1,1.0,'mixed column',,1 -##'with embedded "double quotes"',2,2.0,1.0,,1 -##'strings',3,3.0E5,3,,1 -##'strings',4,-1e-10,,,1 -##""" -## import os -## from datetime import datetime -## fname = 'tmp%s' % datetime.now().strftime("%y%m%d%H%M%S%s") -## f = open(fname, 'w') -## f.write(fcontent) -## f.close() -## mrectxt = fromtextfile(fname,delimitor=',',varnames='ABCDEFG') -## os.unlink(fname) -## # -## assert(isinstance(mrectxt, MaskedRecords)) -## assert_equal(mrectxt.F, [1,1,1,1]) -## assert_equal(mrectxt.E._mask, [1,1,1,1]) -## assert_equal(mrectxt.C, [1,2,3.e+5,-1e-10]) -# -# def test_addfield(self): -# "Tests addfield" - if 1: - [d, m, mrec] = self_data - newfield = masked_array(d+10, mask=m[::-1]) - mrec = addfield(mrec, newfield) - assert_equal(mrec.f2, d+10) - assert_equal(mrec.f2._mask, m[::-1]) -# -# \ No newline at end of file + \ No newline at end of file From scipy-svn at scipy.org Mon Feb 19 01:52:24 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 00:52:24 -0600 (CST) Subject: [Scipy-svn] r2725 - trunk/Lib/sandbox/maskedarray Message-ID: <20070219065224.7CE6239C015@new.scipy.org> Author: pierregm Date: 2007-02-19 00:52:16 -0600 (Mon, 19 Feb 2007) New Revision: 2725 Modified: trunk/Lib/sandbox/maskedarray/CHANGELOG trunk/Lib/sandbox/maskedarray/core.py Log: core : ensure that self._mask is nomask when setting the mask to nomask Modified: trunk/Lib/sandbox/maskedarray/CHANGELOG =================================================================== --- trunk/Lib/sandbox/maskedarray/CHANGELOG 2007-02-19 05:30:05 UTC (rev 2724) +++ trunk/Lib/sandbox/maskedarray/CHANGELOG 2007-02-19 06:52:16 UTC (rev 2725) @@ -31,7 +31,10 @@ be overwritten far too easily. If you want to specify a new value for the mask, you should use the _setmask or __setmask__ methods: these methods ensure that the new mask has the same shape as the data, and that the _hardmask condition -is respected. +is respected. Note that in some particular cases involving subclasses of +MaskedArray, the mask is not always propagated properly. It is recommended to +set the mask of the base object, instead of trying to set the mask through a +view of MaskedArray. Following the suggestions of Reggie Dugard, the class defaults have been suppressed. Unfortunately, that required to add some extra definitions in the @@ -43,7 +46,15 @@ The previous implementation of MaskedArray is called core_ini.py and it can be found in the alternative_versions directory. This folder contains also yet another implementation core_alt. This latter is left for documentation purposes, -and should serve as a template when we'll port the package to C. +and should serve as a template when we'll port the package to C. It introduces +yet another attribute, _masklayer. This attribute is always a ndarray of booleans +with the same shape as the data, that stores the values of the masked. The _mask +attribute is then a property, that returns _masklayer or nomask depending on +the value of the _smallmask flag and the values of _masklayer. This approach +seems to solve the anomaly in mask propagation mentioned earlier. However, some +performance tests show that this approach is significantly slower (from 10% to +50%) than the current implementation. It was therfore decided to leave it out of +the main package. #............................................................................... 2007-01-22 : core : fixed a call to numpy.float128 on 32b machines Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-19 05:30:05 UTC (rev 2724) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-19 06:52:16 UTC (rev 2725) @@ -1163,7 +1163,13 @@ if newmask is not nomask: self._mask.__ior__(newmask) else: - self._mask.flat = newmask + # This one is tricky: if we set the mask that way, we may break the + # propagation. But if we don't, we end up with a mask full of False + # and a test on nomask fails... + if newmask is nomask: + self._mask = nomask + else: + self._mask.flat = newmask if self._mask.shape: self._mask.shape = self.shape _setmask = __setmask__ From scipy-svn at scipy.org Mon Feb 19 02:37:45 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 01:37:45 -0600 (CST) Subject: [Scipy-svn] r2726 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070219073745.ED17439C015@new.scipy.org> Author: pierregm Date: 2007-02-19 01:37:26 -0600 (Mon, 19 Feb 2007) New Revision: 2726 Modified: trunk/Lib/sandbox/timeseries/tdates.py trunk/Lib/sandbox/timeseries/tests/test_dates.py trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tdates : ensures compatibility w/ new MaskedArray + misc cleanup tseries : ensures compatibility w/ new MaskedArray + misc cleanup Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-19 06:52:16 UTC (rev 2725) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-19 07:37:26 UTC (rev 2726) @@ -21,6 +21,8 @@ from numpy import ndarray import numpy.core.numeric as numeric import numpy.core.fromnumeric as fromnumeric +import numpy.core.numerictypes as ntypes +from numpy.core.numerictypes import generic import maskedarray as MA #reload(MA) @@ -225,47 +227,48 @@ @property def day(self): "Returns the day of month." - return self.__getDateInfo('D') + return self.__getdateinfo__('D') @property def day_of_week(self): "Returns the day of week." - return self.__getDateInfo('W') + return self.__getdateinfo__('W') @property def day_of_year(self): "Returns the day of year." - return self.__getDateInfo('R') + return self.__getdateinfo__('R') @property def month(self): "Returns the month." - return self.__getDateInfo('M') + return self.__getdateinfo__('M') @property def quarter(self): "Returns the quarter." - return self.__getDateInfo('Q') + return self.__getdateinfo__('Q') @property def year(self): "Returns the year." - return self.__getDateInfo('Y') + return self.__getdateinfo__('Y') @property def second(self): "Returns the seconds." - return self.__getDateInfo('S') + return self.__getdateinfo__('S') @property def minute(self): "Returns the minutes." - return self.__getDateInfo('T') + return self.__getdateinfo__('T') @property def hour(self): "Returns the hour." - return self.__getDateInfo('H') + return self.__getdateinfo__('H') @property def week(self): "Returns the week." - return self.__getDateInfo('I') + return self.__getdateinfo__('I') - def __getDateInfo(self, info): + def __getdateinfo__(self, info): return int(cseries.getDateInfo(numpy.asarray(self.value), self.freq, info)) + __getDateInfo = __getdateinfo__ def __add__(self, other): if isinstance(other, Date): @@ -497,24 +500,19 @@ >>> for d in DateArray(...): accesses the array element by element. Therefore, `d` is a Date object. """ - def __new__(cls, dates=None, freq='U', copy=False): - if isinstance(dates, DateArray): - cls.__defaultfreq = dates.freq - if not copy: - return dates.view(cls) - return dates.copy().view(cls) + (_tostr, _toord, _steps) = (None, None, None) + def __new__(cls, dates=None, freq=None, copy=False): + # Get the frequency ...... + if freq is None: + _freq = getattr(dates, 'freq', -9999) else: - _dates = numeric.asarray(dates, dtype=int_) - if _dates.ndim == 0: - _dates.shape = (1,) - if copy: - _dates = _dates.copy() - if freq is None: - freq = 'U' - cls.__defaultfreq = corelib.check_freq(freq) - (cls.__toobj, cls.__toord, cls.__tostr) = (None, None, None) - (cls.__steps, cls.__full, cls.__hasdups) = (None, None, None) - return _dates.view(cls) + _freq = freq + cls._defaultfreq = corelib.check_freq(freq) + # Get the dates .......... + _dates = numeric.array(dates, copy=copy, dtype=int_, subok=1).view(cls) + if _dates.ndim == 0: + _dates.shape = (1,) + return _dates def __array_wrap__(self, obj, context=None): if context is None: @@ -523,33 +521,26 @@ raise ArithmeticDateError, "(function %s)" % context[0].__name__ def __array_finalize__(self, obj): - if hasattr(obj, 'freq'): - self.freq = obj.freq - self.freqstr = obj.freqstr - else: - self.freq = self.__defaultfreq - self.freqstr = corelib.freq_tostr(self.__defaultfreq) + self.freq = getattr(obj, 'freq', self._defaultfreq) + self.freqstr = getattr(obj, 'freqstr', corelib.freq_tostr(self.freq)) + for attr in ('_toobj','_toord','_tostr', + '_steps','_full','_hasdups'): + setattr(self, attr, getattr(obj, attr, None)) + return def __getitem__(self, indx): if isinstance(indx, Date): - index = self.find_dates(indx) + indx = self.find_dates(indx) elif numeric.asarray(indx).dtype.kind == 'O': try: indx = self.find_dates(indx) except AttributeError: pass r = ndarray.__getitem__(self, indx) -# return r - if not hasattr(r, "size"): - if isinstance(r, int): - return Date(self.freq, value=r) - else: - return r - elif r.size == 1: - # Only one element, and it's not a scalar: we have a DateArray of size 1 - if len(r.shape) > 0: - r = r.item() + if isinstance(r, (generic, int)): return Date(self.freq, value=r) + elif r.size == 1: + return Date(self.freq, value=r.item()) else: return r @@ -559,43 +550,43 @@ @property def day(self): "Returns the day of month." - return self.__getDateInfo('D') + return self.__getDateInfo__('D') @property def day_of_week(self): "Returns the day of week." - return self.__getDateInfo('W') + return self.__getDateInfo__('W') @property def day_of_year(self): "Returns the day of year." - return self.__getDateInfo('R') + return self.__getDateInfo__('R') @property def month(self): "Returns the month." - return self.__getDateInfo('M') + return self.__getDateInfo__('M') @property def quarter(self): "Returns the quarter." - return self.__getDateInfo('Q') + return self.__getDateInfo__('Q') @property def year(self): "Returns the year." - return self.__getDateInfo('Y') + return self.__getDateInfo__('Y') @property def second(self): "Returns the seconds." - return self.__getDateInfo('S') + return self.__getDateInfo__('S') @property def minute(self): "Returns the minutes." - return self.__getDateInfo('T') + return self.__getDateInfo__('T') @property def hour(self): "Returns the hour." - return self.__getDateInfo('H') + return self.__getDateInfo__('H') @property def week(self): "Returns the week." - return self.__getDateInfo('I') + return self.__getDateInfo__('I') days = day weekdays = day_of_week @@ -620,17 +611,17 @@ def toordinal(self): "Converts the dates from values to ordinals." # Note: we better try to cache the result - if self.__toord is None: + if self._toord is None: # diter = (Date(self.freq, value=d).toordinal() for d in self) diter = (d.toordinal() for d in self) toord = numeric.fromiter(diter, dtype=float_) - self.__toord = toord - return self.__toord + self._toord = toord + return self._toord # def tostring(self): "Converts the dates to strings." # Note: we better cache the result - if self.__tostr is None: + if self._tostr is None: firststr = str(self[0]) if self.size > 0: ncharsize = len(firststr) @@ -638,22 +629,18 @@ dtype='|S%i' % ncharsize) else: tostr = firststr - self.__tostr = tostr - return self.__tostr + self._tostr = tostr + return self._tostr # def asfreq(self, freq=None, relation="BEFORE"): "Converts the dates to another frequency." # Note: As we define a new object, we don't need caching - if freq is None: + if freq is None or freq == -9999: return self tofreq = corelib.check_freq(freq) if tofreq == self.freq: return self - if self.freqstr == 'U': - warnings.warn("Undefined frequency: assuming daily!") - fromfreq = corelib.freq_revdict['D'] - else: - fromfreq = self.freq + fromfreq = self.freq _rel = relation.upper()[0] new = cseries.asfreq(numeric.asarray(self), fromfreq, tofreq, _rel) return DateArray(new, freq=freq) @@ -673,62 +660,54 @@ def date_to_index(self, date): "Returns the index corresponding to one given date, as an integer." - if isDate(date): - if self.isvalid(): - index = date.value - self[0].value - if index < 0 or index > self.size: - raise ValueError, "Date out of bounds!" - return index - else: - index_asarray = (self == date.value).nonzero() - if fromnumeric.size(index_asarray) == 0: - raise ValueError, "Date out of bounds!" - return index_asarray[0][0] + if self.isvalid(): + index = date.value - self[0].value + if index < 0 or index > self.size: + raise ValueError, "Date out of bounds!" + return index else: - #date is a DateArray - def idx_check(val): return (date == val).any() - idx_check_v = numpy.vectorize(idx_check) - return numpy.where(idx_check_v(self.tovalue()))[0] - - + index_asarray = (self == date.value).nonzero() + if fromnumeric.size(index_asarray) == 0: + raise ValueError, "Date out of bounds!" + return index_asarray[0][0] #...................................................... def get_steps(self): """Returns the time steps between consecutive dates. The timesteps have the same unit as the frequency of the series.""" if self.freq == 'U': - warnings.warn("Undefined frequency: assuming daily!") - if self.__steps is None: + warnings.warn("Undefined frequency: assuming integers!") + if self._steps is None: val = numeric.asarray(self).ravel() if val.size > 1: steps = val[1:] - val[:-1] - if self.__full is None: - self.__full = (steps.max() == 1) - if self.__hasdups is None: - self.__hasdups = (steps.min() == 0) + if self._full is None: + self._full = (steps.max() == 1) + if self._hasdups is None: + self._hasdups = (steps.min() == 0) else: - self.__full = True - self.__hasdups = False + self._full = True + self._hasdups = False steps = numeric.array([], dtype=int_) - self.__steps = steps - return self.__steps + self._steps = steps + return self._steps def has_missing_dates(self): "Returns whether the DateArray have missing dates." - if self.__full is None: + if self._full is None: steps = self.get_steps() - return not(self.__full) + return not(self._full) def isfull(self): "Returns whether the DateArray has no missing dates." - if self.__full is None: + if self._full is None: steps = self.get_steps() - return self.__full + return self._full def has_duplicated_dates(self): "Returns whether the DateArray has duplicated dates." - if self.__hasdups is None: + if self._hasdups is None: steps = self.get_steps() - return self.__hasdups + return self._hasdups def isvalid(self): "Returns whether the DateArray is valid: no missing/duplicated dates." @@ -958,7 +937,7 @@ "Returns the doc of the function (from the doc of the method)." try: return getattr(DateArray, self._methodname).__doc__ - except: + except AttributeError: return "???" # def __call__(self, caller, *args, **params): @@ -988,9 +967,14 @@ ################################################################################ if __name__ == '__main__': - assert (Date('D','2007-01')==Date('D',string='2007-01')) - assert (Date('D','2007-01')==Date('D', value=732677)) - assert (Date('D',732677)==Date('D', value=732677)) - n = Date('D','2007-01') - tmp = date_array(n,n+3) - print tmp[0] \ No newline at end of file + from maskedarray.testutils import assert_equal + if 1: + dlist = ['2007-%02i' % i for i in range(1,5)+range(7,13)] + mdates = date_array_fromlist(dlist, 'M') + # Using an integer + assert_equal(mdates[0].value, 24073) + assert_equal(mdates[-1].value, 24084) + # Using a date + lag = mdates.find_dates(mdates[0]) + print mdates[lag] + assert_equal(mdates[lag], mdates[0]) \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tests/test_dates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-19 06:52:16 UTC (rev 2725) +++ trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-19 07:37:26 UTC (rev 2726) @@ -25,13 +25,18 @@ import maskedarray.testutils from maskedarray.testutils import assert_equal, assert_array_equal -from timeseries import tdates -#reload(tdates) +import timeseries.tdates as tdates +reload(tdates) +#from timeseries import tdates +##reload(tdates) +#from timeseries.tdates import date_array_fromlist, Date, DateArray, date_array,\ +# mxDFromString, today +from timeseries.tdates import * +from timeseries.tdates import mxDFromString from timeseries import tcore #reload(tcore) -from timeseries.tdates import date_array_fromlist, Date, DateArray, date_array,\ - mxDFromString, today + class test_creation(NumpyTestCase): "Base test class for MaskedArrays." Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-19 06:52:16 UTC (rev 2725) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-19 07:37:26 UTC (rev 2726) @@ -178,7 +178,8 @@ series.mask = nomask assert(series._mask is nomask) assert(series._series._mask is nomask) - series._series.mask = [1,0,0]*5 + #series._series.mask = [1,0,0]*5 + series.mask = [1,0,0]*5 assert_equal(series._mask, [1,0,0]*5) assert_equal(series._series._mask, [1,0,0]*5) series[2] = masked Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-19 06:52:16 UTC (rev 2725) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-19 07:37:26 UTC (rev 2726) @@ -35,7 +35,7 @@ #from numpy.core.records import recarray from numpy.core.records import fromarrays as recfromarrays -import maskedarray as MA +import maskedarray.core as MA #reload(MA) from maskedarray.core import MaskedArray, MAError, masked, nomask, \ filled, getmask, getmaskarray, make_mask_none, mask_or, make_mask, \ @@ -56,8 +56,6 @@ - - __all__ = [ 'TimeSeriesError','TimeSeriesCompatibilityError','TimeSeries','isTimeSeries', 'time_series', 'tsmasked', @@ -68,10 +66,10 @@ ] #............................................................................... +# +#ufunc_domain = {} +#ufunc_fills = {} -ufunc_domain = {} -ufunc_fills = {} - #### -------------------------------------------------------------------------- #--- ... TimeSeriesError class ... #### -------------------------------------------------------------------------- @@ -95,10 +93,11 @@ msg = "Incompatible starting dates! (%s <> %s)" elif mode == 'size': msg = "Incompatible sizes! (%s <> %s)" + else: + msg = "Incompatibility ! (%s <> %s)" msg = msg % (first, second) TimeSeriesError.__init__(self, msg) - #def _compatibilitycheck(a, b): def _timeseriescompat(a, b): """Checks the date compatibility of two TimeSeries object. @@ -193,6 +192,61 @@ ##### -------------------------------------------------------------------------- ##--- ... Time Series ... ##### -------------------------------------------------------------------------- +class _tsmathmethod(object): + """Defines a wrapper for arithmetic array methods (add, mul...). +When called, returns a new TimeSeries object, with the new series the result of +the method applied on the original series. +The `_dates` part remains unchanged. + """ + def __init__ (self, methodname): + self._name = methodname + # + def __get__(self, obj, objtype=None): + "Gets the calling object." + self.obj = obj + return self + # + def __call__ (self, other, *args): + "Execute the call behavior." + instance = self.obj + if isinstance(other, TimeSeries): + assert(_timeseriescompat(instance, other)) + func = getattr(super(TimeSeries, instance), self._name) + result = func(other, *args) #.view(type(instance)) + result._dates = instance._dates + return result + +class _tsarraymethod(object): + """Defines a wrapper for basic array methods. +When called, returns a new TimeSeries object, with the new series the result of +the method applied on the original series. +If `ondates` is True, the same operation is performed on the `_dates`. +If `ondates` is False, the `_dates` part remains unchanged. + """ + def __init__ (self, methodname, ondates=False): + """abfunc(fillx, filly) must be defined. + abinop(x, filly) = x for all x to enable reduce. + """ + self._name = methodname + self._ondates = ondates + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, *args): + "Execute the call behavior." + _name = self._name + instance = self.obj + func_series = getattr(super(TimeSeries, instance), _name) + result = func_series(*args) + if self._ondates: + result._dates = getattr(instance._dates, _name) + else: + result._dates = instance._dates + return result + + class TimeSeries(MaskedArray, object): """Base class for the definition of time series. A time series is here defined as the combination of three arrays: @@ -207,120 +261,62 @@ The combination of `series` and `dates` is the `data` part. """ options = None + _defaultobserved = None def __new__(cls, data, dates=None, mask=nomask, freq=None, observed=None, start_date=None, dtype=None, copy=False, fill_value=None, keep_mask=True, small_mask=True, hard_mask=False): - #tslog.info("__new__: received data types %s, %s" % (type(data), data)) maparms = dict(copy=copy, dtype=dtype, fill_value=fill_value, keep_mask=keep_mask, small_mask=small_mask, - hard_mask=hard_mask, ) - if isinstance(data, TimeSeries): - # Check dates ........ - if dates is None: - newdates = data._dates - else: - if not hasattr(dates,'freq'): - raise DateError, "Invalid Dates!" - newdates = dates - data._dates = newdates - if hasattr(data, '_data') and hasattr(data._data, '_dates'): - data._data._dates = newdates - cls._defaultdates = newdates - # Check frequency...... - if freq is not None: - freq = corelib.check_freq(freq) - if freq != newdates.freq: - _dates = newdates.tofreq(freq) - else: - freq = newdates.freq - # Check observed....... - if observed is None: - observed = data.observed - else: - observed = corelib.fmtObserv(observed) - cls._defaultobserved = observed - _data = data._series + hard_mask=hard_mask,) + # Get the data ............................... + _data = MaskedArray(data, mask=mask, **maparms).view(cls) + # Get the frequency .......................... + freq = corelib.check_freq(freq) + # Get the dates .............................. + if dates is None: + newdates = getattr(data, '_dates', None) else: - # Check dates ........ - if dates is None: - length = _getdatalength(data) - if length > 0: - newdates = date_array(start_date=start_date, length=length, - freq=freq) - else: - newdates = date_array([], freq=freq) - elif not hasattr(dates, 'freq'): + newdates = dates + if newdates is not None: + if not hasattr(newdates, 'freq'): newdates = date_array(dlist=dates, freq=freq) + if freq is not None and newdates.freq != freq: + newdates = newdates.tofreq(freq) + else: + length = _getdatalength(data) + if length > 0: + newdates = date_array(start_date=start_date, length=length, + freq=freq) else: - newdates = dates - # Check data ......... - _data = data - if hasattr(data, '_mask') : - mask = mask_or(data._mask, mask) - # Set default ........ - cls._defaultdates = newdates - cls._defaultobserved = corelib.fmtObserv(observed) - + newdates = date_array([], freq=freq) + # Get observed ............................... + observed = getattr(data, 'observed', corelib.fmtObserv(observed)) + if _data is masked: assert(numeric.size(newdates)==1) return _data.view(cls) - newdata = super(TimeSeries,cls).__new__(cls, _data, mask=mask, - **maparms) - assert(_datadatescompat(newdata._data,newdates)) - return newdata - - #.................................. - def __array_wrap__(self, obj, context=None): - return TimeSeries(super(TimeSeries,self).__array_wrap__(obj, context), - dates=self._dates) + assert(_datadatescompat(_data,newdates)) + _data._dates = newdates + _data._defaultdates = _data._dates + return _data #............................................ def __array_finalize__(self,obj): - #tslog.info("__array_finalize__ received %s" % type(obj)) - if isinstance(obj, TimeSeries): - self._dates = obj._dates - self._data = obj._series._data - self._mask = obj._series._mask - self._series = obj._series - self._hardmask = obj._series._hardmask - self.observed = obj.observed - self._fill_value = obj._fill_value - else: - self._dates = self._defaultdates - self.observed = self._defaultobserved - self._data = obj - self._mask = self._defaultmask - if obj is masked: - self._series = masked - else: - self._series = MA.array(obj, mask=self._defaultmask, - copy=False, hard_mask=self._defaulthardmask) - self._hardmask = self._defaulthardmask - self.fill_value = self._fill_value - self._mask = self._series._mask - self._data = self._series._data - self._hardmask = self._series._hardmask - # - TimeSeries._defaulthardmask = False - TimeSeries._defaultmask = nomask - #tslog.info("__array_finalize__ sends %s" % type(self)) + MaskedArray.__array_finalize__(self, obj) + self._dates = getattr(obj, '_dates', None) + self.observed = getattr(obj, 'observed', self._defaultobserved) return + #.................................. + def __array_wrap__(self, obj, context=None): + result = super(TimeSeries, self).__array_wrap__(obj, context) + result._dates = self._dates + return result #............................................ - def __getattribute__(self,attr): - "Returns a given attribute." - # Here, we need to be smart: _mask should call _series._mask... - if attr in ['_data','_mask','_hardmask']: - return getattr(self._series,attr) - return super(TimeSeries, self).__getattribute__(attr) - - def __setattribute__(self,attr, value): - """Sets an attribute to a given value.""" - # Same thing here: if we modify ._mask, we need to modify _series._mask - # ...as well - super(TimeSeries, self).__setattribute__(attr, value) - if attr in ['_data','_mask','_hardmask']: - super(self._series.__class__, self._series).__setattribute__(attr, value) - setattr(self._series, attr, value) + def _get_series(self): + if self._mask.ndim == 0 and self._mask: + return masked + return self.view(MaskedArray) + _series = property(fget=_get_series) #............................................ def __checkindex(self, indx): "Checks the validity of an index." @@ -397,43 +393,25 @@ raise MAError, 'Cannot alter the masked element.' (sindx, dindx) = self.__checkindex(indx) #.... - if value is tsmasked: - self._series[sindx] = masked - elif isinstance(value, TimeSeries): - assert(_timeseriescompat(self[sindx], value)) - self._series[sindx] = value._series - else: - self._series[sindx] = value - # Don't forget to update the mask ! - self._mask = self._series._mask - + super(TimeSeries, self).__setitem__(sindx, value) #........................ def __getslice__(self, i, j): "Gets slice described by i, j" (si,di) = self.__checkindex(i) (sj,dj) = self.__checkindex(j) - (data, date) = (self._series[si:sj], self._dates[di:dj]) - return TimeSeries(data, dates=date, copy=False) + result = super(TimeSeries, self).__getitem__(slice(si,sj)) + result._dates = self._dates[di:dj] + return result #.... def __setslice__(self, i, j, value): "Gets item described by i. Not a copy as in previous versions." - (si,di) = self.__checkindex(i) - (sj,dj) = self.__checkindex(j) + (si,_) = self.__checkindex(i) + (sj,_) = self.__checkindex(j) #.... -# data = self._series[i:j] if isinstance(value, TimeSeries): assert(_timeseriescompat(self[si:sj], value)) - self._series[si:sj] = value._series - else: - self._series[si:sj] = value - # Don't forget to update the mask ! - self._mask = self._series._mask + super(TimeSeries, self).__setitem__(slice(si,sj), value) #...................................................... - def __len__(self): - if self.ndim == 0: - return 0 - return ndarray.__len__(self) - #...................................................... def __str__(self): """Returns a string representation of self (w/o the dates...)""" return str(self._series) @@ -466,30 +444,46 @@ 'time': timestr, 'freq': self.freqstr, } #............................................ - def _get_mask(self): - """Returns the current mask.""" - return self._series._mask - def _set_mask(self, mask): - """Sets the mask to `mask`.""" - mask = make_mask(mask, copy=False, small_mask=True) - if mask is not nomask: - if mask.size != self._data.size: - raise ValueError, "Inconsistent shape between data and mask!" - if mask.shape != self._data.shape: - mask.shape = self._data.shape - self._series._mask = mask - else: - self._series._mask = nomask - mask = property(fget=_get_mask, fset=_set_mask, doc="Mask") - + __add__ = _tsmathmethod('__add__') + __radd__ = _tsmathmethod('__add__') + __sub__ = _tsmathmethod('__sub__') + __rsub__ = _tsmathmethod('__rsub__') + __pow__ = _tsmathmethod('__pow__') + __mul__ = _tsmathmethod('__mul__') + __rmul__ = _tsmathmethod('__mul__') + __div__ = _tsmathmethod('__div__') + __rdiv__ = _tsmathmethod('__rdiv__') + __truediv__ = _tsmathmethod('__truediv__') + __rtruediv__ = _tsmathmethod('__rtruediv__') + __floordiv__ = _tsmathmethod('__floordiv__') + __rfloordiv__ = _tsmathmethod('__rfloordiv__') + __eq__ = _tsmathmethod('__eq__') + __ne__ = _tsmathmethod('__ne__') + __lt__ = _tsmathmethod('__lt__') + __le__ = _tsmathmethod('__le__') + __gt__ = _tsmathmethod('__gt__') + __ge__ = _tsmathmethod('__ge__') + + astype = _tsarraymethod('astype') + reshape = _tsarraymethod('reshape', ondates=True) + copy = _tsarraymethod('copy', ondates=True) + compress = _tsarraymethod('compress', ondates=True) + ravel = _tsarraymethod('ravel', ondates=True) + cumsum = _tsarraymethod('cumsum',ondates=False) + cumprod = _tsarraymethod('cumprod',ondates=False) + anom = _tsarraymethod('anom',ondates=False) + +# def nonzero(self): +# """Returns a tuple of ndarrays, one for each dimension of the array, +# containing the indices of the non-zero elements in that dimension.""" +# return self._series.nonzero() + +# filled = _tsarraymethod('filled', ondates=False) + + #............................................ def ids (self): """Return the ids of the data, dates and mask areas""" - return (id(self._series), id(self.dates),) - - def copy(self): - "Returns a copy of the TimeSeries." - return TimeSeries(self, copy=True) - + return (id(self._series), id(self.dates),) #------------------------------------------------------ @property def series(self): @@ -507,7 +501,6 @@ def freqstr(self): """Returns the corresponding frequency (as a string).""" return self._dates.freqstr - @property def day(self): "Returns the day of month for each date in self._dates." @@ -605,10 +598,6 @@ "Converts the dates to another frequency, and adapt the data." return convert(self, freq, func=func, position=position) #..................................................... - def nonzero(self): - """Returns a tuple of ndarrays, one for each dimension of the array, - containing the indices of the non-zero elements in that dimension.""" - return self._series.nonzero() def _attrib_dict(series, exclude=[]): """this function is used for passing through attributes of one @@ -621,128 +610,7 @@ ##### -------------------------------------------------------------------------- ##--- ... Additional methods ... ##### -------------------------------------------------------------------------- -class _inplacemethod(object): - """Defines a wrapper for inplace arithmetic array methods (iadd, imul...). -When called, returns a new TimeSeries object, with the new series the result of -the method applied on the original series. -The `_dates` part remains unchanged. - """ - def __init__ (self, binop): - """abfunc(fillx, filly) must be defined. - abinop(x, filly) = x for all x to enable reduce. - """ - self.f = binop - self.obj = None - # - def __get__(self, obj, objtype=None): - "Gets the calling object." - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - assert(_timeseriescompat(instance,other)) - func = getattr(instance._series, self.f) - func(other, *args) - return instance -#...................................... -TimeSeries.__iadd__ = _inplacemethod('__iadd__') -TimeSeries.__iand__ = _inplacemethod('__iand__') -TimeSeries.__idiv__ = _inplacemethod('__idiv__') -TimeSeries.__isub__ = _inplacemethod('__isub__') -TimeSeries.__imul__ = _inplacemethod('__imul__') - -class _tsmathmethod(object): - """Defines a wrapper for arithmetic array methods (add, mul...). -When called, returns a new TimeSeries object, with the new series the result of -the method applied on the original series. -The `_dates` part remains unchanged. - """ - def __init__ (self, binop): - """abfunc(fillx, filly) must be defined. - abinop(x, filly) = x for all x to enable reduce. - """ - self.f = binop - # - def __get__(self, obj, objtype=None): - "Gets the calling object." - self.obj = obj - return self - # - def __call__ (self, other, *args): - "Execute the call behavior." - instance = self.obj - _dates = instance._dates - #tslog.info("_tsmathmethod: series: %s" % instance,) - #tslog.info("_tsmathmethod: other : %s" % other,) - func = getattr(instance._series, self.f) - if isinstance(other, TimeSeries): - assert(_timeseriescompat(instance, other)) - return instance.__class__(func(other, *args), dates=_dates,) -#...................................... -TimeSeries.__add__ = _tsmathmethod('__add__') -TimeSeries.__radd__ = _tsmathmethod('__add__') -TimeSeries.__sub__ = _tsmathmethod('__sub__') -TimeSeries.__rsub__ = _tsmathmethod('__rsub__') -TimeSeries.__pow__ = _tsmathmethod('__pow__') -TimeSeries.__mul__ = _tsmathmethod('__mul__') -TimeSeries.__rmul__ = _tsmathmethod('__mul__') -TimeSeries.__div__ = _tsmathmethod('__div__') -TimeSeries.__rdiv__ = _tsmathmethod('__rdiv__') -TimeSeries.__truediv__ = _tsmathmethod('__truediv__') -TimeSeries.__rtruediv__ = _tsmathmethod('__rtruediv__') -TimeSeries.__floordiv__ = _tsmathmethod('__floordiv__') -TimeSeries.__rfloordiv__ = _tsmathmethod('__rfloordiv__') -TimeSeries.__eq__ = _tsmathmethod('__eq__') -TimeSeries.__ne__ = _tsmathmethod('__ne__') -TimeSeries.__lt__ = _tsmathmethod('__lt__') -TimeSeries.__le__ = _tsmathmethod('__le__') -TimeSeries.__gt__ = _tsmathmethod('__gt__') -TimeSeries.__ge__ = _tsmathmethod('__ge__') -#................................................ -class _tsarraymethod(object): - """Defines a wrapper for basic array methods. -When called, returns a new TimeSeries object, with the new series the result of -the method applied on the original series. -If `ondates` is True, the same operation is performed on the `_dates`. -If `ondates` is False, the `_dates` part remains unchanged. - """ - def __init__ (self, methodname, ondates=False): - """abfunc(fillx, filly) must be defined. - abinop(x, filly) = x for all x to enable reduce. - """ - self._name = methodname - self._ondates = ondates - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, *args): - "Execute the call behavior." - _name = self._name - instance = self.obj - func_series = getattr(instance._series, _name) - if self._ondates: - func_dates = getattr(instance._dates, _name) - return instance.__class__(func_series(*args), - dates=func_dates(*args)) - else: - return instance.__class__(func_series(*args), - dates=instance._dates) -TimeSeries.astype = _tsarraymethod('astype') -TimeSeries.reshape = _tsarraymethod('reshape', ondates=True) -TimeSeries.copy = _tsarraymethod('copy', ondates=True) -TimeSeries.compress = _tsarraymethod('compress', ondates=True) -TimeSeries.ravel = _tsarraymethod('ravel', ondates=True) -TimeSeries.filled = _tsarraymethod('filled', ondates=False) -TimeSeries.cumsum = _tsarraymethod('cumsum',ondates=False) -TimeSeries.cumprod = _tsarraymethod('cumprod',ondates=False) -TimeSeries.anom = _tsarraymethod('anom',ondates=False) - -#...................................... class _tsaxismethod(object): """Defines a wrapper for array methods working on an axis (mean...). When called, returns a ndarray, as the result of the method applied on the series. @@ -982,6 +850,7 @@ `data` : Array of data. """ + data = numeric.asanyarray(data) if dates is None: length = _getdatalength(data) if length > 0: @@ -1375,12 +1244,6 @@ data = MA.asarray(data) newdatad = numeric.empty(nsize, data.dtype) newdatam = numeric.ones(nsize, bool_) -# if fill_value is None: -# if hasattr(data,'fill_value'): -# fill_value = data.fill_value -# else: -# fill_value = MA.default_fill_value(data) - #data = data.filled(fill_value) #.... if datam is nomask: for (new,old) in zip(newslc,oldslc): @@ -1448,19 +1311,6 @@ if __name__ == '__main__': from maskedarray.testutils import assert_equal import numpy as N + + - if 1: - #TODO: CHECK THAT, AND PUT IT IN tests/test_timeseries IF IT WORKS - # Test 2 points of 3 variables - xx = time_series([[1,2,3],[4,5,6]], start_date=thisday('b')) - assert_equal(xx[0]._data, [[1,2,3]]) - assert_equal(xx[:,0]._data, [1,4]) - # Test a single point of 3 variables - xx = time_series([[1,2,3]], start_date=thisday('b')) - assert_equal(xx[0]._data, [[1,2,3]]) - assert_equal(xx[:,0]._data, [[1]]) - # Test 3 data points - x = time_series([1,2,3], start_date=thisday('b')) - assert_equal(x[0], 1) - # Test using a DateArray as items - assert_equal(x[x._dates[:]],x) From scipy-svn at scipy.org Mon Feb 19 10:08:16 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 09:08:16 -0600 (CST) Subject: [Scipy-svn] r2727 - trunk/Lib/sandbox/timeseries Message-ID: <20070219150816.DFB9639C244@new.scipy.org> Author: mattknox_ca Date: 2007-02-19 09:08:12 -0600 (Mon, 19 Feb 2007) New Revision: 2727 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: fixed problem causing test_fromdatearray in test_timeseries.py to fail (strrep) Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-19 07:37:26 UTC (rev 2726) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-19 15:08:12 UTC (rev 2727) @@ -539,7 +539,11 @@ r = ndarray.__getitem__(self, indx) if isinstance(r, (generic, int)): return Date(self.freq, value=r) - elif r.size == 1: + elif hasattr(r, 'size') and r.size == 1: + # need to check if it has a size attribute for situations + # like when the datearray is the data for a maskedarray + # or some other subclass of ndarray with wierd getitem + # behaviour return Date(self.freq, value=r.item()) else: return r From scipy-svn at scipy.org Mon Feb 19 10:08:59 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 09:08:59 -0600 (CST) Subject: [Scipy-svn] r2728 - trunk/Lib/sandbox/timeseries/tests Message-ID: <20070219150859.8697839C249@new.scipy.org> Author: mattknox_ca Date: 2007-02-19 09:08:55 -0600 (Mon, 19 Feb 2007) New Revision: 2728 Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py Log: Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-19 15:08:12 UTC (rev 2727) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-19 15:08:55 UTC (rev 2728) @@ -75,7 +75,6 @@ def test_fromdatearray(self): _, dates, _ = self.d - data = dates.copy() data = dates series = time_series(data, dates) From scipy-svn at scipy.org Mon Feb 19 10:31:42 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 09:31:42 -0600 (CST) Subject: [Scipy-svn] r2729 - trunk/Lib/sandbox/timeseries Message-ID: <20070219153142.3D7BD39C0CA@new.scipy.org> Author: mattknox_ca Date: 2007-02-19 09:31:37 -0600 (Mon, 19 Feb 2007) New Revision: 2729 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: fixed bug: observed attribute wasn't getting set in __new__ method for TimeSeries Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-19 15:08:55 UTC (rev 2728) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-19 15:31:37 UTC (rev 2729) @@ -299,6 +299,7 @@ assert(_datadatescompat(_data,newdates)) _data._dates = newdates _data._defaultdates = _data._dates + _data.observed = observed return _data #............................................ def __array_finalize__(self,obj): From scipy-svn at scipy.org Mon Feb 19 10:39:22 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 09:39:22 -0600 (CST) Subject: [Scipy-svn] r2730 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070219153922.7E6F439C28F@new.scipy.org> Author: mattknox_ca Date: 2007-02-19 09:39:12 -0600 (Mon, 19 Feb 2007) New Revision: 2730 Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py Log: lots of changes Modified: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-19 15:31:37 UTC (rev 2729) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-19 15:39:12 UTC (rev 2730) @@ -1,3 +1,32 @@ +""" +pyfame TODO: + +- implement "commit" +- implement "create_from_pyobj" + +Add unit tests: + + - db_created + - db_modified + - is_open + - db_desc + - db_doc + - create + - commit + - create_from_pyobj + +Add new functions: + + - set_obj_attr + - get_obj_attr + - modified + - created + - desc + - freq + - copy + - rename +""" + import sys, types, re, os import timeseries as ts @@ -211,6 +240,7 @@ if 'data' in result: vals = result['data'] mask = result['mask'] + if not mask.any(): mask = ma.nomask else: vals = [] mask = ma.nomask @@ -540,26 +570,55 @@ else: cf_write_scalar(self.dbkey, name, fame_data, fame_type) + + def create(name, cls, type, freq=None, basis=None, observed=None): + """create object in database with specified attributes as `name`""" + + if cls not in (mp.HSERIE, mp.HSCALA): + raise ValueError("unrecognized object class: "+str(cls)) + + if freq is None: + if cls == mp.HSCALA: + freq = mp.HUNDFX + else: + raise ValueError("freq must be specified for series") - def desc(self): + if freq in (mp.HUNDFX, mp.HCASEX): + basis = mp.HBSUND + observed = mp.HOBUND + else: + if basis is None: basis = mp.HBSDAY + if observed is None: observed = mp.HOBEND + + cf_create(self.dbkey, name, cls, freq, type, basis, observed) + + def create_from_pyobj(name, pyobj): + """create object of appropriate type in database based on the +python object `pyobj` as `name`. Does not write any data to the +database, simply initializes the object in the database.""" + raise NotImplementedError("function not implemented yet") + + + def db_desc(self): "get 'description' attribute of database" return cf_get_db_attr(self.dbkey, "DESC") - def doc(self): + def db_doc(self): "get 'doc' attribute of database" return cf_get_db_attr(self.dbkey, "DOC") - def created(self): + def db_created(self): "get 'created' attribute of database" fame_date = cf_get_db_attr(self.dbkey, "CREATED") return _famedate_to_tsdate(fame_date, 's') - def modified(self): + def db_modified(self): "get 'modified' attribute of database" fame_date = cf_get_db_attr(self.dbkey, "MODIFIED") return _famedate_to_tsdate(fame_date, 's') def is_open(self): + "returns True if database is open. False otherwise" return cf_get_db_attr(self.dbkey, "ISOPEN") def wildlist(self, exp, wildonly=False): @@ -585,7 +644,7 @@ cf_close(self.dbkey) def commit(self): - pass + raise NotImplementedError("function not implemented yet") def delete(self, name, must_exist=True): """Deletes the specified object(s) from the database""" From scipy-svn at scipy.org Mon Feb 19 10:39:42 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 19 Feb 2007 09:39:42 -0600 (CST) Subject: [Scipy-svn] r2731 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070219153942.B453939C0CA@new.scipy.org> Author: mattknox_ca Date: 2007-02-19 09:39:39 -0600 (Mon, 19 Feb 2007) New Revision: 2731 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-19 15:39:12 UTC (rev 2730) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-19 15:39:39 UTC (rev 2731) @@ -92,65 +92,29 @@ def test_main(self): "execute all the tests. Order is important here" - self._test_write_scalars() - self._test_read_scalars() - - self._test_dict_scalars() + tests = ["_test_write_scalars", "_test_read_scalars", + "_test_dict_scalars", "_test_write_freqs_tser", + "_test_read_freqs_tser", "_test_write_dtypes_tser", + "_test_read_dtypes_tser", "_test_read_range_tser", + "_test_write_append_tser", "_test_read_append_tser", + "_test_write_range_tser", "_test_verify_write_range_tser", + "_test_write_empty_tser", "_test_read_empty_tser", + "_test_overwrite_tser", "_test_assume_exists_tser", + "_test_dict_tser", "_test_write_dtypes_cser", + "_test_read_dtypes_cser", "_test_read_range_cser", + "_test_write_append_cser", "_test_read_append_cser", + "_test_write_range_cser", "_test_verify_write_range_cser", + "_test_write_empty_cser", "_test_read_empty_cser", + "_test_overwrite_cser", "_test_assume_exists_cser", + "_test_dict_cser", "_test_whats", + "_test_exists", "_test_delete", + "_test_wildlist", "_test_restore"] - self._test_write_freqs_tser() - self._test_read_freqs_tser() + for t in tests: + print t + getattr(self, t)() - self._test_write_dtypes_tser() - self._test_read_dtypes_tser() - - self._test_read_range_tser() - self._test_write_append_tser() - self._test_read_append_tser() - - self._test_write_range_tser() - self._test_verify_write_range_tser() - - self._test_write_empty_tser() - self._test_read_empty_tser() - - self._test_overwrite_tser() - - self._test_assume_exists_tser() - - self._test_dict_tser() - - self._test_write_dtypes_cser() - self._test_read_dtypes_cser() - - self._test_read_range_cser() - - self._test_write_append_cser() - self._test_read_append_cser() - - self._test_write_range_cser() - self._test_verify_write_range_cser() - - self._test_write_empty_cser() - self._test_read_empty_cser() - - self._test_overwrite_cser() - - self._test_assume_exists_cser() - - self._test_dict_cser() - - self._test_whats() - - self._test_exists() - - self._test_remove() - - self._test_wildlist() - - self._test_restore() - - def _test_write_scalars(self): "test writing all types of scalar values" for s in data['scalars']: @@ -400,14 +364,14 @@ assert(self.db.exists('$cser_float32')) assert(not self.db.exists('$fake_series')) - def _test_remove(self): - "test remove method" + def _test_delete(self): + "test delete method" assert(self.db.exists('$cser_1')) assert(self.db.exists('$cser_2')) - self.db.remove(['$cser_1', '$cser_2']) + self.db.delete(['$cser_1', '$cser_2']) assert(not self.db.exists('$cser_1')) assert(not self.db.exists('$cser_2')) - self.db.remove('$cser_1', must_exist=False) + self.db.delete('$cser_1', must_exist=False) def _test_wildlist(self): "test wildlist method" @@ -423,7 +387,7 @@ self.db.close() self.db = fame.FameDb("testdb.db",'s') - self.db.remove('$tser_float32') + self.db.delete('$tser_float32') assert(not self.db.exists('$tser_float32')) self.db.restore() assert(self.db.exists('$tser_float32')) From scipy-svn at scipy.org Tue Feb 20 13:12:21 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:12:21 -0600 (CST) Subject: [Scipy-svn] r2732 - in trunk/Lib/sandbox/timeseries: . plotlib Message-ID: <20070220181221.92DEA39C118@new.scipy.org> Author: pierregm Date: 2007-02-20 12:12:18 -0600 (Tue, 20 Feb 2007) New Revision: 2732 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py trunk/Lib/sandbox/timeseries/tdates.py Log: tdates : fixed name __getdateinfo__ mpl_timeseries_new : fixed TimeSeries_DateFormatter Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-19 15:39:39 UTC (rev 2731) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-20 18:12:18 UTC (rev 2732) @@ -1,12 +1,12 @@ """ Classes to plot TimeSeries w/ matplotlib. -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu +:author: Pierre GF Gerard-Marchant & Matt Knox +:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com :date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ -:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ +:version: $Id: tdates.py 2726 2007-02-19 07:37:26Z pierregm $ """ -__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: pierregm $)" __version__ = '1.0' __revision__ = "$Revision: 2676 $" __date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' @@ -133,7 +133,10 @@ def _get_default_annual_spacing(nyears): """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 20: + + if nyears < 11: + (min_spacing, maj_spacing) = (1, 1) + elif nyears < 15: (min_spacing, maj_spacing) = (1, 2) elif nyears < 50: (min_spacing, maj_spacing) = (1, 5) @@ -149,34 +152,6 @@ (min_spacing, maj_spacing) = (20, 100) return (min_spacing, maj_spacing) -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1,4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1,4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) - -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1,3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1,6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1,12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3,12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) - #............................................................................... class TimeSeries_DateLocator(Locator): "Locates the ticks along an axis controlled by a DateArray." @@ -202,6 +177,10 @@ "Returns the default ticks spacing." raise NotImplementedError('Derived must override') + def _get_default_locs(self, vmin, vmax): + "Returns the default ticks spacing." + raise NotImplementedError('Derived must override') + def __call__(self): 'Return the locations of the ticks.' self.verify_intervals() @@ -209,12 +188,12 @@ if vmax < vmin: vmin, vmax = vmax, vmin if self.isdynamic: - base = self._get_default_spacing(vmax-vmin+1) + locs = self._get_default_locs(vmin, vmax) else: base = self.base - d = vmin // base - vmin = (d+1) * base + self.offset - locs = range(vmin, vmax+1, base) + (d, m) = divmod(vmin, base) + vmin = (d+1) * base + locs = range(vmin, vmax+1, base) return locs def autoscale(self): @@ -223,21 +202,12 @@ """ self.verify_intervals() dmin, dmax = self.dataInterval.get_bounds() - if self.isdynamic: - base = self._get_default_spacing(dmax-dmin+1) - else: - base = self.base - (d,m) = divmod(dmin, base) - if m < base/2: - vmin = d * base - else: - vmin = (d+1) * base - (d,m) = divmod(dmax, base) - vmax = (d+1) * base + locs = self._get_default_locs(dmin, dmax) + (vmin, vmax) = locs[[0, -1]] if vmin == vmax: vmin -= 1 vmax += 1 - return nonsingular(vmin, vmax) + return nonsingular(vmin, vmax) #............................................................................... class TimeSeries_AnnualLocator(TimeSeries_DateLocator): @@ -248,12 +218,17 @@ TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, base, quarter, month, day) - def _get_default_spacing(self, span): + def _get_default_locs(self, vmin, vmax): "Returns the default tick spacing for annual data." + span = vmax - vmin + 1 (minor, major) = _get_default_annual_spacing(span) if self.isminor: - return minor - return major + base = minor + else: + base = major + offset = base - (vmin % base) + return N.arange(vmin+offset, vmax+1, base) + #............................................................................... class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): "Locates the ticks along an axis controlled by a quarterly DateArray." @@ -264,12 +239,24 @@ base, quarter, month, day) self.offset=1 - def _get_default_spacing(self, span): - "Returns the default tick spacing for quarterly data." - (minor, major) = _get_default_quarterly_spacing(span) + def _get_default_locs(self, vmin, vmax): + "Returns the default ticks spacing." + nquarters = vmax - vmin + 1 + if nquarters <= 3*4: + (min_spacing, maj_spacing) = (1, 4) + elif nquarters <= 11*4: + (min_spacing, maj_spacing) = (1, 4) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) + min_spacing = min_anndef * 4 + maj_spacing = maj_anndef * 4 if self.isminor: - return minor - return major + base = min_spacing + else: + base = maj_spacing + offset = base - (vmin+4-1) % base + return N.arange(vmin+offset, vmax+1, base) + #............................................................................... class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): "Locates the ticks along an axis controlled by a monthly DateArray." @@ -280,12 +267,28 @@ base, quarter, month, day) self.offset = 1 - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) + def _get_default_locs(self, vmin, vmax): + "Returns the default ticks spacing." + nmonths = vmax - vmin + 1 + if nmonths <= 10: + (min_spacing, maj_spacing) = (1, 3) + elif nmonths <= 2*12: + (min_spacing, maj_spacing) = (1, 6) + elif nmonths <= 3*12: + (min_spacing, maj_spacing) = (1, 12) + elif nmonths <= 11*12: + (min_spacing, maj_spacing) = (3, 12) + else: + (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) + min_spacing = min_anndef * 12 + maj_spacing = maj_anndef * 12 if self.isminor: - return minor - return major + base = min_spacing + offset = ((4 - (vmin-1) % 4) % base) + else: + base = maj_spacing + offset = ((4 - (vmin-1) % 4) % 4) + return N.arange(vmin+offset, vmax+1, base) #............................................................................... class TimeSeries_DailyLocator(TimeSeries_DateLocator): @@ -310,18 +313,21 @@ # if span <= daysperyear//12: minor = default - major = default[(dates.day_of_week == 1)] + major = default[(dates.day == 1)] elif span <= daysperyear//3: - minor = default[(dates.day_of_week == 1)] + minor = default major = default[(dates.day == 1)] elif span <= 1.5 * daysperyear: + monthstart = (dates.day == 1) minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] + major = default[monthstart] elif span <= 3 * daysperyear: + quarterstart = (dates.day == 1) & (dates.month % 3 == 1) minor = default[(dates.day == 1)] - major = default[(dates.day_of_year == 1)] + major = default[quarterstart] elif span <= 11 * daysperyear: - minor = default[(dates.quarter != (dates-1).quarter)] + quarterstart = (dates.day == 1) & (dates.month % 3 == 1) + minor = default[quarterstart] major = default[(dates.day_of_year == 1)] else: (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) @@ -475,16 +481,170 @@ class TimeSeries_DateFormatter(Formatter): """Formats the ticks along a DateArray axis.""" - def __init__(self, freq, fmt=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt + def __init__(self, freq, minor_locator=False, dynamic_mode=True,): + self.format = None self.freqstr = freq + self.locs = [] + self.formatdict = {} + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + + + def _initialize_dates(self, locs): + "Returns a DateArray for the current frequency." + freq = self.freqstr + dates = date_array(dlist=self.locs, freq=freq) + return dates + def set_locs(self, locs): + 'Sets the locations of the ticks' + self.locs = locs + if len(self.locs) > 0: + self.verify_intervals() + d = abs(self.viewInterval.span()) + self._set_format(d) + # def __call__(self, x, pos=0): - return Date(self.freqstr, value=int(x)).strfmt(self.fmt) + if self.isminor: + fmt = self.formatdict.pop(x, '') + if fmt is not '': + retval = Date(self.freqstr, value=int(x)).strfmt(fmt) + else: + retval = '' + else: + retval = '' + return retval + + +#............................................................................... +class TimeSeries_AnnualFormatter(TimeSeries_DateFormatter): + # + def __init__(self, minor_locator=False, dynamic_mode=True,): + TimeSeries_DateFormatter.__init__(self, 'A', + minor_locator=minor_locator, + dynamic_mode=dynamic_mode,) + def _set_format(self, span): + dates = self._initialize_dates(self.locs) + format = N.empty(len(self.locs), dtype="|S2") + format.flat = '' + if span <= 11: + format[:] = "%Y" + elif span < 15: + format[(self.locs % 2 == 0)] = "%Y" + elif span < 50: + format[(self.locs % 5 == 0)] = "%Y" + elif span < 100: + format[(self.locs % 10 == 0)] = "%Y" + elif span < 200: + format[(self.locs % 20 == 0)] = "%Y" + elif span < 400: + format[(self.locs % 25 == 0)] = "%Y" + elif span < 1000: + format[(self.locs % 50 == 0)] = "%Y" + else: + format[(self.locs % 100 == 0)] = "%Y" + self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) + +#............................................................................... +class TimeSeries_QuarterlyFormatter(TimeSeries_DateFormatter): + # + def __init__(self, minor_locator=False, dynamic_mode=True,): + TimeSeries_DateFormatter.__init__(self, 'Q', + minor_locator=minor_locator, + dynamic_mode=dynamic_mode,) + def _set_format(self, span): + dates = self._initialize_dates(self.locs) + format = N.empty(len(self.locs), dtype="|S7") + format.flat = '' + (years,quarters) = divmod(self.locs-1, 4) + if span <= 3*4: + yearchange = (self.locs % 4 == 1) + format[:] = "Q%q" + format[(quarters == 0)] = "Q%q\n%Y" + format[0] = "Q%q\n%Y" + elif span <= 11*4: + format[(years % 2 == 1) & (quarters == 0)] = "%Y" + else: + format[(years % 5 == 4)] = "%Y" + self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) + + +#............................................................................... +class TimeSeries_MonthlyFormatter(TimeSeries_DateFormatter): + # + def __init__(self, minor_locator=False, dynamic_mode=True,): + TimeSeries_DateFormatter.__init__(self, 'M', + minor_locator=minor_locator, + dynamic_mode=dynamic_mode,) + # + def _set_format(self, span): + dates = self._initialize_dates(self.locs) + yearchange = (self.locs % 12 == 1) + format = N.empty(len(self.locs), dtype="|S6") + format.flat = '' + if span <= 1.5 * 12: + format[:] = "%b" + format[yearchange] = "%b\n%Y" + format[0] = "%b\n%Y" + elif span <= 3*12: + format[(dates.month % 2 == 1)] = "%b" + format[yearchange] = "%b\n%Y" + else: + format[yearchange] = "%Y" + self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) + + +#............................................................................... +class TimeSeries_DailyFormatter(TimeSeries_DateFormatter): + # + def __init__(self, freq, minor_locator=False, dynamic_mode=True,): + TimeSeries_DateFormatter.__init__(self, freq, + minor_locator=minor_locator, + dynamic_mode=dynamic_mode,) + if self.freqstr == 'B': + self.daysinyear = 261 + else: + self.daysinyear = 365 + # + def _set_format(self, span): + dayperyear = self.daysinyear + dates = self._initialize_dates(self.locs) + yearchange = (dates.day_of_year == 1) + format = N.empty(len(self.locs), dtype="|S8") + format.flat = '' + if span <= dayperyear // 12: + format[:] = '%d' + format[(dates.day == 1)] = '\n%b' + format[yearchange] = '\n%b\n%Y' + format[0] = '\n%b\n%Y' + elif span <= dayperyear // 4: + format[(dates.day_of_week == 1)] = '%d' + format[(dates.day == 1)] = '\n%b' + format[yearchange] = '\n%b\n%Y' + elif span <= 1.5 * dayperyear: + monthweekchange = (dates.day_of_week == 0) | (dates.day == 1) + format[monthweekchange] = '\n%b' + format[yearchange] = '\n%b\n%Y' + elif span <= 3 * dayperyear: + quarterchange = (dates.months % 3 == 1) & (dates.day == 1) + format[quarterchange] = '%b' + format[yearchange] = '\n%Y' + else: + format[:] = '%Y' + self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) + + +# Monthly: +# if span <= 1.5 * 12: '%b' on minor ticks, '%Y' on major ticks +#elif span <= 3 * 12 : '%b' every even month on minor ticks, '%Y' on major +#elif span <= 11 * 12 : '%Y' on major +# Daily: + + + #####-------------------------------------------------------------------------- #---- --- TimeSeries plots --- #####-------------------------------------------------------------------------- @@ -652,46 +812,50 @@ # Get the locator class ................. if self.freqstr in 'BDU': locator = TimeSeries_DailyLocator + formatter = TimeSeries_DailyFormatter self.xaxis.set_major_locator(locator(self.freqstr, minor_locator=False, dynamic_mode=True)) self.xaxis.set_minor_locator(locator(self.freqstr, minor_locator=True, dynamic_mode=True)) + self.xaxis.set_major_formatter(formatter(self.freqstr, + minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_formatter(formatter(self.freqstr, + minor_locator=True, + dynamic_mode=True)) else: if self.freqstr == 'A': locator = TimeSeries_AnnualLocator + formatter = TimeSeries_AnnualFormatter elif self.freqstr == 'Q': locator = TimeSeries_QuarterlyLocator + formatter = TimeSeries_QuarterlyFormatter elif self.freqstr == 'M': locator = TimeSeries_MonthlyLocator + formatter = TimeSeries_MonthlyFormatter self.xaxis.set_major_locator(locator(minor_locator=False, dynamic_mode=True)) self.xaxis.set_minor_locator(locator(minor_locator=True, dynamic_mode=True)) + self.xaxis.set_major_formatter(formatter(minor_locator=False, + dynamic_mode=True)) + self.xaxis.set_minor_formatter(formatter(minor_locator=True, + dynamic_mode=True)) #........................................ - self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(),rotation=45) +# if rcParams['backend'] == 'PS': +# rotate = False +# warnings.warn("dateplot: PS backend detected, rotate disabled") +# if self.is_last_row(): +# if rotate: +# setp(self.get_xticklabels(),rotation=45) # self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) # self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) # else: # self.set_xticklabels([]) # self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ + TSPlot = TimeSeriesPlot @@ -760,16 +924,16 @@ ################################################################################ if __name__ == '__main__': - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), + da = date_array(start_date=Date(freq='A', year=2003, quarter=3, month=1, day=17), length=51) ser = timeseries.time_series(MA.arange(len(da)), dates=da) ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) +# ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) pylab.figure() pylab.gcf().add_tsplot(111) pylab.gca().tsplot(ser, 'ko-') pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') +# pylab.gca().tsplot(ser_2, 'rs') pylab.show() \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-19 15:39:39 UTC (rev 2731) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-20 18:12:18 UTC (rev 2732) @@ -554,43 +554,43 @@ @property def day(self): "Returns the day of month." - return self.__getDateInfo__('D') + return self.__getdateinfo__('D') @property def day_of_week(self): "Returns the day of week." - return self.__getDateInfo__('W') + return self.__getdateinfo__('W') @property def day_of_year(self): "Returns the day of year." - return self.__getDateInfo__('R') + return self.__getdateinfo__('R') @property def month(self): "Returns the month." - return self.__getDateInfo__('M') + return self.__getdateinfo__('M') @property def quarter(self): "Returns the quarter." - return self.__getDateInfo__('Q') + return self.__getdateinfo__('Q') @property def year(self): "Returns the year." - return self.__getDateInfo__('Y') + return self.__getdateinfo__('Y') @property def second(self): "Returns the seconds." - return self.__getDateInfo__('S') + return self.__getdateinfo__('S') @property def minute(self): "Returns the minutes." - return self.__getDateInfo__('T') + return self.__getdateinfo__('T') @property def hour(self): "Returns the hour." - return self.__getDateInfo__('H') + return self.__getdateinfo__('H') @property def week(self): "Returns the week." - return self.__getDateInfo__('I') + return self.__getdateinfo__('I') days = day weekdays = day_of_week @@ -603,9 +603,9 @@ hours = hour weeks = week - def __getDateInfo(self, info): + def __getdateinfo__(self, info): return numeric.asarray(cseries.getDateInfo(numeric.asarray(self), self.freq, info), dtype=int_) - + __getDateInfo = __getdateinfo__ #.... Conversion methods .................... # def tovalue(self): From scipy-svn at scipy.org Tue Feb 20 13:55:29 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:55:29 -0600 (CST) Subject: [Scipy-svn] r2733 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070220185529.D0D1939C197@new.scipy.org> Author: mattknox_ca Date: 2007-02-20 12:55:20 -0600 (Tue, 20 Feb 2007) New Revision: 2733 Modified: trunk/Lib/sandbox/timeseries/io/fame/__init__.py Log: Modified: trunk/Lib/sandbox/timeseries/io/fame/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-20 18:12:18 UTC (rev 2732) +++ trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-20 18:55:20 UTC (rev 2733) @@ -1 +1,4 @@ -from fame import * \ No newline at end of file +import core +from core import * + +__all__ = core.__all__ From scipy-svn at scipy.org Tue Feb 20 13:56:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:56:04 -0600 (CST) Subject: [Scipy-svn] r2734 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070220185604.51D5039C16C@new.scipy.org> Author: mattknox_ca Date: 2007-02-20 12:55:53 -0600 (Tue, 20 Feb 2007) New Revision: 2734 Added: trunk/Lib/sandbox/timeseries/io/fame/core.py Log: Added: trunk/Lib/sandbox/timeseries/io/fame/core.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-20 18:55:20 UTC (rev 2733) +++ trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-20 18:55:53 UTC (rev 2734) @@ -0,0 +1,1032 @@ +import re, thread + +import numpy +import maskedarray as ma +import timeseries as ts + +import cfame +from cfame import FAME_CONSTANTS + +__all__ = [ + 'FameDb', 'set_option', 'license_expires', 'DBError' + ] + +_g = globals() +for var, val in FAME_CONSTANTS.iteritems(): + _g[var] = val + +__all__.extend(list(FAME_CONSTANTS)) + +def reverse_dict(d): + return dict([(y, x) for x, y in d.iteritems()]) + +def convert_dict(d, key_func=lambda x:x, val_func=lambda x:x): + return dict([(key_func(key), val_func(val)) for key, val in d.iteritems()]) + +basis_map = { HBSUND:"U", + HBSDAY:"D", + HBSBUS:"B"} +basis_revmap = reverse_dict(basis_map) + +observed_map = { HOBUND:"UNDEFINED", + HOBBEG:"BEGINNING", + HOBEND:"ENDING", + HOBAVG:"AVERAGED", + HOBSUM:"SUMMED", + HOBANN:"ANNUALIZED", + HOBFRM:"FORMULA", + HOBHI:"MAXIMUM", + HOBLO:"MINIMUM"} +observed_revmap = reverse_dict(observed_map) +observed_revmap['HIGH'] = HOBHI +observed_revmap['LOW'] = HOBLO + +def translate_basis(basis): + "translate user specified basis to FAME constant" + + if isinstance(basis, str): + freq = ts.freq_tostr(ts.freq_fromstr(basis)) + try: + return basis_revmap[freq] + except KeyError: + raise ValueError("Basis must be " + \ + "'DAILY', 'BUSINESS', or 'UNDEFINED'") + else: + if basis in basis_map: return basis + elif basis == ts.freq_fromstr('D'): return HBSDAY + elif basis == ts.freq_fromstr('B'): return HBSBUS + elif basis == ts.freq_fromstr('U'): return HBSUND + else: + raise ValueError("Invalid Basis value") + +def translate_observed(observed): + "translate user specified observed to FAME constant" + if isinstance(observed, str): + return observed_revmap[ts.fmtObserv(observed)] + elif observed in (observed_map): + return observed + else: + raise ValueError("Invalid Observed value") + +freq_map = { HDAILY:"D", + HBUSNS:"B", + HMONTH:"M", + HWKSUN:"W", + HSEC :"S", + HMIN :"T", + HHOUR :"H", + HQTOCT:"Q", + HQTNOV:"Q", + HQTDEC:"Q", + HANJAN:"A", + HANFEB:"A", + HANMAR:"A", + HANAPR:"A", + HANMAY:"A", + HANJUN:"A", + HANJUL:"A", + HANAUG:"A", + HANSEP:"A", + HANOCT:"A", + HANNOV:"A", + HANDEC:"A" } +freq_map = convert_dict(freq_map, val_func=ts.freq_fromstr) + +freq_revmap = { "D" : HDAILY, + "B" : HBUSNS, + "M" : HMONTH, + "W" : HWKSUN, + "S" : HSEC, + "T" : HMIN, + "H" : HHOUR, + "Q" : HQTDEC, + "A" : HANDEC} +freq_revmap = convert_dict(freq_revmap, key_func=ts.freq_fromstr) + +date_value_adjust = { 'A':1849, + 'Q':7396, + 'M':22188, + 'W':96477, + 'B':482381, + 'D':675333, + 'H':87648, + 'T':5258880, + 'S':315532800} +date_value_adjust = convert_dict(date_value_adjust, key_func=ts.freq_fromstr) + +def fametype_fromdata(data): + """determine fame type code from a data object""" + + if isinstance(data, ts.DateArray) or isinstance(data, ts.Date): + return freq_revmap[data.freq] + elif hasattr(data, 'dtype'): + dtypeStr = str(data.dtype) + + if dtypeStr[:5] == "float": + if int(dtypeStr[5:]) > 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:3] == "int": + if int(dtypeStr[3:]) > 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:4] == "uint": + if int(dtypeStr[4:]) >= 32: return HPRECN + else: return HNUMRC + elif dtypeStr[:2] == "|S" or dtypeStr == 'object': + return HSTRNG + elif dtypeStr == "bool": + return HBOOLN + else: + raise ValueError("Unsupported dtype for fame database: %s", dtypeStr) + + elif isinstance(data, str): + return HSTRNG + elif isinstance(data, (int, float)): + return HPRECN + elif isinstance(data, bool): + return HBOOLN + elif isinstance(data, list): + return HNAMEL + else: + raise ValueError("Unrecognized data type") + +def fametype_tonumpy(fametype): + if fametype >= 8: + # date types + return numpy.int32 + elif fametype == HNAMEL: + return None + else: + typeMap = { + HNUMRC:numpy.float32, + HBOOLN:numpy.int32, + HSTRNG:numpy.object_, + HPRECN:numpy.float64} + return typeMap[fametype] + +class CaseInsensitiveDict(dict): + def __init__(self, data={}): + for i, v in data.iteritems(): + self[i.upper()] = v + + def __getitem__(self, key): + if hasattr(key, 'upper'): key = key.upper() + return super(CaseInsensitiveDict, self).__getitem__(key) + + def __setitem__(self, key, item): + if hasattr(key, 'upper'): key = key.upper() + super(CaseInsensitiveDict, self).__setitem__(key, item) + + +def _single_or_multi_func(dbkey, name, func, *args, **kwargs): + if isinstance(name, str): + single_obj = True + name = [name] + else: + single_obj = False + + result = {} + for n in name: + result[n] = func(dbkey, n, *args, **kwargs) + + if single_obj: + return result.values()[0] + + return result + +def _famedate_to_tsdate(fame_date, freqstr): + "convert integer fame date to a timeseries Date" + value = fame_date + date_value_adjust[ts.freq_fromstr(freqstr)] + return ts.Date(freq=freqstr, value=value) + + +def _fame_params_from_pyobj_scalar(pyobj): + return { + 'cls':HSCALA, + 'freq':HUNDFX, + 'type':fametype_fromdata(pyobj), + 'basis':HBSUND, + 'observed':HOBUND} + +def _fame_params_from_pyobj_tser(pyobj): + + if hasattr(pyobj, "observed"): + fame_observed = observed_revmap[pyobj.observed] + if fame_observed == 0: fame_observed = HOBEND + else: + fame_observed = HOBEND + + return { + 'cls':HSERIE, + 'freq':freq_revmap[pyobj.freq], + 'type':fametype_fromdata(pyobj._data), + 'basis':HBSDAY, + 'observed':fame_observed} + +def _fame_params_from_pyobj_cser(pyobj): + if hasattr(pyobj, "_data"): + fame_data = pyobj._data + else: + fame_data = pyobj + + return { + 'cls':HSERIE, + 'freq':HCASEX, + 'type':fametype_fromdata(fame_data), + 'basis':HBSUND, + 'observed':HOBUND} + + +class DBError(Exception): pass + +class FameDb(object): + """Fame database object. + +:Construction: + x = FameDb(conn_str, mode='r') + +:Paramaters: + - `conn_str` (str) : valid connection string. Can be a physical path, + or channel specification, etc. See FAME documentation on cfmopdb for + valid connection strings. + - `mode` (str, *['r']*) : method of access to the database. Can be one + of the following: + 'r' => read only + 's' => shared + 'o' => overwrite + 'c' => create + 'u' => update + 'w' => write + 'd' => direct + +Notes + - For changes to be posted, you must explictly use the "post" or + "close" methods (changes are posted on close).""" + + def __init__(self, conn_str, mode='r'): + mode = mode.lower() + if mode == 'r': + intmode = HRMODE + elif mode == 's': + intmode = HSMODE + elif mode == 'u': + intmode = HUMODE + elif mode == 'w': + intmode = HWMODE + elif mode == 'd': + intmode = HDMODE + elif mode == 'c': + intmode = HCMODE + elif mode == 'o': + intmode = HOMODE + else: + raise ValueError, "Database access mode not supported." + self.mode = mode + + self.dbkey = cf_open(conn_str, intmode) + + + def read(self, name, + start_date=None, end_date=None, + start_case=None, end_case=None, max_string_len=65): + + """read specified object(s) from database + +:Parameters: + - `name` (string or list of strings) : names of objects that will be + read from the database + + - `start_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or after `start_date` will be read. + If None, data will be read from the first value of the series. + - `end_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or before `end_date` will be read. + If None, data will be read to the last value of the series. + - `start_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or after `start_case` will be read. + If None, data will be read starting from case index 1 + - `end_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or before `end_case` will be read. + If None, data will be read to the last value of the series. + - `max_string_len` (int, *[65]*) : Applies only when readings strings + or series of strings. This is the maximum length of string that can + be read. Lower values result in less memory usage, so you should + specify this as low as is reasonable for your data. + +:Return: + if `name` is a list of strings: + case insensitive dictionary of the objects + if `name` is a single string: + object from database that is stored as `name`""" + + isSingle = False + if isinstance(name, str): + names = [name] + isSingle = True + else: + names = name + + items = CaseInsensitiveDict() + + #default to -1. This will get the entire range + _start_case = _end_case = -1 + _start_date = _end_date = -1 + + range_freq = None + if start_date is not None: + _start_date = start_date.value - date_value_adjust[start_date.freq] + range_freq = freq_revmap[start_date.freq] + + if end_date is not None: + if start_date is not None and start_date.freq != end_date.freq: + raise ValueError("start_date and end_date must be same frequency") + _end_date = end_date.value - date_value_adjust[end_date.freq] + if range_freq is None: + range_freq = freq_revmap[end_date.freq] + + if start_case is not None: _start_case = start_case + if end_case is not None: _end_case = end_case + + if len(set([_start_case, _end_case, _start_date, _end_date, -1])) != 1: + checkFreq = True + else: + checkFreq = False + + for objName in names: + objName = objName.upper() + + if checkFreq: + objFreq = self.obj_size(objName)['freq'] + + if objFreq == range_freq: + start_index, end_index = _start_date, _end_date + elif objFreq == HCASEX: + start_index, end_index = _start_case, _end_case + else: + start_index, end_index = -1, -1 + else: + start_index, end_index = -1, -1 + + result = cf_read(self.dbkey, objName, start_index, + end_index, max_string_len) + + if result['type'] == HBOOLN: + numpyType = numpy.bool_ + else: + numpyType = fametype_tonumpy(result['type']) + + if result['type'] == HNAMEL: + pyObj = [x for x in result['data'][1:-1].split(", ") \ + if x != ''] + + elif result['class'] == HSCALA: + if isinstance(result['data'], str): + if result['mask']: + pyObj = None + else: + pyObj = result['data'] + else: + if result['mask'][0]: + pyObj = None + else: + pyObj = result['data'][0] + if result['type'] >= 8: # date type + value = pyObj+ \ + date_value_adjust[freq_map[result['type']]] + pyObj = ts.Date( + freq=freq_map[result['type']], + value=value) + else: + pyObj = numpyType(pyObj) + + elif result['class'] == HSERIE: + + if 'data' in result: + vals = result['data'] + mask = result['mask'] + if not mask.any(): mask = ma.nomask + else: + vals = [] + mask = ma.nomask + + if result['type'] >= 8: # date type + valadj = date_value_adjust[freq_map[result['type']]] + if len(vals) > 0: vals += valadj + data = ts.DateArray(vals, + freq=freq_map[result['type']]) + else: + data = numpy.array(vals, dtype=numpyType) + + if result['freq'] == HCASEX: + pyObj = ma.array(data, mask=mask) + else: + observed = observed_map[result['observed']] + freq = freq_map[result['freq']] + + if 'data' in result: + start_date = ts.Date( + freq=freq, + value=result['startindex']+date_value_adjust[freq]) + else: + start_date = None + + pyObj = ts.time_series(data, freq=freq, + start_date=start_date, + observed=observed, mask=mask) + + items[objName] = pyObj + + if isSingle: + return items.values()[0] + + return items + + + def write_tser_dict(self, objdict, + overwrite=False, assume_exists=False, + start_date=None, end_date=None): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a time series (calls FameDb.write_tser on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object + names for keys and TimeSeries objects for values + - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in objects in `objdict` will be given priority over pre-existing data in + the db where there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `start_date` (Date, *[None]*) : If None, data will be written from the start of + the series. If specified, only data points on or after start_date will be written. + - `end_date` (Date, *[None]*) : If None, data will be written until the end of + the series. If specified, only data points on or before end_date will be written. +""" + for key, obj in objdict.iteritems(): + self.write_tser(key, obj, overwrite=overwrite, + assume_exists=assume_exists, + start_date=start_date, end_date=end_date) + + + def write_cser_dict(self, objdict, + overwrite=False, assume_exists=False, + zero_represents=1, start_case=None, end_case=None): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a case series (calls FameDb.write_tser on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of arrays to be written as Case Series. + Object names for keys and arrays for values + - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in objects in `objdict` will be given priority over pre-existing data in + the db where there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in + the array represents + - `start_case` (int, *[None]*) : If None, data will be written from the start of + the array. If specified, only data points on or after start_case will be written. + - `end_case` (int, *[None]*) : If None, data will be written until the end of + the array. If specified, only data points on or before end_case will be written. +""" + for key, obj in objdict.iteritems(): + self.write_cser(key, obj, overwrite=overwrite, + assume_exists=assume_exists, + zero_represents=zero_represents, + start_case=start_case, end_case=end_case) + + def write_scalar_dict(self, objdict): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as a scalar (calls FameDb.write_scalar on each key, +value pair) + +:Parameters: + - `objdict` (dict) : dictionary of items to be written as scalars. + Object names for keys and scalar items for values +""" + for key, obj in objdict.iteritems(): + self.write_scalar(key, obj) + + + def write_tser(self, name, tser, + overwrite=False, assume_exists=False, + start_date=None, end_date=None): + """write `tser` to the database as `name` as a time series. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates. + Use fill_missing_dates first on your series if you suspect this is the situation. + TimeSeries must be 1-dimensional + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `tser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `start_date` (Date, *[None]*) : If None, data will be written from the start of + `tser`. If specified, only data points on or after start_date will be written. + - `end_date` (Date, *[None]*) : If None, data will be written until the end of + `tser`. If specified, only data points on or before end_date will be written. +""" + + if not isinstance(tser, ts.TimeSeries): + raise ValueError("tser is not a valid time series") + elif tser.has_missing_dates(): + raise ValueError("tser must not have any missing dates") + elif tser.ndim != 1: + raise ValueError("FAME db only supports 1-dimensional time series") + + exists = self.obj_exists(name) + + if assume_exists and not exists: + raise DBError("%s does not exist" % name) + + if overwrite or not exists: create = True + else: create = False + + fame_params = _fame_params_from_pyobj_tser(tser) + + fame_cls = fame_params['cls'] + fame_type = fame_params['type'] + fame_freq = fame_params['freq'] + fame_basis = fame_params['basis'] + fame_observed = fame_params['observed'] + + if create: + if exists: self.delete_obj(name) + cf_create(self.dbkey, name, fame_cls, fame_freq, fame_type, fame_basis, fame_observed) + + def get_boundary_date(bdate, attr): + if bdate is not None: + if bdate.freq != tser.freq: + raise ValueError(attr+" frequency must be same as tser frequency") + if tser.start_date > bdate or tser.end_date < bdate: + raise ValueError(attr+" outside range of series") + return bdate + else: + return getattr(tser, attr) + + start_date = get_boundary_date(start_date, "start_date") + end_date = get_boundary_date(end_date, "end_date") + + if start_date is not None: + + towrite = tser[start_date:end_date+1] + + start_index = start_date.value + end_index = end_date.value + + # convert integer types to floats since FAME does not have an integer type + newType = fametype_tonumpy(fame_type) + if fame_type >= 8: + # date type + fame_data = towrite._data - date_value_adjust[towrite._data.freq] + elif newType != tser._data.dtype: + fame_data = towrite._data.astype(newType) + else: + fame_data = towrite._data + + if towrite._mask is ma.nomask: + fame_mask = numpy.zeros(towrite._data.shape, dtype=numpy.bool_) + else: + fame_mask = towrite._mask + + start_index -= date_value_adjust[towrite.freq] + end_index -= date_value_adjust[towrite.freq] + + cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_index, end_index, fame_type, fame_freq) + + def write_cser(self, name, cser, overwrite=False, assume_exists=False, zero_represents=1, start_case=None, end_case=None): + """write `cser` to the database as `name` as a case series. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be + written. If `cser` is a MaskedArray, then masked values will be written as ND. + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `cser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in + the array represents + - `start_case` (int, *[None]*) : If None, data will be written from the start of + `cser`. If specified, only data points on or after start_case will be written. + - `end_case` (int, *[None]*) : If None, data will be written until the end of + `cser`. If specified, only data points on or before end_case will be written. +""" + + if not isinstance(cser, numpy.ndarray): + raise ValueError("cser is not a valid ndarray") + elif cser.ndim != 1: + raise ValueError("FAME db only supports 1-dimensional arrays") + + exists = self.obj_exists(name) + if assume_exists and not exists: + raise DBError("%s does not exist" % name) + + if overwrite or not exists: create = True + else: create = False + + fame_params = _fame_params_from_pyobj_cser(cser) + + fame_cls = fame_params['cls'] + fame_type = fame_params['type'] + fame_freq = fame_params['freq'] + fame_basis = fame_params['basis'] + fame_observed = fame_params['observed'] + + if hasattr(cser, "_data"): + fame_data = cser._data + if cser._mask is ma.nomask: + fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) + else: + fame_mask = cser._mask + else: + fame_data = cser + fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) + + if create: + if exists: self.delete_obj(name) + cf_create(self.dbkey, name, fame_cls, fame_freq, fame_type, fame_basis, fame_observed) + + def get_boundary_case(bcase, attr): + if bcase is not None: + idx = bcase - zero_represents + if idx < 0 or idx > cser.size: + raise ValueError("%s outside range of series" % attr) + return bcase + else: + if cser.size == 0: + return None + else: + if attr == 'start_case': + return zero_represents + elif attr == 'end_case': + return zero_represents + cser.size - 1 + else: + raise ValueError("unexpected argument: %s " % attr) + + start_case = get_boundary_case(start_case, "start_case") + end_case = get_boundary_case(end_case, "end_case") + + if start_case is not None: + # convert integer types to floats since FAME does not have an integer type + s = start_case - zero_represents + e = end_case - zero_represents + + fame_data = fame_data[s:e+1] + fame_mask = fame_mask[s:e+1] + newType = fametype_tonumpy(fame_type) + if fame_type >= 8: + # date type + fame_data = fame_data - date_value_adjust[fame_data.freq] + elif newType != fame_data.dtype: + fame_data = fame_data.astype(newType) + + cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_case, end_case, fame_type, fame_freq) + + + def write_scalar(self, name, scalar): + """write `scalar` to the database as `name` as a scalar object. If an +object already exists in the database named as `name` then it is +over-written, otherwise it is created. + +:Parameters: + - `name` (string) : database key that the object will be written to + - `scalar` : one of the following: string, numpy scalar, int, float, + list of strings (for name lists), Date, boolean""" + + fame_params = _fame_params_from_pyobj_scalar(scalar) + fame_type = fame_params['type'] + + if isinstance(scalar, ts.Date): + fame_data = numpy.int32(scalar.value - date_value_adjust[scalar.freq]) + elif hasattr(scalar, "dtype"): + if scalar.ndim != 0: raise ValueError("received non-scalar data") + newType = fametype_tonumpy(fame_type) + if newType != scalar.dtype: fame_data = scalar.astype(newType) + else: fame_data = scalar + elif fame_type == HSTRNG: + fame_data = scalar + elif fame_type == HPRECN: + fame_data = numpy.float64(scalar) + elif fame_type == HBOOLN: + fame_data = numpy.int32(scalar) + elif fame_type == HNAMEL: + fame_data = "{" + ", ".join(scalar) + "}" + else: + raise ValueError("Unrecognized data type") + + if self.obj_exists(name): self.delete_obj(name) + cf_create(self.dbkey, name, + fame_params['cls'], + fame_params['freq'], + fame_params['type'], + fame_params['basis'], + fame_params['observed']) + + # convert integer types to floats since FAME does not have an integer type + newType = fametype_tonumpy(fame_type) + if hasattr(fame_data, 'dtype') and newType != fame_data.dtype: + fame_data = fame_data.astype(newType) + + if fame_type == HNAMEL: + cf_write_namelist(self.dbkey, name, fame_data) + else: + cf_write_scalar(self.dbkey, name, fame_data, fame_type) + + def _create_obj(self, name, cls, type, freq=None, basis=None, observed=None): + """create object in database with specified attributes as `name`. + +You must use the fame constants defined in mapping.py for each of the +parameters. Generally speaking, it is easier to use initialize_obj +with a prototype object. +""" + + if cls not in (HSERIE, HSCALA): + raise ValueError("unrecognized object class: "+str(cls)) + + if freq is None: + if cls == HSCALA: + freq = HUNDFX + else: + raise ValueError("freq must be specified for series") + + if freq in (HUNDFX, HCASEX): + basis = HBSUND + observed = HOBUND + else: + if basis is None: basis = HBSDAY + if observed is None: observed = HOBEND + + cf_create(self.dbkey, name, cls, freq, type, basis, observed) + + def initialize_obj(self, name, pyobj): + """initialize object of appropriate type in database based on the +python object `pyobj` as `name`. Does not write any data to the +database, simply initializes the object in the database.""" + if isinstance(pyobj, ts.TimeSeries): + param_func = _fame_params_from_pyobj_tser + elif isinstance(pyobj, numpy.ndarray): + param_func = _fame_params_from_pyobj_cser + else: + param_func = _fame_params_from_pyobj_scalar + + fame_params = param_func(pyobj) + cf_create(self.dbkey, name, + fame_params['cls'], + fame_params['freq'], + fame_params['type'], + fame_params['basis'], + fame_params['observed']) + + + def db_desc(self): + "get 'description' attribute of database" + return cf_get_db_attr(self.dbkey, "DESC") + + def db_doc(self): + "get 'doc' attribute of database" + return cf_get_db_attr(self.dbkey, "DOC") + + def db_created(self): + "get 'created' attribute of database" + fame_date = cf_get_db_attr(self.dbkey, "CREATED") + return _famedate_to_tsdate(fame_date, 's') + + def db_modified(self): + "get 'modified' attribute of database" + fame_date = cf_get_db_attr(self.dbkey, "MODIFIED") + return _famedate_to_tsdate(fame_date, 's') + + def set_db_desc(self, desc): + "set description attribute of database" + cf_set_db_desc(self.dbkey, desc) + + def set_db_doc(self, doc): + "set doc attribute of database" + cf_set_db_doc(self.dbkey, doc) + + def db_is_open(self): + "returns True if database is open. False otherwise" + return cf_get_db_attr(self.dbkey, "ISOPEN") + + def wildlist(self, exp, wildonly=False): + """performs a wildlist lookup on the database, using Fame syntax +("?" and "^"), returns a normal python list of strings""" + res = cf_wildlist(self.dbkey, exp) + + if wildonly: + exp = exp.replace("?", "(.*)") + exp = exp.replace("^", "(.)") + exp = exp.replace("$","\$") + regex = re.compile(exp) + res = ["".join(regex.match(res[i]).groups()) \ + for i in range(len(res))] + return res + + def close(self): + """Closes the database. Changes will be posted.""" + if self.db_is_open(): + cf_close(self.dbkey) + + def post(self): + cf_post(self.dbkey) + + def restore(self): + """Discard any changes made to the database since it was last opened or posted.""" + cf_restore(self.dbkey) + + def obj_exists(self, name): + return cf_exists(self.dbkey, name) + + def delete_obj(self, name, must_exist=True): + """Deletes the specified object(s) from the database""" + if isinstance(name, str): name = [name] + [cf_delete_obj(self.dbkey, n) for n in name if must_exist or self.obj_exists(n)] + + def obj_size(self, name): + """basic information about the size of an object(s) in a database""" + return _single_or_multi_func(self.dbkey, name, cf_obj_size) + + def obj_freq(self, name): + """get frequency of a FAME time series object in the database""" + obj_sz = self.obj_size(name) + fame_freq = obj_sz['freq'] + if fame_freq == 0: return None + return freq_map[obj_sz['freq']] + + def __ser_date(self, name, date_type): + """helper method for start_date and end_date""" + obj_sz = self.obj_size(name) + fame_freq = obj_sz['freq'] + if fame_freq == 0: return None + + try: + ts_freq = freq_map[obj_sz['freq']] + except KeyError: + raise DBError("unsupported FAME frequency: %i", fame_freq) + + if obj_sz[date_type+'_year'] == -1: return None + + annDate = ts.Date(freq='A', year=obj_sz[date_type+'_year']) + return annDate.asfreq(ts_freq, relation='BEFORE') + (obj_sz[date_type+'_period'] - 1) + + def obj_start_date(self, name): + """get start_date of a FAME time series object""" + return self.__ser_date(name, 'start') + + def obj_end_date(self, name): + """get end_date of a FAME time series object""" + return self.__ser_date(name, 'end') + + def obj_created(self, name): + "get 'created' attribute of object in database" + fame_date = cf_get_obj_attr(self.dbkey, name, "CREATED") + return _famedate_to_tsdate(fame_date, 's') + + def obj_modified(self, name): + "get 'modified' attribute of object in database" + fame_date = cf_get_obj_attr(self.dbkey, name, "MODIFIED") + return _famedate_to_tsdate(fame_date, 's') + + def set_obj_desc(self, name, desc): + "set 'description' attribute of object in database" + cf_set_obj_desc(self.dbkey, name, desc) + + def set_obj_doc(self, name, doc): + "set 'documentation' attribute of object in database" + cf_set_obj_doc(self.dbkey, name, doc) + + def set_obj_basis(self, name, basis): + "set 'basis' attribute of object in database" + basis = translate_basis(basis) + cf_set_obj_basis(self.dbkey, name, basis) + + def set_obj_observed(self, name, observed): + "set 'observed' attribute of object in database" + observed = translate_observed(observed) + cf_set_obj_observed(self.dbkey, name, observed) + + def _whats(self, name): + """Preforms a fame "whats" command on the provided name(s) + +Note: Returns FAME constants which are not directly interpretable +in the context of the timeseries module. For this reason, it is +recommended that you use the obj_* methods to retrieve the desired +information about an object. +""" + return _single_or_multi_func(self.dbkey, name, cf_whats) + + def obj_desc(self, name): + """get desc attribute for an object""" + return self._whats(name)['desc'] + + def obj_doc(self, name): + """get doc attribute for an object""" + return self._whats(name)['doc'] + + def rename_obj(self, name, new_name): + """rename fame object(s) in database""" + if isinstance(name, str): name = [name] + if isinstance(new_name, str): new_name = [new_name] + + if len(name) != len(new_name): + raise ValueError("number of original names does not match " + \ + "number of new names") + + for n, o in zip(name, new_name): + cf_rename_obj(self.dbkey, n, o) + + def copy_obj(self, target_db, source_name, target_name=None): + """copy fame object(s) to another destination""" + if target_name is None: target_name = source_name + if isinstance(source_name, str): source_name = [source_name] + if isinstance(target_name, str): target_name = [target_name] + + if len(source_name) != len(target_name): + raise ValueError("number of source names does not match " + \ + "number of target names") + + for s, t in zip(source_name, target_name): + cf_copy(self.dbkey, target_db.dbkey, s, t) + + +class cFameCall: + """wrapper for cfame functions that acquires and releases a resource lock. +This is needed because the Fame C api is not thread safe.""" + + fameLock = thread.allocate_lock() + + def __init__ (self, func): + self.f = func + self.__doc__ = getattr(func, "__doc__", str(func)) + self.__name__ = getattr(func, "__name__", str(func)) + + def __call__ (self, *args, **kwargs): + "Execute the call behavior." + tmp = self.fameLock.acquire() + try: + result = self.f(*args, **kwargs) + self.fameLock.release() + except: + self.fameLock.release() + raise + + return result + +cf_open = cFameCall(cfame.open) +cf_set_option = cFameCall(cfame.set_option) +cf_close = cFameCall(cfame.close) +cf_post = cFameCall(cfame.post) +cf_restore = cFameCall(cfame.restore) +cf_obj_size = cFameCall(cfame.obj_size) +cf_whats = cFameCall(cfame.whats) +cf_delete_obj = cFameCall(cfame.delete_obj) +cf_create = cFameCall(cfame.create) +cf_read = cFameCall(cfame.read) +cf_write_scalar = cFameCall(cfame.write_scalar) +cf_write_series = cFameCall(cfame.write_series) +cf_write_namelist = cFameCall(cfame.write_namelist) +cf_wildlist = cFameCall(cfame.wildlist) +cf_exists = cFameCall(cfame.exists) +cf_get_db_attr = cFameCall(cfame.get_db_attr) +cf_get_obj_attr = cFameCall(cfame.get_obj_attr) +cf_copy = cFameCall(cfame.copy) +cf_rename_obj = cFameCall(cfame.rename_obj) +cf_license_expires = cFameCall(cfame.license_expires) +cf_set_db_desc = cFameCall(cfame.set_db_desc) +cf_set_db_doc = cFameCall(cfame.set_db_doc) +cf_set_obj_desc = cFameCall(cfame.set_obj_desc) +cf_set_obj_doc = cFameCall(cfame.set_obj_doc) +cf_set_obj_basis = cFameCall(cfame.set_obj_basis) +cf_set_obj_observed = cFameCall(cfame.set_obj_observed) + + +set_option = cf_set_option +set_option.__doc__ = \ +"""Set an option in the C HLI. See the FAME documentation for cfmsopt for a +listing of allowable option settings. + +:Parameters: + - option (str) : name of the option to set + - setting (str) : value of the option to set + +:Example: + set_option("DBSIZE", "LARGE") +""" + +def license_expires(): + """get date that license expires on""" + fame_date = cf_license_expires() + adj_val = date_value_adjust[ts.freq_fromstr('D')] + return ts.Date(freq='D', value=fame_date+adj_val) From scipy-svn at scipy.org Tue Feb 20 13:56:30 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:56:30 -0600 (CST) Subject: [Scipy-svn] r2735 - trunk/Lib/sandbox/timeseries/io/fame/src Message-ID: <20070220185630.7E80239C16C@new.scipy.org> Author: mattknox_ca Date: 2007-02-20 12:56:26 -0600 (Tue, 20 Feb 2007) New Revision: 2735 Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c Log: major additions/changes Modified: trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-20 18:55:53 UTC (rev 2734) +++ trunk/Lib/sandbox/timeseries/io/fame/src/cfame.c 2007-02-20 18:56:26 UTC (rev 2735) @@ -12,6 +12,10 @@ #define MAXNLLENGTH 1000 #define CALLFAME(cmd) Py_BEGIN_ALLOW_THREADS; cmd; Py_END_ALLOW_THREADS; if (checkError(status)) return NULL +#define ADD_INT_TO_DICT(dict, key, val) \ + {PyObject *pyval = PyInt_FromLong(val); \ + PyDict_SetItemString(dict, key, pyval); \ + Py_DECREF(pyval); } //call fame without checking for errors #define CALLFAME_NOCHECK(cmd) Py_BEGIN_ALLOW_THREADS; cmd; Py_END_ALLOW_THREADS; @@ -95,7 +99,8 @@ return PyInt_FromLong(dbkey); } -static char cfame_close_doc[] = "C level portion of the close method."; +static char cfame_close_doc[] = +"C level portion of the close method."; static PyObject * cfame_close(PyObject *self, PyObject *args) { @@ -106,6 +111,52 @@ Py_RETURN_NONE; } +static char cfame_post_doc[] = +"C level portion of the post method."; +static PyObject * +cfame_post(PyObject *self, PyObject *args) +{ + int status, dbkey; + if (!PyArg_ParseTuple(args, "i:post", &dbkey)) return NULL; + + CALLFAME(cfmpodb (&status, dbkey)); + Py_RETURN_NONE; +} + +static char cfame_license_expires_doc[] = +"C level portion of the license_expires method."; +static PyObject * +cfame_license_expires(PyObject *self, PyObject *args) +{ + int status, date; + CALLFAME(cfmexpiration (&status, &date)); + return PyInt_FromLong(date); +} + +static char cfame_copy_doc[] = "C level portion of the copy_object function."; +static PyObject * +cfame_copy(PyObject *self, PyObject *args) +{ + int status, srckey, tarkey; + char *srcname, *tarname; + if (!PyArg_ParseTuple(args, "iiss:copy", &srckey, &tarkey, &srcname, &tarname)) return NULL; + + CALLFAME(cfmcpob (&status, srckey, tarkey, srcname, tarname)); + Py_RETURN_NONE; +} + +static char cfame_rename_obj_doc[] = "C level portion of the rename function."; +static PyObject * +cfame_rename_obj(PyObject *self, PyObject *args) +{ + int status, dbkey; + char *srcname, *tarname; + if (!PyArg_ParseTuple(args, "iss:copy", &dbkey, &srcname, &tarname)) return NULL; + + CALLFAME(cfmrnob (&status, dbkey, srcname, tarname)); + Py_RETURN_NONE; +} + static char cfame_get_db_attr_doc[] = "C level portion of the get_db_attr method."; static PyObject * cfame_get_db_attr(PyObject *self, PyObject *args) @@ -180,7 +231,93 @@ } } +static char cfame_set_db_desc_doc[] = "C level portion of the set_db_desc method."; +static PyObject * +cfame_set_db_desc(PyObject *self, PyObject *args) +{ + int status, dbkey; + char *desc; + if (!PyArg_ParseTuple(args, "is:set_db_desc", &dbkey, &desc)) return NULL; + CALLFAME(cfmddes(&status, dbkey, desc)); + Py_RETURN_NONE; +} +static char cfame_set_db_doc_doc[] = "C level portion of the set_db_doc method."; +static PyObject * +cfame_set_db_doc(PyObject *self, PyObject *args) +{ + int status, dbkey; + char *doc; + if (!PyArg_ParseTuple(args, "is:set_db_doc", &dbkey, &doc)) return NULL; + CALLFAME(cfmddoc(&status, dbkey, doc)); + Py_RETURN_NONE; +} + +static char cfame_set_obj_desc_doc[] = "C level portion of the set_obj_desc method."; +static PyObject * +cfame_set_obj_desc(PyObject *self, PyObject *args) +{ + int status, dbkey; + char *name, *desc; + if (!PyArg_ParseTuple(args, "iss:set_obj_desc", &dbkey, &name, &desc)) return NULL; + CALLFAME(cfmsdes(&status, dbkey, name, desc)); + Py_RETURN_NONE; +} + +static char cfame_set_obj_doc_doc[] = "C level portion of the set_obj_doc method."; +static PyObject * +cfame_set_obj_doc(PyObject *self, PyObject *args) +{ + int status, dbkey; + char *name, *doc; + if (!PyArg_ParseTuple(args, "iss:set_obj_doc", &dbkey, &name, &doc)) return NULL; + CALLFAME(cfmsdoc(&status, dbkey, name, doc)); + Py_RETURN_NONE; +} + +static char cfame_set_obj_basis_doc[] = "C level portion of the set_obj_basis method."; +static PyObject * +cfame_set_obj_basis(PyObject *self, PyObject *args) +{ + int status, dbkey, basis; + char *name; + if (!PyArg_ParseTuple(args, "isi:set_obj_basis", &dbkey, &name, &basis)) return NULL; + CALLFAME(cfmsbas(&status, dbkey, name, basis)); + Py_RETURN_NONE; +} + +static char cfame_set_obj_observed_doc[] = "C level portion of the set_obj_observed method."; +static PyObject * +cfame_set_obj_observed(PyObject *self, PyObject *args) +{ + int status, dbkey, observed; + char *name; + if (!PyArg_ParseTuple(args, "isi:set_obj_observed", &dbkey, &name, &observed)) return NULL; + CALLFAME(cfmsobs(&status, dbkey, name, observed)); + Py_RETURN_NONE; +} + +static char cfame_get_obj_attr_doc[] = "C level portion of the get_obj_attr method."; +static PyObject * +cfame_get_obj_attr(PyObject *self, PyObject *args) +{ + int status, dbkey; + int is_created, is_modified; + int cdate, mdate; + char *name, *attr; + if (!PyArg_ParseTuple(args, "iss:get_obj_attr", &dbkey, &name, &attr)) return NULL; + + is_created = (strcmp(attr, "CREATED") == 0); + is_modified = (strcmp(attr, "MODIFIED") == 0); + + + CALLFAME(cfmgdat(&status, dbkey, name, HSEC, &cdate, &mdate)); + if (is_modified) { return PyInt_FromLong(mdate); } + else { return PyInt_FromLong(cdate); } + +} + + static char cfame_wildlist_doc[] = "C level portion of the wildlist method."; static PyObject * cfame_wildlist(PyObject *self, PyObject *args) @@ -898,9 +1035,9 @@ -static char cfame_delete_doc[] = "C level portion of code for deleting objects."; +static char cfame_delete_obj_doc[] = "C level portion of code for deleting objects."; static PyObject* -cfame_delete(PyObject* self, PyObject* args) +cfame_delete_obj(PyObject* self, PyObject* args) { int status, dbkey; char* object_name; @@ -1109,6 +1246,10 @@ static PyMethodDef cfame_methods[] = { {"open", cfame_open, METH_VARARGS, cfame_open_doc}, {"close", cfame_close, METH_VARARGS, cfame_close_doc}, + {"copy", cfame_copy, METH_VARARGS, cfame_copy_doc}, + {"rename_obj", cfame_rename_obj, METH_VARARGS, cfame_rename_obj_doc}, + {"post", cfame_post, METH_VARARGS, cfame_post_doc}, + {"license_expires", cfame_license_expires, METH_VARARGS, cfame_license_expires_doc}, {"wildlist", cfame_wildlist, METH_VARARGS, cfame_wildlist_doc}, {"read", cfame_read, METH_VARARGS, cfame_read_doc}, {"whats", cfame_whats, METH_VARARGS, cfame_whats_doc}, @@ -1117,21 +1258,318 @@ {"write_scalar", cfame_write_scalar, METH_VARARGS, cfame_write_scalar_doc}, {"write_series", cfame_write_series, METH_VARARGS, cfame_write_series_doc}, {"create", cfame_create, METH_VARARGS, cfame_create_doc}, - {"delete", cfame_delete, METH_VARARGS, cfame_delete_doc}, + {"delete_obj", cfame_delete_obj, METH_VARARGS, cfame_delete_obj_doc}, {"exists", cfame_exists, METH_VARARGS, cfame_exists_doc}, {"write_namelist", cfame_write_namelist, METH_VARARGS, cfame_write_namelist_doc}, {"restore", cfame_restore, METH_VARARGS, cfame_restore_doc}, + {"get_obj_attr", cfame_get_obj_attr, METH_VARARGS, cfame_get_obj_attr_doc}, {"get_db_attr", cfame_get_db_attr, METH_VARARGS, cfame_get_db_attr_doc}, + {"set_db_desc", cfame_set_db_desc, METH_VARARGS, cfame_set_db_desc}, + {"set_db_doc", cfame_set_db_doc, METH_VARARGS, cfame_set_db_doc}, + {"set_obj_desc", cfame_set_obj_desc, METH_VARARGS, cfame_set_obj_desc}, + {"set_obj_doc", cfame_set_obj_doc, METH_VARARGS, cfame_set_obj_doc}, + {"set_obj_basis", cfame_set_obj_basis, METH_VARARGS, cfame_set_obj_basis}, + {"set_obj_observed", cfame_set_obj_observed, METH_VARARGS, cfame_set_obj_observed}, {NULL, NULL} }; PyMODINIT_FUNC initcfame(void) { + PyObject *m, *FAME_CONSTANTS; int status; - cfmini(&status); - Py_InitModule3("cfame", cfame_methods, cfame_doc); + CALLFAME(cfmini(&status)); + if ((m = Py_InitModule3("cfame", cfame_methods, cfame_doc)) == NULL) return NULL; import_array(); + makeTranslationTables(); - makeTranslationTables(); + FAME_CONSTANTS = PyDict_New(); + + // Add all the fame constants to a python dictionary + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSUCC",HSUCC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HINITD",HINITD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNINIT",HNINIT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFIN",HFIN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFILE",HBFILE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBMODE",HBMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBKEY",HBKEY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSRNG",HBSRNG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBERNG",HBERNG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBNRNG",HBNRNG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNOOBJ",HNOOBJ); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBRNG",HBRNG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDUTAR",HDUTAR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBOBJT",HBOBJT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFREQ",HBFREQ); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTRUNC",HTRUNC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNPOST",HNPOST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFUSE",HFUSE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNFMDB",HNFMDB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HRNEXI",HRNEXI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCEXI",HCEXI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNRESW",HNRESW); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBCLAS",HBCLAS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBOBSV",HBOBSV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBBASI",HBBASI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOEXI",HOEXI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBMONT",HBMONT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFLAB",HBFLAB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBMISS",HBMISS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBINDX",HBINDX); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNWILD",HNWILD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBNCHR",HBNCHR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBGROW",HBGROW); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HQUOTA",HQUOTA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOLDDB",HOLDDB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMPOST",HMPOST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSPCDB",HSPCDB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFLAG",HBFLAG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HPACK",HPACK); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNEMPT",HNEMPT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBATTR",HBATTR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDUP",HDUP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBYEAR",HBYEAR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBPER",HBPER); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBDAY",HBDAY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBDATE",HBDATE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSEL",HBSEL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBREL",HBREL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBTIME",HBTIME); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBCPU",HBCPU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HEXPIR",HEXPIR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBPROD",HBPROD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBUNIT",HBUNIT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBCNTX",HBCNTX); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLOCKD",HLOCKD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNETCN",HNETCN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNFAME",HNFAME); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNBACK",HNBACK); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSUSPN",HSUSPN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSRVR",HBSRVR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCLNLM",HCLNLM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBUSER",HBUSER); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSRVST",HSRVST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBOPT",HBOPT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBOPTV",HBOPTV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNSUPP",HNSUPP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBLEN",HBLEN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNULLP",HNULLP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HREADO",HREADO); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNWFEA",HNWFEA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBGLNM",HBGLNM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCLCHN",HCLCHN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDPRMC",HDPRMC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKOPN",HWKOPN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNUFRD",HNUFRD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNOMEM",HNOMEM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFUNC",HBFUNC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBPHAS",HBPHAS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAPOST",HAPOST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUPDRD",HUPDRD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HP1REQ",HP1REQ); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HP2REQ",HP2REQ); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUNEXP",HUNEXP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBVER",HBVER); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNFILE",HNFILE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMFILE",HMFILE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSCLLM",HSCLLM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDBCLM",HDBCLM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSNFIL",HSNFIL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMFIL",HSMFIL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HRESFD",HRESFD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTMOUT",HTMOUT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCHGAC",HCHGAC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFMENV",HFMENV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLICFL",HLICFL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLICNS",HLICNS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HRMTDB",HRMTDB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBCONN",HBCONN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HABORT",HABORT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNCONN",HNCONN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNMCA",HNMCA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBATYP",HBATYP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBASRT",HBASRT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBPRSP",HBPRSP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBGRP",HBGRP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNLOCL",HNLOCL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDHOST",HDHOST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOPENW",HOPENW); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOPEND",HOPEND); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNTWIC",HNTWIC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HPWWOU",HPWWOU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLSERV",HLSERV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLRESV",HLRESV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMAXDB",HMAXDB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLARGE",HLARGE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HREMSUP",HREMSUP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBADVAL",HBADVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNOMAP",HNOMAP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HIFAIL",HIFAIL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFAMER",HFAMER); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFMON",HBFMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNAMLEN",HNAMLEN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNAMSIZ",HNAMSIZ); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HRMODE",HRMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCMODE",HCMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOMODE",HOMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUMODE",HUMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMODE",HSMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWMODE",HWMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDMODE",HDMODE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSERIE",HSERIE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSCALA",HSCALA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFRMLA",HFRMLA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HITEM",HITEM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HGLNAM",HGLNAM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HGLFOR",HGLFOR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUNDFT",HUNDFT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNUMRC",HNUMRC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNAMEL",HNAMEL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBOOLN",HBOOLN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSTRNG",HSTRNG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HPRECN",HPRECN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDATE",HDATE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HRECRD",HRECRD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSUND",HBSUND); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSDAY",HBSDAY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSBUS",HBSBUS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBUND",HOBUND); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBBEG",HOBBEG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBEND",HOBEND); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBAVG",HOBAVG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBSUM",HOBSUM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBANN",HOBANN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBFRM",HOBFRM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBHI",HOBHI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOBLO",HOBLO); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUNDFX",HUNDFX); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDAILY",HDAILY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBUSNS",HBUSNS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKSUN",HWKSUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKMON",HWKMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKTUE",HWKTUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKWED",HWKWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKTHU",HWKTHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKFRI",HWKFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWKSAT",HWKSAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTENDA",HTENDA); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWASUN",HWASUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWAMON",HWAMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWATUE",HWATUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWAWED",HWAWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWATHU",HWATHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWAFRI",HWAFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWASAT",HWASAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBSUN",HWBSUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBMON",HWBMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBTUE",HWBTUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBWED",HWBWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBTHU",HWBTHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBFRI",HWBFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWBSAT",HWBSAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTWICM",HTWICM); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMONTH",HMONTH); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBMNOV",HBMNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBIMON",HBIMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HQTOCT",HQTOCT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HQTNOV",HQTNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HQTDEC",HQTDEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANJAN",HANJAN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANFEB",HANFEB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANMAR",HANMAR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANAPR",HANAPR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANMAY",HANMAY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANJUN",HANJUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANJUL",HANJUL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANAUG",HANAUG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANSEP",HANSEP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANOCT",HANOCT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANNOV",HANNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HANDEC",HANDEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMJUL",HSMJUL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMAUG",HSMAUG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMSEP",HSMSEP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMOCT",HSMOCT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMNOV",HSMNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSMDEC",HSMDEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAYPP",HAYPP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAPPY",HAPPY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSEC",HSEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMIN",HMIN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HHOUR",HHOUR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCASEX",HCASEX); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HJAN",HJAN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFEB",HFEB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMAR",HMAR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAPR",HAPR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMAY",HMAY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HJUN",HJUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HJUL",HJUL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAUG",HAUG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSEP",HSEP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HOCT",HOCT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNOV",HNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HDEC",HDEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYJAN",HFYJAN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYFEB",HFYFEB); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYMAR",HFYMAR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYAPR",HFYAPR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYMAY",HFYMAY); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYJUN",HFYJUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYJUL",HFYJUL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYAUG",HFYAUG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYSEP",HFYSEP); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYOCT",HFYOCT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYNOV",HFYNOV); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYDEC",HFYDEC); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSUN",HSUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMON",HMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTUE",HTUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWED",HWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTHU",HTHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFRI",HFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSAT",HSAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HASUN",HASUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAMON",HAMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HATUE",HATUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAWED",HAWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HATHU",HATHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAFRI",HAFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HASAT",HASAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSUN",HBSUN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBMON",HBMON); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBTUE",HBTUE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBWED",HBWED); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBTHU",HBTHU); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBFRI",HBFRI); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBSAT",HBSAT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYFST",HFYFST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYLST",HFYLST); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HFYAUT",HFYAUT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBEGIN",HBEGIN); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HEND",HEND); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HINTVL",HINTVL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HBEFOR",HBEFOR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HAFTER",HAFTER); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCONT",HCONT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNMVAL",HNMVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNCVAL",HNCVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNAVAL",HNAVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNDVAL",HNDVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HMGVAL",HMGVAL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HUNCHG",HUNCHG); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HSERVR",HSERVR); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCLNT",HCLNT); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HCHANL",HCHANL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HREAD",HREAD); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HWRITE",HWRITE); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HALL",HALL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNTMIS",HNTMIS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HTMIS",HTMIS); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNO",HNO); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HYES",HYES); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HNLALL",HNLALL); + ADD_INT_TO_DICT(FAME_CONSTANTS, "HLI_MAX_STR_LEN",HLI_MAX_STR_LEN); + + PyModule_AddObject(m, "FAME_CONSTANTS", FAME_CONSTANTS); } \ No newline at end of file From scipy-svn at scipy.org Tue Feb 20 13:57:10 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:57:10 -0600 (CST) Subject: [Scipy-svn] r2736 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070220185710.597F139C16C@new.scipy.org> Author: mattknox_ca Date: 2007-02-20 12:57:08 -0600 (Tue, 20 Feb 2007) New Revision: 2736 Removed: trunk/Lib/sandbox/timeseries/io/fame/fame.py Log: replaced with core.py Deleted: trunk/Lib/sandbox/timeseries/io/fame/fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-20 18:56:26 UTC (rev 2735) +++ trunk/Lib/sandbox/timeseries/io/fame/fame.py 2007-02-20 18:57:08 UTC (rev 2736) @@ -1,715 +0,0 @@ -""" -pyfame TODO: - -- implement "commit" -- implement "create_from_pyobj" - -Add unit tests: - - - db_created - - db_modified - - is_open - - db_desc - - db_doc - - create - - commit - - create_from_pyobj - -Add new functions: - - - set_obj_attr - - get_obj_attr - - modified - - created - - desc - - freq - - copy - - rename -""" - -import sys, types, re, os - -import timeseries as ts -import cfame -import mapping as mp - -import numpy -import maskedarray as ma -import thread - -fameLock = thread.allocate_lock() - -class CaseInsensitiveDict(dict): - def __init__(self, data={}): - for i, v in data.iteritems(): - self[i.upper()] = v - - def __getitem__(self, key): - if hasattr(key, 'upper'): key = key.upper() - return super(CaseInsensitiveDict, self).__getitem__(key) - - def __setitem__(self, key, item): - if hasattr(key, 'upper'): key = key.upper() - super(CaseInsensitiveDict, self).__setitem__(key, item) - - -def _single_or_multi_func(dbkey, name, func, *args, **kwargs): - if isinstance(name, str): - single_obj = True - name = [name] - else: - single_obj = False - - result = {} - for n in name: - result[n] = func(dbkey, n, *args, **kwargs) - - if single_obj: - return result.values()[0] - - return result - -def _famedate_to_tsdate(fame_date, freqstr): - "convert integer fame date to a timeseries Date" - value = fame_date + mp.value_adjust[ts.freq_fromstr(freqstr)] - return ts.Date(freq=freqstr, value=value) - -class DBError(Exception): pass - -class FameDb(object): - """Fame database object. - -:Construction: - x = FameDb(conn_str, mode='r') - -:Paramaters: - - `conn_str` (str) : valid connection string. Can be a physical path, - channel specification, etc. - - `mode` (str, *['r']*) : method of access to the database. Can be one - of the following: - 'r' => read only - 's' => shared - 'o' => overwrite - 'c' => create - 'u' => update - 'w' => write - 'd' => direct - -Notes - - For changes to be commited, you must explictly use the "commit" or - "close" methods (changes are commited on close). Changes are NOT - committed when the database object is deleted.""" - def __init__(self, conn_str, mode='r'): - mode = mode.lower() - if mode == 'r': - intmode = mp.HRMODE - elif mode == 's': - intmode = mp.HSMODE - elif mode == 'u': - intmode = mp.HUMODE - elif mode == 'w': - intmode = mp.HWMODE - elif mode == 'd': - intmode = mp.HDMODE - elif mode == 'c': - intmode = mp.HCMODE - elif mode == 'o': - intmode = mp.HOMODE - else: - raise ValueError, "Database access mode not supported." - self.mode = mode - - self.dbkey = cf_open(conn_str, intmode) - - - def read(self, name, - start_date=None, end_date=None, - start_case=None, end_case=None, max_string_len=65): - - """read specified object(s) from database - -:Parameters: - - `name` (string or list of strings) : names of objects that will be - read from the database - - - `start_date` (int, *[None]*) : Applies only when reading time series. - If specified, only data points on or after `start_date` will be read. - If None, data will be read from the first value of the series. - - `end_date` (int, *[None]*) : Applies only when reading time series. - If specified, only data points on or before `end_date` will be read. - If None, data will be read to the last value of the series. - - `start_case` (int, *[None]*) : Applies only when reading case series. - If specified, only data points on or after `start_case` will be read. - If None, data will be read starting from case index 1 - - `end_case` (int, *[None]*) : Applies only when reading case series. - If specified, only data points on or before `end_case` will be read. - If None, data will be read to the last value of the series. - - `max_string_len` (int, *[65]*) : Applies only when readings strings - or series of strings. This is the maximum length of string that can - be read. Lower values result in less memory usage, so you should - specify this as low as is reasonable for your data. - -:Return: - if `name` is a list of strings: - case insensitive dictionary of the objects - if `name` is a single string: - object from database that is stored as `name`""" - - isSingle = False - if isinstance(name, types.StringType): - names = [name] - isSingle = True - else: - names = name - - items = CaseInsensitiveDict() - - #default to -1. This will get the entire range - _start_case = _end_case = -1 - _start_date = _end_date = -1 - - range_freq = None - if start_date is not None: - _start_date = start_date.value - mp.value_adjust[start_date.freq] - range_freq = mp.freqReverseMapping[start_date.freq] - - if end_date is not None: - if start_date is not None and start_date.freq != end_date.freq: - raise ValueError("start_date and end_date must be same frequency") - _end_date = end_date.value - mp.value_adjust[end_date.freq] - if range_freq is None: - range_freq = mp.freqReverseMapping[end_date.freq] - - if start_case is not None: _start_case = start_case - if end_case is not None: _end_case = end_case - - if len(set([_start_case, _end_case, _start_date, _end_date, -1])) != 1: - checkFreq = True - else: - checkFreq = False - - for objName in names: - objName = objName.upper() - - if checkFreq: - objFreq = self.obj_size(objName)['freq'] - - if objFreq == range_freq: - start_index, end_index = _start_date, _end_date - elif objFreq == mp.HCASEX: - start_index, end_index = _start_case, _end_case - else: - start_index, end_index = -1, -1 - else: - start_index, end_index = -1, -1 - - result = cf_read(self.dbkey, objName, start_index, - end_index, max_string_len) - - if result['type'] == mp.HBOOLN: - numpyType = numpy.bool_ - else: - numpyType = mp.fametype_tonumpy(result['type']) - - if result['type'] == mp.HNAMEL: - pyObj = [x for x in result['data'][1:-1].split(", ") \ - if x != ''] - - elif result['class'] == mp.HSCALA: - if isinstance(result['data'], str): - if result['mask']: - pyObj = None - else: - pyObj = result['data'] - else: - if result['mask'][0]: - pyObj = None - else: - pyObj = result['data'][0] - if result['type'] >= 8: # date type - value = pyObj+ \ - mp.value_adjust[mp.freqMapping[result['type']]] - pyObj = ts.Date( - freq=mp.freqMapping[result['type']], - value=value) - else: - pyObj = numpyType(pyObj) - - elif result['class'] == mp.HSERIE: - - if 'data' in result: - vals = result['data'] - mask = result['mask'] - if not mask.any(): mask = ma.nomask - else: - vals = [] - mask = ma.nomask - - if result['type'] >= 8: # date type - valadj = mp.value_adjust[mp.freqMapping[result['type']]] - if len(vals) > 0: vals += valadj - data = ts.DateArray(vals, - freq=mp.freqMapping[result['type']]) - else: - data = numpy.array(vals, dtype=numpyType) - - if result['freq'] == mp.HCASEX: - pyObj = ma.array(data, mask=mask) - else: - observed = mp.observedMapping[result['observed']] - freq = mp.freqMapping[result['freq']] - - if 'data' in result: - start_date = ts.Date( - freq=freq, - value=result['startindex']+mp.value_adjust[freq]) - else: - start_date = None - - pyObj = ts.time_series(data, freq=freq, - start_date=start_date, - observed=observed, mask=mask) - - items[objName] = pyObj - - if isSingle: - return items.values()[0] - - return items - - - def write_tser_dict(self, objdict, - overwrite=False, assume_exists=False, - start_date=None, end_date=None): - """for each key, value pair in the dictionary `objdict` write value to -the database as key, as a time series (calls FameDb.write_tser on each key, -value pair) - -:Parameters: - - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object - names for keys and TimeSeries objects for values - - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in objects in `objdict` will be given priority over pre-existing data in - the db where there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `start_date` (Date, *[None]*) : If None, data will be written from the start of - the series. If specified, only data points on or after start_date will be written. - - `end_date` (Date, *[None]*) : If None, data will be written until the end of - the series. If specified, only data points on or before end_date will be written. -""" - for key, obj in objdict.iteritems(): - self.write_tser(key, obj, overwrite=overwrite, - assume_exists=assume_exists, - start_date=start_date, end_date=end_date) - - - def write_cser_dict(self, objdict, - overwrite=False, assume_exists=False, - zero_represents=1, start_case=None, end_case=None): - """for each key, value pair in the dictionary `objdict` write value to -the database as key, as a case series (calls FameDb.write_tser on each key, -value pair) - -:Parameters: - - `objdict` (dict) : dictionary of arrays to be written as Case Series. - Object names for keys and arrays for values - - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in objects in `objdict` will be given priority over pre-existing data in - the db where there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in - the array represents - - `start_case` (int, *[None]*) : If None, data will be written from the start of - the array. If specified, only data points on or after start_case will be written. - - `end_case` (int, *[None]*) : If None, data will be written until the end of - the array. If specified, only data points on or before end_case will be written. -""" - for key, obj in objdict.iteritems(): - self.write_cser(key, obj, overwrite=overwrite, - assume_exists=assume_exists, - zero_represents=zero_represents, - start_case=start_case, end_case=end_case) - - def write_scalar_dict(self, objdict): - """for each key, value pair in the dictionary `objdict` write value to -the database as key, as a scalar (calls FameDb.write_scalar on each key, -value pair) - -:Parameters: - - `objdict` (dict) : dictionary of items to be written as scalars. - Object names for keys and scalar items for values -""" - for key, obj in objdict.iteritems(): - self.write_scalar(key, obj) - - - def write_tser(self, name, tser, - overwrite=False, assume_exists=False, - start_date=None, end_date=None): - """write `tser` to the database as `name` as a time series. - -:Parameters: - - `name` (string) : database key that the object will be written to - - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates. - Use fill_missing_dates first on your series if you suspect this is the situation. - TimeSeries must be 1-dimensional - - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in `tser` will be given priority over pre-existing data in the db where - there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `start_date` (Date, *[None]*) : If None, data will be written from the start of - `tser`. If specified, only data points on or after start_date will be written. - - `end_date` (Date, *[None]*) : If None, data will be written until the end of - `tser`. If specified, only data points on or before end_date will be written. -""" - - if not isinstance(tser, ts.TimeSeries): - raise ValueError("tser is not a valid time series") - elif tser.has_missing_dates(): - raise ValueError("tser must not have any missing dates") - elif tser.ndim != 1: - raise ValueError("FAME db only supports 1-dimensional time series") - - if assume_exists and not self.exists(name): - raise DBError("%s does not exist" % name) - - if overwrite or not self.exists(name): create = True - else: create = False - - fame_type = mp.fametype_fromdata(tser._data) - fame_freq = mp.freqReverseMapping[tser.freq] - - if create: - - fame_basis = mp.HBSDAY - - if hasattr(tser, "observed"): - fame_observed = mp.observedReverseMapping[tser.observed] - if fame_observed == 0: fame_observed = mp.HOBEND - else: - fame_observed = mp.HOBEND - - if self.exists(name): self.delete(name) - cf_create(self.dbkey, name, mp.HSERIE, fame_freq, fame_type, fame_basis, fame_observed) - - def get_boundary_date(bdate, attr): - if bdate is not None: - if bdate.freq != tser.freq: - raise ValueError(attr+" frequency must be same as tser frequency") - if tser.start_date > bdate or tser.end_date < bdate: - raise ValueError(attr+" outside range of series") - return bdate - else: - return getattr(tser, attr) - - start_date = get_boundary_date(start_date, "start_date") - end_date = get_boundary_date(end_date, "end_date") - - if start_date is not None: - - towrite = tser[start_date:end_date+1] - - start_index = start_date.value - end_index = end_date.value - - # convert integer types to floats since FAME does not have an integer type - newType = mp.fametype_tonumpy(fame_type) - if fame_type >= 8: - # date type - fame_data = towrite._data - mp.value_adjust[towrite._data.freq] - elif newType != tser._data.dtype: - fame_data = towrite._data.astype(newType) - else: - fame_data = towrite._data - - if towrite._mask is ma.nomask: - fame_mask = numpy.zeros(towrite._data.shape, dtype=numpy.bool_) - else: - fame_mask = towrite._mask - - start_index -= mp.value_adjust[towrite.freq] - end_index -= mp.value_adjust[towrite.freq] - - cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_index, end_index, fame_type, fame_freq) - - def write_cser(self, name, cser, overwrite=False, assume_exists=False, zero_represents=1, start_case=None, end_case=None): - """write `cser` to the database as `name` as a case series. - -:Parameters: - - `name` (string) : database key that the object will be written to - - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be - written. If `cser` is a MaskedArray, then masked values will be written as ND. - - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in `cser` will be given priority over pre-existing data in the db where - there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in - the array represents - - `start_case` (int, *[None]*) : If None, data will be written from the start of - `cser`. If specified, only data points on or after start_case will be written. - - `end_case` (int, *[None]*) : If None, data will be written until the end of - `cser`. If specified, only data points on or before end_case will be written. -""" - - if not isinstance(cser, numpy.ndarray): - raise ValueError("cser is not a valid ndarray") - elif cser.ndim != 1: - raise ValueError("FAME db only supports 1-dimensional arrays") - - if assume_exists and not self.exists(name): - raise DBError("%s does not exist" % name) - - if overwrite or not self.exists(name): create = True - else: create = False - - if hasattr(cser, "_data"): - fame_data = cser._data - if cser._mask is ma.nomask: - fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) - else: - fame_mask = cser._mask - else: - fame_data = cser - fame_mask = numpy.zeros(fame_data.shape, dtype=numpy.bool_) - - fame_type = mp.fametype_fromdata(fame_data) - - if create: - if self.exists(name): self.delete(name) - cf_create(self.dbkey, name, mp.HSERIE, mp.HCASEX, fame_type, mp.HBSUND, mp.HOBUND) - - def get_boundary_case(bcase, attr): - if bcase is not None: - idx = bcase - zero_represents - if idx < 0 or idx > cser.size: - raise ValueError("%s outside range of series" % attr) - return bcase - else: - if cser.size == 0: - return None - else: - if attr == 'start_case': - return zero_represents - elif attr == 'end_case': - return zero_represents + cser.size - 1 - else: - raise ValueError("unexpected argument: %s " % attr) - - start_case = get_boundary_case(start_case, "start_case") - end_case = get_boundary_case(end_case, "end_case") - - if start_case is not None: - # convert integer types to floats since FAME does not have an integer type - s = start_case - zero_represents - e = end_case - zero_represents - - fame_data = fame_data[s:e+1] - fame_mask = fame_mask[s:e+1] - newType = mp.fametype_tonumpy(fame_type) - if fame_type >= 8: - # date type - fame_data = fame_data - mp.value_adjust[fame_data.freq] - elif newType != fame_data.dtype: - fame_data = fame_data.astype(newType) - - cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_case, end_case, fame_type, mp.HCASEX) - - - def write_scalar(self, name, scalar): - """write `scalar` to the database as `name` as a scalar object. If an -object already exists in the database named as `name` then it is -over-written, otherwise it is created. - -:Parameters: - - `name` (string) : database key that the object will be written to - - `scalar` : one of the following: string, numpy scalar, int, float, - list of strings (for name lists), Date, boolean""" - - fame_type = mp.fametype_fromdata(scalar) - - if isinstance(scalar, ts.Date): - fame_data = numpy.int32(scalar.value - mp.value_adjust[scalar.freq]) - elif hasattr(scalar, "dtype"): - if scalar.ndim != 0: raise ValueError("received non-scalar data") - newType = mp.fametype_tonumpy(fame_type) - if newType != scalar.dtype: fame_data = scalar.astype(newType) - else: fame_data = scalar - elif fame_type == mp.HSTRNG: - fame_data = scalar - elif fame_type == mp.HPRECN: - fame_data = numpy.float64(scalar) - elif fame_type == mp.HBOOLN: - fame_data = numpy.int32(scalar) - elif fame_type == mp.HNAMEL: - fame_data = "{" + ", ".join(scalar) + "}" - else: - raise ValueError("Unrecognized data type") - - if self.exists(name): self.delete(name) - cf_create(self.dbkey, name, mp.HSCALA, mp.HUNDFX, fame_type, mp.HBSUND, mp.HOBUND) - - # convert integer types to floats since FAME does not have an integer type - newType = mp.fametype_tonumpy(fame_type) - if hasattr(fame_data, 'dtype') and newType != fame_data.dtype: - fame_data = fame_data.astype(newType) - - if fame_type == mp.HNAMEL: - cf_write_namelist(self.dbkey, name, fame_data) - else: - cf_write_scalar(self.dbkey, name, fame_data, fame_type) - - - def create(name, cls, type, freq=None, basis=None, observed=None): - """create object in database with specified attributes as `name`""" - - if cls not in (mp.HSERIE, mp.HSCALA): - raise ValueError("unrecognized object class: "+str(cls)) - - if freq is None: - if cls == mp.HSCALA: - freq = mp.HUNDFX - else: - raise ValueError("freq must be specified for series") - - if freq in (mp.HUNDFX, mp.HCASEX): - basis = mp.HBSUND - observed = mp.HOBUND - else: - if basis is None: basis = mp.HBSDAY - if observed is None: observed = mp.HOBEND - - cf_create(self.dbkey, name, cls, freq, type, basis, observed) - - def create_from_pyobj(name, pyobj): - """create object of appropriate type in database based on the -python object `pyobj` as `name`. Does not write any data to the -database, simply initializes the object in the database.""" - raise NotImplementedError("function not implemented yet") - - - def db_desc(self): - "get 'description' attribute of database" - return cf_get_db_attr(self.dbkey, "DESC") - - def db_doc(self): - "get 'doc' attribute of database" - return cf_get_db_attr(self.dbkey, "DOC") - - def db_created(self): - "get 'created' attribute of database" - fame_date = cf_get_db_attr(self.dbkey, "CREATED") - return _famedate_to_tsdate(fame_date, 's') - - def db_modified(self): - "get 'modified' attribute of database" - fame_date = cf_get_db_attr(self.dbkey, "MODIFIED") - return _famedate_to_tsdate(fame_date, 's') - - def is_open(self): - "returns True if database is open. False otherwise" - return cf_get_db_attr(self.dbkey, "ISOPEN") - - def wildlist(self, exp, wildonly=False): - """performs a wildlist lookup on the database, using Fame syntax -("?" and "^"), returns a normal python list of strings""" - res = cf_wildlist(self.dbkey, exp) - - if wildonly: - exp = exp.replace("?", "(.*)") - exp = exp.replace("^", "(.)") - exp = exp.replace("$","\$") - regex = re.compile(exp) - res = ["".join(regex.match(res[i]).groups()) \ - for i in range(len(res))] - return res - - def exists(self, name): - return cf_exists(self.dbkey, name) - - def close(self): - """Closes the database. Changes will be posted.""" - if self.is_open(): - cf_close(self.dbkey) - - def commit(self): - raise NotImplementedError("function not implemented yet") - - def delete(self, name, must_exist=True): - """Deletes the specified object(s) from the database""" - if isinstance(name, str): name = [name] - [cf_delete(self.dbkey, n) for n in name if must_exist or self.exists(n)] - - def obj_size(self, name): - """basic information about the size of an object(s) in a database""" - return _single_or_multi_func(self.dbkey, name, cf_obj_size) - - def whats(self, name): - """Preforms a fame "whats" command on the provided name(s)""" - return _single_or_multi_func(self.dbkey, name, cf_whats) - - def restore(self): - """Discard any changes made to the database since it was last opened or posted.""" - return cf_restore(self.dbkey) - - -class cFameCall: - """wrapper for cfame functions that acquires and releases a resource lock. -This is needed because the Fame C api is not thread safe.""" - - def __init__ (self, func): - self.f = func - self.__doc__ = getattr(func, "__doc__", str(func)) - self.__name__ = getattr(func, "__name__", str(func)) - - def __call__ (self, *args, **kwargs): - "Execute the call behavior." - tmp = fameLock.acquire() - try: - result = self.f(*args, **kwargs) - fameLock.release() - except: - fameLock.release() - raise - - return result - -cf_open = cFameCall(cfame.open) -cf_set_option = cFameCall(cfame.set_option) -cf_close = cFameCall(cfame.close) -cf_restore = cFameCall(cfame.restore) -cf_obj_size = cFameCall(cfame.obj_size) -cf_whats = cFameCall(cfame.whats) -cf_delete = cFameCall(cfame.delete) -cf_create = cFameCall(cfame.create) -cf_read = cFameCall(cfame.read) -cf_write_scalar = cFameCall(cfame.write_scalar) -cf_write_series = cFameCall(cfame.write_series) -cf_write_namelist = cFameCall(cfame.write_namelist) -cf_wildlist = cFameCall(cfame.wildlist) -cf_exists = cFameCall(cfame.exists) -cf_get_db_attr = cFameCall(cfame.get_db_attr) - -set_option = cf_set_option -set_option.__doc__ = \ -"""Set an option in the C HLI. See the FAME documentation for cfmsopt for a -listing of allowable option settings. - -:Parameters: - - option (str) : name of the option to set - - setting (str) : value of the option to set - -:Example: - set_option("DBSIZE", "LARGE") -""" From scipy-svn at scipy.org Tue Feb 20 13:57:25 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 20 Feb 2007 12:57:25 -0600 (CST) Subject: [Scipy-svn] r2737 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070220185725.1963B39C16B@new.scipy.org> Author: mattknox_ca Date: 2007-02-20 12:57:23 -0600 (Tue, 20 Feb 2007) New Revision: 2737 Removed: trunk/Lib/sandbox/timeseries/io/fame/mapping.py Log: merged into core.py Deleted: trunk/Lib/sandbox/timeseries/io/fame/mapping.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/mapping.py 2007-02-20 18:57:08 UTC (rev 2736) +++ trunk/Lib/sandbox/timeseries/io/fame/mapping.py 2007-02-20 18:57:23 UTC (rev 2737) @@ -1,205 +0,0 @@ - - -# --------------------------- -# For fametype mapping -import types -import numpy -from timeseries import TimeSeries, Date, DateArray, freq_fromstr - - -# --------------------------- -# Fame specific constants - -HRMODE = 1 # READ -HCMODE = 2 # CREATE -HOMODE = 3 # OVERWRITE -HUMODE = 4 # UPDATE -HSMODE = 5 # SHARED -HWMODE = 6 # WRITE -HDMODE = 7 # DIRECT WRITE - -#** FAME Data Object Classes ** - -HSERIE = 1 # SERIES -HSCALA = 2 # SCALAR -HFRMLA = 3 # FORMULA -HITEM = 4 # ITEM -HGLNAM = 5 # GLNAME -HGLFOR = 6 # GLFORMULA - -#** FAME Data Object Types ** - -HUNDFT = 0 # Undefined -HNUMRC = 1 # NUMERIC -HNAMEL = 2 # NAMELIST -HBOOLN = 3 # BOOLEAN -HSTRNG = 4 # STRING -HPRECN = 5 # PRECISION -HDATE = 6 # General DATE -HRECRD = 7 # RECORD - -#** FAME Frequencies ** - -HUNDFX = 0 # Undefined -HDAILY = 8 # DAILY -HBUSNS = 9 # BUSINESS -HWKSUN = 16 #WEEKLY (SUNDAY) -HMONTH = 129 # MONTHLY -HCASEX = 232 # CASE -HSEC = 226 # SECONDLY -HMIN = 227 # MINUTELY -HHOUR = 228 # HOURLY -HQTOCT = 160 # QUARTERLY (OCTOBER) -HQTNOV = 161 # QUARTERLY (NOVEMBER) -HQTDEC = 162 # QUARTERLY (DECEMBER) -HANJAN = 192 # ANNUAL (JANUARY) -HANFEB = 193 # ANNUAL (FEBRUARY) -HANMAR = 194 # ANNUAL (MARCH) -HANAPR = 195 # ANNUAL (APRIL) -HANMAY = 196 # ANNUAL (MAY) -HANJUN = 197 # ANNUAL (JUNE) -HANJUL = 198 # ANNUAL (JULY) -HANAUG = 199 # ANNUAL (AUGUST) -HANSEP = 200 # ANNUAL (SEPTEMBER) -HANOCT = 201 # ANNUAL (OCTOBER) -HANNOV = 202 # ANNUAL (NOVEMBER) -HANDEC = 203 # ANNUAL (DECEMBER) - -#** FAME BASIS Attribute Settings ** - -HBSUND = 0 # Undefined -HBSDAY = 1 # DAILY -HBSBUS = 2 # BUSINESS - -#** FAME OBSERVED Attribute Settings ** - -HOBUND = 0 # Undefined -HOBBEG = 1 # BEGINNING -HOBEND = 2 # ENDING -HOBAVG = 3 # AVERAGED -HOBSUM = 4 # SUMMED -HOBANN = 5 # ANNUALIZED -HOBFRM = 6 # FORMULA -HOBHI = 7 # HIGH -HOBLO = 8 # LOW - -def reverse_dict(d): - return dict([(y, x) for x, y in d.iteritems()]) - -basisMapping = { HBSUND:"UNDEFINED", - HBSDAY:"D", - HBSBUS:"B"} -basisReverseMapping = reverse_dict(basisMapping) - -observedMapping = { HOBUND:"UNDEFINED", - HOBBEG: "BEGINNING", - HOBEND: "ENDING", - HOBAVG: "AVERAGED", - HOBSUM: "SUMMED", - HOBANN: "ANNUALIZED", - HOBFRM: "FORMULA", - HOBHI: "MAXIMUM", - HOBLO: "MINIMUM" } - -observedReverseMapping = reverse_dict(observedMapping) - -freqMapping = { HDAILY:"D", - HBUSNS:"B", - HMONTH:"M", - HWKSUN:"W", - HSEC :"S", - HMIN :"T", - HHOUR :"H", - HQTOCT:"Q", - HQTNOV:"Q", - HQTDEC:"Q", - HANJAN:"A", - HANFEB:"A", - HANMAR:"A", - HANAPR:"A", - HANMAY:"A", - HANJUN:"A", - HANJUL:"A", - HANAUG:"A", - HANSEP:"A", - HANOCT:"A", - HANNOV:"A", - HANDEC:"A" } - -freqMapping = dict([(x, freq_fromstr(val)) for x, val in freqMapping.iteritems()]) - -freqReverseMapping = { "D" : HDAILY, - "B" : HBUSNS, - "M" : HMONTH, - "W" : HWKSUN, - "S" : HSEC, - "T" : HMIN, - "H" : HHOUR, - "Q" : HQTDEC, - "A" : HANDEC} - -freqReverseMapping = dict([(freq_fromstr(x), val) for x, val in freqReverseMapping.iteritems()]) - -value_adjust = { - 'A':1849, - 'Q':7396, - 'M':22188, - 'W':96477, - 'B':482381, - 'D':675333, - 'H':87648, - 'T':5258880, - 'S':315532800} - -value_adjust = dict([(freq_fromstr(x), val) for x, val in value_adjust.iteritems()]) - - -def fametype_fromdata(data): - """determine fame type code from a data object""" - - if isinstance(data, DateArray) or isinstance(data, Date): - return freqReverseMapping[data.freq] - elif hasattr(data, 'dtype'): - dtypeStr = str(data.dtype) - - if dtypeStr[:5] == "float": - if int(dtypeStr[5:]) > 32: return HPRECN - else: return HNUMRC - elif dtypeStr[:3] == "int": - if int(dtypeStr[3:]) > 32: return HPRECN - else: return HNUMRC - elif dtypeStr[:4] == "uint": - if int(dtypeStr[4:]) >= 32: return HPRECN - else: return HNUMRC - elif dtypeStr[:2] == "|S" or dtypeStr == 'object': - return HSTRNG - elif dtypeStr == "bool": - return HBOOLN - else: - raise ValueError("Unsupported dtype for fame database: %s", dtypeStr) - - elif type(data) == types.StringType: - return HSTRNG - elif type(data) in (types.IntType, types.FloatType): - return HPRECN - elif type(data) == types.BooleanType: - return HBOOLN - elif type(data) == types.ListType: - return HNAMEL - else: - raise ValueError("Unrecognized data type") - -def fametype_tonumpy(fametype): - if fametype >= 8: - # date types - return numpy.int32 - elif fametype == HNAMEL: - return None - else: - typeMap = { - HNUMRC:numpy.float32, - HBOOLN:numpy.int32, - HSTRNG:numpy.object_, - HPRECN:numpy.float64} - return typeMap[fametype] - From scipy-svn at scipy.org Wed Feb 21 12:54:16 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 21 Feb 2007 11:54:16 -0600 (CST) Subject: [Scipy-svn] r2738 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070221175416.1945839C19C@new.scipy.org> Author: mattknox_ca Date: 2007-02-21 11:54:09 -0600 (Wed, 21 Feb 2007) New Revision: 2738 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py Log: fairly major overhaul of tick labelling and spacing Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-20 18:57:23 UTC (rev 2737) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-21 17:54:09 UTC (rev 2738) @@ -131,27 +131,375 @@ #---- --- Locators --- ##### ------------------------------------------------------------------------- +def _year_start(_dates): + return (_dates.year != (_dates-1).year) + +def _quarter_start(_dates): + return (_dates.quarter != (_dates-1).quarter) + +def _month_start(_dates): + return (_dates.month != (_dates-1).month) + +def _week_start(_dates): + return (_dates.day_of_week == 1) + def _get_default_annual_spacing(nyears): """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 11: (min_spacing, maj_spacing) = (1, 1) - elif nyears < 15: + elif nyears < 20: (min_spacing, maj_spacing) = (1, 2) elif nyears < 50: (min_spacing, maj_spacing) = (1, 5) elif nyears < 100: (min_spacing, maj_spacing) = (5, 10) elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: + elif nyears < 600: (min_spacing, maj_spacing) = (10, 50) else: - (min_spacing, maj_spacing) = (20, 100) + factor = nyears // 1000 + 1 + (min_spacing, maj_spacing) = (factor*20, factor*100) return (min_spacing, maj_spacing) + +def _BreakDown_ParamCheck(locator, formatter): + if not locator and not formatter: + raise ValueError("Must specify either locator or formatter") + + if locator and formatter: + raise ValueError("Must specify only one of locator or formatter") + +def _Daily_BreakDown(dates, locator=False, formatter=False): + + _BreakDown_ParamCheck(locator, formatter) + + if dates.freqstr == 'B': periodsperyear = 261 + elif dates.freqstr == 'D': periodsperyear = 365 + else: raise ValueError("unexpected frequency") + + vmin = dates[0].value + vmax = dates[-1].value + span = vmax - vmin + 1 + + if locator: + default = N.arange(vmin, vmax+1) + else: #formatter + format = N.empty(dates.size, dtype="|S8") + format.flat = '' + + if span <= (periodsperyear//12 - 2): + + month_start = _month_start(dates) + + if locator: + major = default[month_start] + minor = default + else: + year_start = _year_start(dates) + year_start[0] = False + month_start[0] = False + + format[:] = '%d' + format[month_start] = '%d\n%b' + format[year_start] = '%d\n%b\n%Y' + + if not year_start.any(): + if not month_start.any(): + if dates.size > 1: idx = 1 + else: idx = 0 + format[idx] = '%d\n%b\n%Y' + else: + format[N.where(month_start)[0][0]] = '%d\n%b\n%Y' + + elif span <= periodsperyear//4: + + month_start = _month_start(dates) + + if locator: + major = default[month_start] + minor = default + else: + week_start = _week_start(dates) + year_start = _year_start(dates) + + week_start[0] = False + month_start[0] = False + year_start[0] = False + + format[week_start] = '%d' + format[month_start] = '\n\n%b' + format[year_start] = '\n\n%b\n%Y' + + if not year_start.any(): + if not month_start.any(): + format[N.where(week_start)[0][0]] = '\n\n%b\n%Y' + else: + format[N.where(month_start)[0][0]] = '\n\n%b\n%Y' + + elif span <= 1.15 * periodsperyear: + month_start = _month_start(dates) + + if locator: + week_start = _week_start(dates) + minor_idx = week_start | month_start + minor_idx[0] = True + major = default[month_start] + minor = default[minor_idx] + else: + year_start = _year_start(dates) + month_start[0] = False + year_start[0] = False + format[month_start] = '%b' + format[year_start] = '%b\n%Y' + + if not year_start.any(): + format[N.where(month_start)[0][0]] = '%b\n%Y' + + elif span <= 2.5 * periodsperyear: + + year_start = _year_start(dates) + month_start = _month_start(dates) + + if locator: + major = default[year_start] + minor = default[month_start] + else: + quarter_start = _quarter_start(dates) + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + + elif span <= 4 * periodsperyear: + + year_start = _year_start(dates) + month_start = _month_start(dates) + + if locator: + major = default[year_start] + minor = default[month_start] + else: + jan = (dates.month == 1) + jul = (dates.month == 7) + jan_or_jul = month_start & (jan | jul) + format[jan_or_jul] = '%b' + format[year_start] = '%b\n%Y' + + elif span <= 11 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + quarter_start = _quarter_start(dates) + major = default[year_start] + minor = default[quarter_start] + else: + format[year_start] = '%Y' + + else: + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) + year_start = _year_start(dates) + + major_idx = year_start & (dates.years % maj_anndef == 0) + + if locator: + major = default[major_idx] + minor = default[year_start & (dates.years % min_anndef == 0)] + else: + format[major_idx] = '%Y' + + if locator: + return minor, major + else: + return format + + +def _Monthly_BreakDown(dates, locator=False, formatter=False): + + _BreakDown_ParamCheck(locator, formatter) + + if dates.freqstr != 'M': raise ValueError("unexpected frequency") + + periodsperyear = 12 + + vmin = dates[0].value + vmax = dates[-1].value + span = vmax - vmin + 1 + + if locator: + default = N.arange(vmin, vmax+1) + else: #formatter + format = N.empty(dates.size, dtype="|S8") + format.flat = '' + + if span <= 1.15 * periodsperyear: + year_start = _year_start(dates) + + if locator: + major = default[year_start] + minor = default + else: + year_start[0] = False + + format[:] = '%b' + format[year_start] = '%b\n%Y' + + if not year_start.any(): + if dates.size > 1: idx = 1 + else: idx = 0 + format[idx] = '%b\n%Y' + + elif span <= 2.5 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + major = default[year_start] + minor = default + else: + quarter_start = _quarter_start(dates) + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + + elif span <= 4 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + major = default[year_start] + minor = default + else: + months = dates.month + format[(months == 1) | (months == 7)] = '%b' + format[year_start] = '%b\n%Y' + + elif span <= 11 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + quarter_start = _quarter_start(dates) + major = default[year_start] + minor = default[quarter_start] + else: + format[year_start] = '%Y' + + else: + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) + year_start = _year_start(dates) + + major_idx = year_start & (dates.years % maj_anndef == 0) + + if locator: + major = default[major_idx] + minor = default[year_start & (dates.years % min_anndef == 0)] + else: + format[major_idx] = '%Y' + + if locator: + return minor, major + else: + return format + + +def _Quarterly_BreakDown(dates, locator=False, formatter=False): + + _BreakDown_ParamCheck(locator, formatter) + + if dates.freqstr != 'Q': raise ValueError("unexpected frequency") + + periodsperyear = 4 + + vmin = dates[0].value + vmax = dates[-1].value + span = vmax - vmin + 1 + + if locator: + default = N.arange(vmin, vmax+1) + else: #formatter + format = N.empty(dates.size, dtype="|S8") + format.flat = '' + + if span <= 3.5 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + major = default[year_start] + minor = default + else: + year_start[0] = False + + format[:] = 'Q%q' + format[year_start] = 'Q%q\n%Y' + + if not year_start.any(): + if dates.size > 1: idx = 1 + else: idx = 0 + format[idx] = 'Q%q\n%Y' + + elif span <= 11 * periodsperyear: + + year_start = _year_start(dates) + + if locator: + major = default[year_start] + minor = default + else: + format[year_start] = '%Y' + + else: + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) + year_start = _year_start(dates) + + major_idx = year_start & (dates.years % maj_anndef == 0) + + if locator: + major = default[major_idx] + minor = default[year_start & (dates.years % min_anndef == 0)] + else: + format[major_idx] = '%Y' + + if locator: + return minor, major + else: + return format + + +def _Annual_BreakDown(dates, locator=False, formatter=False): + + _BreakDown_ParamCheck(locator, formatter) + + if dates.freqstr != 'A': raise ValueError("unexpected frequency") + + vmin = dates[0].value + vmax = dates[-1].value + span = vmax - vmin + 1 + + if locator: + default = N.arange(vmin, vmax+1) + else: #formatter + format = N.empty(dates.size, dtype="|S8") + format.flat = '' + + (min_anndef, maj_anndef) = _get_default_annual_spacing(span) + year_start = _year_start(dates) + + major_idx = year_start & (dates.years % maj_anndef == 0) + + if locator: + major = default[major_idx] + minor = default[year_start & (dates.years % min_anndef == 0)] + else: + format[major_idx] = '%Y' + + if locator: + return minor, major + else: + return format + #............................................................................... class TimeSeries_DateLocator(Locator): "Locates the ticks along an axis controlled by a DateArray." @@ -208,7 +556,13 @@ vmin -= 1 vmax += 1 return nonsingular(vmin, vmax) - + +def _generic_get_default_locs(self, vmin, vmax, BreakDownFunc): + dates = self._initialize_dates(vmin, vmax) + minor, major = BreakDownFunc(dates, locator=True) + if self.isminor: return minor + return major + #............................................................................... class TimeSeries_AnnualLocator(TimeSeries_DateLocator): "Locates the ticks along an axis controlled by an annual DateArray." @@ -220,14 +574,7 @@ def _get_default_locs(self, vmin, vmax): "Returns the default tick spacing for annual data." - span = vmax - vmin + 1 - (minor, major) = _get_default_annual_spacing(span) - if self.isminor: - base = minor - else: - base = major - offset = base - (vmin % base) - return N.arange(vmin+offset, vmax+1, base) + return _generic_get_default_locs(self, vmin, vmax, _Annual_BreakDown) #............................................................................... class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): @@ -241,21 +588,7 @@ def _get_default_locs(self, vmin, vmax): "Returns the default ticks spacing." - nquarters = vmax - vmin + 1 - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1, 4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1, 4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - if self.isminor: - base = min_spacing - else: - base = maj_spacing - offset = base - (vmin+4-1) % base - return N.arange(vmin+offset, vmax+1, base) + return _generic_get_default_locs(self, vmin, vmax, _Quarterly_BreakDown) #............................................................................... class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): @@ -269,27 +602,8 @@ def _get_default_locs(self, vmin, vmax): "Returns the default ticks spacing." - nmonths = vmax - vmin + 1 - if nmonths <= 10: - (min_spacing, maj_spacing) = (1, 3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1, 6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1, 12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3, 12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - if self.isminor: - base = min_spacing - offset = ((4 - (vmin-1) % 4) % base) - else: - base = maj_spacing - offset = ((4 - (vmin-1) % 4) % 4) - return N.arange(vmin+offset, vmax+1, base) - + return _generic_get_default_locs(self, vmin, vmax, _Monthly_BreakDown) + #............................................................................... class TimeSeries_DailyLocator(TimeSeries_DateLocator): "Locates the ticks along an axis controlled by a daily DateArray." @@ -298,74 +612,12 @@ base=1, quarter=1, month=1, day=1): TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 self._cacheddates = None def _get_default_locs(self, vmin, vmax): "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - # - if span <= daysperyear//12: - minor = default - major = default[(dates.day == 1)] - elif span <= daysperyear//3: - minor = default - major = default[(dates.day == 1)] - elif span <= 1.5 * daysperyear: - monthstart = (dates.day == 1) - minor = default[(dates.day_of_week == 1)] - major = default[monthstart] - elif span <= 3 * daysperyear: - quarterstart = (dates.day == 1) & (dates.month % 3 == 1) - minor = default[(dates.day == 1)] - major = default[quarterstart] - elif span <= 11 * daysperyear: - quarterstart = (dates.day == 1) & (dates.month % 3 == 1) - minor = default[quarterstart] - major = default[(dates.day_of_year == 1)] - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.day_of_year == 1) - minor = default[annual & (dates.years % min_anndef == 0)] - major = default[annual & (dates.years % maj_anndef == 0)] - if self.isminor: - return minor - return major + return _generic_get_default_locs(self, vmin, vmax, _Daily_BreakDown) - def __call__(self): - 'Return the locations of the ticks' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d,m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0,-1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - #............................................................................... class TimeSeries_YearLocator(TimeSeries_DateLocator): """Locates ticks along a Date axis, for each (multiple of) year. @@ -516,7 +768,11 @@ retval = '' return retval - +def _generic_set_format(self, BreakDownFunc): + dates = self._initialize_dates(self.locs) + format = BreakDownFunc(dates, formatter=True) + return dict([(x,f) for (x,f) in zip(self.locs, format)]) + #............................................................................... class TimeSeries_AnnualFormatter(TimeSeries_DateFormatter): # @@ -526,26 +782,7 @@ dynamic_mode=dynamic_mode,) def _set_format(self, span): - dates = self._initialize_dates(self.locs) - format = N.empty(len(self.locs), dtype="|S2") - format.flat = '' - if span <= 11: - format[:] = "%Y" - elif span < 15: - format[(self.locs % 2 == 0)] = "%Y" - elif span < 50: - format[(self.locs % 5 == 0)] = "%Y" - elif span < 100: - format[(self.locs % 10 == 0)] = "%Y" - elif span < 200: - format[(self.locs % 20 == 0)] = "%Y" - elif span < 400: - format[(self.locs % 25 == 0)] = "%Y" - elif span < 1000: - format[(self.locs % 50 == 0)] = "%Y" - else: - format[(self.locs % 100 == 0)] = "%Y" - self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) + self.formatdict = _generic_set_format(self, _Annual_BreakDown) #............................................................................... class TimeSeries_QuarterlyFormatter(TimeSeries_DateFormatter): @@ -556,21 +793,7 @@ dynamic_mode=dynamic_mode,) def _set_format(self, span): - dates = self._initialize_dates(self.locs) - format = N.empty(len(self.locs), dtype="|S7") - format.flat = '' - (years,quarters) = divmod(self.locs-1, 4) - if span <= 3*4: - yearchange = (self.locs % 4 == 1) - format[:] = "Q%q" - format[(quarters == 0)] = "Q%q\n%Y" - format[0] = "Q%q\n%Y" - elif span <= 11*4: - format[(years % 2 == 1) & (quarters == 0)] = "%Y" - else: - format[(years % 5 == 4)] = "%Y" - self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) - + self.formatdict = _generic_set_format(self, _Quarterly_BreakDown) #............................................................................... class TimeSeries_MonthlyFormatter(TimeSeries_DateFormatter): @@ -581,21 +804,7 @@ dynamic_mode=dynamic_mode,) # def _set_format(self, span): - dates = self._initialize_dates(self.locs) - yearchange = (self.locs % 12 == 1) - format = N.empty(len(self.locs), dtype="|S6") - format.flat = '' - if span <= 1.5 * 12: - format[:] = "%b" - format[yearchange] = "%b\n%Y" - format[0] = "%b\n%Y" - elif span <= 3*12: - format[(dates.month % 2 == 1)] = "%b" - format[yearchange] = "%b\n%Y" - else: - format[yearchange] = "%Y" - self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) - + self.formatdict = _generic_set_format(self, _Monthly_BreakDown) #............................................................................... class TimeSeries_DailyFormatter(TimeSeries_DateFormatter): @@ -604,47 +813,10 @@ TimeSeries_DateFormatter.__init__(self, freq, minor_locator=minor_locator, dynamic_mode=dynamic_mode,) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 # def _set_format(self, span): - dayperyear = self.daysinyear - dates = self._initialize_dates(self.locs) - yearchange = (dates.day_of_year == 1) - format = N.empty(len(self.locs), dtype="|S8") - format.flat = '' - if span <= dayperyear // 12: - format[:] = '%d' - format[(dates.day == 1)] = '\n%b' - format[yearchange] = '\n%b\n%Y' - format[0] = '\n%b\n%Y' - elif span <= dayperyear // 4: - format[(dates.day_of_week == 1)] = '%d' - format[(dates.day == 1)] = '\n%b' - format[yearchange] = '\n%b\n%Y' - elif span <= 1.5 * dayperyear: - monthweekchange = (dates.day_of_week == 0) | (dates.day == 1) - format[monthweekchange] = '\n%b' - format[yearchange] = '\n%b\n%Y' - elif span <= 3 * dayperyear: - quarterchange = (dates.months % 3 == 1) & (dates.day == 1) - format[quarterchange] = '%b' - format[yearchange] = '\n%Y' - else: - format[:] = '%Y' - self.formatdict = dict([(x,f) for (x,f) in zip(self.locs, format)]) - + self.formatdict = _generic_set_format(self, _Daily_BreakDown) -# Monthly: -# if span <= 1.5 * 12: '%b' on minor ticks, '%Y' on major ticks -#elif span <= 3 * 12 : '%b' every even month on minor ticks, '%Y' on major -#elif span <= 11 * 12 : '%Y' on major -# Daily: - - - #####-------------------------------------------------------------------------- #---- --- TimeSeries plots --- #####-------------------------------------------------------------------------- From scipy-svn at scipy.org Wed Feb 21 16:44:40 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 21 Feb 2007 15:44:40 -0600 (CST) Subject: [Scipy-svn] r2739 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070221214440.063CA39C044@new.scipy.org> Author: mattknox_ca Date: 2007-02-21 15:44:34 -0600 (Wed, 21 Feb 2007) New Revision: 2739 Modified: trunk/Lib/sandbox/timeseries/io/fame/core.py Log: lots of tweaking Modified: trunk/Lib/sandbox/timeseries/io/fame/core.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-21 17:54:09 UTC (rev 2738) +++ trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-21 21:44:34 UTC (rev 2739) @@ -176,23 +176,6 @@ if hasattr(key, 'upper'): key = key.upper() super(CaseInsensitiveDict, self).__setitem__(key, item) - -def _single_or_multi_func(dbkey, name, func, *args, **kwargs): - if isinstance(name, str): - single_obj = True - name = [name] - else: - single_obj = False - - result = {} - for n in name: - result[n] = func(dbkey, n, *args, **kwargs) - - if single_obj: - return result.values()[0] - - return result - def _famedate_to_tsdate(fame_date, freqstr): "convert integer fame date to a timeseries Date" value = fame_date + date_value_adjust[ts.freq_fromstr(freqstr)] @@ -292,31 +275,31 @@ """read specified object(s) from database :Parameters: - - `name` (string or list of strings) : names of objects that will be - read from the database + - `name` (string or list of strings) : names of objects that will be + read from the database - - `start_date` (int, *[None]*) : Applies only when reading time series. - If specified, only data points on or after `start_date` will be read. - If None, data will be read from the first value of the series. - - `end_date` (int, *[None]*) : Applies only when reading time series. - If specified, only data points on or before `end_date` will be read. - If None, data will be read to the last value of the series. - - `start_case` (int, *[None]*) : Applies only when reading case series. - If specified, only data points on or after `start_case` will be read. - If None, data will be read starting from case index 1 - - `end_case` (int, *[None]*) : Applies only when reading case series. - If specified, only data points on or before `end_case` will be read. - If None, data will be read to the last value of the series. - - `max_string_len` (int, *[65]*) : Applies only when readings strings - or series of strings. This is the maximum length of string that can - be read. Lower values result in less memory usage, so you should - specify this as low as is reasonable for your data. + - `start_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or after `start_date` will be read. + If None, data will be read from the first value of the series. + - `end_date` (int, *[None]*) : Applies only when reading time series. + If specified, only data points on or before `end_date` will be read. + If None, data will be read to the last value of the series. + - `start_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or after `start_case` will be read. + If None, data will be read starting from case index 1 + - `end_case` (int, *[None]*) : Applies only when reading case series. + If specified, only data points on or before `end_case` will be read. + If None, data will be read to the last value of the series. + - `max_string_len` (int, *[65]*) : Applies only when readings strings + or series of strings. This is the maximum length of string that can + be read. Lower values result in less memory usage, so you should + specify this as low as is reasonable for your data. :Return: - if `name` is a list of strings: - case insensitive dictionary of the objects - if `name` is a single string: - object from database that is stored as `name`""" + if `name` is a list of strings: + case insensitive dictionary of the objects + if `name` is a single string: + object from database that is stored as `name`""" isSingle = False if isinstance(name, str): @@ -439,8 +422,35 @@ return items.values()[0] return items +#.............................................................................. + def write_dict(self, objdict, + overwrite=False, assume_exists=False, + start_date=None, end_date=None, + zero_represents=1, start_case=None, end_case=None): + """for each key, value pair in the dictionary `objdict` write value to +the database as key, as appropriate type (calls FameDb.write on +each key, value pair) - +:Parameters: + - `objdict` (dict) : dictionary of objects to be written. Object names + for keys and objects to be written for values + - `overwrite` (boolean, *[False]*) : See documentation for write_tser and + write_cser + - `assume_exists` (boolean, *[False]*) : See documentation for write_tser + and write_cser + - `start_date` (Date, *[None]*) : See documentation for write_tser + - `end_date` (Date, *[None]*) : See documentation for write_tser + - `zero_represents` (int, *[1]*) : See documentation for write_cser + - `start_case` (int, *[None]*) : See documentation for write_cser + - `end_case` (int, *[None]*) : See documentation for write_cser +""" + for key, obj in objdict.iteritems(): + self.write(key, obj, + overwrite=overwrite, assume_exists=assume_exists, + start_date=start_date, end_date=end_date, + zero_represents=zero_represents, + start_case=start_case, end_case=end_case) +#.............................................................................. def write_tser_dict(self, objdict, overwrite=False, assume_exists=False, start_date=None, end_date=None): @@ -449,26 +459,18 @@ value pair) :Parameters: - - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object - names for keys and TimeSeries objects for values - - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in objects in `objdict` will be given priority over pre-existing data in - the db where there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `start_date` (Date, *[None]*) : If None, data will be written from the start of - the series. If specified, only data points on or after start_date will be written. - - `end_date` (Date, *[None]*) : If None, data will be written until the end of - the series. If specified, only data points on or before end_date will be written. + - `objdict` (dict) : dictionary of TimeSeries objects to be written. Object + names for keys and TimeSeries objects for values + - `overwrite` (boolean, *[False]*) : See documentation for write_tser + - `assume_exists` (boolean, *[False]*) : See documentation for write_tser + - `start_date` (Date, *[None]*) : See documentation for write_tser + - `end_date` (Date, *[None]*) : See documentation for write_tser """ for key, obj in objdict.iteritems(): self.write_tser(key, obj, overwrite=overwrite, assume_exists=assume_exists, start_date=start_date, end_date=end_date) - - +#.............................................................................. def write_cser_dict(self, objdict, overwrite=False, assume_exists=False, zero_represents=1, start_case=None, end_case=None): @@ -477,62 +479,86 @@ value pair) :Parameters: - - `objdict` (dict) : dictionary of arrays to be written as Case Series. - Object names for keys and arrays for values - - `overwrite (boolean, *[False]*) : If True, if the key exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in objects in `objdict` will be given priority over pre-existing data in - the db where there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in - the array represents - - `start_case` (int, *[None]*) : If None, data will be written from the start of - the array. If specified, only data points on or after start_case will be written. - - `end_case` (int, *[None]*) : If None, data will be written until the end of - the array. If specified, only data points on or before end_case will be written. + - `objdict` (dict) : dictionary of arrays to be written as Case Series. + Object names for keys and arrays for values + - `overwrite` (boolean, *[False]*) : See documentation for write_cser + - `assume_exists` (boolean, *[False]*) : See documentation for write_cser + - `zero_represents` (int, *[1]*) : See documentation for write_cser + - `start_case` (int, *[None]*) : See documentation for write_cser + - `end_case` (int, *[None]*) : See documentation for write_cser """ for key, obj in objdict.iteritems(): self.write_cser(key, obj, overwrite=overwrite, assume_exists=assume_exists, zero_represents=zero_represents, start_case=start_case, end_case=end_case) - +#.............................................................................. def write_scalar_dict(self, objdict): """for each key, value pair in the dictionary `objdict` write value to the database as key, as a scalar (calls FameDb.write_scalar on each key, value pair) :Parameters: - - `objdict` (dict) : dictionary of items to be written as scalars. - Object names for keys and scalar items for values + - `objdict` (dict) : dictionary of items to be written as scalars. + Object names for keys and scalar items for values """ for key, obj in objdict.iteritems(): self.write_scalar(key, obj) +#.............................................................................. + def write(self, name, pyobj, + overwrite=False, assume_exists=False, + start_date=None, end_date=None, + zero_represents=1, start_case=None, end_case=None): + """wrapper for write_tser, write_cser, and write_scalar which chooses +appropriate method by inspecting `pyobj` - +:Parameters: + - `name` (string) : database key that the object will be written to + - `pyobj` (object) : any valid object that can be written by write_scalar, + write_tser, or write_cser + - `overwrite` (boolean, *[False]*) : See documentation for write_tser and + write_cser + - `assume_exists` (boolean, *[False]*) : See documentation for write_tser + and write_cser + - `start_date` (Date, *[None]*) : See documentation for write_tser + - `end_date` (Date, *[None]*) : See documentation for write_tser + - `zero_represents` (int, *[1]*) : See documentation for write_cser + - `start_case` (int, *[None]*) : See documentation for write_cser + - `end_case` (int, *[None]*) : See documentation for write_cser +""" + if isinstance(pyobj, ts.TimeSeries): + self.write_tser(name, pyobj, overwrite=overwrite, + assume_exists=assume_exists, + start_date=start_date, end_date=end_date) + elif isinstance(pyobj, numpy.ndarray) and pyobj.ndim == 1: + self.write_cser(name, pyobj, overwrite=overwrite, + assume_exists=assume_exists, + zero_represents=zero_represents, + start_case=start_case, end_case=end_case) + else: + self.write_scalar(name, pyobj) +#.............................................................................. def write_tser(self, name, tser, overwrite=False, assume_exists=False, start_date=None, end_date=None): """write `tser` to the database as `name` as a time series. :Parameters: - - `name` (string) : database key that the object will be written to - - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates. - Use fill_missing_dates first on your series if you suspect this is the situation. - TimeSeries must be 1-dimensional - - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in `tser` will be given priority over pre-existing data in the db where - there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `start_date` (Date, *[None]*) : If None, data will be written from the start of - `tser`. If specified, only data points on or after start_date will be written. - - `end_date` (Date, *[None]*) : If None, data will be written until the end of - `tser`. If specified, only data points on or before end_date will be written. + - `name` (string) : database key that the object will be written to + - `tser` (TimeSeries) : TimeSeries object to be written. Cannot have missing dates. + Use fill_missing_dates first on your series if you suspect this is the situation. + TimeSeries must be 1-dimensional + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `tser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `start_date` (Date, *[None]*) : If None, data will be written from the start of + `tser`. If specified, only data points on or after start_date will be written. + - `end_date` (Date, *[None]*) : If None, data will be written until the end of + `tser`. If specified, only data points on or before end_date will be written. """ if not isinstance(tser, ts.TimeSeries): @@ -601,27 +627,29 @@ end_index -= date_value_adjust[towrite.freq] cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_index, end_index, fame_type, fame_freq) - - def write_cser(self, name, cser, overwrite=False, assume_exists=False, zero_represents=1, start_case=None, end_case=None): +#.............................................................................. + def write_cser(self, name, cser, + overwrite=False, assume_exists=False, + zero_represents=1, start_case=None, end_case=None): """write `cser` to the database as `name` as a case series. :Parameters: - - `name` (string) : database key that the object will be written to - - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be - written. If `cser` is a MaskedArray, then masked values will be written as ND. - - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it - will be overwritten. If False, data will be added to series that already exist - (data in `cser` will be given priority over pre-existing data in the db where - there is overlap) - - `assume_exists` (boolean, *[False]*) : If True, an error will be - raised if the series does not exist. If False, the series will be - created if it does not exist already. - - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in - the array represents - - `start_case` (int, *[None]*) : If None, data will be written from the start of - `cser`. If specified, only data points on or after start_case will be written. - - `end_case` (int, *[None]*) : If None, data will be written until the end of - `cser`. If specified, only data points on or before end_case will be written. + - `name` (string) : database key that the object will be written to + - `cser` (ndarray) : 1-dimensional ndarray (or subclass of ndarray) object to be + written. If `cser` is a MaskedArray, then masked values will be written as ND. + - `overwrite (boolean, *[False]*) : If True, if `name` exists in the database it + will be overwritten. If False, data will be added to series that already exist + (data in `cser` will be given priority over pre-existing data in the db where + there is overlap) + - `assume_exists` (boolean, *[False]*) : If True, an error will be + raised if the series does not exist. If False, the series will be + created if it does not exist already. + - `zero_represents` (int, *[1]*) : the case index for FAME that index zero in + the array represents + - `start_case` (int, *[None]*) : If None, data will be written from the start of + `cser`. If specified, only data points on or after start_case will be written. + - `end_case` (int, *[None]*) : If None, data will be written until the end of + `cser`. If specified, only data points on or before end_case will be written. """ if not isinstance(cser, numpy.ndarray): @@ -693,8 +721,7 @@ fame_data = fame_data.astype(newType) cfame.write_series(self.dbkey, name, fame_data, fame_mask, start_case, end_case, fame_type, fame_freq) - - +#.............................................................................. def write_scalar(self, name, scalar): """write `scalar` to the database as `name` as a scalar object. If an object already exists in the database named as `name` then it is @@ -743,7 +770,21 @@ cf_write_namelist(self.dbkey, name, fame_data) else: cf_write_scalar(self.dbkey, name, fame_data, fame_type) - +#.............................................................................. + def delete_obj(self, name, must_exist=True): + """Deletes the specified object(s) from the database + +:Parameters: + - `name` (string of list of strings) : name of object(s) to delete from + database + - `must_exist` (boolean, *[True]*) : If True, an error will be raised if + you try to delete an object that does not exists. If False, deletion + will only be attempted for objects that actually exist, and other + entries will be ignored. +""" + if isinstance(name, str): name = [name] + [cf_delete_obj(self.dbkey, n) for n in name if must_exist or self.obj_exists(n)] + def _create_obj(self, name, cls, type, freq=None, basis=None, observed=None): """create object in database with specified attributes as `name`. @@ -769,7 +810,7 @@ if observed is None: observed = HOBEND cf_create(self.dbkey, name, cls, freq, type, basis, observed) - + def initialize_obj(self, name, pyobj): """initialize object of appropriate type in database based on the python object `pyobj` as `name`. Does not write any data to the @@ -789,25 +830,129 @@ fame_params['basis'], fame_params['observed']) + def rename_obj(self, name, new_name): + """rename fame object in database""" + cf_rename_obj(self.dbkey, name, new_name) + + def copy_obj(self, target_db, source_name, target_name=None): + """copy fame object to another destination""" + if target_name is None: target_name = source_name + cf_copy(self.dbkey, target_db.dbkey, source_name, target_name) +#.............................................................................. + def set_obj_desc(self, name, desc): + "set 'description' attribute of object in database" + cf_set_obj_desc(self.dbkey, name, desc) + def set_obj_doc(self, name, doc): + "set 'documentation' attribute of object in database" + cf_set_obj_doc(self.dbkey, name, doc) + + def set_obj_basis(self, name, basis): + "set 'basis' attribute of object in database" + basis = translate_basis(basis) + cf_set_obj_basis(self.dbkey, name, basis) + + def set_obj_observed(self, name, observed): + "set 'observed' attribute of object in database" + observed = translate_observed(observed) + cf_set_obj_observed(self.dbkey, name, observed) +#.............................................................................. + def _whats(self, name): + """Preforms a fame "whats" command on the provided name + +Note: Returns FAME constants which are not directly interpretable +in the context of the timeseries module. For this reason, it is +recommended that you use the obj_* methods to retrieve the desired +information about an object. +""" + return cf_whats(self.dbkey, name) + + def __ser_date(self, name, date_type): + """helper method for start_date and end_date""" + obj_sz = self.obj_size(name) + fame_freq = obj_sz['freq'] + if fame_freq == 0: return None + + try: + ts_freq = freq_map[obj_sz['freq']] + except KeyError: + raise DBError("unsupported FAME frequency: %i", fame_freq) + + if obj_sz[date_type+'_year'] == -1: return None + + annDate = ts.Date(freq='A', year=obj_sz[date_type+'_year']) + return annDate.asfreq(ts_freq, relation='BEFORE') + (obj_sz[date_type+'_period'] - 1) + + def obj_size(self, name): + """basic information about the size of an object in a database""" + return cf_obj_size(self.dbkey, name) + + def obj_desc(self, name): + """get desc attribute for an object""" + return self._whats(name)['desc'] + + def obj_doc(self, name): + """get doc attribute for an object""" + return self._whats(name)['doc'] + + def obj_exists(self, name): + return cf_exists(self.dbkey, name) + + def obj_freq(self, name): + """get frequency of a FAME time series object in the database""" + obj_sz = self.obj_size(name) + fame_freq = obj_sz['freq'] + if fame_freq == 0: return None + return freq_map[obj_sz['freq']] + + def obj_basis(self, name): + """get basis attribute of a FAME time series object in the database""" + return self._whats(name)['basis'] + + def obj_observed(self, name): + """get observed attribute of a FAME time series object in the database""" + return observed_map[self._whats(name)['observ']] + + def obj_start_date(self, name): + """get start_date of a FAME time series object""" + return self.__ser_date(name, 'start') + + def obj_end_date(self, name): + """get end_date of a FAME time series object""" + return self.__ser_date(name, 'end') + + def obj_created(self, name): + "get 'created' attribute of object in database" + fame_date = cf_get_obj_attr(self.dbkey, name, "CREATED") + return _famedate_to_tsdate(fame_date, 's') + + def obj_modified(self, name): + "get 'modified' attribute of object in database" + fame_date = cf_get_obj_attr(self.dbkey, name, "MODIFIED") + return _famedate_to_tsdate(fame_date, 's') +#.............................................................................. def db_desc(self): "get 'description' attribute of database" return cf_get_db_attr(self.dbkey, "DESC") - + def db_doc(self): "get 'doc' attribute of database" return cf_get_db_attr(self.dbkey, "DOC") - + def db_created(self): "get 'created' attribute of database" fame_date = cf_get_db_attr(self.dbkey, "CREATED") return _famedate_to_tsdate(fame_date, 's') - + def db_modified(self): "get 'modified' attribute of database" fame_date = cf_get_db_attr(self.dbkey, "MODIFIED") return _famedate_to_tsdate(fame_date, 's') + def db_is_open(self): + "returns True if database is open. False otherwise" + return cf_get_db_attr(self.dbkey, "ISOPEN") + def set_db_desc(self, desc): "set description attribute of database" cf_set_db_desc(self.dbkey, desc) @@ -815,11 +960,7 @@ def set_db_doc(self, doc): "set doc attribute of database" cf_set_db_doc(self.dbkey, doc) - - def db_is_open(self): - "returns True if database is open. False otherwise" - return cf_get_db_attr(self.dbkey, "ISOPEN") - +#.............................................................................. def wildlist(self, exp, wildonly=False): """performs a wildlist lookup on the database, using Fame syntax ("?" and "^"), returns a normal python list of strings""" @@ -833,7 +974,7 @@ res = ["".join(regex.match(res[i]).groups()) \ for i in range(len(res))] return res - +#.............................................................................. def close(self): """Closes the database. Changes will be posted.""" if self.db_is_open(): @@ -846,121 +987,7 @@ """Discard any changes made to the database since it was last opened or posted.""" cf_restore(self.dbkey) - def obj_exists(self, name): - return cf_exists(self.dbkey, name) - - def delete_obj(self, name, must_exist=True): - """Deletes the specified object(s) from the database""" - if isinstance(name, str): name = [name] - [cf_delete_obj(self.dbkey, n) for n in name if must_exist or self.obj_exists(n)] - def obj_size(self, name): - """basic information about the size of an object(s) in a database""" - return _single_or_multi_func(self.dbkey, name, cf_obj_size) - - def obj_freq(self, name): - """get frequency of a FAME time series object in the database""" - obj_sz = self.obj_size(name) - fame_freq = obj_sz['freq'] - if fame_freq == 0: return None - return freq_map[obj_sz['freq']] - - def __ser_date(self, name, date_type): - """helper method for start_date and end_date""" - obj_sz = self.obj_size(name) - fame_freq = obj_sz['freq'] - if fame_freq == 0: return None - - try: - ts_freq = freq_map[obj_sz['freq']] - except KeyError: - raise DBError("unsupported FAME frequency: %i", fame_freq) - - if obj_sz[date_type+'_year'] == -1: return None - - annDate = ts.Date(freq='A', year=obj_sz[date_type+'_year']) - return annDate.asfreq(ts_freq, relation='BEFORE') + (obj_sz[date_type+'_period'] - 1) - - def obj_start_date(self, name): - """get start_date of a FAME time series object""" - return self.__ser_date(name, 'start') - - def obj_end_date(self, name): - """get end_date of a FAME time series object""" - return self.__ser_date(name, 'end') - - def obj_created(self, name): - "get 'created' attribute of object in database" - fame_date = cf_get_obj_attr(self.dbkey, name, "CREATED") - return _famedate_to_tsdate(fame_date, 's') - - def obj_modified(self, name): - "get 'modified' attribute of object in database" - fame_date = cf_get_obj_attr(self.dbkey, name, "MODIFIED") - return _famedate_to_tsdate(fame_date, 's') - - def set_obj_desc(self, name, desc): - "set 'description' attribute of object in database" - cf_set_obj_desc(self.dbkey, name, desc) - - def set_obj_doc(self, name, doc): - "set 'documentation' attribute of object in database" - cf_set_obj_doc(self.dbkey, name, doc) - - def set_obj_basis(self, name, basis): - "set 'basis' attribute of object in database" - basis = translate_basis(basis) - cf_set_obj_basis(self.dbkey, name, basis) - - def set_obj_observed(self, name, observed): - "set 'observed' attribute of object in database" - observed = translate_observed(observed) - cf_set_obj_observed(self.dbkey, name, observed) - - def _whats(self, name): - """Preforms a fame "whats" command on the provided name(s) - -Note: Returns FAME constants which are not directly interpretable -in the context of the timeseries module. For this reason, it is -recommended that you use the obj_* methods to retrieve the desired -information about an object. -""" - return _single_or_multi_func(self.dbkey, name, cf_whats) - - def obj_desc(self, name): - """get desc attribute for an object""" - return self._whats(name)['desc'] - - def obj_doc(self, name): - """get doc attribute for an object""" - return self._whats(name)['doc'] - - def rename_obj(self, name, new_name): - """rename fame object(s) in database""" - if isinstance(name, str): name = [name] - if isinstance(new_name, str): new_name = [new_name] - - if len(name) != len(new_name): - raise ValueError("number of original names does not match " + \ - "number of new names") - - for n, o in zip(name, new_name): - cf_rename_obj(self.dbkey, n, o) - - def copy_obj(self, target_db, source_name, target_name=None): - """copy fame object(s) to another destination""" - if target_name is None: target_name = source_name - if isinstance(source_name, str): source_name = [source_name] - if isinstance(target_name, str): target_name = [target_name] - - if len(source_name) != len(target_name): - raise ValueError("number of source names does not match " + \ - "number of target names") - - for s, t in zip(source_name, target_name): - cf_copy(self.dbkey, target_db.dbkey, s, t) - - class cFameCall: """wrapper for cfame functions that acquires and releases a resource lock. This is needed because the Fame C api is not thread safe.""" From scipy-svn at scipy.org Wed Feb 21 16:45:03 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 21 Feb 2007 15:45:03 -0600 (CST) Subject: [Scipy-svn] r2740 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070221214503.AF20539C044@new.scipy.org> Author: mattknox_ca Date: 2007-02-21 15:45:00 -0600 (Wed, 21 Feb 2007) New Revision: 2740 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: added more tests Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-21 21:44:34 UTC (rev 2739) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-21 21:45:00 UTC (rev 2740) @@ -9,6 +9,7 @@ __revision__ = "$Revision: 2578 $" __date__ = '$Date: 2007-01-17 14:25:10 -0500 (Wed, 17 Jan 2007) $' +import os import numpy as N from numpy import bool_, complex_, float_, int_, object_ import numpy.core.fromnumeric as fromnumeric @@ -83,6 +84,9 @@ data['scalars']['boolean'] = True for f in data['dates']: data['scalars']['date_'+f] = data['dates'][f] + +_desc = "my desc\nline 2" +_doc = "my doc\nline 2" class test_write(NumpyTestCase): @@ -108,7 +112,10 @@ "_test_overwrite_cser", "_test_assume_exists_cser", "_test_dict_cser", "_test_whats", "_test_exists", "_test_delete", - "_test_wildlist", "_test_restore"] + "_test_wildlist", "_test_restore", + "_test_db_attribs", "_test_initialize_obj_and_post", + "_test_copy_rename", "_test_obj_attribs", + "_test_misc_funcs"] for t in tests: print t @@ -357,21 +364,21 @@ def _test_whats(self): "test whats method" # just make sure it doesn't crash for now - what_dict = self.db.whats('$tser_float32') + what_dict = self.db._whats('$tser_float32') def _test_exists(self): "test exists method" - assert(self.db.exists('$cser_float32')) - assert(not self.db.exists('$fake_series')) + assert(self.db.obj_exists('$cser_float32')) + assert(not self.db.obj_exists('$fake_series')) def _test_delete(self): "test delete method" - assert(self.db.exists('$cser_1')) - assert(self.db.exists('$cser_2')) - self.db.delete(['$cser_1', '$cser_2']) - assert(not self.db.exists('$cser_1')) - assert(not self.db.exists('$cser_2')) - self.db.delete('$cser_1', must_exist=False) + assert(self.db.obj_exists('$cser_1')) + assert(self.db.obj_exists('$cser_2')) + self.db.delete_obj(['$cser_1', '$cser_2']) + assert(not self.db.obj_exists('$cser_1')) + assert(not self.db.obj_exists('$cser_2')) + self.db.delete_obj('$cser_1', must_exist=False) def _test_wildlist(self): "test wildlist method" @@ -387,11 +394,111 @@ self.db.close() self.db = fame.FameDb("testdb.db",'s') - self.db.delete('$tser_float32') - assert(not self.db.exists('$tser_float32')) + self.db.delete_obj('$tser_float32') + assert(not self.db.obj_exists('$tser_float32')) self.db.restore() - assert(self.db.exists('$tser_float32')) + assert(self.db.obj_exists('$tser_float32')) + def _test_db_attribs(self): + "test setting and retrieving database attributes" + self.db.set_db_desc(_desc) + self.db.set_db_doc(_doc) + + created = self.db.db_created() + modified = self.db.db_modified() + desc = self.db.db_desc() + doc = self.db.db_doc() + + assert(abs(ts.thisday('s') - created) < 100) + assert(abs(ts.thisday('s') - modified) < 100) + assert_equal(desc, _desc) + assert_equal(doc, _doc) + + assert(self.db.db_is_open()) + self.db.close() + assert(not self.db.db_is_open()) + self.db = fame.FameDb("testdb.db",'s') + + def _test_initialize_obj_and_post(self): + """test initializing an object and posting of database""" + self.db.initialize_obj("$postobj", ts.thisday('B')) + exist_script = "from timeseries.io import fame;" + exist_script += "db = fame.FameDb('testdb.db', 'r');" + exist_script += "print db.obj_exists('$postobj');" + + proc = os.popen('python -c "'+exist_script+'"') + exists = proc.readlines()[0].strip('\n') + proc.close() + + assert_equal(exists, "False") + + self.db.post() + + proc = os.popen('python -c "'+exist_script+'"') + exists = proc.readlines()[0].strip('\n') + proc.close() + + assert_equal(exists, "True") + + def _test_copy_rename(self): + "test copying and renaming an object" + db2 = fame.FameDb("testdb2.db", 'o') + self.db.copy_obj(db2, "$tser_float32", "$copied_obj") + orig_obj = self.db.read("$tser_float32") + copied_obj = db2.read("$copied_obj") + assert_array_equal(orig_obj, copied_obj) + + db2.rename_obj("$copied_obj", "$renamed_obj") + assert(db2.obj_exists("$renamed_obj")) + assert(not db2.obj_exists("$copied_obj")) + + db2.close() + + def _test_obj_attribs(self): + "test getting and setting object attributes" + assert_equal(self.db.obj_freq("$freq_b"), data['freqs']['b'].freq) + + assert_equal(self.db.obj_start_date("$freq_b"), + data['freqs']['b'].start_date) + assert_equal(self.db.obj_end_date("$freq_b"), + data['freqs']['b'].end_date) + + created = self.db.obj_created("$freq_b") + modified = self.db.obj_modified("$freq_b") + + assert(abs(ts.thisday('s') - created) < 100) + assert(abs(ts.thisday('s') - modified) < 100) + + self.db.set_obj_desc("$freq_b", _desc) + self.db.set_obj_doc("$freq_b", _doc) + + desc = self.db.obj_desc("$freq_b") + doc = self.db.obj_doc("$freq_b") + + assert_equal(desc, _desc) + assert_equal(doc, _doc) + + self.db.set_obj_basis("$freq_b", fame.HBSDAY) + assert_equal(self.db.obj_basis("$freq_b"), fame.HBSDAY) + self.db.set_obj_basis("$freq_b", fame.HBSBUS) + assert_equal(self.db.obj_basis("$freq_b"), fame.HBSBUS) + + self.db.set_obj_observed("$freq_b", "END") + assert_equal(self.db.obj_observed("$freq_b"), "ENDING") + + self.db.set_obj_observed("$freq_b", "MAX") + assert_equal(self.db.obj_observed("$freq_b"), "MAXIMUM") + + self.db.set_obj_observed("$freq_b", "AVERAGE") + assert_equal(self.db.obj_observed("$freq_b"), "AVERAGED") + + def _test_misc_funcs(self): + "test FAME functions that aren't database methods" + assert_equal(fame.license_expires().freq, ts.freq_fromstr('D')) + + # just test that this doesn't crash for now + fame.set_option("DBSIZE", "LARGE") + def tearDown(self): self.db.close() From scipy-svn at scipy.org Wed Feb 21 19:43:53 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 21 Feb 2007 18:43:53 -0600 (CST) Subject: [Scipy-svn] r2741 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070222004353.CD48B39C017@new.scipy.org> Author: pierregm Date: 2007-02-21 18:43:49 -0600 (Wed, 21 Feb 2007) New Revision: 2741 Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py Log: yet another attempt to stabilize mpl_timeseries... we're getting close! Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-21 21:45:00 UTC (rev 2740) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-22 00:43:49 UTC (rev 2741) @@ -0,0 +1,829 @@ +""" +Classes to plot TimeSeries w/ matplotlib. + +:author: Pierre GF Gerard-Marchant & Matt Knox +:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com +:date: $Date$ +:version: $Id$ +""" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" +__version__ = '1.0' +__revision__ = "$Revision$" +__date__ = '$Date$' + + +import matplotlib +from matplotlib import pylab, rcParams +from matplotlib.artist import setp +from matplotlib.axes import Subplot, PolarSubplot +from matplotlib.cbook import flatten +from matplotlib.collections import LineCollection +from matplotlib.contour import ContourSet +from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ + DateFormatter +from matplotlib.figure import Figure +from matplotlib.legend import Legend +from matplotlib.mlab import meshgrid +from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ + Locator, FixedLocator +#from matplotlib.transforms import nonsingular + +import numpy as N +import maskedarray as MA + +import timeseries +from timeseries import date_array, Date, DateArray, TimeSeries + +import warnings + +#####--------------------------------------------------------------------------- +#---- --- Matplotlib extensions --- +#####--------------------------------------------------------------------------- + +def add_generic_subplot(figure_instance, *args, **kwargs): + """Generalizes the `add_subplot` figure method to generic subplots. +The specific Subplot object class to add is given through the keywords +`SubplotClass` or `class`. + +:Parameters: + `figure_instance` : Figure object + Figure to which the generic subplot should be attached. + `args` : Misc + Miscellaneous arguments to the subplot. + `kwargs` : Dictionary + Keywords. Same keywords as `Subplot`, with the addition of + - `SubplotClass` : Type of subplot + - `subclass` : Shortcut to `SubplotClass`. + - any keyword required by the `SubplotClass` subclass. + """ + + key = figure_instance._make_key(*args, **kwargs) + #TODO: Find why, sometimes, key is not hashable (even if tuple) + # else, there's a fix below + try: + key.__hash__() + except TypeError: + key = str(key) + # + if figure_instance._seen.has_key(key): + ax = figure_instance._seen[key] + figure_instance.sca(ax) + return ax + # + if not len(args): + return +# if hasattr(args[0], '__array__'): +# fixedargs = args[1:] +# else: +# fixedargs = args + # + SubplotClass = kwargs.pop("SubplotClass", Subplot) + SubplotClass = kwargs.pop("subclass",SubplotClass) + if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): + a = args[0] + assert(a.get_figure() is figure_instance) +# a.set_figure(figure_instance) + else: + ispolar = kwargs.pop('polar', False) + if ispolar: + a = PolarSubplot(figure_instance, *args, **kwargs) + else: + a = SubplotClass(figure_instance, *args, **kwargs) + + figure_instance.axes.append(a) + figure_instance._axstack.push(a) + figure_instance.sca(a) + figure_instance._seen[key] = a + return a + + +def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): + ''' + Ensure the endpoints of a range are not too close together. + + "too close" means the interval is smaller than 'tiny' times + the maximum absolute value. + + If they are too close, each will be moved by the 'expander'. + If 'increasing' is True and vmin > vmax, they will be swapped. + ''' + #TODO: Remove that when matplotlib incorporate it by default + swapped = False + if vmax < vmin: + vmin, vmax = vmax, vmin + swapped = True + if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: + if vmin == 0.0: + vmin = -expander + vmax = expander + else: + vmin -= expander*abs(vmin) + vmax += expander*abs(vmax) + if swapped and not increasing: + vmin, vmax = vmax, vmin + return vmin, vmax + +##### ------------------------------------------------------------------------- +#---- --- Locators --- +##### ------------------------------------------------------------------------- + +def _get_default_annual_spacing(nyears): + """Returns a default spacing between consecutive ticks for annual data.""" + if nyears < 11: + (min_spacing, maj_spacing) = (1, 1) + elif nyears < 20: + (min_spacing, maj_spacing) = (1, 2) + elif nyears < 50: + (min_spacing, maj_spacing) = (1, 5) + elif nyears < 100: + (min_spacing, maj_spacing) = (5, 10) + elif nyears < 200: + (min_spacing, maj_spacing) = (5, 25) + elif nyears < 600: + (min_spacing, maj_spacing) = (10, 50) + else: + factor = nyears // 1000 + 1 + (min_spacing, maj_spacing) = (factor*20, factor*100) + return (min_spacing, maj_spacing) + + +def period_break(dates, period): + """Returns the indices where the given period changes. + +:Parameters: + dates : DateArray + Array of dates to monitor. + period : string + Name of the period to monitor. + """ + current = getattr(dates, period) + previous = getattr(dates-1, period) + return (current - previous).nonzero()[0] + + +def _daily_finder(locs, freqstr, aslocator): + + if freqstr == 'B': + periodsperyear = 261 + elif freqstr == 'D': + periodsperyear = 365 + else: + raise ValueError("unexpected frequency") + + locs = N.asarray(locs) + (vmin, vmax) = locs[[0,-1]] + span = vmax - vmin + 1 + dates = date_array(start_date=Date(freqstr,vmin), + end_date=Date(freqstr, vmax)) + # Initialize the output + if aslocator: + default = N.arange(vmin, vmax+1) + else: #asformatter + format = N.empty(locs.size, dtype="|S8") + format.flat = '' + # Case 1. Less than a month + if span <= (periodsperyear//12 - 2): + month_start = period_break(dates,'month') + if aslocator: + major = default[month_start] + minor = default + else: + year_start = period_break(dates,'year') + format[:] = '%d' + format[month_start] = '%d\n%b' + format[year_start] = '%d\n%b\n%Y' + if year_start.size == 0: + if month_start.size == 0: + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = '%d\n%b\n%Y' + else: + format[month_break[0]] = '%d\n%b\n%Y' + # Case 2. Less than three months + elif span <= periodsperyear//4: + month_start = period_break(dates,'month') + if aslocator: + major = default[month_start] + minor = default + else: + week_start = (dates.day_of_week == 1) + year_start = period_break(dates,'year') + + week_start[0] = False + month_start[0] = False + year_start[0] = False + + format[week_start] = '%d' + format[month_start] = '\n\n%b' + format[year_start] = '\n\n%b\n%Y' + + if year_start.size == 0: + month_break = month_start.nonzero()[0] + if month_break.size == 0: + week_break = week_start.nonzero()[0] + format[week_break[0]] = '\n\n%b\n%Y' + else: + format[month_break[0]] = '\n\n%b\n%Y' + # Case 3. Less than 14 months ............... + elif span <= 1.15 * periodsperyear: + month_start = period_break(dates,'month') + if aslocator: + week_start = period_break(dates, 'week') + minor_idx = (week_start | month_start) + minor_idx[0] = True + major = default[month_start] + minor = default[minor_idx] + else: + year_start = period_break(dates,'year') + month_start[0] = False + year_start[0] = False + format[month_start] = '%b' + format[year_start] = '%b\n%Y' + if not year_start.size: + format[month_break[0]] = '%b\n%Y' + # Case 4. Less than 2.5 years ............... + elif span <= 2.5 * periodsperyear: + year_start = period_break(dates,'year') + if aslocator: + month_start = period_break(dates, 'quarter') + major = default[year_start] + minor = default[month_start] + else: + quarter_start = period_break(dates, 'quarter') + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + # Case 4. Less than 4 years ................. + elif span <= 4 * periodsperyear: + year_start = period_break(dates,'year') + month_start = period_break(dates, 'month') + if aslocator: + major = default[year_start] + minor = default[month_start] + else: + month_break = dates[month_start].month + jan_or_jul = month_start[(month_break == 1 | month_break == 7)] + format[jan_or_jul] = '%b' + format[year_start] = '%b\n%Y' + # Case 5. Less than 11 years ................ + elif span <= 11 * periodsperyear: + year_start = period_break(dates,'year') + if aslocator: + quarter_start = period_break(dates, 'quarter') + major = default[year_start] + minor = default[quarter_start] + else: + format[year_start] = '%Y' + # Case 6. More than 12 years ................ + else: + year_start = period_break(dates,'year') + year_break = dates[year_start].years + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + major_idx = year_start[(year_break % maj_anndef == 0)] + if aslocator: + major = default[major_idx] + minor_idx = year_start[(year_break % min_anndef == 0)] + minor = default[minor_idx] + else: + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) +#............................................................................... +def _monthly_finder(locs, freqstr, aslocator): + if freqstr != 'M': + raise ValueError("unexpected frequency") + periodsperyear = 12 + locs = N.asarray(locs) + (vmin, vmax) = locs[[0,-1]] + (vmin, vmax) = (int(vmin), int(vmax+1)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + year_start = (dates % 12 == 1).nonzero()[0] + #............................................ + if span <= 1.15 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + format[:] = '%b' + format[year_start] = '%b\n%Y' + + if not year_start.size: + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = '%b\n%Y' + #........................ + elif span <= 2.5 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + quarter_start = (dates % 3 == 1).nonzero() + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + #....................... + elif span <= 4 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + jan_or_jul = (dates % 12 == 1) | (dates % 12 == 7) + format[jan_or_jul] = '%b' + format[year_start] = '%b\n%Y' + #........................ + elif span <= 11 * periodsperyear: + if aslocator: + quarter_start = (dates % 3 == 1).nonzero() + major = dates[year_start] + minor = dates[quarter_start] + else: + format[year_start] = '%Y' + #......................... + else: + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + years = dates[year_start]//12 + 1 + major_idx = year_start[(years % maj_anndef == 0)] + if aslocator: + major = dates[major_idx] + minor = dates[year_start[(years % min_anndef == 0)]] + else: + format[major_idx] = '%Y' + #........................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) +#............................................................................... +def _quarterly_finder(locs, freqstr, aslocator): + if freqstr != 'Q': + raise ValueError("unexpected frequency") + periodsperyear = 4 + locs = N.asarray(locs) + (vmin, vmax) = locs[[0,-1]] + (vmin, vmax) = (int(vmin), int(vmax+1)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + year_start = (dates % 4 == 1).nonzero()[0] + #............................................ + if span <= 3.5 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + format[:] = 'Q%q' + format[year_start] = 'Q%q\n%Y' + if not year_start.size: + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = 'Q%q\n%Y' + #............................................ + elif span <= 11 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + format[year_start] = '%Y' + #............................................ + else: + years = dates[year_start]//4 + 1 + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + major_idx = year_start[(years % maj_anndef == 0)] + if aslocator: + major = dates[major_idx] + minor = dates[year_start[(years % min_anndef == 0)]] + else: + print "major_idx",major_idx + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) +#............................................................................... +def _annual_finder(locs, freqstr, aslocator): + if freqstr != 'Q': + raise ValueError("unexpected frequency") + locs = N.asarray(locs) + (vmin, vmax) = locs[[0,-1]] + (vmin, vmax) = (int(vmin), int(vmax+1)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + #............................................ + (min_anndef, maj_anndef) = _get_default_annual_spacing(span) + major_idx = dates % maj_anndef == 0 + if aslocator: + major = dates[major_idx] + minor = dates[(dates % min_anndef == 0)] + else: + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) + +#............................................................................... +class TimeSeries_DateLocator(Locator): + "Locates the ticks along an axis controlled by a DateArray." + + def __init__(self, freq, minor_locator=False, dynamic_mode=True, + base=1, quarter=1, month=1, day=1): + self.freqstr = freq + self.base = base + (self.quarter, self.month, self.day) = (quarter, month, day) + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + #..... + if freq == 'A': + self.finder = _annual_finder + elif freq == 'Q': + self.finder = _quarterly_finder + elif freq == 'M': + self.finder = _monthly_finder + elif freq == 'D': + self.finder = _daily_finder + + def asminor(self): + "Returns the locator set to minor mode." + self.isminor = True + return self + + def asmajor(self): + "Returns the locator set to major mode." + self.isminor = False + return self + + def _get_default_locs(self, vmin, vmax): + "Returns the default locations of ticks." + (minor, major) = self.finder(N.arange(vmin, vmax+1), self.freqstr, True) + if self.isminor: + return minor + return major + + def __call__(self): + 'Return the locations of the ticks.' + self.verify_intervals() + vmin, vmax = self.viewInterval.get_bounds() + if vmax < vmin: + vmin, vmax = vmax, vmin + if self.isdynamic: + locs = self._get_default_locs(vmin, vmax) + else: + base = self.base + (d, m) = divmod(vmin, base) + vmin = (d+1) * base + locs = range(vmin, vmax+1, base) + return locs + + def autoscale(self): + """Sets the view limits to the nearest multiples of base that contain + the data. + """ + self.verify_intervals() + dmin, dmax = self.dataInterval.get_bounds() + locs = self._get_default_locs(dmin, dmax) + (vmin, vmax) = locs[[0, -1]] + if vmin == vmax: + vmin -= 1 + vmax += 1 + return nonsingular(vmin, vmax) + +#####--------------------------------------------------------------------------- +#---- --- Formatter --- +#####--------------------------------------------------------------------------- +class TimeSeries_DateFormatter(Formatter): + """Formats the ticks along a DateArray axis.""" + + def __init__(self, freq, minor_locator=False, dynamic_mode=True,): + self.format = None + self.freqstr = freq + self.locs = [] + self.formatdict = {} + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + #..... + if freq == 'A': + self.finder = _annual_finder + elif freq == 'Q': + self.finder = _quarterly_finder + elif freq == 'M': + self.finder = _monthly_finder + elif freq == 'D': + self.finder = _daily_finder + + def asminor(self): + "Returns the formatter set to minor mode." + self.isminor = True + return self + + def asmajor(self): + "Returns the fromatter set to major mode." + self.isminor = False + return self + + def _set_default_format(self, vmin, vmax): + "Returns the default ticks spacing." + self.formatdict = self.finder(self.locs, self.freqstr, False) + return self.formatdict + + def set_locs(self, locs): + 'Sets the locations of the ticks' + self.locs = locs + if len(self.locs) > 0: + self.verify_intervals() + self._set_default_format(locs[0], locs[-1]) + # + def __call__(self, x, pos=0): + if self.isminor: + fmt = self.formatdict.pop(x, '') + if fmt is not '': + retval = Date(self.freqstr, value=int(x)).strfmt(fmt) + else: + retval = '' + else: + retval = '' + return retval + + + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries plots --- +#####-------------------------------------------------------------------------- +class TimeSeriesPlot(Subplot, object): + """Defines a time series based subclass of Subplot.""" + def __init__(self, fig=None, *args, **kwargs): + """ +Accepts the same keywords as a standard subplot, plus a specific `series` keyword. + +:Parameters: + `fig` : Figure + Base figure. + +:Keywords: + `series` : TimeSeries + Data to plot + + """ + # Retrieve the series ................... + _series = kwargs.pop('series',None) + Subplot.__init__(self,fig,*args,**kwargs) +# # Force fig to be defined ..... +# if fig is None: +# fig = TSFigure(_series) + # Process options ....................... + if _series is not None: + assert hasattr(_series, "dates") + self._series = _series.ravel() + self.xdata = _series.dates + self.freqstr = _series.dates.freqstr + self.xaxis.set_major_locator + + else: + self._series = None + self.xdata = None + self.freqstr = None + self._austoscale = False + # Get the data to plot + self.legendsymbols = [] + self.legendlabels = [] + #............................................ + def set_ydata(self, series=None): + """Sets the base time series.""" + if self._series is not None: + print "WARNING ! Base series is being changed.""" + self._series = series.ravel() + if isinstance(series, TimeSeries): + self.xdata = self.series.dates + #.... + def get_ydata(self): + """Gets the base time series.""" + return self._series + ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') + #............................................ + def _check_plot_params(self,*args): + """Defines the plot coordinates (and basic plotting arguments).""" + remaining = list(args) + # No args ? Use defaults, if any + if len(args) == 0: + if self.xdata is None: + raise ValueError, "No date information available!" + return (self.xdata, self.ydata) + output = [] + while len(remaining) > 0: + a = remaining.pop(0) + # The argument is a format: use default dates/ + if isinstance(a,str): + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, self.ydata, a]) + # The argument is a TimeSeries: use its dates for x + elif isinstance(a, TimeSeries): + (x,y) = (a._dates, a._series) + if len(remaining) > 0 and isinstance(remaining[0], str): + b = remaining.pop(0) + output.extend([x,y,b]) + else: + output.extend([x,y]) + # The argument is a DateArray............ + elif isinstance(a, (Date, DateArray)): + # Force to current freq + if self.freqstr is not None: + if a.freqstr != self.freqstr: + a = a.asfreq(self.freqstr) + # There's an argument after + if len(remaining) > 0: + #...and it's a format string + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata, b]) + #... and it's another date: use the default + elif isinstance(remaining[0], DateArray): + if self.ydata is None: + raise ValueError, "No data information available!" + else: + output.extend([a, self.ydata]) + #... and it must be some data + else: + b = remaining.pop(0) + if len(remaining) > 0: + if isinstance(remaining[0], str): + c = remaining.pop(0) + output.extend([a,b,c]) + else: + output.extend([a,b]) + else: + if self.ydata is None: + raise ValueError, "No data information available!" + # Otherwise.............................. + elif len(remaining) > 0: + if isinstance(remaining[0], str): + b = remaining.pop(0) + if self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a, b]) + elif self.xdata is None: + raise ValueError, "No date information available!" + else: + output.extend([self.xdata, a]) + # Reinitialize the plot if needed ........... + if self.xdata is None: + self.xdata = output[0] + self.freqstr = self.xdata.freqstr + # Force the xdata to the current frequency + elif output[0].freqstr != self.freqstr: + output = list(output) + output[0] = output[0].asfreq(self.freqstr) + return output + #............................................ + def tsplot(self,*parms,**kwargs): + """Plots the data parsed in argument. +This command accepts the same keywords as `matplotlib.plot`.""" + #print "Parameters: %s - %i" % (parms, len(parms)) + parms = self._check_plot_params(*parms) + self.legendlabels.append(kwargs.get('label',None)) + Subplot.plot(self, *parms,**kwargs) + #............................................ + def format_dateaxis(self,maj_spacing=None, min_spacing=None, + strformat="%Y", rotate=True): + """Pretty-formats the date axis (x-axis). + +:Parameters: + `major` : Integer *[5]* + Major tick locator, in years (major tick every `major` years). + `minor` : Integer *[12]* + Minor tick locator, in months (minor ticks every `minor` months). + `strformat` : String *['%Y']* + String format for major ticks ("%Y"). + """ + # Get the locator class ................. + majlocator = TimeSeries_DateLocator(self.freqstr, dynamic_mode=True, + minor_locator=False) + minlocator = TimeSeries_DateLocator(self.freqstr, dynamic_mode=True, + minor_locator=True) + self.xaxis.set_major_locator(majlocator) + self.xaxis.set_minor_locator(minlocator) + # Get the formatter ..................... + majformatter = TimeSeries_DateFormatter(self.freqstr, dynamic_mode=True, + minor_locator=False) + minformatter = TimeSeries_DateFormatter(self.freqstr, dynamic_mode=True, + minor_locator=True) + self.xaxis.set_major_formatter(majformatter) + self.xaxis.set_minor_formatter(minformatter) + #........................................ +# if rcParams['backend'] == 'PS': +# rotate = False +# warnings.warn("dateplot: PS backend detected, rotate disabled") +# if self.is_last_row(): +# if rotate: +# setp(self.get_xticklabels(),rotation=45) + +TSPlot = TimeSeriesPlot + + +#####-------------------------------------------------------------------------- +#---- --- TimeSeries Figures --- +#####-------------------------------------------------------------------------- +class TimeSeriesFigure(Figure): + """Time Series Figure: all subplots share the same time series. + """ + def __init__(self, series=None, **kwargs): + self._series = series + Figure.__init__(self,**kwargs) + fspnum = kwargs.pop('fspnum',None) + if fspnum is not None: + self.add_tsplot(fspnum, series=series) + #......... + def add_tsplot(self, *args, **kwargs): + """Adds a `TimeSeriesPlot` subplot to the figure.""" + kwargs.update(SubplotClass=TimeSeriesPlot, + series=self._series) + return add_generic_subplot(self, *args, **kwargs) + add_plot = add_tsplot +TSFigure = TimeSeriesFigure +#................................................ +def tsfigure(series, **figargs): + """Creates a new `TimeSeriesFigure` object. + +:Parameters: + `series` : TimeSeries object + Input data. + `figargs` : Dictionary + Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. + """ + figargs.update(FigureClass=TSFigure) + figargs.update(series=series) + fig = pylab.figure(**figargs) + return fig + +def add_tsplot(axes, *args, **kwargs): + kwargs.update(SubplotClass=TimeSeriesPlot) + if 'series' not in kwargs.keys(): + kwargs['series'] = None + return add_generic_subplot(axes, *args, **kwargs) +Figure.add_tsplot = add_tsplot + + +def tsplot(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = pylab.ishold() + h = kwargs.pop('hold', None) + if h is not None: + pylab.hold(h) + try: + ret = pylab.gca().add_tsplot(*args, **kwargs) + pylab.draw_if_interactive() + except: + pylab.hold(b) + raise + + pylab.hold(b) + return ret + +################################################################################ +if __name__ == '__main__': + + da = date_array(start_date=Date(freq='M', year=2003, quarter=3, month=1, day=17), + length=51) + ser = timeseries.time_series(MA.arange(len(da)), dates=da) + ser[4] = MA.masked +# ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) + + pylab.figure() + pylab.gcf().add_tsplot(111) + pylab.gca().tsplot(ser, 'ko-') + pylab.gca().format_dateaxis() +# pylab.gca().tsplot(ser_2, 'rs') + pylab.show() + \ No newline at end of file Property changes on: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id From scipy-svn at scipy.org Wed Feb 21 20:58:38 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 21 Feb 2007 19:58:38 -0600 (CST) Subject: [Scipy-svn] r2742 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070222015838.5433039C00F@new.scipy.org> Author: pierregm Date: 2007-02-21 19:58:34 -0600 (Wed, 21 Feb 2007) New Revision: 2742 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py Log: mpl_timeseries: _daily_finder: fixed a pb w/ daily/business freqs Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-22 00:43:49 UTC (rev 2741) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-22 01:58:34 UTC (rev 2742) @@ -161,7 +161,7 @@ return (current - previous).nonzero()[0] -def _daily_finder(locs, freqstr, aslocator): +def _daily_finder(vmin, vmax, freqstr, aslocator): if freqstr == 'B': periodsperyear = 261 @@ -170,16 +170,14 @@ else: raise ValueError("unexpected frequency") - locs = N.asarray(locs) - (vmin, vmax) = locs[[0,-1]] + (vmin, vmax) = (int(vmin), int(vmax)) span = vmax - vmin + 1 dates = date_array(start_date=Date(freqstr,vmin), end_date=Date(freqstr, vmax)) + default = N.arange(vmin, vmax+1) # Initialize the output - if aslocator: - default = N.arange(vmin, vmax+1) - else: #asformatter - format = N.empty(locs.size, dtype="|S8") + if not aslocator: + format = N.empty(default.shape, dtype="|S10") format.flat = '' # Case 1. Less than a month if span <= (periodsperyear//12 - 2): @@ -200,7 +198,7 @@ idx = 0 format[idx] = '%d\n%b\n%Y' else: - format[month_break[0]] = '%d\n%b\n%Y' + format[month_start[0]] = '%d\n%b\n%Y' # Case 2. Less than three months elif span <= periodsperyear//4: month_start = period_break(dates,'month') @@ -210,22 +208,17 @@ else: week_start = (dates.day_of_week == 1) year_start = period_break(dates,'year') - - week_start[0] = False - month_start[0] = False - year_start[0] = False - +# week_start[0] = False +# month_start[0] = False +# year_start[0] = False format[week_start] = '%d' format[month_start] = '\n\n%b' format[year_start] = '\n\n%b\n%Y' - if year_start.size == 0: - month_break = month_start.nonzero()[0] - if month_break.size == 0: - week_break = week_start.nonzero()[0] - format[week_break[0]] = '\n\n%b\n%Y' + if month_start.size == 0: + format[week_start[0]] = '\n\n%b\n%Y' else: - format[month_break[0]] = '\n\n%b\n%Y' + format[month_start[0]] = '\n\n%b\n%Y' # Case 3. Less than 14 months ............... elif span <= 1.15 * periodsperyear: month_start = period_break(dates,'month') @@ -242,7 +235,7 @@ format[month_start] = '%b' format[year_start] = '%b\n%Y' if not year_start.size: - format[month_break[0]] = '%b\n%Y' + format[month_start[0]] = '%b\n%Y' # Case 4. Less than 2.5 years ............... elif span <= 2.5 * periodsperyear: year_start = period_break(dates,'year') @@ -293,14 +286,12 @@ return minor, major else: formatted = (format != '') - return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) + return dict([(d,f) for (d,f) in zip(default[formatted],format[formatted])]) #............................................................................... -def _monthly_finder(locs, freqstr, aslocator): +def _monthly_finder(vmin, vmax, freqstr, aslocator): if freqstr != 'M': raise ValueError("unexpected frequency") periodsperyear = 12 - locs = N.asarray(locs) - (vmin, vmax) = locs[[0,-1]] (vmin, vmax) = (int(vmin), int(vmax+1)) span = vmax - vmin + 1 #............................................ @@ -367,12 +358,10 @@ formatted = (format != '') return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) #............................................................................... -def _quarterly_finder(locs, freqstr, aslocator): +def _quarterly_finder(vmin, vmax, freqstr, aslocator): if freqstr != 'Q': raise ValueError("unexpected frequency") - periodsperyear = 4 - locs = N.asarray(locs) - (vmin, vmax) = locs[[0,-1]] + periodsperyear = 4 (vmin, vmax) = (int(vmin), int(vmax+1)) span = vmax - vmin + 1 #............................................ @@ -420,11 +409,9 @@ formatted = (format != '') return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) #............................................................................... -def _annual_finder(locs, freqstr, aslocator): +def _annual_finder(vmin, vmax, freqstr, aslocator): if freqstr != 'Q': - raise ValueError("unexpected frequency") - locs = N.asarray(locs) - (vmin, vmax) = locs[[0,-1]] + raise ValueError("unexpected frequency") (vmin, vmax) = (int(vmin), int(vmax+1)) span = vmax - vmin + 1 #............................................ @@ -465,7 +452,7 @@ self.finder = _quarterly_finder elif freq == 'M': self.finder = _monthly_finder - elif freq == 'D': + elif freq in 'BD': self.finder = _daily_finder def asminor(self): @@ -480,7 +467,7 @@ def _get_default_locs(self, vmin, vmax): "Returns the default locations of ticks." - (minor, major) = self.finder(N.arange(vmin, vmax+1), self.freqstr, True) + (minor, major) = self.finder(vmin, vmax, self.freqstr, True) if self.isminor: return minor return major @@ -534,7 +521,7 @@ self.finder = _quarterly_finder elif freq == 'M': self.finder = _monthly_finder - elif freq == 'D': + elif freq in 'BD': self.finder = _daily_finder def asminor(self): @@ -549,7 +536,7 @@ def _set_default_format(self, vmin, vmax): "Returns the default ticks spacing." - self.formatdict = self.finder(self.locs, self.freqstr, False) + self.formatdict = self.finder(vmin, vmax, self.freqstr, False) return self.formatdict def set_locs(self, locs): @@ -814,10 +801,10 @@ ################################################################################ if __name__ == '__main__': - da = date_array(start_date=Date(freq='M', year=2003, quarter=3, month=1, day=17), - length=51) + da = date_array(start_date=Date(freq='B', year=2003, quarter=3, month=1, day=17), + length=10) ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked +# ser[4] = MA.masked # ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) pylab.figure() From scipy-svn at scipy.org Thu Feb 22 03:46:23 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 02:46:23 -0600 (CST) Subject: [Scipy-svn] r2743 - trunk/Lib/sandbox/image Message-ID: <20070222084623.453B539C0C9@new.scipy.org> Author: rkern Date: 2007-02-22 02:46:08 -0600 (Thu, 22 Feb 2007) New Revision: 2743 Added: trunk/Lib/sandbox/image/setup.py Removed: trunk/Lib/sandbox/image/setup_image.py Modified: trunk/Lib/sandbox/image/__init__.py trunk/Lib/sandbox/image/color.py trunk/Lib/sandbox/image/transforms.py Log: Update the image package in the sandbox so it builds and runs. Also added CIE L*u*v* colorspace transformation functions. Modified: trunk/Lib/sandbox/image/__init__.py =================================================================== --- trunk/Lib/sandbox/image/__init__.py 2007-02-22 01:58:34 UTC (rev 2742) +++ trunk/Lib/sandbox/image/__init__.py 2007-02-22 08:46:08 UTC (rev 2743) @@ -2,7 +2,5 @@ # image - Image Processing Tools # -from info_image import __doc__ - from color import * from transforms import * Modified: trunk/Lib/sandbox/image/color.py =================================================================== --- trunk/Lib/sandbox/image/color.py 2007-02-22 01:58:34 UTC (rev 2742) +++ trunk/Lib/sandbox/image/color.py 2007-02-22 08:46:08 UTC (rev 2743) @@ -1,6 +1,6 @@ -import numpy as sb -import scipy +import numpy as np +from scipy import linalg import os # Various utilities and data for color processing @@ -25,7 +25,7 @@ [0.177, 0.813, 0.011], [0.000, 0.010, 0.990]] -rgbcie_from_xyz = scipy.linalg.inv(xyz_from_rgbcie) +rgbcie_from_xyz = linalg.inv(xyz_from_rgbcie) rgbntsc_from_xyz = [[1.910, -0.533, -0.288], [-0.985, 2.000, -0.028], @@ -45,7 +45,7 @@ [0.212671, 0.715160, 0.072169], [0.019334, 0.119193, 0.950227]] -rgb_from_xyz = scipy.linalg.inv(xyz_from_rgb) +rgb_from_xyz = linalg.inv(xyz_from_rgb) # From http://www.mir.com/DMG/ycbcr.html @@ -53,7 +53,7 @@ [-0.168736, -0.331264, 0.5], [0.5, -0.418688, -0.081312]] -rgbp_from_ycbcr = scipy.linalg.inv(ycbcr_from_rgbp) +rgbp_from_ycbcr = linalg.inv(ycbcr_from_rgbp) # LMS color space spectral matching curves provide the @@ -94,7 +94,7 @@ for name in ['ciexyz31_1.txt','ciexyz64_1.txt','ciexyzjv.txt', 'sbrgb2.txt','linss2_10e_1.txt']: k = k + 1 - name = os.path.join(os.path.split(__file__)[0],'colordata',name) + name = os.path.join(os.path.dirname(__file__),name) afile = open(name) lines = afile.readlines() afile.close() @@ -152,15 +152,15 @@ def tri2chr(tri,axis=None): """Convert tristimulus values to chromoticity values""" - tri = sb.asarray(tri) + tri = np.asarray(tri) n = len(tri.shape) if axis is None: axis = coloraxis(tri.shape) slices = [] for k in range(n): slices.append(slice(None)) - slices[axis] = sb.newaxis - norm = sb.sum(tri,axis=axis)[slices] + slices[axis] = np.newaxis + norm = np.sum(tri,axis=axis)[slices] slices[axis] = slice(None,2) out = tri[slices]/norm return out @@ -174,17 +174,17 @@ raise ValueError, "No Color axis found." def convert(matrix,TTT,axis=None): - TTT = sb.asarray(TTT) + TTT = np.asarray(TTT) if axis is None: axis = coloraxis(TTT.shape) if (axis != 0): - TTT = sb.swapaxes(TTT,0,axis) + TTT = np.swapaxes(TTT,0,axis) oldshape = TTT.shape - TTT = sb.reshape(TTT,(3,-1)) - OUT = sb.dot(matrix, TTT) + TTT = np.reshape(TTT,(3,-1)) + OUT = np.dot(matrix, TTT) OUT.shape = oldshape if (axis != 0): - OUT = sb.swapaxes(OUT,axis,0) + OUT = np.swapaxes(OUT,axis,0) return OUT def xyz2rgbcie(xyz,axis=None): @@ -216,12 +216,12 @@ return x, y, z, axis def join_colors(c1,c2,c3,axis): - c1,c2,c3 = sb.asarray(c1),sb.asarray(c2),sb.asarray(c3) + c1,c2,c3 = np.asarray(c1),np.asarray(c2),np.asarray(c3) newshape = c1.shape[:axis] + (1,) + c1.shape[axis:] c1.shape = newshape c2.shape = newshape c3.shape = newshape - return sb.concatenate((c1,c2,c3),axis=axis) + return np.concatenate((c1,c2,c3),axis=axis) def xyz2lab(xyz, axis=None, wp=whitepoints['D65'][-1], doclip=1): x,y,z,axis = separate_colors(xyz,axis) @@ -229,21 +229,21 @@ def f(t): eps = 216/24389. kap = 24389/27. - return sb.where(t > eps, - sb.power(t, 1.0/3), + return np.where(t > eps, + np.power(t, 1.0/3), (kap*t + 16.0)/116) fx,fy,fz = f(xn), f(yn), f(zn) L = 116*fy - 16 a = 500*(fx - fy) b = 200*(fy - fz) if doclip: - L = sb.clip(L, 0.0, 100.0) - a = sb.clip(a, -500.0, 500.0) - b = sb.clip(b, -200.0, 200.0) + L = np.clip(L, 0.0, 100.0) + a = np.clip(a, -500.0, 500.0) + b = np.clip(b, -200.0, 200.0) return join_colors(L,a,b,axis) def lab2xyz(lab, axis=None, wp=whitepoints['D65'][-1]): - lab = sb.asarray(lab) + lab = np.asarray(lab) L,a,b,axis = separate_colors(lab,axis) fy = (L+16)/116.0 fz = fy - b / 200. @@ -251,8 +251,8 @@ def finv(y): eps3 = (216/24389.)**3 kap = 24389/27. - return sb.where(y > eps3, - sb.power(y,3), + return np.where(y > eps3, + np.power(y,3), (116*y-16)/kap) xr, yr, zr = finv(fx), finv(fy), finv(fz) return join_colors(xr*wp[0],yr*wp[1],zr*wp[2],axis) @@ -263,6 +263,51 @@ def lab2rgb(lab): return xyz2rgb(lab2xyz(lab)) +def _uv(x, y, z): + """ The u, v formulae for CIE 1976 L*u*v* computations. + """ + denominator = (x + 15*y + 3*z) + zeros = (denominator == 0.0) + denominator[zeros] = 1.0 + u_numerator = 4 * x + u_numerator[zeros] = 4.0 + v_numerator = 9 * y + v_numerator[zeros] = 9.0 / 15.0 + + return u_numerator/denominator, v_numerator/denominator + +def xyz2luv(xyz, axis=None, wp=whitepoints['D65'][-1]): + x, y, z, axis = separate_colors(xyz, axis) + xn, yn, zn = x/wp[0], y/wp[1], z/wp[2] + Ls = 116.0 * np.power(yn, 1./3) - 16.0 + small_mask = (y <= 0.008856*wp[1]) + Ls[small_mask] = 903.0 * y[small_mask] / wp[1] + unp, vnp = _uv(*wp) + up, vp = _uv(x, y, z) + us = 13 * Ls * (up - unp) + vs = 13 * Ls * (vp - vnp) + + return join_colors(Ls, us, vs, axis) + +def luv2xyz(luv, axis=None, wp=whitepoints['D65'][-1]): + Ls, us, vs, axis = separate_colors(luv, axis) + unp, vnp = _uv(*wp) + small_mask = (Ls <= 903.3 * 0.008856) + y = wp[1] * ((Ls + 16.0) / 116.0) ** 3 + y[small_mask] = Ls * wp[1] / 903.0 + up = us / (13*Ls) + us + vp = vs / (13*Ls) + vs + x = 9.0 * y * up / (4.0 * vp) + z = -x / 3.0 - 5.0 * y + 3.0 * y/vp + + return join_colors(x, y, z, axis) + +def rgb2luv(rgb): + return xyz2luv(rgb2xyz(rgb)) + +def luv2rgb(luv): + return xyz2rgb(luv2xyz(luv)) + # RGB values that will be displayed on a screen are always # R'G'B' values. To get the XYZ value of the color that will be # displayed you need a calibrated monitor with a profile @@ -291,10 +336,10 @@ # rgbp = rgb**(1.0/gamma) def rgb2rgbp(rgb,gamma=None): - rgb = sb.asarray(rgb) + rgb = np.asarray(rgb) if gamma is None: eps = 0.0031308 - return sb.where(rgb < eps, 12.92*rgb, + return np.where(rgb < eps, 12.92*rgb, 1.055*rgb**(1.0/2.4) - 0.055) else: return rgb**(1.0/gamma) @@ -306,11 +351,11 @@ # rgb = rgbp**gamma # def rgbp2rgb(rgbp,gamma=None): - rgbp = sb.asarray(rgbp) + rgbp = np.asarray(rgbp) if gamma is None: eps = 0.04045 - return sb.where(rgbp <= eps, rgbp / 12.92, - sb.power((rgbp + 0.055)/1.055,2.4)) + return np.where(rgbp <= eps, rgbp / 12.92, + np.power((rgbp + 0.055)/1.055,2.4)) else: return rgbp**gamma @@ -340,9 +385,9 @@ def ycbcr_8bit(ycbcr,axis=None): y,cb,cr,axis = separate_colors(ycbcr,axis) - Y = sb.asarray((y*219 + 16),sb.UInt8) - Cb = sb.asarray((cb*224 + 128),sb.UInt8) - Cr = sb.asarray((cr*224 + 128),sb.UInt8) + Y = np.asarray((y*219 + 16),np.uint8) + Cb = np.asarray((cb*224 + 128),np.uint8) + Cr = np.asarray((cr*224 + 128),np.uint8) return join_colors(Y,Cb,Cr,axis) def ycbcr_norm(YCbCr,axis=None): Copied: trunk/Lib/sandbox/image/setup.py (from rev 2742, trunk/Lib/sandbox/image/setup_image.py) =================================================================== --- trunk/Lib/sandbox/image/setup_image.py 2007-02-22 01:58:34 UTC (rev 2742) +++ trunk/Lib/sandbox/image/setup.py 2007-02-22 08:46:08 UTC (rev 2743) @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +def configuration(parent_package='', top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('image', parent_package, top_path) + config.add_data_files("""ciexyz31_1.txt ciexyz64_1.txt ciexyzjv.txt + linss2_10e_1.txt sbrgb2.txt""".split()) + + return config + +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(**configuration()) Deleted: trunk/Lib/sandbox/image/setup_image.py =================================================================== --- trunk/Lib/sandbox/image/setup_image.py 2007-02-22 01:58:34 UTC (rev 2742) +++ trunk/Lib/sandbox/image/setup_image.py 2007-02-22 08:46:08 UTC (rev 2743) @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -from glob import glob -import os - -def configuration(parent_package='', parent_path=None): - from numpy.distutils.system_info import get_info, dict_append - from numpy.distutils.misc_util import default_config_dict, \ - dot_join, get_path - - package = 'image' - config = default_config_dict(package,parent_package) - - local_path = get_path(__name__, parent_path) - image_path = os.path.join(parent_package,'image') - - color_files = glob(os.path.join(local_path, '*.txt')) - data_path = os.path.join(image_path, 'colordata') - config['data_files'].extend([(data_path, color_files)]) - - return config - -if __name__ == '__main__': - from numpy.distutils.core import setup - setup(**configuration()) Modified: trunk/Lib/sandbox/image/transforms.py =================================================================== --- trunk/Lib/sandbox/image/transforms.py 2007-02-22 01:58:34 UTC (rev 2742) +++ trunk/Lib/sandbox/image/transforms.py 2007-02-22 08:46:08 UTC (rev 2743) @@ -1,6 +1,13 @@ -import numpy as sb -pi = sb.pi +import numpy as np +from numpy import conj, dot, ogrid, pi, r_, sin, transpose, zeros +from scipy import linalg + +__all__ = """dct idct dct2 idct2 dctn idctn dct2raw idct2raw +dst idst dst2 idst2 dstn idstn +digitrevorder bitrevorder wht +""".split() + # fast discrete cosine transforms of real sequences (using the fft) # These implement the DCT-II and inverse DCT-II (DCT-III) # described at http://en.wikipedia.org/wiki/Discrete_cosine_transform @@ -28,7 +35,7 @@ else: newshape = list(x.shape) newshape[axis] = 2*N - xtilde = sb.empty(newshape,sb.float64) + xtilde = np.empty(newshape,np.float64) slices[0][axis] = slice(None,N) slices[2][axis] = slice(N,None) slices[3][axis] = slice(None,None,-1) @@ -37,9 +44,9 @@ slices[k] = tuple(slices[k]) xtilde[slices[0]] = x[slices[1]] xtilde[slices[2]] = x[slices[3]] - Xt = sb.fft(xtilde,axis=axis) - pk = sb.exp(-1j*pi*sb.arange(N)/(2*N)) - newshape = sb.ones(n) + Xt = np.fft(xtilde,axis=axis) + pk = np.exp(-1j*pi*np.arange(N)/(2*N)) + newshape = np.ones(n) newshape[axis] = N pk.shape = newshape @@ -47,7 +54,7 @@ pk /= 2; Xt = Xt[slices[0]] - return sb.real(Xt*pk) + return np.real(Xt*pk) def idct(v,axis=-1): @@ -59,13 +66,13 @@ slices[k] = [] for j in range(n): slices[k].append(slice(None)) - k = sb.arange(N) + k = np.arange(N) if even: - ak = sb.r_[1.0,[2]*(N-1)]*sb.exp(1j*pi*k/(2*N)) - newshape = sb.ones(n) + ak = np.r_[1.0,[2]*(N-1)]*np.exp(1j*pi*k/(2*N)) + newshape = np.ones(n) newshape[axis] = N ak.shape = newshape - xhat = sb.real(sb.ifft(v*ak,axis=axis)) + xhat = np.real(np.ifft(v*ak,axis=axis)) x = 0.0*v slices[0][axis] = slice(None,None,2) slices[1][axis] = slice(None,N/2) @@ -77,13 +84,13 @@ x[slices[2]] = xhat[slices[3]] return x else: - ak = 2*sb.exp(1j*pi*k/(2*N)) - newshape = sb.ones(n) + ak = 2*np.exp(1j*pi*k/(2*N)) + newshape = np.ones(n) newshape[axis] = N ak.shape = newshape newshape = list(v.shape) newshape[axis] = 2*N - Y = zeros(newshape,sb.Complex) + Y = zeros(newshape,np.complex128) #Y[:N] = ak*v #Y[(N+1):] = conj(Y[N:0:-1]) slices[0][axis] = slice(None,N) @@ -92,7 +99,7 @@ slices[3][axis] = slice((N-1),0,-1) Y[slices[0]] = ak*v Y[slices[2]] = conj(Y[slices[3]]) - x = sb.real(sb.ifft(Y,axis=axis))[slices[0]] + x = np.real(np.ifft(Y,axis=axis))[slices[0]] return x def dct2(x,axes=(-1,-2)): @@ -103,7 +110,7 @@ def dctn(x,axes=None): if axes is None: - axes = sb.arange(len(x.shape)) + axes = np.arange(len(x.shape)) res = x for k in axes: res = dct(res,axis=k) @@ -111,7 +118,7 @@ def idctn(v,axes=None): if axes is None: - axes = sb.arange(len(v.shape)) + axes = np.arange(len(v.shape)) res = v for k in axes: res = idct(res,axis=k) @@ -120,7 +127,7 @@ def makeC(N): n,l = ogrid[:N,:N] - C = sb.cos(pi*(2*n+1)*l/(2*N)) + C = np.cos(pi*(2*n+1)*l/(2*N)) return C def dct2raw(x): @@ -159,7 +166,7 @@ slices[k].append(slice(None)) newshape = list(x.shape) newshape[axis] = 2*(N+1) - xtilde = sb.zeros(newshape,sb.float64) + xtilde = np.zeros(newshape,np.float64) slices[0][axis] = slice(1,N+1) slices[1][axis] = slice(N+2,None) slices[2][axis] = slice(None,None,-1) @@ -167,8 +174,8 @@ slices[k] = tuple(slices[k]) xtilde[slices[0]] = x xtilde[slices[1]] = -x[slices[2]] - Xt = sb.fft(xtilde,axis=axis) - return (-sb.imag(Xt)/2)[slices[0]] + Xt = np.fft(xtilde,axis=axis) + return (-np.imag(Xt)/2)[slices[0]] def idst(v,axis=-1): n = len(v.shape) @@ -180,7 +187,7 @@ slices[k].append(slice(None)) newshape = list(v.shape) newshape[axis] = 2*(N+1) - Xt = sb.zeros(newshape,sb.Complex) + Xt = np.zeros(newshape,np.complex128) slices[0][axis] = slice(1,N+1) slices[1][axis] = slice(N+2,None) slices[2][axis] = slice(None,None,-1) @@ -189,7 +196,7 @@ slices[k] = tuple(slices[k]) Xt[slices[0]] = -val Xt[slices[1]] = val[slices[2]] - xhat = sb.real(sb.ifft(Xt,axis=axis)) + xhat = np.real(np.ifft(Xt,axis=axis)) return xhat[slices[0]] def dst2(x,axes=(-1,-2)): @@ -200,7 +207,7 @@ def dstn(x,axes=None): if axes is None: - axes = sb.arange(len(x.shape)) + axes = np.arange(len(x.shape)) res = x for k in axes: res = dst(res,axis=k) @@ -208,14 +215,14 @@ def idstn(v,axes=None): if axes is None: - axes = sb.arange(len(v.shape)) + axes = np.arange(len(v.shape)) res = v for k in axes: res = idst(res,axis=k) return res def digitrevorder(x,base): - x = sb.asarray(x) + x = np.asarray(x) rem = N = len(x) L = 0 while 1: @@ -227,7 +234,7 @@ rem = intd L += 1 vec = r_[[base**n for n in range(L)]] - newx = x[sb.newaxis,:]*vec[:,sb.newaxis] + newx = x[np.newaxis,:]*vec[:,np.newaxis] # compute digits for k in range(L-1,-1,-1): newx[k] = x // vec[k] @@ -260,8 +267,8 @@ Digital Signal Processing" Spring Verlag, New York 1975. page-111. """ N = len(data) - L = sb.log2(N); - if ((L-sb.floor(L)) > 0.0): + L = np.log2(N); + if ((L-np.floor(L)) > 0.0): raise ValueError, "Length must be power of 2" x=bitrevorder(data); From scipy-svn at scipy.org Thu Feb 22 04:41:10 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 03:41:10 -0600 (CST) Subject: [Scipy-svn] r2744 - trunk/Lib/misc Message-ID: <20070222094110.5ECA039C01A@new.scipy.org> Author: stefan Date: 2007-02-22 03:41:02 -0600 (Thu, 22 Feb 2007) New Revision: 2744 Modified: trunk/Lib/misc/pilutil.py Log: Clean up misc.pilutil.imshow. Modified: trunk/Lib/misc/pilutil.py =================================================================== --- trunk/Lib/misc/pilutil.py 2007-02-22 08:46:08 UTC (rev 2743) +++ trunk/Lib/misc/pilutil.py 2007-02-22 09:41:02 UTC (rev 2744) @@ -2,6 +2,7 @@ import types import numpy +import tempfile from numpy import amin, amax, ravel, asarray, cast, arange, \ ones, newaxis, transpose, mgrid, iscomplexobj, sum, zeros, uint8 @@ -226,19 +227,22 @@ """Simple showing of an image through an external viewer. """ im = toimage(arr) - if (len(arr.shape) == 3) and (arr.shape[2] == 4): - try: - import os - im.save('/tmp/scipy_imshow.png') - if os.system("(xv /tmp/scipy_imshow.png; rm -f /tmp/scipy_imshow.png)&"): - raise RuntimeError - return - except: - print "Warning: Alpha channel may not be handled correctly." + fnum,fname = tempfile.mkstemp('.png') + try: + im.save(fname) + except: + raise RuntimeError("Error saving temporary image data.") - im.show() - return + import os + os.close(fnum) + cmd = os.environ.get('SCIPY_PIL_IMAGE_VIEWER','see') + status = os.system("%s %s" % (cmd,fname)) + + os.unlink(fname) + if status != 0: + raise RuntimeError('Could not execute image viewer.') + def imresize(arr,size): """Resize an image. From scipy-svn at scipy.org Thu Feb 22 04:42:14 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 03:42:14 -0600 (CST) Subject: [Scipy-svn] r2745 - trunk/Lib/sandbox/montecarlo/tests Message-ID: <20070222094214.0866F39C01A@new.scipy.org> Author: stefan Date: 2007-02-22 03:42:06 -0600 (Thu, 22 Feb 2007) New Revision: 2745 Modified: trunk/Lib/sandbox/montecarlo/tests/test_dictsampler.py Log: Fix generator usage in sandbox.montecarlo. Modified: trunk/Lib/sandbox/montecarlo/tests/test_dictsampler.py =================================================================== --- trunk/Lib/sandbox/montecarlo/tests/test_dictsampler.py 2007-02-22 09:41:02 UTC (rev 2744) +++ trunk/Lib/sandbox/montecarlo/tests/test_dictsampler.py 2007-02-22 09:42:06 UTC (rev 2745) @@ -37,7 +37,7 @@ #import pdb #pdb.set_trace() s = sampler.sample(n) - assert sum(s[i]=='b' for i in range(n),axis=0)*1./n > 0.75 + assert sum((s[i]=='b' for i in range(n)),axis=0)*1./n > 0.75 #lam = 10.0 #n = 35 From scipy-svn at scipy.org Thu Feb 22 05:04:40 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 04:04:40 -0600 (CST) Subject: [Scipy-svn] r2746 - trunk/Lib/sandbox/image Message-ID: <20070222100440.02BB539C0CD@new.scipy.org> Author: stefan Date: 2007-02-22 04:04:33 -0600 (Thu, 22 Feb 2007) New Revision: 2746 Modified: trunk/Lib/sandbox/image/setup.py Log: Fix typo in image.setup. Modified: trunk/Lib/sandbox/image/setup.py =================================================================== --- trunk/Lib/sandbox/image/setup.py 2007-02-22 09:42:06 UTC (rev 2745) +++ trunk/Lib/sandbox/image/setup.py 2007-02-22 10:04:33 UTC (rev 2746) @@ -3,7 +3,7 @@ def configuration(parent_package='', top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('image', parent_package, top_path) - config.add_data_files("""ciexyz31_1.txt ciexyz64_1.txt ciexyzjv.txt + config.add_data_files(*"""ciexyz31_1.txt ciexyz64_1.txt ciexyzjv.txt linss2_10e_1.txt sbrgb2.txt""".split()) return config From scipy-svn at scipy.org Thu Feb 22 10:01:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 09:01:55 -0600 (CST) Subject: [Scipy-svn] r2747 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070222150155.5C67D39C0F3@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 09:01:51 -0600 (Thu, 22 Feb 2007) New Revision: 2747 Added: trunk/Lib/sandbox/timeseries/io/fame/const.py Log: Added: trunk/Lib/sandbox/timeseries/io/fame/const.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/const.py 2007-02-22 10:04:33 UTC (rev 2746) +++ trunk/Lib/sandbox/timeseries/io/fame/const.py 2007-02-22 15:01:51 UTC (rev 2747) @@ -0,0 +1,11 @@ +import cfame +from cfame import FAME_CONSTANTS + +"""add constants in cfame.FAME_CONSTANTS dictionary to global namespace +for this module""" + +_g = globals() +for var, val in FAME_CONSTANTS.iteritems(): + _g[var] = val + +__all__ = list(FAME_CONSTANTS) \ No newline at end of file From scipy-svn at scipy.org Thu Feb 22 10:02:19 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 09:02:19 -0600 (CST) Subject: [Scipy-svn] r2748 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070222150219.E727139C0F3@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 09:02:16 -0600 (Thu, 22 Feb 2007) New Revision: 2748 Modified: trunk/Lib/sandbox/timeseries/io/fame/core.py Log: moved fame constants into their own name space Modified: trunk/Lib/sandbox/timeseries/io/fame/core.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-22 15:01:51 UTC (rev 2747) +++ trunk/Lib/sandbox/timeseries/io/fame/core.py 2007-02-22 15:02:16 UTC (rev 2748) @@ -5,18 +5,12 @@ import timeseries as ts import cfame -from cfame import FAME_CONSTANTS +from const import * __all__ = [ 'FameDb', 'set_option', 'license_expires', 'DBError' ] -_g = globals() -for var, val in FAME_CONSTANTS.iteritems(): - _g[var] = val - -__all__.extend(list(FAME_CONSTANTS)) - def reverse_dict(d): return dict([(y, x) for x, y in d.iteritems()]) From scipy-svn at scipy.org Thu Feb 22 10:02:34 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 09:02:34 -0600 (CST) Subject: [Scipy-svn] r2749 - trunk/Lib/sandbox/timeseries/io/fame Message-ID: <20070222150234.D66D639C0F3@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 09:02:31 -0600 (Thu, 22 Feb 2007) New Revision: 2749 Modified: trunk/Lib/sandbox/timeseries/io/fame/__init__.py Log: Modified: trunk/Lib/sandbox/timeseries/io/fame/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-22 15:02:16 UTC (rev 2748) +++ trunk/Lib/sandbox/timeseries/io/fame/__init__.py 2007-02-22 15:02:31 UTC (rev 2749) @@ -1,4 +1,6 @@ import core from core import * +import const __all__ = core.__all__ +__all__.append('const') From scipy-svn at scipy.org Thu Feb 22 14:27:49 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 13:27:49 -0600 (CST) Subject: [Scipy-svn] r2750 - trunk/Lib/sandbox/timeseries Message-ID: <20070222192749.7EB1A39C141@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 13:27:45 -0600 (Thu, 22 Feb 2007) New Revision: 2750 Modified: trunk/Lib/sandbox/timeseries/tcore.py Log: fixed typo Modified: trunk/Lib/sandbox/timeseries/tcore.py =================================================================== --- trunk/Lib/sandbox/timeseries/tcore.py 2007-02-22 15:02:31 UTC (rev 2749) +++ trunk/Lib/sandbox/timeseries/tcore.py 2007-02-22 19:27:45 UTC (rev 2750) @@ -86,7 +86,7 @@ 2000: ['Q','QUARTER','QUARTERLY',], 3000: ['M','MONTH','MONTHLY',], 4000: ['W','WEEK','WEEKLY',], - 5000: ['B','BUSINESS','BUSINESSLYT'], + 5000: ['B','BUSINESS','BUSINESSLY'], 6000: ['D','DAY','DAILY',], 7000: ['H','HOUR','HOURLY',], 8000: ['T','MINUTE','MINUTELY',], From scipy-svn at scipy.org Thu Feb 22 15:49:50 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 14:49:50 -0600 (CST) Subject: [Scipy-svn] r2751 - trunk/Lib/sandbox/timeseries/src Message-ID: <20070222204950.2619739C00B@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 14:49:45 -0600 (Thu, 22 Feb 2007) New Revision: 2751 Modified: trunk/Lib/sandbox/timeseries/src/cseries.c Log: changed frequency constants and added python dictionary of the constants Modified: trunk/Lib/sandbox/timeseries/src/cseries.c =================================================================== --- trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-22 19:27:45 UTC (rev 2750) +++ trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-22 20:49:45 UTC (rev 2751) @@ -7,17 +7,22 @@ static char cseries_doc[] = "Speed sensitive time series operations"; -#define FANN 1000 /* Annual */ -#define FQTR 2000 /* Quarterly */ -#define FMTH 3000 /* Monthly */ -#define FWK 4000 /* Weekly */ -#define FBUS 5000 /* Business days */ -#define FDL 6000 /* Daily */ -#define FHR 7000 /* Hourly */ -#define FMIN 8000 /* Minutely */ -#define FSEC 9000 /* Secondly */ -#define FUND -9999 /* Undefined */ +#define FR_ANN 1000 /* Annual */ +#define FR_QTR 2000 /* Quarterly */ +#define FR_MTH 3000 /* Monthly */ +#define FR_WK 4000 /* Weekly */ +#define FR_BUS 5000 /* Business days */ +#define FR_DAY 6000 /* Daily */ +#define FR_HR 7000 /* Hourly */ +#define FR_MIN 8000 /* Minutely */ +#define FR_SEC 9000 /* Secondly */ +#define FR_UND -9999 /* Undefined */ +#define ADD_INT_TO_DICT(dict, key, val) \ + {PyObject *pyval = PyInt_FromLong(val); \ + PyDict_SetItemString(dict, key, pyval); \ + Py_DECREF(pyval); } + static long minval_D_toHighFreq = 719163; /////////////////////////////////////////////////////////////////////// @@ -409,138 +414,138 @@ switch(fromFreq) { - case FANN: + case FR_ANN: switch(toFreq) { - case FQTR: return &asfreq_AtoQ; - case FMTH: return &asfreq_AtoM; - case FWK: return &asfreq_AtoW; - case FBUS: return &asfreq_AtoB; - case FDL: return &asfreq_AtoD; - case FHR: return &asfreq_AtoH; - case FMIN: return &asfreq_AtoT; - case FSEC: return &asfreq_AtoS; + case FR_QTR: return &asfreq_AtoQ; + case FR_MTH: return &asfreq_AtoM; + case FR_WK: return &asfreq_AtoW; + case FR_BUS: return &asfreq_AtoB; + case FR_DAY: return &asfreq_AtoD; + case FR_HR: return &asfreq_AtoH; + case FR_MIN: return &asfreq_AtoT; + case FR_SEC: return &asfreq_AtoS; default: return &nofunc; } - case FQTR: + case FR_QTR: switch(toFreq) { - case FANN: return &asfreq_QtoA; - case FMTH: return &asfreq_QtoM; - case FWK: return &asfreq_QtoW; - case FBUS: return &asfreq_QtoB; - case FDL: return &asfreq_QtoD; - case FHR: return &asfreq_QtoH; - case FMIN: return &asfreq_QtoT; - case FSEC: return &asfreq_QtoS; + case FR_ANN: return &asfreq_QtoA; + case FR_MTH: return &asfreq_QtoM; + case FR_WK: return &asfreq_QtoW; + case FR_BUS: return &asfreq_QtoB; + case FR_DAY: return &asfreq_QtoD; + case FR_HR: return &asfreq_QtoH; + case FR_MIN: return &asfreq_QtoT; + case FR_SEC: return &asfreq_QtoS; default: return &nofunc; } - case FMTH: + case FR_MTH: switch(toFreq) { - case FANN: return &asfreq_MtoA; - case FQTR: return &asfreq_MtoQ; - case FWK: return &asfreq_MtoW; - case FBUS: return &asfreq_MtoB; - case FDL: return &asfreq_MtoD; - case FHR: return &asfreq_MtoH; - case FMIN: return &asfreq_MtoT; - case FSEC: return &asfreq_MtoS; + case FR_ANN: return &asfreq_MtoA; + case FR_QTR: return &asfreq_MtoQ; + case FR_WK: return &asfreq_MtoW; + case FR_BUS: return &asfreq_MtoB; + case FR_DAY: return &asfreq_MtoD; + case FR_HR: return &asfreq_MtoH; + case FR_MIN: return &asfreq_MtoT; + case FR_SEC: return &asfreq_MtoS; default: return &nofunc; } - case FWK: + case FR_WK: switch(toFreq) { - case FANN: return &asfreq_WtoA; - case FQTR: return &asfreq_WtoQ; - case FMTH: return &asfreq_WtoM; - case FBUS: return &asfreq_WtoB; - case FDL: return &asfreq_WtoD; - case FHR: return &asfreq_WtoH; - case FMIN: return &asfreq_WtoT; - case FSEC: return &asfreq_WtoS; + case FR_ANN: return &asfreq_WtoA; + case FR_QTR: return &asfreq_WtoQ; + case FR_MTH: return &asfreq_WtoM; + case FR_BUS: return &asfreq_WtoB; + case FR_DAY: return &asfreq_WtoD; + case FR_HR: return &asfreq_WtoH; + case FR_MIN: return &asfreq_WtoT; + case FR_SEC: return &asfreq_WtoS; default: return &nofunc; } - case FBUS: + case FR_BUS: switch(toFreq) { - case FANN: return &asfreq_BtoA; - case FQTR: return &asfreq_BtoQ; - case FMTH: return &asfreq_BtoM; - case FWK: return &asfreq_BtoW; - case FDL: return &asfreq_BtoD; - case FHR: return &asfreq_BtoH; - case FMIN: return &asfreq_BtoT; - case FSEC: return &asfreq_BtoS; + case FR_ANN: return &asfreq_BtoA; + case FR_QTR: return &asfreq_BtoQ; + case FR_MTH: return &asfreq_BtoM; + case FR_WK: return &asfreq_BtoW; + case FR_DAY: return &asfreq_BtoD; + case FR_HR: return &asfreq_BtoH; + case FR_MIN: return &asfreq_BtoT; + case FR_SEC: return &asfreq_BtoS; default: return &nofunc; } - case FDL: + case FR_DAY: switch(toFreq) { - case FANN: return &asfreq_DtoA; - case FQTR: return &asfreq_DtoQ; - case FMTH: return &asfreq_DtoM; - case FWK: return &asfreq_DtoW; - case FBUS: + case FR_ANN: return &asfreq_DtoA; + case FR_QTR: return &asfreq_DtoQ; + case FR_MTH: return &asfreq_DtoM; + case FR_WK: return &asfreq_DtoW; + case FR_BUS: if (forConvert) { return &asfreq_DtoB_forConvert; } else { return &asfreq_DtoB; } - case FDL: return &asfreq_DtoD; - case FHR: return &asfreq_DtoH; - case FMIN: return &asfreq_DtoT; - case FSEC: return &asfreq_DtoS; + case FR_DAY: return &asfreq_DtoD; + case FR_HR: return &asfreq_DtoH; + case FR_MIN: return &asfreq_DtoT; + case FR_SEC: return &asfreq_DtoS; default: return &nofunc; } - case FHR: + case FR_HR: switch(toFreq) { - case FANN: return &asfreq_HtoA; - case FQTR: return &asfreq_HtoQ; - case FMTH: return &asfreq_HtoM; - case FWK: return &asfreq_HtoW; - case FBUS: + case FR_ANN: return &asfreq_HtoA; + case FR_QTR: return &asfreq_HtoQ; + case FR_MTH: return &asfreq_HtoM; + case FR_WK: return &asfreq_HtoW; + case FR_BUS: if (forConvert) { return &asfreq_HtoB_forConvert; } else { return &asfreq_HtoB; } - case FDL: return &asfreq_HtoD; - case FMIN: return &asfreq_HtoT; - case FSEC: return &asfreq_HtoS; + case FR_DAY: return &asfreq_HtoD; + case FR_MIN: return &asfreq_HtoT; + case FR_SEC: return &asfreq_HtoS; default: return &nofunc; } - case FMIN: + case FR_MIN: switch(toFreq) { - case FANN: return &asfreq_TtoA; - case FQTR: return &asfreq_TtoQ; - case FMTH: return &asfreq_TtoM; - case FWK: return &asfreq_TtoW; - case FBUS: + case FR_ANN: return &asfreq_TtoA; + case FR_QTR: return &asfreq_TtoQ; + case FR_MTH: return &asfreq_TtoM; + case FR_WK: return &asfreq_TtoW; + case FR_BUS: if (forConvert) { return &asfreq_TtoB_forConvert; } else { return &asfreq_TtoB; } - case FDL: return &asfreq_TtoD; - case FHR: return &asfreq_TtoH; - case FSEC: return &asfreq_TtoS; + case FR_DAY: return &asfreq_TtoD; + case FR_HR: return &asfreq_TtoH; + case FR_SEC: return &asfreq_TtoS; default: return &nofunc; } - case FSEC: + case FR_SEC: switch(toFreq) { - case FANN: return &asfreq_StoA; - case FQTR: return &asfreq_StoQ; - case FMTH: return &asfreq_StoM; - case FWK: return &asfreq_StoW; - case FBUS: + case FR_ANN: return &asfreq_StoA; + case FR_QTR: return &asfreq_StoQ; + case FR_MTH: return &asfreq_StoM; + case FR_WK: return &asfreq_StoW; + case FR_BUS: if (forConvert) { return &asfreq_StoB_forConvert; } else { return &asfreq_StoB; } - case FDL: return &asfreq_StoD; - case FHR: return &asfreq_StoH; - case FMIN: return &asfreq_StoT; + case FR_DAY: return &asfreq_StoD; + case FR_HR: return &asfreq_StoH; + case FR_MIN: return &asfreq_StoT; default: return &nofunc; } default: return &nofunc; @@ -567,80 +572,80 @@ switch(fromFreq) { - case FANN: return 1; - case FQTR: + case FR_ANN: return 1; + case FR_QTR: switch(toFreq) { - case FANN: return 4; + case FR_ANN: return 4; default: return 1; } - case FMTH: //monthly + case FR_MTH: //monthly switch(toFreq) { - case FANN: return 12; - case FQTR: return 3; + case FR_ANN: return 12; + case FR_QTR: return 3; default: return 1; } - case FWK: //monthly + case FR_WK: //monthly switch(toFreq) { - case FANN: return 53; - case FQTR: return 13; - case FMTH: return 4; + case FR_ANN: return 53; + case FR_QTR: return 13; + case FR_MTH: return 4; default: return 1; } - case FBUS: //business + case FR_BUS: //business switch(toFreq) { - case FANN: return maxBusDaysPerYear;; - case FQTR: return maxBusDaysPerQuarter; - case FMTH: return maxBusDaysPerMonth; - case FWK: return 5; + case FR_ANN: return maxBusDaysPerYear;; + case FR_QTR: return maxBusDaysPerQuarter; + case FR_MTH: return maxBusDaysPerMonth; + case FR_WK: return 5; default: return 1; } - case FDL: //daily + case FR_DAY: //daily switch(toFreq) { - case FANN: return maxDaysPerYear;; - case FQTR: return maxDaysPerQuarter; - case FMTH: return maxDaysPerMonth; - case FWK: return 7; + case FR_ANN: return maxDaysPerYear;; + case FR_QTR: return maxDaysPerQuarter; + case FR_MTH: return maxDaysPerMonth; + case FR_WK: return 7; default: return 1; } - case FHR: //hourly + case FR_HR: //hourly switch(toFreq) { - case FANN: return 24 * maxDaysPerYear;; - case FQTR: return 24 * maxDaysPerQuarter; - case FMTH: return 24 * maxDaysPerMonth; - case FWK: return 24 * 7; - case FDL: return 24; - case FBUS: return 24; + case FR_ANN: return 24 * maxDaysPerYear;; + case FR_QTR: return 24 * maxDaysPerQuarter; + case FR_MTH: return 24 * maxDaysPerMonth; + case FR_WK: return 24 * 7; + case FR_DAY: return 24; + case FR_BUS: return 24; default: return 1; } - case FMIN: //minutely + case FR_MIN: //minutely switch(toFreq) { - case FANN: return 24 * 60 * maxDaysPerYear;; - case FQTR: return 24 * 60 * maxDaysPerQuarter; - case FMTH: return 24 * 60 * maxDaysPerMonth; - case FWK: return 24 * 60 * 7; - case FDL: return 24 * 60; - case FBUS: return 24 * 60; - case FHR: return 60; + case FR_ANN: return 24 * 60 * maxDaysPerYear;; + case FR_QTR: return 24 * 60 * maxDaysPerQuarter; + case FR_MTH: return 24 * 60 * maxDaysPerMonth; + case FR_WK: return 24 * 60 * 7; + case FR_DAY: return 24 * 60; + case FR_BUS: return 24 * 60; + case FR_HR: return 60; default: return 1; } - case FSEC: //minutely + case FR_SEC: //minutely switch(toFreq) { - case FANN: return 24 * 60 * 60 * maxDaysPerYear;; - case FQTR: return 24 * 60 * 60 * maxDaysPerQuarter; - case FMTH: return 24 * 60 * 60 * maxDaysPerMonth; - case FWK: return 24 * 60 * 60 * 7; - case FDL: return 24 * 60 * 60; - case FBUS: return 24 * 60 * 60; - case FHR: return 60 * 60; - case FMIN: return 60; + case FR_ANN: return 24 * 60 * 60 * maxDaysPerYear;; + case FR_QTR: return 24 * 60 * 60 * maxDaysPerQuarter; + case FR_MTH: return 24 * 60 * 60 * maxDaysPerMonth; + case FR_WK: return 24 * 60 * 60 * 7; + case FR_DAY: return 24 * 60 * 60; + case FR_BUS: return 24 * 60 * 60; + case FR_HR: return 60 * 60; + case FR_MIN: return 60; default: return 1; } default: return 1; @@ -864,7 +869,7 @@ ISOWeekTuple = PyObject_GetAttrString((PyObject*)dateObj, "iso_week"); if (!PyArg_ParseTuple(ISOWeekTuple,"iii;need a ISO Week 3-tuple (year,week,day)", - &year, &week, &day)) return NULL; + &year, &week, &day)) return -9999; Py_DECREF(ISOWeekTuple); @@ -880,13 +885,13 @@ switch(freq) { - case FHR: + case FR_HR: periodsPerDay = 24; break; - case FMIN: + case FR_MIN: periodsPerDay = 24*60; break; - case FSEC: + case FR_SEC: periodsPerDay = 24*60*60; break; default: @@ -914,7 +919,6 @@ PyObject *val; long dateNum, dInfo; long absdate; - //double absdate, abstime; double abstime; long (*toDaily)(long, char) = NULL; @@ -926,7 +930,7 @@ iterSource = (PyArrayIterObject *)PyArray_IterNew((PyObject *)array); iterResult = (PyArrayIterObject *)PyArray_IterNew((PyObject *)newArray); - toDaily = get_asfreq_func(freq, FDL, 0); + toDaily = get_asfreq_func(freq, FR_DAY, 0); switch(*info) { @@ -968,7 +972,6 @@ val = PyArray_GETITEM(array, iterSource->dataptr); dateNum = PyInt_AsLong(val); - //absdate = (double)toDaily(dateNum, 'B'); absdate = toDaily(dateNum, 'B'); abstime = getAbsTime(freq, absdate, dateNum); @@ -1001,7 +1004,24 @@ PyMODINIT_FUNC initcseries(void) { - Py_InitModule3("cseries", cseries_methods, cseries_doc); + PyObject *m, *TSER_CONSTANTS; + m = Py_InitModule3("cseries", cseries_methods, cseries_doc); mxDateTime_ImportModuleAndAPI(); import_array(); + + TSER_CONSTANTS = PyDict_New(); + + // Add all the frequency constants to a python dictionary + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_ANN", FR_ANN); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_QTR", FR_QTR); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_MTH", FR_MTH); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_WK", FR_WK); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_BUS", FR_BUS); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_DAY", FR_DAY); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_HR", FR_HR); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_MIN", FR_MIN); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_SEC", FR_SEC); + ADD_INT_TO_DICT(TSER_CONSTANTS, "FR_UND", FR_UND); + + PyModule_AddObject(m, "TSER_CONSTANTS", TSER_CONSTANTS); } \ No newline at end of file From scipy-svn at scipy.org Thu Feb 22 15:50:15 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 14:50:15 -0600 (CST) Subject: [Scipy-svn] r2752 - trunk/Lib/sandbox/timeseries Message-ID: <20070222205015.B15B739C0B8@new.scipy.org> Author: mattknox_ca Date: 2007-02-22 14:50:12 -0600 (Thu, 22 Feb 2007) New Revision: 2752 Modified: trunk/Lib/sandbox/timeseries/tcore.py Log: changed frequency dict to use constants defined in C Modified: trunk/Lib/sandbox/timeseries/tcore.py =================================================================== --- trunk/Lib/sandbox/timeseries/tcore.py 2007-02-22 20:49:45 UTC (rev 2751) +++ trunk/Lib/sandbox/timeseries/tcore.py 2007-02-22 20:50:12 UTC (rev 2752) @@ -18,6 +18,15 @@ import maskedarray as MA from maskedarray import masked, nomask, getmask +from cseries import TSER_CONSTANTS + +"""add constants in cfame.FAME_CONSTANTS dictionary to global namespace +for this module""" + +_g = globals() +_g.update(TSER_CONSTANTS) + + #####--------------------------------------------------------------------------- #---- --- Generic functions --- #####--------------------------------------------------------------------------- @@ -82,16 +91,16 @@ -freq_dict = { 1000: ['A','Y','ANNUAL','ANNUALLY','YEAR','YEARLY'], - 2000: ['Q','QUARTER','QUARTERLY',], - 3000: ['M','MONTH','MONTHLY',], - 4000: ['W','WEEK','WEEKLY',], - 5000: ['B','BUSINESS','BUSINESSLY'], - 6000: ['D','DAY','DAILY',], - 7000: ['H','HOUR','HOURLY',], - 8000: ['T','MINUTE','MINUTELY',], - 9000: ['S','SECOND','SECONDLY',], - -9999: ['U','UNDEF','UNDEFINED'], +freq_dict = { FR_ANN: ['A','Y','ANNUAL','ANNUALLY','YEAR','YEARLY'], + FR_QTR: ['Q','QUARTER','QUARTERLY',], + FR_MTH: ['M','MONTH','MONTHLY',], + FR_WK: ['W','WEEK','WEEKLY',], + FR_BUS: ['B','BUSINESS','BUSINESSLY'], + FR_DAY: ['D','DAY','DAILY',], + FR_HR: ['H','HOUR','HOURLY',], + FR_MIN: ['T','MINUTE','MINUTELY',], + FR_SEC: ['S','SECOND','SECONDLY',], + FR_UND: ['U','UNDEF','UNDEFINED'], } freq_revdict = reverse_dict(freq_dict) From scipy-svn at scipy.org Thu Feb 22 16:00:39 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 15:00:39 -0600 (CST) Subject: [Scipy-svn] r2753 - trunk/Lib/sandbox/image Message-ID: <20070222210039.6F5CE39C00B@new.scipy.org> Author: rkern Date: 2007-02-22 15:00:31 -0600 (Thu, 22 Feb 2007) New Revision: 2753 Modified: trunk/Lib/sandbox/image/color.py Log: Correct L*u*v* transformation for scalars. Modified: trunk/Lib/sandbox/image/color.py =================================================================== --- trunk/Lib/sandbox/image/color.py 2007-02-22 20:50:12 UTC (rev 2752) +++ trunk/Lib/sandbox/image/color.py 2007-02-22 21:00:31 UTC (rev 2753) @@ -268,11 +268,10 @@ """ denominator = (x + 15*y + 3*z) zeros = (denominator == 0.0) - denominator[zeros] = 1.0 - u_numerator = 4 * x - u_numerator[zeros] = 4.0 - v_numerator = 9 * y - v_numerator[zeros] = 9.0 / 15.0 + denominator = np.where(zeros, 1.0, denominator) + # I'm not entirely sure about these defaults when X=Y=Z=0. + u_numerator = np.where(zeros, 4.0, 4*x) + v_numerator = np.where(zeros, 9.0, 9 * y) return u_numerator/denominator, v_numerator/denominator From scipy-svn at scipy.org Thu Feb 22 22:17:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 21:17:04 -0600 (CST) Subject: [Scipy-svn] r2754 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070223031704.CBDF639C018@new.scipy.org> Author: pierregm Date: 2007-02-22 21:16:56 -0600 (Thu, 22 Feb 2007) New Revision: 2754 Added: trunk/Lib/sandbox/timeseries/.project Modified: trunk/Lib/sandbox/timeseries/tdates.py trunk/Lib/sandbox/timeseries/tests/test_dates.py trunk/Lib/sandbox/timeseries/tests/test_multitimeseries.py trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tmulti.py trunk/Lib/sandbox/timeseries/tseries.py Log: tdates : fixed a pb in DateArray where the frequence was not properly updated : optimization of DateArray.__new__ tseries: optimization of TimeSeries.__getitem__ : fixed a pb w/ _tsarraymethod when ondates=True : modified __check_index to allow access to rows/columns of a reshaped 1d series. : add transpose tmulti : update to take into account the modidications of mrecords Added: trunk/Lib/sandbox/timeseries/.project =================================================================== --- trunk/Lib/sandbox/timeseries/.project 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/.project 2007-02-23 03:16:56 UTC (rev 2754) @@ -0,0 +1,17 @@ + + + scipy_svn_timeseries + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -25,7 +25,6 @@ from numpy.core.numerictypes import generic import maskedarray as MA -#reload(MA) import mx.DateTime as mxD from mx.DateTime.Parser import DateFromString as mxDFromString @@ -40,7 +39,8 @@ 'DateError', 'ArithmeticDateError', 'FrequencyDateError','InsufficientDateError', 'datearray','date_array', 'date_array_fromlist', 'date_array_fromrange', 'day_of_week','day_of_year','day','month','quarter','year','hour','minute','second', -'truncateDate','monthToQuarter','thisday','today','prevbusday','asfreq' +'truncateDate','monthToQuarter','thisday','today','prevbusday','asfreq', +'period_break' ] @@ -224,6 +224,9 @@ hour, minute, second)) self.value = self.__value() + def __getitem__(self, indx): + return self + @property def day(self): "Returns the day of month." @@ -385,6 +388,7 @@ # A date is always valid by itself, but we need the object to support the function # when we're working with singletons. return True + #...................................................... #####--------------------------------------------------------------------------- @@ -488,6 +492,52 @@ 'equal','not_equal','less','less_equal', 'greater','greater_equal', 'isnan'] +class _datearithmetics(object): + """Defines a wrapper for arithmetic methods. +Instead of directly calling a ufunc, the corresponding method of the `array._data` +object is called instead. +If `asdates` is True, a DateArray object is returned , else a regular ndarray +is returned. + """ + def __init__ (self, methodname, asdates=True): + """ +:Parameters: + - `methodname` (String) : Method name. + """ + self.methodname = methodname + self._asdates = asdates + self.__doc__ = getattr(methodname, '__doc__') + self.obj = None + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, other, *args, **kwargs): + "Execute the call behavior." + instance = self.obj + freq = instance.freq + if 'context' not in kwargs: + kwargs['context'] = 'DateOK' + method = getattr(super(DateArray,instance), self.methodname) + if isinstance(other, DateArray): + if other.freq != freq: + raise FrequencyDateError("Cannot operate on dates", \ + freq, other.freq) + elif isinstance(other, Date): + if other.freq != freq: + raise FrequencyDateError("Cannot operate on dates", \ + freq, other.freq) + other = other.value + elif isinstance(other, ndarray): + if other.dtype.kind not in ['i','f']: + raise ArithmeticDateError + if self._asdates: + return instance.__class__(method(other, *args), + freq=freq) + else: + return method(other, *args) + class DateArray(ndarray): """Defines a ndarray of dates, as ordinals. @@ -506,12 +556,14 @@ if freq is None: _freq = getattr(dates, 'freq', -9999) else: - _freq = freq - cls._defaultfreq = corelib.check_freq(freq) + _freq = corelib.check_freq(freq) + cls._defaultfreq = corelib.check_freq(_freq) # Get the dates .......... - _dates = numeric.array(dates, copy=copy, dtype=int_, subok=1).view(cls) + _dates = numeric.array(dates, copy=copy, dtype=int_, subok=1) if _dates.ndim == 0: _dates.shape = (1,) + _dates = _dates.view(cls) + _dates.freq = _freq return _dates def __array_wrap__(self, obj, context=None): @@ -551,6 +603,17 @@ def __repr__(self): return ndarray.__repr__(self) #...................................................... + __add__ = _datearithmetics('__add__', asdates=True) + __radd__ = _datearithmetics('__add__', asdates=True) + __sub__ = _datearithmetics('__sub__', asdates=True) + __rsub__ = _datearithmetics('__rsub__', asdates=True) + __le__ = _datearithmetics('__le__', asdates=False) + __lt__ = _datearithmetics('__lt__', asdates=False) + __ge__ = _datearithmetics('__ge__', asdates=False) + __gt__ = _datearithmetics('__gt__', asdates=False) + __eq__ = _datearithmetics('__eq__', asdates=False) + __ne__ = _datearithmetics('__ne__', asdates=False) + #...................................................... @property def day(self): "Returns the day of month." @@ -717,64 +780,10 @@ "Returns whether the DateArray is valid: no missing/duplicated dates." return (self.isfull() and not self.has_duplicated_dates()) #...................................................... -class _datearithmetics(object): - """Defines a wrapper for arithmetic methods. -Instead of directly calling a ufunc, the corresponding method of the `array._data` -object is called instead. -If `asdates` is True, a DateArray object is returned , else a regular ndarray -is returned. - """ - def __init__ (self, methodname, asdates=True): - """ -:Parameters: - - `methodname` (String) : Method name. - """ - self.methodname = methodname - self._asdates = asdates - self.__doc__ = getattr(methodname, '__doc__') - self.obj = None - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, other, *args, **kwargs): - "Execute the call behavior." - instance = self.obj - freq = instance.freq - if 'context' not in kwargs: - kwargs['context'] = 'DateOK' - method = getattr(super(DateArray,instance), self.methodname) - if isinstance(other, DateArray): - if other.freq != freq: - raise FrequencyDateError("Cannot operate on dates", \ - freq, other.freq) -# other = - elif isinstance(other, Date): - if other.freq != freq: - raise FrequencyDateError("Cannot operate on dates", \ - freq, other.freq) - other = other.value - elif isinstance(other, ndarray): - if other.dtype.kind not in ['i','f']: - raise ArithmeticDateError - if self._asdates: - return instance.__class__(method(other, *args), - freq=freq) - else: - return method(other, *args) + #............................ -DateArray.__add__ = _datearithmetics('__add__', asdates=True) -DateArray.__radd__ = _datearithmetics('__add__', asdates=True) -DateArray.__sub__ = _datearithmetics('__sub__', asdates=True) -DateArray.__rsub__ = _datearithmetics('__rsub__', asdates=True) -DateArray.__le__ = _datearithmetics('__le__', asdates=False) -DateArray.__lt__ = _datearithmetics('__lt__', asdates=False) -DateArray.__ge__ = _datearithmetics('__ge__', asdates=False) -DateArray.__gt__ = _datearithmetics('__gt__', asdates=False) -DateArray.__eq__ = _datearithmetics('__eq__', asdates=False) -DateArray.__ne__ = _datearithmetics('__ne__', asdates=False) + #####--------------------------------------------------------------------------- #---- --- DateArray functions --- #####--------------------------------------------------------------------------- @@ -892,6 +901,8 @@ start_date = dlist # Case #2: we have a starting date .......... if start_date is None: + if length == 0: + return DateArray([], freq=freq) raise InsufficientDateError if not isDate(start_date): dmsg = "Starting date should be a valid Date instance! " @@ -905,7 +916,7 @@ else: if not isDate(end_date): raise DateError, "Ending date should be a valid Date instance!" - length = end_date - start_date + length = int(end_date - start_date) if include_last: length += 1 # dlist = [(start_date+i).value for i in range(length)] @@ -968,6 +979,20 @@ second = _frommethod('second') +def period_break(dates, period): + """Returns the indices where the given period changes. + +:Parameters: + dates : DateArray + Array of dates to monitor. + period : string + Name of the period to monitor. + """ + current = getattr(dates, period) + previous = getattr(dates-1, period) + return (current - previous).nonzero()[0] + + ################################################################################ if __name__ == '__main__': @@ -981,4 +1006,17 @@ # Using a date lag = mdates.find_dates(mdates[0]) print mdates[lag] - assert_equal(mdates[lag], mdates[0]) \ No newline at end of file + assert_equal(mdates[lag], mdates[0]) + if 1: + hodie = today('D') + D = DateArray(today('D')) + assert_equal(D.freq, 6000) + + if 1: + freqs = [x[0] for x in corelib.freq_dict.values() if x[0] != 'U'] + print freqs + for f in freqs: + print f + today = thisday(f) + assert(Date(freq=f, value=today.value) == today) + \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tests/test_dates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -26,15 +26,12 @@ from maskedarray.testutils import assert_equal, assert_array_equal import timeseries.tdates as tdates -reload(tdates) #from timeseries import tdates -##reload(tdates) #from timeseries.tdates import date_array_fromlist, Date, DateArray, date_array,\ # mxDFromString, today from timeseries.tdates import * from timeseries.tdates import mxDFromString from timeseries import tcore -#reload(tcore) class test_creation(NumpyTestCase): Modified: trunk/Lib/sandbox/timeseries/tests/test_multitimeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_multitimeseries.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tests/test_multitimeseries.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -18,16 +18,15 @@ from numpy.testing.utils import build_err_msg import maskedarray.testutils -#reload(maskedarray.testutils) from maskedarray.testutils import assert_equal, assert_array_equal import maskedarray.core as MA import maskedarray.mrecords as MR +from maskedarray.mrecords import addfield from maskedarray.core import getmaskarray, nomask, masked_array from timeseries import tmulti -reload(tmulti) from timeseries.tmulti import MultiTimeSeries, TimeSeries,\ fromarrays, fromtextfile, fromrecords, \ date_array, time_series @@ -63,11 +62,14 @@ assert_equal(mts['f0']._mask, m) # assert(isinstance(mts[0], MultiTimeSeries)) - assert_equal(mts[0]._data, mrec[0]) - assert_equal(mts[0]._dates, dates[0]) + assert_equal(mts._data[0], mrec[0]) + # We can't use assert_equal here, as it tries to convert the tuple into a singleton + assert(mts[0]._data.view(N.ndarray) == mrec[0]) + assert_equal(mts._dates[0], dates[0]) + assert_equal(mts[0]._dates, dates[0]) # assert(isinstance(mts['2007-01'], MultiTimeSeries)) - assert_equal(mts['2007-01']._data, mrec[0]) + assert(mts['2007-01']._data == mrec[0]) assert_equal(mts['2007-01']._dates, dates[0]) # assert_equal(mts.f0, time_series(d, dates=dates, mask=m)) @@ -131,7 +133,7 @@ def test_addfield(self): "Tests addfield" [d, m, mrec, dlist, dates, ts, mts] = self.data - mts.addfield(masked_array(d+10, mask=m[::-1])) + mts = addfield(mts, masked_array(d+10, mask=m[::-1])) assert_equal(mts.f2, d+10) assert_equal(mts.f2._mask, m[::-1]) Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -26,7 +26,6 @@ from maskedarray.testutils import assert_equal, assert_array_equal from timeseries import tseries -#reload(tseries) from timeseries.tseries import Date, date_array_fromlist, date_array, thisday from timeseries.tseries import time_series, TimeSeries, adjust_endpoints, \ mask_period, align_series, fill_missing_dates, tsmasked, concatenate_series @@ -263,6 +262,44 @@ assert_equal(ser_x[:,0], time_series(a, d)) assert_equal(ser_x[:,:], ser_x) + def test_onnd(self): + "Tests getitem on a nD series" + hodie = thisday('D') + # Case 1D + series = time_series(N.arange(5), mask=[1,0,0,0,0], start_date=hodie) + assert_equal(series[0], 0) + # Case 1D + mask + series = time_series(N.arange(5), mask=[1,0,0,0,0], start_date=hodie) + assert series[0] is tsmasked + # Case 2D + series = time_series(N.arange(10).reshape(5,2), start_date=hodie) + assert_equal(len(series), 5) + assert_equal(series[0], [[0,1]]) + assert_equal(series[0]._dates, (hodie)) + assert_equal(series[:,0], [0,2,4,6,8]) + assert_equal(series[:,0]._dates, series._dates) + # Case 2D + mask + series = time_series(N.arange(10).reshape(5,2), start_date=hodie, + mask=[[1,1],[0,0],[0,0],[0,0],[0,0]]) + assert_equal(len(series), 5) + assert_equal(series[0], [[0,1]]) + assert_equal(series[0]._mask, [[1,1]]) + assert_equal(series[0]._dates, (hodie)) + assert_equal(series[:,0]._data, [0,2,4,6,8]) + assert_equal(series[:,0]._mask, [1,0,0,0,0]) + assert_equal(series[:,0]._dates, series._dates) + # Case 3D + series = time_series(N.arange(30).reshape(5,3,2), start_date=hodie) + x = series[0] + assert_equal(len(series), 5) + assert_equal(series[0], [[[0,1],[2,3],[4,5]]]) + assert_equal(series[0]._dates, (hodie)) + assert_equal(series[:,0], series._data[:,0]) + assert_equal(series[:,0]._dates, series._dates) + x = series[:,:,0] + assert_equal(series[:,:,0], series._data[:,:,0]) + assert_equal(series[:,:,0]._dates, series._dates) + class test_functions(NumpyTestCase): "Some getitem tests" def __init__(self, *args, **kwds): Modified: trunk/Lib/sandbox/timeseries/tmulti.py =================================================================== --- trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -27,8 +27,6 @@ from numpy.core.records import fromarrays as recfromarrays import maskedarray as MA -#import numpy.core.ma as MA -#reload(MA) #MaskedArray = MA.MaskedArray from maskedarray.core import MaskedArray, MAError, default_fill_value, \ masked_print_option @@ -36,7 +34,6 @@ make_mask_none, mask_or, masked_array, filled import maskedarray.mrecords as MR -#reload(MR) from maskedarray.mrecords import _checknames, _guessvartypes, openfile,\ MaskedRecords from maskedarray.mrecords import fromrecords as mrecfromrecords @@ -74,7 +71,6 @@ formats += ',' return formats[:-1] - @@ -102,12 +98,12 @@ byteorder=byteorder, aligned=aligned) # if isinstance(data, MultiTimeSeries): - cls._defaultfieldmask = data._series._fieldmask - cls._defaulthardmask = data._series._hardmask | hard_mask - cls._fill_value = data._series._fill_value - return data._data.view(cls) +# if copy: +# data = data.copy() + data._hardmask = data._hardmask | hard_mask + return data # ....................................... - _data = MaskedRecords(data, mask=mask, dtype=dtype, **mroptions) + _data = MaskedRecords(data, mask=mask, dtype=dtype, **mroptions).view(cls) if dates is None: length = _getdatalength(data) newdates = date_array(start_date=start_date, length=length, @@ -116,72 +112,67 @@ newdates = date_array(dlist=dates, freq=freq) else: newdates = dates - cls._defaultdates = newdates - cls._defaultobserved = observed + _data._dates = newdates + _data._observed = observed cls._defaultfieldmask = _data._fieldmask # - return _data.view(cls) -# -# #.................................. -# def __array_wrap__(self, obj, context=None): -# """Special hook for ufuncs. -#Wraps the numpy array and sets the mask according to context. -# """ -## mclass = self.__class__ -# #.......... -# if context is None: -## return mclass(obj, mask=self._mask, copy=False) -# return MaskedArray(obj, mask=self._mask, copy=False, -# dtype=obj.dtype, -# fill_value=self.fill_value, ) -# #.......... -# (func, args) = context[:2] -# -## return mclass(obj, copy=False, mask=m) -# return MultiTimeSeries(obj, copy=False, mask=m,) -## dtype=obj.dtype, fill_value=self._fill_value) - def __array_finalize__(self,obj): - if isinstance(obj, MultiTimeSeries): - self.__dict__.update(_dates=obj._dates, - _series=obj._series, - _data=obj._series._data, - _fieldmask=obj._series._fieldmask, - _hardmask=obj._series._hardmask, - _fill_value=obj._fill_value + return _data + + def __array_finalize__(self,obj): + if isinstance(obj, (MaskedRecords)): + self.__dict__.update(_fieldmask=obj._fieldmask, + _hardmask=obj._hardmask, + _fill_value=obj._fill_value, ) + if isinstance(obj, MultiTimeSeries): + self.__dict__.update(observed=obj.observed, + _dates=obj._dates) + else: + self.__dict__.update(observed=None, + _dates=[]) else: - self.__dict__.update(_data = obj.view(recarray), - _dates = self._defaultdates, - _series = MaskedRecords(obj, dtype=obj.dtype), - _fieldmask = self._defaultfieldmask, - _hardmask = self._defaulthardmask, - fill_value = self._fill_value + self.__dict__.update(_dates = [], + observed=None, + _fieldmask = nomask, + _hardmask = False, + fill_value = None ) - MultiTimeSeries._defaultfieldmask = nomask - MultiTimeSeries._defaulthardmask = False return + + + def _getdata(self): + "Returns the data as a recarray." + return self.view(recarray) + _data = property(fget=_getdata) + + def _getseries(self): + "Returns the data as a MaskedRecord array." + return self.view(MaskedRecords) + _series = property(fget=_getseries) + #...................................................... def __getattribute__(self, attr): - try: - # Returns a generic attribute - return object.__getattribute__(self,attr) - except AttributeError: - # OK, so attr must be a field name - pass - # Get the list of fields ...... - _names = self.dtype.names - _local = self.__dict__ - _mask = _local['_fieldmask'] - if attr in _names: - _data = _local['_data'] - obj = numeric.asarray(_data.__getattribute__(attr)).view(MaskedArray) - obj._mask = make_mask(_mask.__getattribute__(attr)) - return obj - elif attr == '_mask': - if self.size > 1: - return _mask.view((bool_, len(self.dtype))).all(1) - return _mask.view((bool_, len(self.dtype))) - raise AttributeError,"No attribute '%s' !" % attr + return MaskedRecords.__getattribute__(self,attr) +# try: +# # Returns a generic attribute +# return object.__getattribute__(self,attr) +# except AttributeError: +# # OK, so attr must be a field name +# pass +# # Get the list of fields ...... +# _names = self.dtype.names +# _local = self.__dict__ +# _mask = _local['_fieldmask'] +# if attr in _names: +# _data = self._data +# obj = numeric.asarray(_data.__getattribute__(attr)).view(MaskedArray) +# obj._mask = make_mask(_mask.__getattribute__(attr)) +# return obj +# elif attr == '_mask': +# if self.size > 1: +# return _mask.view((bool_, len(self.dtype))).all(1) +# return _mask.view((bool_, len(self.dtype))) +# raise AttributeError,"No attribute '%s' !" % attr def __setattr__(self, attr, val): newattr = attr not in self.__dict__ @@ -195,7 +186,7 @@ exctype, value = sys.exc_info()[:2] raise exctype, value else: - if attr not in list(self.dtype.names) + ['_mask']: + if attr not in list(self.dtype.names) + ['_dates','_mask']: return ret if newattr: # We just added this one try: # or this setattr worked on an internal @@ -234,59 +225,36 @@ _localdict = self.__dict__ # We want a field ........ if indx in self.dtype.names: - obj = _localdict['_series'][indx].view(TimeSeries) + obj = self._data[indx].view(TimeSeries) + obj._dates = _localdict['_dates'] obj._mask = make_mask(_localdict['_fieldmask'][indx]) return obj # We want some elements .. - (sindx, dindx) = super(MultiTimeSeries, self)._TimeSeries__checkindex(indx) - return MultiTimeSeries(_localdict['_series'][sindx], - dates=_localdict['_dates'][dindx], -# mask=_localdict['_fieldmask'][indx], - dtype=self.dtype) + (sindx, dindx) = self._TimeSeries__checkindex(indx) +# obj = numeric.array(self._data[sindx], +# copy=False, subok=True).view(type(self)) + obj = numeric.array(self._data[sindx], copy=False, subok=True) + obj = obj.view(type(self)) + obj.__dict__.update(_dates=_localdict['_dates'][dindx], + _fieldmask=_localdict['_fieldmask'][sindx], + _fill_value=_localdict['_fill_value']) + return obj def __getslice__(self, i, j): """Returns the slice described by [i,j].""" _localdict = self.__dict__ (si, di) = super(MultiTimeSeries, self)._TimeSeries__checkindex(i) (sj, dj) = super(MultiTimeSeries, self)._TimeSeries__checkindex(j) - return MultiTimeSeries(_localdict['_data'][si:sj], - mask=_localdict['_fieldmask'][si:sj], - dates=_localdict['_dates'][di:dj], - dtype=self.dtype) + newdata = self._data[si:sj].view(type(self)) + newdata.__dict__.update(_dates=_localdict['_dates'][di:dj], + _mask=_localdict['_fieldmask'][si:sj]) + return newdata def __setslice__(self, i, j, value): """Sets the slice described by [i,j] to `value`.""" - _localdict = self.__dict__ - d = _localdict['_data'] - t = _localdict['_dates'] - m = _localdict['_fieldmask'] - names = self.dtype.names - if value is masked: - for n in names: - m[i:j][n] = masked - elif not self._hardmask: - fval = filled(value) - mval = getmaskarray(value) - for n in names: - d[n][i:j] = fval - m[n][i:j] = mval - else: - mindx = getmaskarray(self)[i:j] - val = masked_array(value, mask=mindx, keep_mask=True) - valmask = getmask(value) - if valmask is nomask: - for n in names: - mval = mask_or(m[n][i:j], valmask) - d[n][i:j][~mval] = filled(value) - elif valmask.size > 1: - for n in names: - mval = mask_or(m[n][i:j], valmask) - d[n][i:j][~mval] = fval[~mval] - m[n][i:j] = mask_or(m[n][i:j], mval) + self.view(MaskedRecords).__setslice__(i,j,value) + return - return MultiTimeSeries(d, mask=m, dates=t[i:j], dtype=self.dtype) - - #...................................................... def __str__(self): """x.__str__() <==> str(x) @@ -320,25 +288,6 @@ fmt % (' fill_value', self._fill_value), ' )']) return str("\n".join(reprstr)) - #...................................................... - def view(self, obj): - """Returns a view of the mrecarray.""" - try: - if issubclass(obj, ndarray): - return ndarray.view(self, obj) - except TypeError: - pass - dtype = numeric.dtype(obj) - if dtype.fields is None: - return self.__array__().view(dtype) - return ndarray.view(self, obj) - #............................................ -# def 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 argument." @@ -347,10 +296,8 @@ dates=_localdict['_dates'].copy(), mask=_localdict['_fieldmask'].copy(), dtype=self.dtype) - #............................................. - def addfield(self, newfield, newfieldname=None): - MaskedRecords.addfield(self, newfield, newfieldname) + #####--------------------------------------------------------------------------- #---- --- Constructors --- #####--------------------------------------------------------------------------- @@ -561,13 +508,11 @@ return MultiTimeSeries(_datalist, dates=newdates, dtype=mdescr) - -################################################################################ - ################################################################################ if __name__ == '__main__': import numpy as N + from maskedarray.testutils import assert_equal if 1: d = N.arange(5) m = MA.make_mask([1,0,0,1,1]) @@ -579,4 +524,17 @@ dates = date_array(dlist) ts = time_series(mrec,dates) mts = MultiTimeSeries(mrec,dates) - self_data = [d, m, mrec, dlist, dates, ts, mts] \ No newline at end of file + self_data = [d, m, mrec, dlist, dates, ts, mts] + + if 1: + mts[:2] = 5 + assert_equal(mts.f0._data, [5,5,2,3,4]) + assert_equal(mts.f1._data, [5,5,2,1,0]) + assert_equal(mts.f0._mask, [0,0,0,1,1]) + assert_equal(mts.f1._mask, [0,0,0,0,1]) + mts.harden_mask() + mts[-2:] = 5 + assert_equal(mts.f0._data, [5,5,2,3,4]) + assert_equal(mts.f1._data, [5,5,2,5,0]) + assert_equal(mts.f0._mask, [0,0,0,1,1]) + assert_equal(mts.f1._mask, [0,0,0,0,1]) \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-22 21:00:31 UTC (rev 2753) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-23 03:16:56 UTC (rev 2754) @@ -32,27 +32,23 @@ import numpy.core.fromnumeric as fromnumeric import numpy.core.numeric as numeric import numpy.core.umath as umath -#from numpy.core.records import recarray +from numpy.core.records import recarray from numpy.core.records import fromarrays as recfromarrays import maskedarray.core as MA -#reload(MA) from maskedarray.core import MaskedArray, MAError, masked, nomask, \ filled, getmask, getmaskarray, make_mask_none, mask_or, make_mask, \ masked_array import tcore as corelib -#reload(corelib) from tcore import * import tdates -#reload(tdates) from tdates import DateError, InsufficientDateError from tdates import Date, isDate, DateArray, isDateArray, \ - date_array, date_array_fromlist, date_array_fromrange, thisday + date_array, date_array_fromlist, date_array_fromrange, thisday, today import cseries -#reload(cseries) @@ -162,7 +158,8 @@ if dsize == tsize: return True elif data.ndim > 1: - dsize = numeric.asarray(data.shape)[:-1].prod() + #dsize = numeric.asarray(data.shape)[:-1].prod() + dsize = data.shape[0] if dsize == tsize: return True elif data.ndim == 0 and tsize <= 1: @@ -241,11 +238,40 @@ func_series = getattr(super(TimeSeries, instance), _name) result = func_series(*args) if self._ondates: - result._dates = getattr(instance._dates, _name) + result._dates = getattr(instance._dates, _name)(*args) else: result._dates = instance._dates return result +class _tsaxismethod(object): + """Defines a wrapper for array methods working on an axis (mean...). +When called, returns a ndarray, as the result of the method applied on the series. + """ + def __init__ (self, methodname): + """abfunc(fillx, filly) must be defined. + abinop(x, filly) = x for all x to enable reduce. + """ + self._name = methodname + # + def __get__(self, obj, objtype=None): + self.obj = obj + return self + # + def __call__ (self, *args, **params): + "Execute the call behavior." + (_dates, _series) = (self.obj._dates, self.obj._series) + func = getattr(_series, self._name) + result = func(*args, **params) + if _dates.size == _series.size: + return result + else: + try: + axis = params.get('axis', args[0]) + if axis in [-1, _series.ndim-1]: + result = TimeSeries(result, dates=_dates) + except IndexError: + pass + return result class TimeSeries(MaskedArray, object): """Base class for the definition of time series. @@ -284,9 +310,9 @@ if freq is not None and newdates.freq != freq: newdates = newdates.tofreq(freq) else: - length = _getdatalength(data) - if length > 0: - newdates = date_array(start_date=start_date, length=length, + dshape = _data.shape + if len(dshape) > 0: + newdates = date_array(start_date=start_date, length=dshape[0], freq=freq) else: newdates = date_array([], freq=freq) @@ -304,8 +330,8 @@ #............................................ def __array_finalize__(self,obj): MaskedArray.__array_finalize__(self, obj) - self._dates = getattr(obj, '_dates', None) - self.observed = getattr(obj, 'observed', self._defaultobserved) + self._dates = getattr(obj, '_dates', []) + self.observed = getattr(obj, 'observed', None) return #.................................. def __array_wrap__(self, obj, context=None): @@ -323,7 +349,7 @@ "Checks the validity of an index." if isinstance(indx, int): return (indx, indx) - if isinstance(indx, str): + elif isinstance(indx, str): indx = self._dates.date_to_index(Date(self._dates.freq, string=indx)) return (indx, indx) elif isDate(indx): @@ -345,9 +371,12 @@ elif isinstance(indx, tuple): if len(indx) > self.shape: raise IndexError, "Too many indices" - elif len(indx)==2: - return (indx,indx[0]) - return (indx,indx[:-1]) + if self._dates.size == self.size: + return (indx, indx) + return (indx,indx[0]) +# elif len(indx)==2: +# return (indx,indx[0]) +# return (indx,indx[:-1]) elif isTimeSeries(indx): indx = indx._series if getmask(indx) is not nomask: @@ -360,31 +389,47 @@ Returns the item described by i. Not a copy as in previous versions. """ (sindx, dindx) = self.__checkindex(indx) - data = self._series[sindx] - date = self._dates[dindx] + newdata = numeric.array(self._series[sindx], copy=False, subok=True) + newdate = self._dates[dindx] m = self._mask - - singlepoint = (len(numeric.shape(date))==0) - + singlepoint = (len(numeric.shape(newdate))==0) if singlepoint: - if data is not masked and self.ndim > 1: - data = data.reshape((list((1,)) + list(data.shape))) - date = date_array(start_date=date, length=1, freq=date.freq) - - if m is nomask: - return TimeSeries(data, dates=date, mask=nomask, keep_mask=True, - copy=False) + if newdata is masked: + newdata = tsmasked + newdata._dates = newdate + return newdata + elif self.ndim > 1: + # CHECK: use reshape, or set shape ? + newshape = (list((1,)) + list(newdata.shape)) + newdata.shape = newshape + newdata = newdata.view(type(self)) + newdata._dates = newdate + return newdata +# CHECK : The implementation below should work, but does not. Why ? +# newdata = numeric.array(self._data[sindx], copy=False) +# newdates = self._dates[dindx] +# if self._mask is not nomask: +# newmask = self._mask.copy()[sindx] +# else: +# newmask = nomask +# singlepoint = (len(numeric.shape(newdates))==0) +# if singlepoint: +# if newmask.ndim == 0 and newmask: +# output = tsmasked +# output._dates = newdates +# return output +# if self.ndim > 1: +# # CHECK: use reshape, or set shape ? +# newdata = newdata.reshape((list((1,)) + list(newdata.shape))) +# if newmask is not nomask: +# newmask.shape = newdata.shape +# newdata = newdata.view(type(self)) +# newdata._dates = newdates +# newdata._mask = newmask +# return newdata - mi = m[sindx] - if mi.size == 1: - if mi: - output = tsmasked - output._dates = date - return output - return TimeSeries(data, dates=date, mask=nomask) - else: - return TimeSeries(data, dates=date, mask=mi) + #........................ def __setitem__(self, indx, value): """x.__setitem__(i, y) <==> x[i]=y @@ -465,7 +510,6 @@ __gt__ = _tsmathmethod('__gt__') __ge__ = _tsmathmethod('__ge__') - astype = _tsarraymethod('astype') reshape = _tsarraymethod('reshape', ondates=True) copy = _tsarraymethod('copy', ondates=True) compress = _tsarraymethod('compress', ondates=True) @@ -474,6 +518,17 @@ cumprod = _tsarraymethod('cumprod',ondates=False) anom = _tsarraymethod('anom',ondates=False) + sum = _tsaxismethod('sum') + prod = _tsaxismethod('prod') + mean = _tsaxismethod('mean') + var = _tsaxismethod('var') + varu = _tsaxismethod('varu') + std = _tsaxismethod('std') + stdu = _tsaxismethod('stdu') + all = _tsaxismethod('all') + any = _tsaxismethod('any') + + # def nonzero(self): # """Returns a tuple of ndarrays, one for each dimension of the array, # containing the indices of the non-zero elements in that dimension.""" @@ -599,6 +654,21 @@ "Converts the dates to another frequency, and adapt the data." return convert(self, freq, func=func, position=position) #..................................................... + def transpose(self, *axes): + if self._dates.size == self.size: + result = super(TimeSeries, self).transpose(*axes) + result._dates = self._dates.transpose(*axes) + else: + errmsg = "Operation not permitted on multi-variable series" + print "AXES:",axes + if (len(axes)==0) or axes[0] != 0: + raise ValueError, errmsg + else: + result = super(TimeSeries, self).transpose(*axes) + result._dates = self._dates + return result + + def _attrib_dict(series, exclude=[]): """this function is used for passing through attributes of one @@ -612,45 +682,7 @@ ##--- ... Additional methods ... ##### -------------------------------------------------------------------------- -class _tsaxismethod(object): - """Defines a wrapper for array methods working on an axis (mean...). -When called, returns a ndarray, as the result of the method applied on the series. - """ - def __init__ (self, methodname): - """abfunc(fillx, filly) must be defined. - abinop(x, filly) = x for all x to enable reduce. - """ - self._name = methodname - # - def __get__(self, obj, objtype=None): - self.obj = obj - return self - # - def __call__ (self, *args, **params): - "Execute the call behavior." - (_dates, _series) = (self.obj._dates, self.obj._series) - func = getattr(_series, self._name) - result = func(*args, **params) - if _series.ndim < 2 or _dates.size == _series.size: - return result - else: - try: - axis = params.get('axis', args[0]) - if axis in [-1, _series.ndim-1]: - result = TimeSeries(result, dates=_dates) - except IndexError: - pass - return result #....................................... -TimeSeries.sum = _tsaxismethod('sum') -TimeSeries.prod = _tsaxismethod('prod') -TimeSeries.mean = _tsaxismethod('mean') -TimeSeries.var = _tsaxismethod('var') -TimeSeries.varu = _tsaxismethod('varu') -TimeSeries.std = _tsaxismethod('std') -TimeSeries.stdu = _tsaxismethod('stdu') -TimeSeries.all = _tsaxismethod('all') -TimeSeries.any = _tsaxismethod('any') class _tsblockedmethods(object): @@ -668,7 +700,7 @@ # def __call__ (self, *args, **params): raise NotImplementedError -TimeSeries.transpose = _tsarraymethod('transpose', ondates=True) +#TimeSeries.transpose = _tsarraymethod('transpose', ondates=True) TimeSeries.swapaxes = _tsarraymethod('swapaxes', ondates=True) @@ -851,14 +883,15 @@ `data` : Array of data. """ - data = numeric.asanyarray(data) + data = numeric.array(data, copy=False, subok=True) if dates is None: - length = _getdatalength(data) - if length > 0: + dshape = data.shape + if len(dshape) > 0: dates = date_array(start_date=start_date, end_date=end_date, - length=length, include_last=include_last, freq=freq) + length=dshape[0], include_last=include_last, + freq=freq) else: - dates = date_array([], freq=freq) + dates = date_array([], freq=freq) elif not isinstance(dates, DateArray): dates = date_array(dlist=dates, freq=freq) return TimeSeries(data=data, dates=dates, mask=mask, observed=observed, @@ -1310,8 +1343,31 @@ ################################################################################ if __name__ == '__main__': - from maskedarray.testutils import assert_equal + from maskedarray.testutils import assert_equal, assert_array_equal import numpy as N - - + if 1: + dlist = ['2007-01-%02i' % i for i in range(1,11)] + dates = date_array_fromlist(dlist) + data = masked_array(numeric.arange(10), mask=[1,0,0,0,0]*2, dtype=float_) + ser1d = time_series(data, dlist) + + serfolded = ser1d.reshape((5,2)) + assert_equal(serfolded._dates.shape, (5,2)) + assert_equal(serfolded[0], time_series([0,1],mask=[1,0], + start_date=dates[0])) + assert_equal(serfolded[:,0], + time_series(ser1d[::2], dates=dates[::2])) + sertrans = serfolded.transpose() + assert_equal(sertrans.shape, (2,5)) + + assert ser1d[0] is tsmasked + print "OK" + if 1: + hodie = today('D') + ser2d = time_series(N.arange(10).reshape(5,2), start_date=hodie, + mask=[[1,1],[0,0],[0,0],[0,0],[0,0]]) + if 1: + hodie = today('D') + ser3d = time_series(N.arange(30).reshape(5,3,2), start_date=hodie,) + From scipy-svn at scipy.org Thu Feb 22 22:25:49 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Thu, 22 Feb 2007 21:25:49 -0600 (CST) Subject: [Scipy-svn] r2755 - trunk/Lib/sandbox/maskedarray Message-ID: <20070223032549.CE9F839C018@new.scipy.org> Author: pierregm Date: 2007-02-22 21:25:46 -0600 (Thu, 22 Feb 2007) New Revision: 2755 Modified: trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/mrecords.py Log: mrecords : fixed _getformats for the case when data is a (subclass of) recarray core : in array_finalize, use fromnumeric.reshape instead of setting shape Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-23 03:16:56 UTC (rev 2754) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-23 03:25:46 UTC (rev 2755) @@ -1171,7 +1171,7 @@ else: self._mask.flat = newmask if self._mask.shape: - self._mask.shape = self.shape + self._mask = numeric.reshape(self._mask, self.shape) _setmask = __setmask__ def _get_mask(self): Modified: trunk/Lib/sandbox/maskedarray/mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-23 03:16:56 UTC (rev 2754) +++ trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-23 03:25:46 UTC (rev 2755) @@ -41,7 +41,7 @@ def _getformats(data): """Returns the formats of each array of arraylist as a comma-separated string.""" - if isinstance(data, record): + if hasattr(data,'dtype'): return ",".join([desc[1] for desc in data.dtype.descr]) formats = '' @@ -644,4 +644,15 @@ return newdata ################################################################################ - \ No newline at end of file +if __name__ == '__main__': + import numpy as N + if 1: + d = N.arange(5) + m = MA.make_mask([1,0,0,1,1]) + base_d = N.r_[d,d[::-1]].reshape(2,-1).T + base_m = N.r_[[m, m[::-1]]].T + base = MA.array(base_d, mask=base_m) + mrecord = fromarrays(base.T,) + + mrec = MaskedRecords(mrecord) + \ No newline at end of file From scipy-svn at scipy.org Fri Feb 23 15:27:10 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 23 Feb 2007 14:27:10 -0600 (CST) Subject: [Scipy-svn] r2756 - trunk/Lib/sandbox/timeseries Message-ID: <20070223202710.F0AA239C093@new.scipy.org> Author: mattknox_ca Date: 2007-02-23 14:27:07 -0600 (Fri, 23 Feb 2007) New Revision: 2756 Added: trunk/Lib/sandbox/timeseries/license.txt Log: added EGENIX public license for functions borrowed from mx.DateTime Added: trunk/Lib/sandbox/timeseries/license.txt =================================================================== --- trunk/Lib/sandbox/timeseries/license.txt 2007-02-23 03:25:46 UTC (rev 2755) +++ trunk/Lib/sandbox/timeseries/license.txt 2007-02-23 20:27:07 UTC (rev 2756) @@ -0,0 +1,97 @@ +The timeseries module contains code borrowed from the EGENIX mx.DateTime +package. As such, it is subject to the terms of the EGENIX PUBLIC +LICENSE AGREEMENT VERSION 1.0.0 (included below). + +Functions in cseries.c marked in the section labelled +//DERIVED FROM mx.DateTime +are slightly modified versions of functions found in mxDateTime.c in the +mx.DateTime source code. + +================================================================= + +EGENIX.COM PUBLIC LICENSE AGREEMENT VERSION 1.0.0 + +1. Introduction + +This "License Agreement" is between eGenix.com Software, Skills and Services +GmbH ("eGenix.com"), having an office at Pastor-Loeh-Str. 48, D-40764 +Langenfeld, Germany, and the Individual or Organization ("Licensee") accessing +and otherwise using this software in source or binary form and its associated +documentation ("the Software"). + + +2. License + +Subject to the terms and conditions of this eGenix.com Public License Agreement, +eGenix.com hereby grants Licensee a non-exclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, prepare +derivative works, distribute, and otherwise use the Software alone or in any +derivative version, provided, however, that the eGenix.com Public License +Agreement is retained in the Software, or in any derivative version of the +Software prepared by Licensee. + + +3. NO WARRANTY + +eGenix.com is making the Software available to Licensee on an "AS IS" basis. +SUBJECT TO ANY STATUTORY WARRANTIES WHICH CAN NOT BE EXCLUDED, EGENIX.COM MAKES +NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT +LIMITATION, EGENIX.COM MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF +MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE +SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. + + +4. LIMITATION OF LIABILITY + +EGENIX.COM SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS (INCLUDING, +WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, +LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) AS A RESULT OF USING, +MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF +ADVISED OF THE POSSIBILITY THEREOF. + +SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR +CONSEQUENTIAL DAMAGES, SO THE ABOVE EXCLUSION OR LIMITATION MAY NOT APPLY TO +LICENSEE. + + +5. Termination + +This License Agreement will automatically terminate upon a material breach of +its terms and conditions. + + +6. General + +Nothing in this License Agreement affects any statutory rights of consumers that +cannot be waived or limited by contract. + +Nothing in this License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between eGenix.com and Licensee. + +If any provision of this License Agreement shall be unlawful, void, or for any +reason unenforceable, such provision shall be modified to the extent necessary +to render it enforceable without losing its intent, or, if no such modification +is possible, be severed from this License Agreement and shall not affect the +validity and enforceability of the remaining provisions of this License +Agreement. + +This License Agreement shall be governed by and interpreted in all respects by +the law of Germany, excluding conflict of law provisions. It shall not be +governed by the United Nations Convention on Contracts for International Sale of +Goods. + +This License Agreement does not grant permission to use eGenix.com trademarks or +trade names in a trademark sense to endorse or promote products or services of +Licensee, or any third party. + +The controlling language of this License Agreement is English. If Licensee has +received a translation into another language, it has been provided for +Licensee's convenience only. + + +7. Agreement + +By downloading, copying, installing or otherwise using the Software, Licensee +agrees to be bound by the terms and conditions of this License Agreement. + From scipy-svn at scipy.org Fri Feb 23 15:27:39 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 23 Feb 2007 14:27:39 -0600 (CST) Subject: [Scipy-svn] r2757 - trunk/Lib/sandbox/timeseries/src Message-ID: <20070223202739.A484539C093@new.scipy.org> Author: mattknox_ca Date: 2007-02-23 14:27:35 -0600 (Fri, 23 Feb 2007) New Revision: 2757 Modified: trunk/Lib/sandbox/timeseries/src/cseries.c Log: major overhaul. Eliminated use of mx.DateTime Modified: trunk/Lib/sandbox/timeseries/src/cseries.c =================================================================== --- trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-23 20:27:07 UTC (rev 2756) +++ trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-23 20:27:35 UTC (rev 2757) @@ -2,7 +2,6 @@ #include #include #include -#include "mxDateTime.h" #include "arrayobject.h" static char cseries_doc[] = "Speed sensitive time series operations"; @@ -12,7 +11,7 @@ #define FR_MTH 3000 /* Monthly */ #define FR_WK 4000 /* Weekly */ #define FR_BUS 5000 /* Business days */ -#define FR_DAY 6000 /* Daily */ +#define FR_DAY 6000 /* Daily */ #define FR_HR 7000 /* Hourly */ #define FR_MIN 8000 /* Minutely */ #define FR_SEC 9000 /* Secondly */ @@ -23,79 +22,417 @@ PyDict_SetItemString(dict, key, pyval); \ Py_DECREF(pyval); } -static long minval_D_toHighFreq = 719163; -/////////////////////////////////////////////////////////////////////// +//DERIVED FROM mx.DateTime +/* +===================================================== +== Functions in the following section are borrowed == +== from mx.DateTime, and in many cases slightly == +== modified == +===================================================== +*/ -// helpers for frequency conversion routines // +#define Py_AssertWithArg(x,errortype,errorstr,a1) {if (!(x)) {PyErr_Format(errortype,errorstr,a1);goto onError;}} +#define Py_Error(errortype,errorstr) {PyErr_SetString(errortype,errorstr);goto onError;} -static long DtoB_weekday(long fromDate) { return (((fromDate) / 7) * 5) + (fromDate)%7; } +static PyObject *DateCalc_Error; /* Error Exception object */ +static PyObject *DateCalc_RangeError; /* Error Exception object */ -static long DtoB_WeekendToMonday(mxDateTimeObject *dailyDate) { +#define DINFO_ERR -99 - long absdate = dailyDate->absdate; - if (dailyDate->day_of_week > 4) { - //change to Monday after weekend - absdate += (7 - dailyDate->day_of_week); +#define GREGORIAN_CALENDAR 0 +#define JULIAN_CALENDAR 1 + +#define SECONDS_PER_DAY ((double) 86400.0) + +/* Table with day offsets for each month (0-based, without and with leap) */ +static int month_offset[2][13] = { + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* Table of number of days in a month (0-based, without and with leap) */ +static int days_in_month[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } +}; + +struct date_info { + long absdate; + double abstime; + + double second; + int minute; + int hour; + int day; + int month; + int quarter; + int year; + int day_of_week; + int day_of_year; + int calendar; +}; + + +/* Return 1/0 iff year points to a leap year in calendar. */ +static +int dInfoCalc_Leapyear(register long year, + int calendar) +{ + if (calendar == GREGORIAN_CALENDAR) { + return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); + } else { + return (year % 4 == 0); } - return DtoB_weekday(absdate); } -static long DtoB_WeekendToFriday(mxDateTimeObject *dailyDate) { +static +int dInfoCalc_ISOWeek(struct date_info dinfo) +{ + int week; - long absdate = dailyDate->absdate; - if (dailyDate->day_of_week > 4) { - //change to friday before weekend - absdate -= (dailyDate->day_of_week - 4); + /* Estimate */ + week = (dinfo.day_of_year-1) - dinfo.day_of_week + 3; + if (week >= 0) week = week / 7 + 1; + + /* Verify */ + if (week < 0) { + /* The day lies in last week of the previous year */ + if ((week > -2) || + (week == -2 && dInfoCalc_Leapyear(dinfo.year-1, dinfo.calendar))) + week = 53; + else + week = 52; + } else if (week == 53) { + /* Check if the week belongs to year or year+1 */ + if (31-dinfo.day + dinfo.day_of_week < 3) { + week = 1; + } } - return DtoB_weekday(absdate); + + return week; } -static long absdate_from_ymd(int y, int m, int d) { - mxDateTimeObject *tempDate; - long result; - tempDate = (mxDateTimeObject *)mxDateTime.DateTime_FromDateAndTime(y,m,d,0,0,0); - result = (long)(tempDate->absdate); - Py_DECREF(tempDate); - return result; +/* Return the day of the week for the given absolute date. */ +static +int dInfoCalc_DayOfWeek(register long absdate) +{ + int day_of_week; + + if (absdate >= 1) { + day_of_week = (absdate - 1) % 7; + } else { + day_of_week = 6 - ((-absdate) % 7); + } + return day_of_week; } +/* Return the year offset, that is the absolute date of the day + 31.12.(year-1) in the given calendar. -static mxDateTimeObject *day_before_mxobj(int y, int m, int d) { - return (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(absdate_from_ymd(y, m, d) - 1, 0); + Note: + For the Julian calendar we shift the absdate (which is measured + using the Gregorian Epoch) value by two days because the Epoch + (0001-01-01) in the Julian calendar lies 2 days before the Epoch in + the Gregorian calendar. */ +static +int dInfoCalc_YearOffset(register long year, + int calendar) +{ + year--; + if (calendar == GREGORIAN_CALENDAR) { + if (year >= 0 || -1/4 == -1) + return year*365 + year/4 - year/100 + year/400; + else + return year*365 + (year-3)/4 - (year-99)/100 + (year-399)/400; + } + else if (calendar == JULIAN_CALENDAR) { + if (year >= 0 || -1/4 == -1) + return year*365 + year/4 - 2; + else + return year*365 + (year-3)/4 - 2; + } + Py_Error(DateCalc_Error, "unknown calendar"); + onError: + return -1; } -/* -returns Date(y, m, d) converted to business frequency. -If the initial result is a weekend, the following Monday is returned -*/ -static long busday_before(int y, int m, int d) { - mxDateTimeObject *dailyDate; - long result; - dailyDate = (mxDateTimeObject *)mxDateTime.DateTime_FromDateAndTime(y,m,d,0,0,0); - result = DtoB_WeekendToMonday(dailyDate); +/* Set the instance's value using the given date and time. calendar + may be set to the flags: GREGORIAN_CALENDAR, + JULIAN_CALENDAR to indicate the calendar to be used. */ - Py_DECREF(dailyDate); - return result; +static +int dInfoCalc_SetFromDateAndTime(struct date_info *dinfo, + int year, + int month, + int day, + int hour, + int minute, + double second, + int calendar) +{ + + /* Calculate the absolute date */ + { + int leap; + long yearoffset,absdate; + + /* Range check */ + Py_AssertWithArg(year > -(INT_MAX / 366) && year < (INT_MAX / 366), + DateCalc_RangeError, + "year out of range: %i", + year); + + /* Is it a leap year ? */ + leap = dInfoCalc_Leapyear(year,calendar); + + /* Negative month values indicate months relative to the years end */ + if (month < 0) month += 13; + Py_AssertWithArg(month >= 1 && month <= 12, + DateCalc_RangeError, + "month out of range (1-12): %i", + month); + + /* Negative values indicate days relative to the months end */ + if (day < 0) day += days_in_month[leap][month - 1] + 1; + Py_AssertWithArg(day >= 1 && day <= days_in_month[leap][month - 1], + DateCalc_RangeError, + "day out of range: %i", + day); + + yearoffset = dInfoCalc_YearOffset(year,calendar); + if (yearoffset == -1 && PyErr_Occurred()) goto onError; + + absdate = day + month_offset[leap][month - 1] + yearoffset; + + dinfo->absdate = absdate; + + dinfo->year = year; + dinfo->month = month; + dinfo->day = day; + + dinfo->day_of_week = dInfoCalc_DayOfWeek(absdate); + dinfo->day_of_year = (short)(absdate - yearoffset); + + dinfo->calendar = calendar; + } + + /* Calculate the absolute time */ + { + Py_AssertWithArg(hour >= 0 && hour <= 23, + DateCalc_RangeError, + "hour out of range (0-23): %i", + hour); + Py_AssertWithArg(minute >= 0 && minute <= 59, + DateCalc_RangeError, + "minute out of range (0-59): %i", + minute); + Py_AssertWithArg(second >= (double)0.0 && + (second < (double)60.0 || + (hour == 23 && minute == 59 && + second < (double)61.0)), + DateCalc_RangeError, + "second out of range (0.0 - <60.0; <61.0 for 23:59): %f", + second); + + dinfo->abstime = (double)(hour*3600 + minute*60) + second; + + dinfo->hour = hour; + dinfo->minute = minute; + dinfo->second = second; + } + return 0; + onError: + return -1; } + +/* Sets the date part of the date_info struct using the indicated + calendar. + + XXX This could also be done using some integer arithmetics rather + than with this iterative approach... */ +static +int dInfoCalc_SetFromAbsDate(register struct date_info *dinfo, + long absdate, + int calendar) +{ + register long year; + long yearoffset; + int leap,dayoffset; + int *monthoffset; + + /* Approximate year */ + if (calendar == GREGORIAN_CALENDAR) { + year = (long)(((double)absdate) / 365.2425); + } else if (calendar == JULIAN_CALENDAR) { + year = (long)(((double)absdate) / 365.25); + } else { + Py_Error(DateCalc_Error, "unknown calendar"); + } + if (absdate > 0) year++; + + /* Apply corrections to reach the correct year */ + while (1) { + /* Calculate the year offset */ + yearoffset = dInfoCalc_YearOffset(year,calendar); + if (yearoffset == -1 && PyErr_Occurred()) + goto onError; + + /* Backward correction: absdate must be greater than the + yearoffset */ + if (yearoffset >= absdate) { + year--; + continue; + } + + dayoffset = absdate - yearoffset; + leap = dInfoCalc_Leapyear(year,calendar); + + /* Forward correction: non leap years only have 365 days */ + if (dayoffset > 365 && !leap) { + year++; + continue; + } + break; + } + + dinfo->year = year; + dinfo->calendar = calendar; + + /* Now iterate to find the month */ + monthoffset = month_offset[leap]; + { + register int month; + + for (month = 1; month < 13; month++) { + if (monthoffset[month] >= dayoffset) + break; + } + + dinfo->month = month; + dinfo->quarter = ((month-1)/3)+1; + dinfo->day = dayoffset - month_offset[leap][month-1]; + } + + + dinfo->day_of_week = dInfoCalc_DayOfWeek(absdate); + dinfo->day_of_year = dayoffset; + dinfo->absdate = absdate; + + return 0; + + onError: + return -1; +} + +/* Sets the time part of the DateTime object. */ +static +int dInfoCalc_SetFromAbsTime(struct date_info *dinfo, + double abstime) +{ + int inttime; + int hour,minute; + double second; + + inttime = (int)abstime; + hour = inttime / 3600; + minute = (inttime % 3600) / 60; + second = abstime - (double)(hour*3600 + minute*60); + + dinfo->hour = hour; + dinfo->minute = minute; + dinfo->second = second; + + dinfo->abstime = abstime; + + return 0; +} + +/* Set the instance's value using the given date and time. calendar + may be set to the flags: GREGORIAN_CALENDAR, JULIAN_CALENDAR to + indicate the calendar to be used. */ +static +int dInfoCalc_SetFromAbsDateTime(struct date_info *dinfo, + long absdate, + double abstime, + int calendar) +{ + + /* Bounds check */ + Py_AssertWithArg(abstime >= 0.0 && abstime <= SECONDS_PER_DAY, + DateCalc_Error, + "abstime out of range (0.0 - 86400.0): %f", + abstime); + + /* Calculate the date */ + if (dInfoCalc_SetFromAbsDate(dinfo, + absdate, + calendar)) + goto onError; + + /* Calculate the time */ + if (dInfoCalc_SetFromAbsTime(dinfo, + abstime)) + goto onError; + + return 0; + onError: + return -1; +} + /* -returns Date(y, m, d) - 1 converted to business frequency. -If the initial result is a weekend, the preceding Friday is returned +==================================================== +== End of section borrowed from mx.DateTime == +==================================================== */ -static long busday_after(int y, int m, int d) { - mxDateTimeObject *dailyDate; - long result; - dailyDate = day_before_mxobj(y,m,d); - result = DtoB_WeekendToFriday(dailyDate); - Py_DECREF(dailyDate); - return result; +////////////////////////////////////////////////////////// + +static long minval_D_toHighFreq = 719163; + +/////////////////////////////////////////////////////////////////////// + +static long absdatetime_hour(long absdate, long time) { + } +/////////////////////////////////////////////////////////////////////// + +// helpers for frequency conversion routines // + +static long DtoB_weekday(long fromDate) { return (((fromDate) / 7) * 5) + (fromDate)%7; } + +static long DtoB_WeekendToMonday(struct date_info dinfo) { + + long absdate = dinfo.absdate; + if (dinfo.day_of_week > 4) { + //change to Monday after weekend + absdate += (7 - dinfo.day_of_week); + } + return DtoB_weekday(absdate); +} + +static long DtoB_WeekendToFriday(struct date_info dinfo) { + + long absdate = dinfo.absdate; + if (dinfo.day_of_week > 4) { + //change to friday before weekend + absdate -= (dinfo.day_of_week - 4); + } + return DtoB_weekday(absdate); +} + +static long absdate_from_ymd(int y, int m, int d) { + struct date_info tempDate; + if (dInfoCalc_SetFromDateAndTime(&tempDate, y, m, d, 0, 0, 0, GREGORIAN_CALENDAR)) return DINFO_ERR; + return tempDate.absdate; +} + + /////////////////////////////////////////////// // frequency specifc conversion routines @@ -104,67 +441,55 @@ //************ FROM DAILY *************** static long asfreq_DtoA(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(fromDate, 0); - result = (long)(mxDate->year); - Py_DECREF(mxDate); - return result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate, + GREGORIAN_CALENDAR)) return DINFO_ERR; + return (long)(dinfo.year); } static long asfreq_DtoQ(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(fromDate, 0); - result = (long)((mxDate->year - 1) * 4 + (mxDate->month-1)/3 + 1); - Py_DECREF(mxDate); - return result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate, + GREGORIAN_CALENDAR)) return DINFO_ERR; + return (long)((dinfo.year - 1) * 4 + dinfo.quarter); } static long asfreq_DtoM(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(fromDate, 0); - result = (long)((mxDate->year - 1) * 12 + mxDate->month); - Py_DECREF(mxDate); - return result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate, + GREGORIAN_CALENDAR)) return DINFO_ERR; + return (long)((dinfo.year - 1) * 12 + dinfo.month); } static long asfreq_DtoW(long fromDate, char relation) { return (fromDate - 1)/7 + 1; } static long asfreq_DtoB(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(fromDate, 0); + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate, + GREGORIAN_CALENDAR)) return DINFO_ERR; + if (relation == 'B') { - result = DtoB_WeekendToFriday(mxDate); + return DtoB_WeekendToFriday(dinfo); } else { - result = DtoB_WeekendToMonday(mxDate); + return DtoB_WeekendToMonday(dinfo); } - - Py_DECREF(mxDate); - return result; } static long asfreq_DtoB_forConvert(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(fromDate, 0); + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, fromDate, + GREGORIAN_CALENDAR)) return DINFO_ERR; - if (mxDate->day_of_week > 4) { - result = -1; + if (dinfo.day_of_week > 4) { + return -1; } else { - result = DtoB_weekday(mxDate->absdate); + return DtoB_weekday(fromDate); } - - Py_DECREF(mxDate); - return result; } // needed for getDateInfo function @@ -271,14 +596,13 @@ return asfreq_DtoM(asfreq_WtoD(fromDate, 'A'), relation); } static long asfreq_WtoB(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(asfreq_WtoD(fromDate, relation), 0); - if (relation == 'B') { result = DtoB_WeekendToMonday(mxDate); } - else { result = DtoB_WeekendToFriday(mxDate); } - Py_DECREF(mxDate); - return result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_WtoD(fromDate, relation), + GREGORIAN_CALENDAR)) return DINFO_ERR; + + if (relation == 'B') { return DtoB_WeekendToMonday(dinfo); } + else { return DtoB_WeekendToFriday(dinfo); } } static long asfreq_WtoH(long fromDate, char relation) { @@ -297,14 +621,16 @@ static long asfreq_MtoD(long fromDate, char relation) { - long y, m; + long y, m, absdate; if (relation == 'B') { MtoD_ym(fromDate, &y, &m); - return absdate_from_ymd(y, m, 1); + if ((absdate = absdate_from_ymd(y, m, 1)) == DINFO_ERR) return DINFO_ERR; + return absdate; } else { MtoD_ym(fromDate+1, &y, &m); - return absdate_from_ymd(y, m, 1) - 1; + if ((absdate = absdate_from_ymd(y, m, 1)) == DINFO_ERR) return DINFO_ERR; + return absdate-1; } } @@ -315,14 +641,12 @@ static long asfreq_MtoB(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_MtoD(fromDate, relation), + GREGORIAN_CALENDAR)) return DINFO_ERR; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(asfreq_MtoD(fromDate, relation), 0); - if (relation == 'B') { result = DtoB_WeekendToMonday(mxDate); } - else { result = DtoB_WeekendToFriday(mxDate); } - Py_DECREF(mxDate); - return result; + if (relation == 'B') { return DtoB_WeekendToMonday(dinfo); } + else { return DtoB_WeekendToFriday(dinfo); } } static long asfreq_MtoH(long fromDate, char relation) { return asfreq_DtoH(asfreq_MtoD(fromDate, relation), relation); } @@ -338,14 +662,16 @@ static long asfreq_QtoD(long fromDate, char relation) { - long y, m; + long y, m, absdate; if (relation == 'B') { QtoD_ym(fromDate, &y, &m); - return absdate_from_ymd(y, m, 1); + if ((absdate = absdate_from_ymd(y, m, 1)) == DINFO_ERR) return DINFO_ERR; + return absdate; } else { QtoD_ym(fromDate+1, &y, &m); - return absdate_from_ymd(y, m, 1) - 1; + if ((absdate = absdate_from_ymd(y, m, 1)) == DINFO_ERR) return DINFO_ERR; + return absdate - 1; } } @@ -360,14 +686,12 @@ static long asfreq_QtoB(long fromDate, char relation) { - mxDateTimeObject *mxDate; - long result; + struct date_info dinfo; + if (dInfoCalc_SetFromAbsDate(&dinfo, asfreq_QtoD(fromDate, relation), + GREGORIAN_CALENDAR)) return DINFO_ERR; - mxDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(asfreq_QtoD(fromDate, relation), 0); - if (relation == 'B') { result = DtoB_WeekendToMonday(mxDate); } - else { result = DtoB_WeekendToFriday(mxDate); } - Py_DECREF(mxDate); - return result; + if (relation == 'B') { return DtoB_WeekendToMonday(dinfo); } + else { return DtoB_WeekendToFriday(dinfo); } } @@ -379,8 +703,14 @@ //************ FROM ANNUAL *************** static long asfreq_AtoD(long fromDate, char relation) { - if (relation == 'B') { return absdate_from_ymd(fromDate,1,1); } - else { return absdate_from_ymd(fromDate+1,1,1) - 1; } + long absdate; + if (relation == 'B') { + if ((absdate = absdate_from_ymd(fromDate,1,1)) == DINFO_ERR) return DINFO_ERR; + return absdate; + } else { + if ((absdate = absdate_from_ymd(fromDate+1,1,1)) == DINFO_ERR) return DINFO_ERR; + return absdate - 1; + } } static long asfreq_AtoQ(long fromDate, char relation) { @@ -396,8 +726,29 @@ static long asfreq_AtoW(long fromDate, char relation) { return asfreq_DtoW(asfreq_AtoD(fromDate, relation), relation); } static long asfreq_AtoB(long fromDate, char relation) { - if (relation == 'B') { return busday_before(fromDate,1,1); } - else { return busday_after(fromDate+1,1,1); } + + struct date_info dailyDate; + + if (relation == 'B') { + if (dInfoCalc_SetFromDateAndTime(&dailyDate, + fromDate,1,1, 0, 0, 0, + GREGORIAN_CALENDAR)) return DINFO_ERR; + return DtoB_WeekendToMonday(dailyDate); + } else { + long absdate; + + if (dInfoCalc_SetFromDateAndTime(&dailyDate, + fromDate+1,1,1, 0, 0, 0, + GREGORIAN_CALENDAR)) return DINFO_ERR; + + absdate = dailyDate.absdate - 1; + + if(dInfoCalc_SetFromAbsDate(&dailyDate, + absdate, + GREGORIAN_CALENDAR)) return DINFO_ERR; + + return DtoB_WeekendToFriday(dailyDate); + } } static long asfreq_AtoH(long fromDate, char relation) { return asfreq_DtoH(asfreq_AtoD(fromDate, relation), relation); } @@ -856,14 +1207,15 @@ } -static long dInfo_year(mxDateTimeObject *dateObj) { return dateObj->year; } -static long dInfo_quarter(mxDateTimeObject *dateObj) { return ((dateObj->month-1)/3)+1; } -static long dInfo_month(mxDateTimeObject *dateObj) { return dateObj->month; } -static long dInfo_day(mxDateTimeObject *dateObj) { return dateObj->day; } -static long dInfo_day_of_year(mxDateTimeObject *dateObj) { return dateObj->day_of_year; } -static long dInfo_day_of_week(mxDateTimeObject *dateObj) { return dateObj->day_of_week; } -static long dInfo_week(mxDateTimeObject *dateObj) { +static int dInfo_year(struct date_info dateObj) { return dateObj.year; } +static int dInfo_quarter(struct date_info dateObj) { return dateObj.quarter; } +static int dInfo_month(struct date_info dateObj) { return dateObj.month; } +static int dInfo_day(struct date_info dateObj) { return dateObj.day; } +static int dInfo_day_of_year(struct date_info dateObj) { return dateObj.day_of_year; } +static int dInfo_day_of_week(struct date_info dateObj) { return dateObj.day_of_week; } +static int dInfo_week(struct date_info dateObj) { return dInfoCalc_ISOWeek(dateObj); } +/* int year, week, day; PyObject *ISOWeekTuple = NULL; ISOWeekTuple = PyObject_GetAttrString((PyObject*)dateObj, "iso_week"); @@ -874,11 +1226,14 @@ Py_DECREF(ISOWeekTuple); return (long)week; + dInfoCalc_ISOWeek(dinfo) } -static long dInfo_hour(mxDateTimeObject *dateObj) { return dateObj->hour; } -static long dInfo_minute(mxDateTimeObject *dateObj) { return dateObj->minute; } -static long dInfo_second(mxDateTimeObject *dateObj) { return (long)dateObj->second; } +*/ +static int dInfo_hour(struct date_info dateObj) { return dateObj.hour; } +static int dInfo_minute(struct date_info dateObj) { return dateObj.minute; } +static int dInfo_second(struct date_info dateObj) { return (int)dateObj.second; } + static double getAbsTime(int freq, long dailyDate, long originalDate) { long startOfDay, periodsPerDay; @@ -914,7 +1269,7 @@ PyArrayObject *array; PyArrayObject *newArray; PyArrayIterObject *iterSource, *iterResult; - mxDateTimeObject *convDate; + struct date_info convDate; PyObject *val; long dateNum, dInfo; @@ -922,7 +1277,7 @@ double abstime; long (*toDaily)(long, char) = NULL; - long (*getDateInfo)(mxDateTimeObject*) = NULL; + int (*getDateInfo)(struct date_info) = NULL; if (!PyArg_ParseTuple(args, "Ois:getDateInfo(array, freq, info)", &array, &freq, &info)) return NULL; newArray = (PyArrayObject *)PyArray_Copy(array); @@ -972,12 +1327,12 @@ val = PyArray_GETITEM(array, iterSource->dataptr); dateNum = PyInt_AsLong(val); + Py_DECREF(val); absdate = toDaily(dateNum, 'B'); abstime = getAbsTime(freq, absdate, dateNum); - convDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(absdate, abstime); + if(dInfoCalc_SetFromAbsDateTime(&convDate, absdate, abstime, GREGORIAN_CALENDAR)) return NULL; dInfo = getDateInfo(convDate); - Py_DECREF(convDate); PyArray_SETITEM(newArray, iterResult->dataptr, PyInt_FromLong(dInfo)); @@ -989,9 +1344,9 @@ Py_DECREF(iterResult); return (PyObject *) newArray; - } + /////////////////////////////////////////////////////////////////////// static PyMethodDef cseries_methods[] = { @@ -1006,7 +1361,6 @@ { PyObject *m, *TSER_CONSTANTS; m = Py_InitModule3("cseries", cseries_methods, cseries_doc); - mxDateTime_ImportModuleAndAPI(); import_array(); TSER_CONSTANTS = PyDict_New(); From scipy-svn at scipy.org Fri Feb 23 15:46:27 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 23 Feb 2007 14:46:27 -0600 (CST) Subject: [Scipy-svn] r2758 - trunk/Lib/sandbox/timeseries/src Message-ID: <20070223204627.A549F39C093@new.scipy.org> Author: mattknox_ca Date: 2007-02-23 14:46:24 -0600 (Fri, 23 Feb 2007) New Revision: 2758 Modified: trunk/Lib/sandbox/timeseries/src/cseries.c Log: added additional error handling Modified: trunk/Lib/sandbox/timeseries/src/cseries.c =================================================================== --- trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-23 20:27:35 UTC (rev 2757) +++ trunk/Lib/sandbox/timeseries/src/cseries.c 2007-02-23 20:46:24 UTC (rev 2758) @@ -22,7 +22,10 @@ PyDict_SetItemString(dict, key, pyval); \ Py_DECREF(pyval); } +#define DINFO_ERR -99 +#define CHECK_ASFREQ(result) if ((result) == DINFO_ERR) return NULL + //DERIVED FROM mx.DateTime /* ===================================================== @@ -38,8 +41,8 @@ static PyObject *DateCalc_Error; /* Error Exception object */ static PyObject *DateCalc_RangeError; /* Error Exception object */ -#define DINFO_ERR -99 + #define GREGORIAN_CALENDAR 0 #define JULIAN_CALENDAR 1 @@ -1067,13 +1070,17 @@ asfreq_endpoints = get_asfreq_func(fromFreq, toFreq, 0); //convert start index to new frequency - newStartTemp = asfreq_main(startIndex, 'B'); - if (newStartTemp < 1) { newStart = asfreq_endpoints(startIndex, 'A'); } + CHECK_ASFREQ(newStartTemp = asfreq_main(startIndex, 'B')); + if (newStartTemp < 1) { + CHECK_ASFREQ(newStart = asfreq_endpoints(startIndex, 'A')); + } else { newStart = newStartTemp; } //convert end index to new frequency - newEndTemp = asfreq_main(startIndex+array->dimensions[0]-1, 'A'); - if (newEndTemp < 1) { newEnd = asfreq_endpoints(startIndex+array->dimensions[0]-1, 'B'); } + CHECK_ASFREQ(newEndTemp = asfreq_main(startIndex+array->dimensions[0]-1, 'A')); + if (newEndTemp < 1) { + CHECK_ASFREQ(newEnd = asfreq_endpoints(startIndex+array->dimensions[0]-1, 'B')); + } else { newEnd = newEndTemp; } if (newStart < 1) { @@ -1085,9 +1092,10 @@ newHeight = get_height(fromFreq, toFreq); if (newHeight > 1) { - + long tempval; asfreq_reverse = get_asfreq_func(toFreq, fromFreq, 0); - currPerLen = startIndex - asfreq_reverse(newStart, 'B'); + CHECK_ASFREQ(tempval = asfreq_reverse(newStart, 'B')); + currPerLen = startIndex - tempval; nd = 2; dim = PyDimMem_NEW(nd); @@ -1118,7 +1126,7 @@ val = PyArray_GETITEM(array, PyArray_GetPtr(array, &i)); valMask = PyArray_GETITEM(mask, PyArray_GetPtr(mask, &i)); - currIndex = asfreq_main(startIndex + i, relation); + CHECK_ASFREQ(currIndex = asfreq_main(startIndex + i, relation)); newIdx[0] = currIndex-newStart; if (newHeight > 1) { @@ -1188,7 +1196,7 @@ fromDateObj = PyArray_GETITEM(fromDates, iterFrom->dataptr); fromDate = PyInt_AsLong(fromDateObj); - toDate = asfreq_main(fromDate, relation[0]); + CHECK_ASFREQ(toDate = asfreq_main(fromDate, relation[0])); toDateObj = PyInt_FromLong(toDate); PyArray_SETITEM(toDates, iterTo->dataptr, toDateObj); @@ -1214,22 +1222,6 @@ static int dInfo_day_of_year(struct date_info dateObj) { return dateObj.day_of_year; } static int dInfo_day_of_week(struct date_info dateObj) { return dateObj.day_of_week; } static int dInfo_week(struct date_info dateObj) { return dInfoCalc_ISOWeek(dateObj); } - -/* - int year, week, day; - PyObject *ISOWeekTuple = NULL; - ISOWeekTuple = PyObject_GetAttrString((PyObject*)dateObj, "iso_week"); - - if (!PyArg_ParseTuple(ISOWeekTuple,"iii;need a ISO Week 3-tuple (year,week,day)", - &year, &week, &day)) return -9999; - - Py_DECREF(ISOWeekTuple); - - return (long)week; - dInfoCalc_ISOWeek(dinfo) -} -*/ - static int dInfo_hour(struct date_info dateObj) { return dateObj.hour; } static int dInfo_minute(struct date_info dateObj) { return dateObj.minute; } static int dInfo_second(struct date_info dateObj) { return (int)dateObj.second; } @@ -1328,7 +1320,7 @@ val = PyArray_GETITEM(array, iterSource->dataptr); dateNum = PyInt_AsLong(val); Py_DECREF(val); - absdate = toDaily(dateNum, 'B'); + CHECK_ASFREQ(absdate = toDaily(dateNum, 'B')); abstime = getAbsTime(freq, absdate, dateNum); if(dInfoCalc_SetFromAbsDateTime(&convDate, absdate, abstime, GREGORIAN_CALENDAR)) return NULL; From scipy-svn at scipy.org Fri Feb 23 16:20:23 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Fri, 23 Feb 2007 15:20:23 -0600 (CST) Subject: [Scipy-svn] r2759 - trunk/Lib/sandbox/timeseries Message-ID: <20070223212023.0E59739C057@new.scipy.org> Author: mattknox_ca Date: 2007-02-23 15:20:19 -0600 (Fri, 23 Feb 2007) New Revision: 2759 Modified: trunk/Lib/sandbox/timeseries/setup.py Log: removed references to mx.DateTime Modified: trunk/Lib/sandbox/timeseries/setup.py =================================================================== --- trunk/Lib/sandbox/timeseries/setup.py 2007-02-23 20:46:24 UTC (rev 2758) +++ trunk/Lib/sandbox/timeseries/setup.py 2007-02-23 21:20:19 UTC (rev 2759) @@ -6,23 +6,14 @@ import os from os.path import join -def check_mxDateTime(): - try: - import mx.DateTime - except ImportError: - raise ImportError,"mx.DateTime should already be installed !" - else: - return os.path.dirname(mx.DateTime.mxDateTime.__file__) - def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration, get_numpy_include_dirs nxheader = join(get_numpy_include_dirs()[0],'numpy',) - mxlib = check_mxDateTime() confgr = Configuration('timeseries',parent_package,top_path) sources = join('src', 'cseries.c') confgr.add_extension('cseries', sources=[sources,], - include_dirs=[mxlib, nxheader], + include_dirs=[nxheader], ) confgr.add_data_dir('doc') confgr.add_data_dir('examples') From scipy-svn at scipy.org Sun Feb 25 21:55:03 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 25 Feb 2007 20:55:03 -0600 (CST) Subject: [Scipy-svn] r2760 - trunk/Lib/sandbox/timeseries Message-ID: <20070226025503.C971F39C114@new.scipy.org> Author: pierregm Date: 2007-02-25 20:54:59 -0600 (Sun, 25 Feb 2007) New Revision: 2760 Modified: trunk/Lib/sandbox/timeseries/__init__.py trunk/Lib/sandbox/timeseries/tdates.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries : forced a singleton to have a DateArray _dates : added empty_like, where dates are copied from the model tdates : reset the default freq to -9999 in DateArray.__array_finalize__ : DateArray: put the cached info in a single directory '_cachedinfo' : fixed illegal propagation of cachedinfo : made freqstr a property __init__: added import plotlib Modified: trunk/Lib/sandbox/timeseries/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/__init__.py 2007-02-23 21:20:19 UTC (rev 2759) +++ trunk/Lib/sandbox/timeseries/__init__.py 2007-02-26 02:54:59 UTC (rev 2760) @@ -22,7 +22,8 @@ import tmulti from tmulti import * import reportlib -from reportlib import * +from reportlib import * +import plotlib __all__ = ['tdates', 'tseries','tmulti','reportlib'] __all__ += tdates.__all__ Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-23 21:20:19 UTC (rev 2759) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-26 02:54:59 UTC (rev 2760) @@ -550,7 +550,8 @@ >>> for d in DateArray(...): accesses the array element by element. Therefore, `d` is a Date object. """ - (_tostr, _toord, _steps) = (None, None, None) + _defcachedinfo = dict(toobj=None, tostr=None, toord=None, + steps=None, full=None, hasdups=None) def __new__(cls, dates=None, freq=None, copy=False): # Get the frequency ...... if freq is None: @@ -573,11 +574,11 @@ raise ArithmeticDateError, "(function %s)" % context[0].__name__ def __array_finalize__(self, obj): - self.freq = getattr(obj, 'freq', self._defaultfreq) - self.freqstr = getattr(obj, 'freqstr', corelib.freq_tostr(self.freq)) - for attr in ('_toobj','_toord','_tostr', - '_steps','_full','_hasdups'): - setattr(self, attr, getattr(obj, attr, None)) + self.freq = getattr(obj, 'freq', -9999) + self._cachedinfo = dict(toobj=None, tostr=None, toord=None, + steps=None, full=None, hasdups=None) + if hasattr(obj,'_cachedinfo'): + self._cachedinfo.update(obj._cachedinfo) return def __getitem__(self, indx): @@ -598,6 +599,10 @@ # behaviour return Date(self.freq, value=r.item()) else: + r._cachedinfo.update(dict(steps=None, full=None, hasdups=None)) + for attr in ('tostr','toobj','toord'): + if r._cachedinfo[attr] is not None: + r._cachedinfo[attr] = r._cachedinfo[attr][indx] return r def __repr__(self): @@ -615,6 +620,10 @@ __ne__ = _datearithmetics('__ne__', asdates=False) #...................................................... @property + def freqstr(self): + "Returns the frequency string code." + return corelib.freq_tostr(self.freq) + @property def day(self): "Returns the day of month." return self.__getdateinfo__('D') @@ -667,7 +676,9 @@ weeks = week def __getdateinfo__(self, info): - return numeric.asarray(cseries.getDateInfo(numeric.asarray(self), self.freq, info), dtype=int_) + return numeric.asarray(cseries.getDateInfo(numeric.asarray(self), + self.freq, info), + dtype=int_) __getDateInfo = __getdateinfo__ #.... Conversion methods .................... # @@ -678,17 +689,17 @@ def toordinal(self): "Converts the dates from values to ordinals." # Note: we better try to cache the result - if self._toord is None: + if self._cachedinfo['toord'] is None: # diter = (Date(self.freq, value=d).toordinal() for d in self) diter = (d.toordinal() for d in self) toord = numeric.fromiter(diter, dtype=float_) - self._toord = toord - return self._toord + self._cachedinfo['toord'] = toord + return self._cachedinfo['toord'] # def tostring(self): "Converts the dates to strings." # Note: we better cache the result - if self._tostr is None: + if self._cachedinfo['tostr'] is None: firststr = str(self[0]) if self.size > 0: ncharsize = len(firststr) @@ -696,8 +707,8 @@ dtype='|S%i' % ncharsize) else: tostr = firststr - self._tostr = tostr - return self._tostr + self._cachedinfo['tostr'] = tostr + return self._cachedinfo['tostr'] # def asfreq(self, freq=None, relation="BEFORE"): "Converts the dates to another frequency." @@ -743,38 +754,39 @@ The timesteps have the same unit as the frequency of the series.""" if self.freq == 'U': warnings.warn("Undefined frequency: assuming integers!") - if self._steps is None: + if self._cachedinfo['steps'] is None: + _cached = self._cachedinfo val = numeric.asarray(self).ravel() if val.size > 1: steps = val[1:] - val[:-1] - if self._full is None: - self._full = (steps.max() == 1) - if self._hasdups is None: - self._hasdups = (steps.min() == 0) + if _cached['full'] is None: + _cached['full'] = (steps.max() == 1) + if _cached['hasdups'] is None: + _cached['hasdups'] = (steps.min() == 0) else: - self._full = True - self._hasdups = False + _cached['full'] = True + _cached['hasdups'] = False steps = numeric.array([], dtype=int_) - self._steps = steps - return self._steps + self._cachedinfo['steps'] = steps + return self._cachedinfo['steps'] def has_missing_dates(self): "Returns whether the DateArray have missing dates." - if self._full is None: + if self._cachedinfo['full'] is None: steps = self.get_steps() - return not(self._full) + return not(self._cachedinfo['full']) def isfull(self): "Returns whether the DateArray has no missing dates." - if self._full is None: + if self._cachedinfo['full'] is None: steps = self.get_steps() - return self._full + return self._cachedinfo['full'] def has_duplicated_dates(self): "Returns whether the DateArray has duplicated dates." - if self._hasdups is None: + if self._cachedinfo['hasdups'] is None: steps = self.get_steps() - return self._hasdups + return self._cachedinfo['hasdups'] def isvalid(self): "Returns whether the DateArray is valid: no missing/duplicated dates." @@ -996,6 +1008,7 @@ ################################################################################ if __name__ == '__main__': + import maskedarray.testutils from maskedarray.testutils import assert_equal if 1: dlist = ['2007-%02i' % i for i in range(1,5)+range(7,13)] @@ -1019,4 +1032,11 @@ print f today = thisday(f) assert(Date(freq=f, value=today.value) == today) + + if 1: + D = date_array(start_date=thisday('D'), length=5) + Dstr = D.tostring() + assert_equal(D.tostring(), Dstr) + DL = D[[0,-1]] + assert_equal(DL.tostring(), Dstr[[0,-1]]) \ No newline at end of file Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-23 21:20:19 UTC (rev 2759) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-26 02:54:59 UTC (rev 2760) @@ -291,7 +291,7 @@ def __new__(cls, data, dates=None, mask=nomask, freq=None, observed=None, start_date=None, dtype=None, copy=False, fill_value=None, - keep_mask=True, small_mask=True, hard_mask=False): + keep_mask=True, small_mask=True, hard_mask=False, **options): maparms = dict(copy=copy, dtype=dtype, fill_value=fill_value, keep_mask=keep_mask, small_mask=small_mask, hard_mask=hard_mask,) @@ -394,6 +394,7 @@ m = self._mask singlepoint = (len(numeric.shape(newdate))==0) if singlepoint: + newdate = DateArray(newdate) if newdata is masked: newdata = tsmasked newdata._dates = newdate @@ -655,20 +656,36 @@ return convert(self, freq, func=func, position=position) #..................................................... def transpose(self, *axes): + """ a.transpose(*axes) + + Returns a view of 'a' with axes transposed. If no axes are given, + or None is passed, switches the order of the axes. For a 2-d + array, this is the usual matrix transpose. If axes are given, + they describe how the axes are permuted. + + """ if self._dates.size == self.size: result = super(TimeSeries, self).transpose(*axes) result._dates = self._dates.transpose(*axes) else: errmsg = "Operation not permitted on multi-variable series" - print "AXES:",axes if (len(axes)==0) or axes[0] != 0: - raise ValueError, errmsg + raise TimeSeriesError, errmsg else: result = super(TimeSeries, self).transpose(*axes) result._dates = self._dates return result + #...................................................... + def copy_attributes(self, oldseries, exclude=[]): + "Copies the attributes from oldseries if they are not in the exclude list." + attrlist = ['fill_value', 'observed'] + if not isinstance(oldseries, TimeSeries): + msg = "Series should be a valid TimeSeries object! (got <%s> instead)" + raise TimeSeriesError, msg % type(oldseries) + for attr in attrlist: + if not attr in exclude: + setattr(self, attr, getattr(oldseries, attr)) - def _attrib_dict(series, exclude=[]): """this function is used for passing through attributes of one @@ -676,7 +693,7 @@ result = {'fill_value':series.fill_value, 'observed':series.observed} return dict(filter(lambda x: x[0] not in exclude, result.iteritems())) - + ##### -------------------------------------------------------------------------- ##--- ... Additional methods ... @@ -1061,14 +1078,14 @@ newshape[0] = len(newdates) newshape = tuple(newshape) - newdata = masked_array(numeric.empty(newshape, dtype=a.dtype), mask=True) - #backup the series attributes - options = dict(fill_value=a.fill_value, observed=a.observed) - newseries = TimeSeries(newdata, newdates, **options) + newseries = numeric.empty(newshape, dtype=a.dtype).view(type(a)) + newseries.__setmask__(numeric.ones(newseries.shape, dtype=bool_)) + newseries._dates = newdates if dstart is not None: start_date = max(start_date, dstart) end_date = min(end_date, dend) + 1 newseries[start_date:end_date] = a[start_date:end_date] + newseries.copy_attributes(a) return newseries #.................................................................... def align_series(*series, **kwargs): @@ -1105,7 +1122,7 @@ aligned = align_series #.................................................................... def convert(series, freq, func='auto', position='END'): - """Converts a series to a frequency + """Converts a series to a frequency. When converting to a lower frequency, func is a function that acts on a 1-d array and returns a scalar or 1-d array. func should handle @@ -1156,12 +1173,20 @@ if tempData.ndim == 2 and func is not None: tempData = MA.apply_along_axis(func, -1, tempData) - - newseries = TimeSeries(tempData, freq=toFreq, - observed=series.observed, - start_date=start_date) + + newseries = tempData.view(type(series)) + newseries._dates = date_array(start_date=start_date, length=len(newseries), + freq=toFreq) + newseries.copy_attributes(series) return newseries + +def group_byperiod(series, freq, func='auto', position='END'): + """Converts a series to a frequency, without any processing. + """ + return convert(series, freq, func=None, position='END') + TimeSeries.convert = convert +TimeSeries.group_byperiod = group_byperiod #............................................................................... def tshift(series, nper, copy=True): @@ -1207,7 +1232,9 @@ newdata[:-nper] = inidata[nper:] else: newdata = inidata - newseries = TimeSeries(newdata, series._dates, **options) + newseries = newdata.view(type(series)) + newseries._dates = series._dates + newseries.copy_attributes(series) return newseries TimeSeries.tshift = tshift #............................................................................... @@ -1339,7 +1366,12 @@ else: newdata[~keeper] = 0 return newseries - +#............................................................................... +def empty_like(series): + result = N.empty_like(series).view(type(series)) + result._dates = series._dates + result._mask = series._mask + return result ################################################################################ if __name__ == '__main__': @@ -1361,13 +1393,21 @@ sertrans = serfolded.transpose() assert_equal(sertrans.shape, (2,5)) - assert ser1d[0] is tsmasked - print "OK" if 1: hodie = today('D') ser2d = time_series(N.arange(10).reshape(5,2), start_date=hodie, mask=[[1,1],[0,0],[0,0],[0,0],[0,0]]) + try: + ser2d_transpose = ser2d.transpose() + except TimeSeriesError: + pass if 1: hodie = today('D') ser3d = time_series(N.arange(30).reshape(5,3,2), start_date=hodie,) + try: + ser3d_transpose = ser3d.transpose() + except TimeSeriesError: + pass + assert_equal(ser3d.transpose(0,2,1).shape, (5,2,3)) + From scipy-svn at scipy.org Sun Feb 25 22:21:42 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Sun, 25 Feb 2007 21:21:42 -0600 (CST) Subject: [Scipy-svn] r2761 - trunk/Lib/sandbox/maskedarray Message-ID: <20070226032142.67CCE39C106@new.scipy.org> Author: pierregm Date: 2007-02-25 21:21:38 -0600 (Sun, 25 Feb 2007) New Revision: 2761 Modified: trunk/Lib/sandbox/maskedarray/extras.py trunk/Lib/sandbox/maskedarray/testutils.py Log: testutils : prevent chararrays to be transformed to float + force elementwise comparison for object/string ndarrays Modified: trunk/Lib/sandbox/maskedarray/extras.py =================================================================== --- trunk/Lib/sandbox/maskedarray/extras.py 2007-02-26 02:54:59 UTC (rev 2760) +++ trunk/Lib/sandbox/maskedarray/extras.py 2007-02-26 03:21:38 UTC (rev 2761) @@ -41,7 +41,7 @@ #............................................................................... def issequence(seq): - """Returns True if the argumnet is a sequence (ndarray, list or tuple).""" + """Returns True if the argument is a sequence (ndarray, list or tuple).""" if isinstance(seq, ndarray): return True elif isinstance(seq, tuple): Modified: trunk/Lib/sandbox/maskedarray/testutils.py =================================================================== --- trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-26 02:54:59 UTC (rev 2760) +++ trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-26 03:21:38 UTC (rev 2761) @@ -39,10 +39,19 @@ y = filled(masked_array(d2, copy=False, mask=m), 1).astype(float_) d = N.less_equal(umath.absolute(x-y), atol + rtol * umath.absolute(y)) return d.ravel() -#............................ +#................................................ +def _assert_equal_on_sequences(actual, desired, err_msg=''): + "Asserts the equality of two non-array sequences." + assert_equal(len(actual),len(desired),err_msg) + for k in range(len(desired)): + assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k,err_msg)) + return + + def assert_equal(actual,desired,err_msg=''): """Asserts that two items are equal. """ + # Case #1: dictionary ..... if isinstance(desired, dict): assert isinstance(actual, dict), repr(type(actual)) assert_equal(len(actual),len(desired),err_msg) @@ -50,15 +59,21 @@ assert actual.has_key(k), repr(k) assert_equal(actual[k], desired[k], 'key=%r\n%s' % (k,err_msg)) return + # Case #2: lists ..... if isinstance(desired, (list,tuple)) and isinstance(actual, (list,tuple)): - assert_equal(len(actual),len(desired),err_msg) - for k in range(len(desired)): - assert_equal(actual[k], desired[k], 'item=%r\n%s' % (k,err_msg)) + return _assert_equal_on_sequences(actual, desired, err_msg='') + if not (isinstance(actual, ndarray) or isinstance(desired, ndarray)): + msg = build_err_msg([actual, desired], err_msg,) + assert desired == actual, msg return - if isinstance(actual, ndarray) or isinstance(desired, ndarray): + # Case #4. arrays or equivalent + actual = N.array(actual, copy=False, subok=True) + desired = N.array(desired, copy=False, subok=True) + if actual.dtype.char in "OS" and desired.dtype.char in "OS": + return _assert_equal_on_sequences(actual.tolist(), + desired.tolist(), + err_msg='') return assert_array_equal(actual, desired, err_msg) - msg = build_err_msg([actual, desired], err_msg,) - assert desired == actual, msg #............................. def fail_if_equal(actual,desired,err_msg='',): """Raises an assertion error if two items are equal. @@ -100,13 +115,13 @@ x = masked_array(xf, copy=False, mask=m).filled(fill_value) y = masked_array(yf, copy=False, mask=m).filled(fill_value) - if (x.dtype.char != "O"): + if (x.dtype.char != "O") and (x.dtype.char != "S"): x = x.astype(float_) if isinstance(x, N.ndarray) and x.size > 1: x[N.isnan(x)] = 0 elif N.isnan(x): x = 0 - if (y.dtype.char != "O"): + if (y.dtype.char != "O") and (y.dtype.char != "S"): y = y.astype(float_) if isinstance(y, N.ndarray) and y.size > 1: y[N.isnan(y)] = 0 @@ -162,11 +177,13 @@ """Checks the elementwise equality of two masked arrays, up to a given number of decimals.""" def compare(x, y): + "Returns the result of the loose comparison between x and y)." return approx(x,y) assert_array_compare(compare, x, y, err_msg=err_msg, header='Arrays are not almost equal') #............................ def assert_array_less(x, y, err_msg=''): + "Checks that x is smaller than y elementwise." assert_array_compare(less, x, y, err_msg=err_msg, header='Arrays are not less-ordered') #............................ From scipy-svn at scipy.org Mon Feb 26 07:26:07 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 26 Feb 2007 06:26:07 -0600 (CST) Subject: [Scipy-svn] r2762 - trunk/Lib/interpolate Message-ID: <20070226122607.CF69239C108@new.scipy.org> Author: jtravs Date: 2007-02-26 06:26:04 -0600 (Mon, 26 Feb 2007) New Revision: 2762 Modified: trunk/Lib/interpolate/fitpack.py Log: Submit fix for ticket #379 Modified: trunk/Lib/interpolate/fitpack.py =================================================================== --- trunk/Lib/interpolate/fitpack.py 2007-02-26 03:21:38 UTC (rev 2761) +++ trunk/Lib/interpolate/fitpack.py 2007-02-26 12:26:04 UTC (rev 2762) @@ -252,7 +252,7 @@ _curfit_cache = {'t': array([],float), 'wrk': array([],float), 'iwrk':array([],int32)} -def splrep(x,y,w=None,xb=None,xe=None,k=3,task=0,s=1e-3,t=None, +def splrep(x,y,w=None,xb=None,xe=None,k=3,task=0,s=None,t=None, full_output=0,per=0,quiet=1): """Find the B-spline representation of 1-D curve. @@ -292,7 +292,8 @@ weights represent the inverse of the standard-deviation of y, then a good s value should be found in the range (m-sqrt(2*m),m+sqrt(2*m)) where m is the number of datapoints in x, y, and w. - default : s=m-sqrt(2*m) + default : s=m-sqrt(2*m) if weights are supplied. + s = 0.0 (interpolating) if no weights are supplied. t -- The knots needed for task=-1. If given then task is automatically set to -1. full_output -- If non-zero, then return optional outputs. @@ -335,8 +336,12 @@ _curfit_cache = {} x,y=map(myasarray,[x,y]) m=len(x) - if w is None: w=ones(m,float) - else: w=myasarray(w) + if w is None: + w=ones(m,float) + if s is None: s = 0.0 + else: + w=myasarray(w) + if s is None: s = m-sqrt(2*m) if not len(w) == m: raise TypeError,' len(w)=%d is not equal to m=%d'%(len(w),m) if (m != len(y)) or (m != len(w)): raise TypeError, 'Lengths of the first three arguments (x,y,w) must be equal' @@ -346,7 +351,6 @@ if xb is None: xb=x[0] if xe is None: xe=x[-1] if not (-1<=task<=1): raise TypeError, 'task must be either -1,0, or 1' - if s is None: s = m-sqrt(2*m) if t is not None: task = -1 if task == -1: From scipy-svn at scipy.org Mon Feb 26 07:42:46 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 26 Feb 2007 06:42:46 -0600 (CST) Subject: [Scipy-svn] r2763 - trunk/Lib/sandbox/spline Message-ID: <20070226124246.2D38B39C239@new.scipy.org> Author: jtravs Date: 2007-02-26 06:42:43 -0600 (Mon, 26 Feb 2007) New Revision: 2763 Modified: trunk/Lib/sandbox/spline/fitpack.py Log: Update sandbox.spline to correspond to recent changes to interpolate. Modified: trunk/Lib/sandbox/spline/fitpack.py =================================================================== --- trunk/Lib/sandbox/spline/fitpack.py 2007-02-26 12:26:04 UTC (rev 2762) +++ trunk/Lib/sandbox/spline/fitpack.py 2007-02-26 12:42:43 UTC (rev 2763) @@ -266,7 +266,7 @@ _splrep_cache = {} _percur_cache = {} -def splrep(x,y,w=None,xb=None,xe=None,k=3,task=0,s=1e-3,t=None, +def splrep(x,y,w=None,xb=None,xe=None,k=3,task=0,s=None,t=None, full_output=0,per=0,quiet=1): """Find the B-spline representation of 1-D curve. @@ -304,7 +304,8 @@ weights represent the inverse of the standard-deviation of y, then a good s value should be found in the range (m-sqrt(2*m),m+sqrt(2*m)) where m is the number of datapoints in x, y, and w. - default : s=m-sqrt(2*m) + default : s = m-sqrt(2*m) if weights are supplied. + s = 0.0 (interpolating) if no weights are supplied. t -- The knots needed for task=-1. If given then task is automatically set to -1. full_output -- If non-zero, then return optional outputs. @@ -342,8 +343,12 @@ """ x,y=map(myasarray,[x,y]) m=len(x) - if w is None: w=ones(m,float) - else: w=myasarray(w) + if w is None: + w=ones(m,float) + if s is None: s = 0.0 + else: + w=myasarray(w) + if s is None: s = m-sqrt(2*m) if not len(w) == m: raise TypeError,' len(w)=%d is not equal to m=%d'%(len(w),m) if (m != len(y)) or (m != len(w)): @@ -356,7 +361,6 @@ if xb is None: xb=x[0] if xe is None: xe=x[-1] if not (-1<=task<=1): raise TypeError, 'task must be either -1,0, or 1' - if s is None: s = m-sqrt(2*m) if t is not None: task = -1 if task == -1: @@ -381,21 +385,22 @@ _percur_cache['wrk']=wrk _percur_cache['iwrk']=iwrk _percur_cache['n']=n - if task == 0: - spl = spline.UnivariateSpline(x,y,w,[xb,xe],k=k,s=s) - elif task == 1: - try: - spl = _splrep_cache['spl'] - except KeyError: - raise ValueError, 'task=1 can only be called after task=0' - spl.set_smoothing_factor(s) - elif task == -1: - t = t[where(t>xb)] - t = t[where(t=0: - _splrep_cache['spl'] = spl - x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier = spl._data + else: + if task == 0: + spl = spline.UnivariateSpline(x,y,w,[xb,xe],k=k,s=s) + elif task == 1: + try: + spl = _splrep_cache['spl'] + except KeyError: + raise ValueError, 'task=1 can only be called after task=0' + spl.set_smoothing_factor(s) + elif task == -1: + t = t[where(t>xb)] + t = t[where(t=0: + _splrep_cache['spl'] = spl + x,y,w,xb,xe,k,s,n,t,c,fp,fpint,nrdata,ier = spl._data tck = (t[:n],c[:n-k-1],k) if ier<=0 and not quiet: print _iermess[ier][0] From scipy-svn at scipy.org Mon Feb 26 16:43:19 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Mon, 26 Feb 2007 15:43:19 -0600 (CST) Subject: [Scipy-svn] r2764 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070226214319.913CC39C110@new.scipy.org> Author: mattknox_ca Date: 2007-02-26 15:43:15 -0600 (Mon, 26 Feb 2007) New Revision: 2764 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py Log: - fixed some bugs - changed code to use frequency constants instead of freqstr for checking frequencies Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-26 12:42:43 UTC (rev 2763) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-26 21:43:15 UTC (rev 2764) @@ -32,6 +32,7 @@ import maskedarray as MA import timeseries +import timeseries as TS from timeseries import date_array, Date, DateArray, TimeSeries import warnings @@ -160,25 +161,38 @@ previous = getattr(dates-1, period) return (current - previous).nonzero()[0] +def has_level_label(label_flags): + """returns true if the label_flags indicate there is at least +one label for this level""" + if label_flags.size == 0 or \ + (label_flags.size == 1 and label_flags[0] == 0): + return False + else: + return True -def _daily_finder(vmin, vmax, freqstr, aslocator): +def _daily_finder(vmin, vmax, freq, aslocator): - if freqstr == 'B': + if freq == TS.FR_BUS: periodsperyear = 261 - elif freqstr == 'D': + elif freq == TS.FR_DAY: periodsperyear = 365 else: raise ValueError("unexpected frequency") (vmin, vmax) = (int(vmin), int(vmax)) span = vmax - vmin + 1 - dates = date_array(start_date=Date(freqstr,vmin), - end_date=Date(freqstr, vmax)) + dates = date_array(start_date=Date(freq,vmin), + end_date=Date(freq, vmax)) default = N.arange(vmin, vmax+1) # Initialize the output if not aslocator: format = N.empty(default.shape, dtype="|S10") format.flat = '' + + def first_label(label_flags): + if label_flags[0] == 0: return label_flags[1] + else: return label_flags[0] + # Case 1. Less than a month if span <= (periodsperyear//12 - 2): month_start = period_break(dates,'month') @@ -190,15 +204,15 @@ format[:] = '%d' format[month_start] = '%d\n%b' format[year_start] = '%d\n%b\n%Y' - if year_start.size == 0: - if month_start.size == 0: + if not has_level_label(year_start): + if not has_level_label(month_start): if dates.size > 1: idx = 1 else: idx = 0 format[idx] = '%d\n%b\n%Y' else: - format[month_start[0]] = '%d\n%b\n%Y' + format[first_label(month_start)] = '%d\n%b\n%Y' # Case 2. Less than three months elif span <= periodsperyear//4: month_start = period_break(dates,'month') @@ -206,36 +220,37 @@ major = default[month_start] minor = default else: - week_start = (dates.day_of_week == 1) + week_start = period_break(dates,'week') year_start = period_break(dates,'year') -# week_start[0] = False -# month_start[0] = False -# year_start[0] = False + format[week_start] = '%d' format[month_start] = '\n\n%b' format[year_start] = '\n\n%b\n%Y' - if year_start.size == 0: - if month_start.size == 0: - format[week_start[0]] = '\n\n%b\n%Y' + if not has_level_label(year_start): + if not has_level_label(month_start): + format[first_label(week_start)] = '\n\n%b\n%Y' else: - format[month_start[0]] = '\n\n%b\n%Y' + format[first_label(month_start)] = '\n\n%b\n%Y' # Case 3. Less than 14 months ............... elif span <= 1.15 * periodsperyear: - month_start = period_break(dates,'month') + if aslocator: - week_start = period_break(dates, 'week') - minor_idx = (week_start | month_start) - minor_idx[0] = True - major = default[month_start] + d_minus_1 = dates-1 + + month_diff = N.abs(dates.month - d_minus_1.month) + week_diff = N.abs(dates.week - d_minus_1.week) + minor_idx = (month_diff + week_diff).nonzero()[0] + + major = default[month_diff != 0] minor = default[minor_idx] else: year_start = period_break(dates,'year') - month_start[0] = False - year_start[0] = False + month_start = period_break(dates,'month') + format[month_start] = '%b' format[year_start] = '%b\n%Y' - if not year_start.size: - format[month_start[0]] = '%b\n%Y' + if not has_level_label(year_start): + format[first_label(month_start)] = '%b\n%Y' # Case 4. Less than 2.5 years ............... elif span <= 2.5 * periodsperyear: year_start = period_break(dates,'year') @@ -256,7 +271,7 @@ minor = default[month_start] else: month_break = dates[month_start].month - jan_or_jul = month_start[(month_break == 1 | month_break == 7)] + jan_or_jul = month_start[(month_break == 1) | (month_break == 7)] format[jan_or_jul] = '%b' format[year_start] = '%b\n%Y' # Case 5. Less than 11 years ................ @@ -288,11 +303,12 @@ formatted = (format != '') return dict([(d,f) for (d,f) in zip(default[formatted],format[formatted])]) #............................................................................... -def _monthly_finder(vmin, vmax, freqstr, aslocator): - if freqstr != 'M': +def _monthly_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_MTH: raise ValueError("unexpected frequency") periodsperyear = 12 - (vmin, vmax) = (int(vmin), int(vmax+1)) + + (vmin, vmax) = (int(vmin), int(vmax)) span = vmax - vmin + 1 #............................................ dates = N.arange(vmin, vmax+1) @@ -305,10 +321,11 @@ major = dates[year_start] minor = dates else: + format[:] = '%b' format[year_start] = '%b\n%Y' - if not year_start.size: + if not has_level_label(year_start): if dates.size > 1: idx = 1 else: @@ -358,11 +375,11 @@ formatted = (format != '') return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) #............................................................................... -def _quarterly_finder(vmin, vmax, freqstr, aslocator): - if freqstr != 'Q': +def _quarterly_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_QTR: raise ValueError("unexpected frequency") periodsperyear = 4 - (vmin, vmax) = (int(vmin), int(vmax+1)) + (vmin, vmax) = (int(vmin), int(vmax)) span = vmax - vmin + 1 #............................................ dates = N.arange(vmin, vmax+1) @@ -377,7 +394,7 @@ else: format[:] = 'Q%q' format[year_start] = 'Q%q\n%Y' - if not year_start.size: + if not has_level_label(year_start): if dates.size > 1: idx = 1 else: @@ -400,7 +417,6 @@ major = dates[major_idx] minor = dates[year_start[(years % min_anndef == 0)]] else: - print "major_idx",major_idx format[major_idx] = '%Y' #............................................ if aslocator: @@ -409,8 +425,8 @@ formatted = (format != '') return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) #............................................................................... -def _annual_finder(vmin, vmax, freqstr, aslocator): - if freqstr != 'Q': +def _annual_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_ANN: raise ValueError("unexpected frequency") (vmin, vmax) = (int(vmin), int(vmax+1)) span = vmax - vmin + 1 @@ -439,20 +455,20 @@ def __init__(self, freq, minor_locator=False, dynamic_mode=True, base=1, quarter=1, month=1, day=1): - self.freqstr = freq + self.freq = freq self.base = base (self.quarter, self.month, self.day) = (quarter, month, day) self.isminor = minor_locator self.isdynamic = dynamic_mode self.offset = 0 #..... - if freq == 'A': + if freq == TS.FR_ANN: self.finder = _annual_finder - elif freq == 'Q': + elif freq == TS.FR_QTR: self.finder = _quarterly_finder - elif freq == 'M': + elif freq == TS.FR_MTH: self.finder = _monthly_finder - elif freq in 'BD': + elif freq in (TS.FR_BUS, TS.FR_DAY): self.finder = _daily_finder def asminor(self): @@ -467,7 +483,7 @@ def _get_default_locs(self, vmin, vmax): "Returns the default locations of ticks." - (minor, major) = self.finder(vmin, vmax, self.freqstr, True) + (minor, major) = self.finder(vmin, vmax, self.freq, True) if self.isminor: return minor return major @@ -508,20 +524,20 @@ def __init__(self, freq, minor_locator=False, dynamic_mode=True,): self.format = None - self.freqstr = freq + self.freq = freq self.locs = [] self.formatdict = {} self.isminor = minor_locator self.isdynamic = dynamic_mode self.offset = 0 #..... - if freq == 'A': + if freq == TS.FR_ANN: self.finder = _annual_finder - elif freq == 'Q': + elif freq == TS.FR_QTR: self.finder = _quarterly_finder - elif freq == 'M': + elif freq == TS.FR_MTH: self.finder = _monthly_finder - elif freq in 'BD': + elif freq in (TS.FR_BUS, TS.FR_DAY): self.finder = _daily_finder def asminor(self): @@ -536,7 +552,7 @@ def _set_default_format(self, vmin, vmax): "Returns the default ticks spacing." - self.formatdict = self.finder(vmin, vmax, self.freqstr, False) + self.formatdict = self.finder(vmin, vmax, self.freq, False) return self.formatdict def set_locs(self, locs): @@ -550,7 +566,7 @@ if self.isminor: fmt = self.formatdict.pop(x, '') if fmt is not '': - retval = Date(self.freqstr, value=int(x)).strfmt(fmt) + retval = Date(self.freq, value=int(x)).strfmt(fmt) else: retval = '' else: @@ -589,13 +605,13 @@ assert hasattr(_series, "dates") self._series = _series.ravel() self.xdata = _series.dates - self.freqstr = _series.dates.freqstr + self.freq = _series.dates.freq self.xaxis.set_major_locator else: self._series = None self.xdata = None - self.freqstr = None + self.freq = None self._austoscale = False # Get the data to plot self.legendsymbols = [] @@ -642,9 +658,9 @@ # The argument is a DateArray............ elif isinstance(a, (Date, DateArray)): # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) + if self.freq is not None: + if a.freq != self.freq: + a = a.asfreq(self.freq) # There's an argument after if len(remaining) > 0: #...and it's a format string @@ -687,11 +703,11 @@ # Reinitialize the plot if needed ........... if self.xdata is None: self.xdata = output[0] - self.freqstr = self.xdata.freqstr + self.freq = self.xdata.freq # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: + elif output[0].freq != self.freq: output = list(output) - output[0] = output[0].asfreq(self.freqstr) + output[0] = output[0].asfreq(self.freq) return output #............................................ def tsplot(self,*parms,**kwargs): @@ -715,16 +731,16 @@ String format for major ticks ("%Y"). """ # Get the locator class ................. - majlocator = TimeSeries_DateLocator(self.freqstr, dynamic_mode=True, + majlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, minor_locator=False) - minlocator = TimeSeries_DateLocator(self.freqstr, dynamic_mode=True, + minlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, minor_locator=True) self.xaxis.set_major_locator(majlocator) self.xaxis.set_minor_locator(minlocator) # Get the formatter ..................... - majformatter = TimeSeries_DateFormatter(self.freqstr, dynamic_mode=True, + majformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, minor_locator=False) - minformatter = TimeSeries_DateFormatter(self.freqstr, dynamic_mode=True, + minformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, minor_locator=True) self.xaxis.set_major_formatter(majformatter) self.xaxis.set_minor_formatter(minformatter) From scipy-svn at scipy.org Tue Feb 27 09:01:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:01:13 -0600 (CST) Subject: [Scipy-svn] r2765 - in trunk/Lib/sandbox/timeseries: archived_version plotlib Message-ID: <20070227140113.253FA39C037@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:01:03 -0600 (Tue, 27 Feb 2007) New Revision: 2765 Added: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py Removed: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Log: Moved remotely Copied: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py (from rev 2764, trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py) Deleted: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-26 21:43:15 UTC (rev 2764) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-27 14:01:03 UTC (rev 2765) @@ -1,775 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu -:date: $Date$ -:version: $Id$ -""" -__author__ = "Pierre GF Gerard-Marchant ($Author$)" -__version__ = '1.0' -__revision__ = "$Revision$" -__date__ = '$Date$' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator - -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass",SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: - (min_spacing, maj_spacing) = (10, 50) - else: - (min_spacing, maj_spacing) = (20, 100) - return (min_spacing, maj_spacing) - -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1,4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1,4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) - -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1,3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1,6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1,12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3,12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freqstr = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - base = self._get_default_spacing(vmax-vmin+1) - else: - base = self.base - d = vmin // base - vmin = (d+1) * base + self.offset - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - if self.isdynamic: - base = self._get_default_spacing(dmax-dmin+1) - else: - base = self.base - (d,m) = divmod(dmin, base) - if m < base/2: - vmin = d * base - else: - vmin = (d+1) * base - (d,m) = divmod(dmax, base) - vmax = (d+1) * base - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_spacing(self, span): - "Returns the default tick spacing for annual data." - (minor, major) = _get_default_annual_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for quarterly data." - (minor, major) = _get_default_quarterly_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) - if self.isminor: - return minor - return major - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - # - if span <= daysperyear//12: - minor = default - major = default[(dates.day_of_week == 1)] - elif span <= daysperyear//3: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 1.5 * daysperyear: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 3 * daysperyear: - minor = default[(dates.day == 1)] - major = default[(dates.day_of_year == 1)] - elif span <= 11 * daysperyear: - minor = default[(dates.quarter != (dates-1).quarter)] - major = default[(dates.day_of_year == 1)] - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.day_of_year == 1) - minor = default[annual & (dates.years % min_anndef == 0)] - major = default[annual & (dates.years % maj_anndef == 0)] - if self.isminor: - return minor - return major - - def __call__(self): - 'Return the locations of the ticks' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d,m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0,-1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, fmt=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt - self.freqstr = freq - - def __call__(self, x, pos=0): - return Date(self.freqstr, value=int(x)).strfmt(self.fmt) - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series',None) - Subplot.__init__(self,fig,*args,**kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freqstr = _series.dates.freqstr - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freqstr = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self,*args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a,str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x,y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x,y,b]) - else: - output.extend([x,y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a,b,c]) - else: - output.extend([a,b]) - # continue - else: - if self.ydata is None: - raise ValueError, "No data information available!" - #else: - # break - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - #continue - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - #continue - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freqstr = self.xdata.freqstr - # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: - output = list(output) - output[0] = output[0].asfreq(self.freqstr) - return output - #............................................ - def tsplot(self,*parms,**kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label',None)) - Subplot.plot(self, *parms,**kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) - #............................................ - def format_dateaxis(self,maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=True): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - self.xaxis.set_major_locator(locator(minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(minor_locator=True, - dynamic_mode=True)) - #........................................ - self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(),rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self,**kwargs) - fspnum = kwargs.pop('fspnum',None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#Figure.add_tsplot = -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), - length=51) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file From scipy-svn at scipy.org Tue Feb 27 09:01:43 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:01:43 -0600 (CST) Subject: [Scipy-svn] r2766 - in trunk/Lib/sandbox/timeseries: archived_version plotlib Message-ID: <20070227140143.DEC7739C037@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:01:41 -0600 (Tue, 27 Feb 2007) New Revision: 2766 Added: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_matt.py Removed: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py Log: Moved remotely Copied: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_matt.py (from rev 2765, trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py) Deleted: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-27 14:01:03 UTC (rev 2765) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_matt.py 2007-02-27 14:01:41 UTC (rev 2766) @@ -1,1006 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu -:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ -:version: $Id: mpl_timeseries.py 2676 2007-02-03 04:19:06Z pierregm $ -""" -__author__ = "Pierre GF Gerard-Marchant ($Author: pierregm $)" -__version__ = '1.0' -__revision__ = "$Revision: 2676 $" -__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator - -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass", SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - - if nyears < 11: - (min_spacing, maj_spacing) = (1, 1) - elif nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: - (min_spacing, maj_spacing) = (10, 50) - else: - (min_spacing, maj_spacing) = (20, 100) - return (min_spacing, maj_spacing) - -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1, 4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1, 4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) - -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1, 3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1, 6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1, 12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3, 12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freqstr = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def _get_default_locs(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def __call__(self): - 'Return the locations of the ticks' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d, m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0, -1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - - return nonsingular(vmin, vmax) - - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span) - - major_ticks = default[(dates.years % maj_anndef == 0)] - - if self.isminor: - minor_ticks = default[(dates.years % min_anndef == 0)] - - self.fmt_strs = ['%Y'] - self.label_flags = [major_ticks] - - return minor_ticks - - return major_ticks - - -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - - if self.isminor: - self.fmt_strs, self.label_flags = [], [] - - if span <= 3 * 4: - - major_ticks = default[(dates.year != (dates - 1).year)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('Q%q') - self.label_flags.append(default) - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - elif span <= 11 * 4: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - else: - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/4) - annual = (dates.year != (dates-1).year) - - major_ticks = default[annual & (dates.years % maj_anndef == 0)] - - if self.isminor: - _temp_idx = annual & (dates.years % min_anndef == 0) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - if self.isminor: - if default.size > 0: - """ensure that at least one value for every - level of label is output""" - for x, lf in enumerate(self.label_flags): - if lf.size == 0: - self.label_flags[x] = default[0:1] - - return minor_ticks - return major_ticks - -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, 'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for monthly data." - - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - - if self.isminor: - self.fmt_strs, self.label_flags = [], [] - - if span <= 1.5 * 12: - - major_ticks = default[(dates.year != (dates - 1).year)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('%b') - self.label_flags.append(minor_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - elif span <= 3 * 12: - - major_ticks = default[(dates.year != (dates - 1).year)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('%b') - self.label_flags.append(default[(dates.month - 1)% 2 == 0]) - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - elif span <= 11 * 12: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - minor_ticks = default[(dates.quarter != (dates - 1).quarter)] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - else: - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/12) - annual = (dates.year != (dates-1).year) - - major_ticks = default[annual & (dates.years % maj_anndef == 0)] - - if self.isminor: - _temp_idx = annual & (dates.years % min_anndef == 0) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - if self.isminor: - if default.size > 0: - """ensure that at least one value for every - level of label is output""" - for x, lf in enumerate(self.label_flags): - if lf.size == 0: - self.label_flags[x] = default[0:1] - - return minor_ticks - return major_ticks - - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - - if self.isminor: - self.fmt_strs, self.label_flags = [], [] - - if span <= daysperyear//12: - - major_ticks = default[(dates.month != (dates-1).month)] - - if self.isminor: - minor_ticks = default - - self.fmt_strs.append('%d') - self.label_flags.append(minor_ticks) - - self.fmt_strs.append('%b') - self.label_flags.append(major_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - - elif span <= daysperyear//4: - - major_ticks = default[(dates.month != (dates-1).month)] - - if self.isminor: - - minor_ticks = default - - self.fmt_strs.append('%d') - self.label_flags.append(default[(dates.day_of_week == 1)]) - - self.fmt_strs.append('%b') - self.label_flags.append(major_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - - elif span <= 1.5 * daysperyear: - - _month_starts = (dates.month != (dates-1).month) - - major_ticks = default[_month_starts] - - if self.isminor: - - _temp_idx = (dates.day_of_week == 1) | _month_starts - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%b') - self.label_flags.append(major_ticks) - - self.fmt_strs.append('%Y') - self.label_flags.append(default[(dates.year != (dates-1).year)]) - - - elif span <= 3 * daysperyear: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - _temp_idx = (dates.month != (dates-1).month) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%b') - self.label_flags.append(default[(dates.quarter != (dates-1).quarter)]) - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - elif span <= 11 * daysperyear: - - major_ticks = default[(dates.year != (dates-1).year)] - - if self.isminor: - - _temp_idx = (dates.quarter != (dates-1).quarter) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.year != (dates-1).year) - - major_ticks = default[annual & (dates.years % maj_anndef == 0)] - - if self.isminor: - _temp_idx = annual & (dates.years % min_anndef == 0) - if _temp_idx.size > 0: _temp_idx[0] = True - minor_ticks = default[_temp_idx] - - self.fmt_strs.append('%Y') - self.label_flags.append(major_ticks) - - if self.isminor: - - if default.size > 0: - """ensure that at least one value for every - level of label is output""" - for x, lf in enumerate(self.label_flags): - if lf.size == 0: - self.label_flags[x] = default[0:1] - - return minor_ticks - return major_ticks - - -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, fmt=None, locator=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt - self.freqstr = freq - self.locator = locator - - def __call__(self, x, pos=0): - - if self.locator is not None: - lc = self.locator - - if lc.isminor: - nls = '\n' - retval = '' - for flags, fmt in zip(lc.label_flags, lc.fmt_strs): - if N.where(flags == x)[0].size > 0: - retval += Date(self.freqstr, value=int(x)).strfmt(fmt) + nls - else: - retval += nls - retval = retval.rstrip(nls) - else: - retval = '' - else: - retval = Date(self.freqstr, value=int(x)).strfmt(self.fmt) - - return retval - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series', None) - Subplot.__init__(self, fig, *args, **kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freqstr = _series.dates.freqstr - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freqstr = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self, *args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a, str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x, y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x, y, b]) - else: - output.extend([x, y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a, b, c]) - else: - output.extend([a, b]) - # continue - else: - if self.ydata is None: - raise ValueError, "No data information available!" - #else: - # break - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - #continue - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - #continue - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freqstr = self.xdata.freqstr - # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: - output = list(output) - output[0] = output[0].asfreq(self.freqstr) - return output - #............................................ - def tsplot(self, *parms, **kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label', None)) - Subplot.plot(self, *parms, **kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) - #............................................ - def format_dateaxis(self, maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=False): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - major_locator = locator(self.freqstr, - minor_locator=False, - dynamic_mode=True) - minor_locator = locator(self.freqstr, - minor_locator=True, - dynamic_mode=True) - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - major_locator = locator(minor_locator=False, - dynamic_mode=True) - minor_locator = locator(minor_locator=True, - dynamic_mode=True) - - self.xaxis.set_major_locator(major_locator) - self.xaxis.set_minor_locator(minor_locator) - #........................................ - self.xaxis.set_major_formatter( - TimeSeries_DateFormatter(self.freqstr, locator=major_locator)) - self.xaxis.set_minor_formatter( - TimeSeries_DateFormatter(self.freqstr, locator=minor_locator)) - - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(), rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self, **kwargs) - fspnum = kwargs.pop('fspnum', None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#Figure.add_tsplot = -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), - length=51) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file From scipy-svn at scipy.org Tue Feb 27 09:01:57 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:01:57 -0600 (CST) Subject: [Scipy-svn] r2767 - in trunk/Lib/sandbox/timeseries: archived_version plotlib Message-ID: <20070227140157.5C70B39C037@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:01:54 -0600 (Tue, 27 Feb 2007) New Revision: 2767 Added: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_new.py Removed: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py Log: Moved remotely Copied: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_new.py (from rev 2766, trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py) Deleted: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-27 14:01:41 UTC (rev 2766) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_new.py 2007-02-27 14:01:54 UTC (rev 2767) @@ -1,1111 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com -:date: $Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $ -:version: $Id: tdates.py 2726 2007-02-19 07:37:26Z pierregm $ -""" -__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: pierregm $)" -__version__ = '1.0' -__revision__ = "$Revision: 2676 $" -__date__ = '$Date: 2007-02-02 23:19:06 -0500 (Fri, 02 Feb 2007) $' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator - -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass",SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _year_start(_dates): - return (_dates.year != (_dates-1).year) - -def _quarter_start(_dates): - return (_dates.quarter != (_dates-1).quarter) - -def _month_start(_dates): - return (_dates.month != (_dates-1).month) - -def _week_start(_dates): - return (_dates.day_of_week == 1) - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 11: - (min_spacing, maj_spacing) = (1, 1) - elif nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 600: - (min_spacing, maj_spacing) = (10, 50) - else: - factor = nyears // 1000 + 1 - (min_spacing, maj_spacing) = (factor*20, factor*100) - return (min_spacing, maj_spacing) - - -def _BreakDown_ParamCheck(locator, formatter): - if not locator and not formatter: - raise ValueError("Must specify either locator or formatter") - - if locator and formatter: - raise ValueError("Must specify only one of locator or formatter") - -def _Daily_BreakDown(dates, locator=False, formatter=False): - - _BreakDown_ParamCheck(locator, formatter) - - if dates.freqstr == 'B': periodsperyear = 261 - elif dates.freqstr == 'D': periodsperyear = 365 - else: raise ValueError("unexpected frequency") - - vmin = dates[0].value - vmax = dates[-1].value - span = vmax - vmin + 1 - - if locator: - default = N.arange(vmin, vmax+1) - else: #formatter - format = N.empty(dates.size, dtype="|S8") - format.flat = '' - - if span <= (periodsperyear//12 - 2): - - month_start = _month_start(dates) - - if locator: - major = default[month_start] - minor = default - else: - year_start = _year_start(dates) - year_start[0] = False - month_start[0] = False - - format[:] = '%d' - format[month_start] = '%d\n%b' - format[year_start] = '%d\n%b\n%Y' - - if not year_start.any(): - if not month_start.any(): - if dates.size > 1: idx = 1 - else: idx = 0 - format[idx] = '%d\n%b\n%Y' - else: - format[N.where(month_start)[0][0]] = '%d\n%b\n%Y' - - elif span <= periodsperyear//4: - - month_start = _month_start(dates) - - if locator: - major = default[month_start] - minor = default - else: - week_start = _week_start(dates) - year_start = _year_start(dates) - - week_start[0] = False - month_start[0] = False - year_start[0] = False - - format[week_start] = '%d' - format[month_start] = '\n\n%b' - format[year_start] = '\n\n%b\n%Y' - - if not year_start.any(): - if not month_start.any(): - format[N.where(week_start)[0][0]] = '\n\n%b\n%Y' - else: - format[N.where(month_start)[0][0]] = '\n\n%b\n%Y' - - elif span <= 1.15 * periodsperyear: - month_start = _month_start(dates) - - if locator: - week_start = _week_start(dates) - minor_idx = week_start | month_start - minor_idx[0] = True - major = default[month_start] - minor = default[minor_idx] - else: - year_start = _year_start(dates) - month_start[0] = False - year_start[0] = False - format[month_start] = '%b' - format[year_start] = '%b\n%Y' - - if not year_start.any(): - format[N.where(month_start)[0][0]] = '%b\n%Y' - - elif span <= 2.5 * periodsperyear: - - year_start = _year_start(dates) - month_start = _month_start(dates) - - if locator: - major = default[year_start] - minor = default[month_start] - else: - quarter_start = _quarter_start(dates) - format[quarter_start] = '%b' - format[year_start] = '%b\n%Y' - - elif span <= 4 * periodsperyear: - - year_start = _year_start(dates) - month_start = _month_start(dates) - - if locator: - major = default[year_start] - minor = default[month_start] - else: - jan = (dates.month == 1) - jul = (dates.month == 7) - jan_or_jul = month_start & (jan | jul) - format[jan_or_jul] = '%b' - format[year_start] = '%b\n%Y' - - elif span <= 11 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - quarter_start = _quarter_start(dates) - major = default[year_start] - minor = default[quarter_start] - else: - format[year_start] = '%Y' - - else: - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) - year_start = _year_start(dates) - - major_idx = year_start & (dates.years % maj_anndef == 0) - - if locator: - major = default[major_idx] - minor = default[year_start & (dates.years % min_anndef == 0)] - else: - format[major_idx] = '%Y' - - if locator: - return minor, major - else: - return format - - -def _Monthly_BreakDown(dates, locator=False, formatter=False): - - _BreakDown_ParamCheck(locator, formatter) - - if dates.freqstr != 'M': raise ValueError("unexpected frequency") - - periodsperyear = 12 - - vmin = dates[0].value - vmax = dates[-1].value - span = vmax - vmin + 1 - - if locator: - default = N.arange(vmin, vmax+1) - else: #formatter - format = N.empty(dates.size, dtype="|S8") - format.flat = '' - - if span <= 1.15 * periodsperyear: - year_start = _year_start(dates) - - if locator: - major = default[year_start] - minor = default - else: - year_start[0] = False - - format[:] = '%b' - format[year_start] = '%b\n%Y' - - if not year_start.any(): - if dates.size > 1: idx = 1 - else: idx = 0 - format[idx] = '%b\n%Y' - - elif span <= 2.5 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - major = default[year_start] - minor = default - else: - quarter_start = _quarter_start(dates) - format[quarter_start] = '%b' - format[year_start] = '%b\n%Y' - - elif span <= 4 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - major = default[year_start] - minor = default - else: - months = dates.month - format[(months == 1) | (months == 7)] = '%b' - format[year_start] = '%b\n%Y' - - elif span <= 11 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - quarter_start = _quarter_start(dates) - major = default[year_start] - minor = default[quarter_start] - else: - format[year_start] = '%Y' - - else: - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) - year_start = _year_start(dates) - - major_idx = year_start & (dates.years % maj_anndef == 0) - - if locator: - major = default[major_idx] - minor = default[year_start & (dates.years % min_anndef == 0)] - else: - format[major_idx] = '%Y' - - if locator: - return minor, major - else: - return format - - -def _Quarterly_BreakDown(dates, locator=False, formatter=False): - - _BreakDown_ParamCheck(locator, formatter) - - if dates.freqstr != 'Q': raise ValueError("unexpected frequency") - - periodsperyear = 4 - - vmin = dates[0].value - vmax = dates[-1].value - span = vmax - vmin + 1 - - if locator: - default = N.arange(vmin, vmax+1) - else: #formatter - format = N.empty(dates.size, dtype="|S8") - format.flat = '' - - if span <= 3.5 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - major = default[year_start] - minor = default - else: - year_start[0] = False - - format[:] = 'Q%q' - format[year_start] = 'Q%q\n%Y' - - if not year_start.any(): - if dates.size > 1: idx = 1 - else: idx = 0 - format[idx] = 'Q%q\n%Y' - - elif span <= 11 * periodsperyear: - - year_start = _year_start(dates) - - if locator: - major = default[year_start] - minor = default - else: - format[year_start] = '%Y' - - else: - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/periodsperyear) - year_start = _year_start(dates) - - major_idx = year_start & (dates.years % maj_anndef == 0) - - if locator: - major = default[major_idx] - minor = default[year_start & (dates.years % min_anndef == 0)] - else: - format[major_idx] = '%Y' - - if locator: - return minor, major - else: - return format - - -def _Annual_BreakDown(dates, locator=False, formatter=False): - - _BreakDown_ParamCheck(locator, formatter) - - if dates.freqstr != 'A': raise ValueError("unexpected frequency") - - vmin = dates[0].value - vmax = dates[-1].value - span = vmax - vmin + 1 - - if locator: - default = N.arange(vmin, vmax+1) - else: #formatter - format = N.empty(dates.size, dtype="|S8") - format.flat = '' - - (min_anndef, maj_anndef) = _get_default_annual_spacing(span) - year_start = _year_start(dates) - - major_idx = year_start & (dates.years % maj_anndef == 0) - - if locator: - major = default[major_idx] - minor = default[year_start & (dates.years % min_anndef == 0)] - else: - format[major_idx] = '%Y' - - if locator: - return minor, major - else: - return format - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freqstr = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def _get_default_locs(self, vmin, vmax): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d, m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0, -1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -def _generic_get_default_locs(self, vmin, vmax, BreakDownFunc): - dates = self._initialize_dates(vmin, vmax) - minor, major = BreakDownFunc(dates, locator=True) - if self.isminor: return minor - return major - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick spacing for annual data." - return _generic_get_default_locs(self, vmin, vmax, _Annual_BreakDown) - -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - - def _get_default_locs(self, vmin, vmax): - "Returns the default ticks spacing." - return _generic_get_default_locs(self, vmin, vmax, _Quarterly_BreakDown) - -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_locs(self, vmin, vmax): - "Returns the default ticks spacing." - return _generic_get_default_locs(self, vmin, vmax, _Monthly_BreakDown) - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - return _generic_get_default_locs(self, vmin, vmax, _Daily_BreakDown) - -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, minor_locator=False, dynamic_mode=True,): - self.format = None - self.freqstr = freq - self.locs = [] - self.formatdict = {} - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - - def _initialize_dates(self, locs): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(dlist=self.locs, freq=freq) - return dates - - def set_locs(self, locs): - 'Sets the locations of the ticks' - self.locs = locs - if len(self.locs) > 0: - self.verify_intervals() - d = abs(self.viewInterval.span()) - self._set_format(d) - # - def __call__(self, x, pos=0): - if self.isminor: - fmt = self.formatdict.pop(x, '') - if fmt is not '': - retval = Date(self.freqstr, value=int(x)).strfmt(fmt) - else: - retval = '' - else: - retval = '' - return retval - -def _generic_set_format(self, BreakDownFunc): - dates = self._initialize_dates(self.locs) - format = BreakDownFunc(dates, formatter=True) - return dict([(x,f) for (x,f) in zip(self.locs, format)]) - -#............................................................................... -class TimeSeries_AnnualFormatter(TimeSeries_DateFormatter): - # - def __init__(self, minor_locator=False, dynamic_mode=True,): - TimeSeries_DateFormatter.__init__(self, 'A', - minor_locator=minor_locator, - dynamic_mode=dynamic_mode,) - - def _set_format(self, span): - self.formatdict = _generic_set_format(self, _Annual_BreakDown) - -#............................................................................... -class TimeSeries_QuarterlyFormatter(TimeSeries_DateFormatter): - # - def __init__(self, minor_locator=False, dynamic_mode=True,): - TimeSeries_DateFormatter.__init__(self, 'Q', - minor_locator=minor_locator, - dynamic_mode=dynamic_mode,) - - def _set_format(self, span): - self.formatdict = _generic_set_format(self, _Quarterly_BreakDown) - -#............................................................................... -class TimeSeries_MonthlyFormatter(TimeSeries_DateFormatter): - # - def __init__(self, minor_locator=False, dynamic_mode=True,): - TimeSeries_DateFormatter.__init__(self, 'M', - minor_locator=minor_locator, - dynamic_mode=dynamic_mode,) - # - def _set_format(self, span): - self.formatdict = _generic_set_format(self, _Monthly_BreakDown) - -#............................................................................... -class TimeSeries_DailyFormatter(TimeSeries_DateFormatter): - # - def __init__(self, freq, minor_locator=False, dynamic_mode=True,): - TimeSeries_DateFormatter.__init__(self, freq, - minor_locator=minor_locator, - dynamic_mode=dynamic_mode,) - # - def _set_format(self, span): - self.formatdict = _generic_set_format(self, _Daily_BreakDown) - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series',None) - Subplot.__init__(self,fig,*args,**kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freqstr = _series.dates.freqstr - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freqstr = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self,*args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a,str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x,y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x,y,b]) - else: - output.extend([x,y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a,b,c]) - else: - output.extend([a,b]) - # continue - else: - if self.ydata is None: - raise ValueError, "No data information available!" - #else: - # break - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - #continue - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - #continue - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freqstr = self.xdata.freqstr - # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: - output = list(output) - output[0] = output[0].asfreq(self.freqstr) - return output - #............................................ - def tsplot(self,*parms,**kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label',None)) - Subplot.plot(self, *parms,**kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) - #............................................ - def format_dateaxis(self,maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=True): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - formatter = TimeSeries_DailyFormatter - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - self.xaxis.set_major_formatter(formatter(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_formatter(formatter(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - formatter = TimeSeries_AnnualFormatter - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - formatter = TimeSeries_QuarterlyFormatter - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - formatter = TimeSeries_MonthlyFormatter - self.xaxis.set_major_locator(locator(minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(minor_locator=True, - dynamic_mode=True)) - self.xaxis.set_major_formatter(formatter(minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_formatter(formatter(minor_locator=True, - dynamic_mode=True)) - #........................................ -# if rcParams['backend'] == 'PS': -# rotate = False -# warnings.warn("dateplot: PS backend detected, rotate disabled") -# if self.is_last_row(): -# if rotate: -# setp(self.get_xticklabels(),rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') - -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self,**kwargs) - fspnum = kwargs.pop('fspnum',None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#Figure.add_tsplot = -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='A', year=2003, quarter=3, month=1, day=17), - length=51) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked -# ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() -# pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file From scipy-svn at scipy.org Tue Feb 27 09:02:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:02:13 -0600 (CST) Subject: [Scipy-svn] r2768 - in trunk/Lib/sandbox/timeseries: archived_version plotlib Message-ID: <20070227140213.91A0539C037@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:02:11 -0600 (Tue, 27 Feb 2007) New Revision: 2768 Added: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Removed: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py Log: Moved remotely Deleted: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py 2007-02-27 14:01:54 UTC (rev 2767) +++ trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py 2007-02-27 14:02:11 UTC (rev 2768) @@ -1,775 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu -:date: $Date$ -:version: $Id$ -""" -__author__ = "Pierre GF Gerard-Marchant ($Author$)" -__version__ = '1.0' -__revision__ = "$Revision$" -__date__ = '$Date$' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator - -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass",SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: - (min_spacing, maj_spacing) = (10, 50) - else: - (min_spacing, maj_spacing) = (20, 100) - return (min_spacing, maj_spacing) - -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1,4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1,4) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) - -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1,3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1,6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1,12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3,12) - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freqstr = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') - - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - base = self._get_default_spacing(vmax-vmin+1) - else: - base = self.base - d = vmin // base - vmin = (d+1) * base + self.offset - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - if self.isdynamic: - base = self._get_default_spacing(dmax-dmin+1) - else: - base = self.base - (d,m) = divmod(dmin, base) - if m < base/2: - vmin = d * base - else: - vmin = (d+1) * base - (d,m) = divmod(dmax, base) - vmax = (d+1) * base - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_spacing(self, span): - "Returns the default tick spacing for annual data." - (minor, major) = _get_default_annual_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for quarterly data." - (minor, major) = _get_default_quarterly_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) - if self.isminor: - return minor - return major - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - # - if span <= daysperyear//12: - minor = default - major = default[(dates.day_of_week == 1)] - elif span <= daysperyear//3: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 1.5 * daysperyear: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 3 * daysperyear: - minor = default[(dates.day == 1)] - major = default[(dates.day_of_year == 1)] - elif span <= 11 * daysperyear: - minor = default[(dates.quarter != (dates-1).quarter)] - major = default[(dates.day_of_year == 1)] - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.day_of_year == 1) - minor = default[annual & (dates.years % min_anndef == 0)] - major = default[annual & (dates.years % maj_anndef == 0)] - if self.isminor: - return minor - return major - - def __call__(self): - 'Return the locations of the ticks' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d,m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0,-1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, fmt=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt - self.freqstr = freq - - def __call__(self, x, pos=0): - return Date(self.freqstr, value=int(x)).strfmt(self.fmt) - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series',None) - Subplot.__init__(self,fig,*args,**kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freqstr = _series.dates.freqstr - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freqstr = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self,*args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a,str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x,y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x,y,b]) - else: - output.extend([x,y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a,b,c]) - else: - output.extend([a,b]) - # continue - else: - if self.ydata is None: - raise ValueError, "No data information available!" - #else: - # break - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - #continue - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - #continue - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freqstr = self.xdata.freqstr - # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: - output = list(output) - output[0] = output[0].asfreq(self.freqstr) - return output - #............................................ - def tsplot(self,*parms,**kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label',None)) - Subplot.plot(self, *parms,**kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) - #............................................ - def format_dateaxis(self,maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=True): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - self.xaxis.set_major_locator(locator(minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(minor_locator=True, - dynamic_mode=True)) - #........................................ - self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(),rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self,**kwargs) - fspnum = kwargs.pop('fspnum',None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#Figure.add_tsplot = -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), - length=51) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file Copied: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py (from rev 2767, trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries.py) From scipy-svn at scipy.org Tue Feb 27 09:05:05 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:05:05 -0600 (CST) Subject: [Scipy-svn] r2769 - trunk/Lib/sandbox/timeseries/plotlib Message-ID: <20070227140505.D067E39C2B9@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:05:02 -0600 (Tue, 27 Feb 2007) New Revision: 2769 Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py Log: major overhaul Modified: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-27 14:02:11 UTC (rev 2768) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries.py 2007-02-27 14:05:02 UTC (rev 2769) @@ -1,12 +1,12 @@ """ Classes to plot TimeSeries w/ matplotlib. -:author: Pierre GF Gerard-Marchant -:contact: pierregm_at_uga_edu +:author: Pierre GF Gerard-Marchant & Matt Knox +:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com :date: $Date$ :version: $Id$ """ -__author__ = "Pierre GF Gerard-Marchant ($Author$)" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" __version__ = '1.0' __revision__ = "$Revision$" __date__ = '$Date$' @@ -26,17 +26,14 @@ from matplotlib.mlab import meshgrid from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ Locator, FixedLocator - #from matplotlib.transforms import nonsingular import numpy as N import maskedarray as MA import timeseries +import timeseries as TS from timeseries import date_array, Date, DateArray, TimeSeries -#from tdates import date_array, Date -#import tseries -#from tseries import TimeSeries import warnings @@ -133,207 +130,366 @@ def _get_default_annual_spacing(nyears): """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 20: + if nyears < 11: + (min_spacing, maj_spacing) = (1, 1) + elif nyears < 20: (min_spacing, maj_spacing) = (1, 2) elif nyears < 50: (min_spacing, maj_spacing) = (1, 5) elif nyears < 100: (min_spacing, maj_spacing) = (5, 10) elif nyears < 200: - (min_spacing, maj_spacing) = (5, 20) - elif nyears < 400: (min_spacing, maj_spacing) = (5, 25) - elif nyears < 1000: + elif nyears < 600: (min_spacing, maj_spacing) = (10, 50) else: - (min_spacing, maj_spacing) = (20, 100) + factor = nyears // 1000 + 1 + (min_spacing, maj_spacing) = (factor*20, factor*100) return (min_spacing, maj_spacing) -def _get_default_quarterly_spacing(nquarters): - """Returns a default spacing between consecutive ticks for quarterly data.""" - if nquarters <= 3*4: - (min_spacing, maj_spacing) = (1,4) - elif nquarters <= 11*4: - (min_spacing, maj_spacing) = (1,4) + +def period_break(dates, period): + """Returns the indices where the given period changes. + +:Parameters: + dates : DateArray + Array of dates to monitor. + period : string + Name of the period to monitor. + """ + current = getattr(dates, period) + previous = getattr(dates-1, period) + return (current - previous).nonzero()[0] + +def has_level_label(label_flags): + """returns true if the label_flags indicate there is at least +one label for this level""" + if label_flags.size == 0 or \ + (label_flags.size == 1 and label_flags[0] == 0): + return False else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nquarters//4) - min_spacing = min_anndef * 4 - maj_spacing = maj_anndef * 4 - return (min_spacing, maj_spacing) + return True -def _get_default_monthly_spacing(nmonths): - """Returns a default spacing between consecutive ticks for monthly data.""" - if nmonths <= 10: - (min_spacing, maj_spacing) = (1,3) - elif nmonths <= 2*12: - (min_spacing, maj_spacing) = (1,6) - elif nmonths <= 3*12: - (min_spacing, maj_spacing) = (1,12) - elif nmonths <= 11*12: - (min_spacing, maj_spacing) = (3,12) +def _daily_finder(vmin, vmax, freq, aslocator): + + if freq == TS.FR_BUS: + periodsperyear = 261 + elif freq == TS.FR_DAY: + periodsperyear = 365 + else: + raise ValueError("unexpected frequency") + + (vmin, vmax) = (int(vmin), int(vmax)) + span = vmax - vmin + 1 + dates = date_array(start_date=Date(freq,vmin), + end_date=Date(freq, vmax)) + default = N.arange(vmin, vmax+1) + # Initialize the output + if not aslocator: + format = N.empty(default.shape, dtype="|S10") + format.flat = '' + + def first_label(label_flags): + if label_flags[0] == 0: return label_flags[1] + else: return label_flags[0] + + # Case 1. Less than a month + if span <= (periodsperyear//12 - 2): + month_start = period_break(dates,'month') + if aslocator: + major = default[month_start] + minor = default + else: + year_start = period_break(dates,'year') + format[:] = '%d' + format[month_start] = '%d\n%b' + format[year_start] = '%d\n%b\n%Y' + if not has_level_label(year_start): + if not has_level_label(month_start): + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = '%d\n%b\n%Y' + else: + format[first_label(month_start)] = '%d\n%b\n%Y' + # Case 2. Less than three months + elif span <= periodsperyear//4: + month_start = period_break(dates,'month') + if aslocator: + major = default[month_start] + minor = default + else: + week_start = period_break(dates,'week') + year_start = period_break(dates,'year') + + format[week_start] = '%d' + format[month_start] = '\n\n%b' + format[year_start] = '\n\n%b\n%Y' + if not has_level_label(year_start): + if not has_level_label(month_start): + format[first_label(week_start)] = '\n\n%b\n%Y' + else: + format[first_label(month_start)] = '\n\n%b\n%Y' + # Case 3. Less than 14 months ............... + elif span <= 1.15 * periodsperyear: + + if aslocator: + d_minus_1 = dates-1 + + month_diff = N.abs(dates.month - d_minus_1.month) + week_diff = N.abs(dates.week - d_minus_1.week) + minor_idx = (month_diff + week_diff).nonzero()[0] + + major = default[month_diff != 0] + minor = default[minor_idx] + else: + year_start = period_break(dates,'year') + month_start = period_break(dates,'month') + + format[month_start] = '%b' + format[year_start] = '%b\n%Y' + if not has_level_label(year_start): + format[first_label(month_start)] = '%b\n%Y' + # Case 4. Less than 2.5 years ............... + elif span <= 2.5 * periodsperyear: + year_start = period_break(dates,'year') + if aslocator: + month_start = period_break(dates, 'quarter') + major = default[year_start] + minor = default[month_start] + else: + quarter_start = period_break(dates, 'quarter') + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + # Case 4. Less than 4 years ................. + elif span <= 4 * periodsperyear: + year_start = period_break(dates,'year') + month_start = period_break(dates, 'month') + if aslocator: + major = default[year_start] + minor = default[month_start] + else: + month_break = dates[month_start].month + jan_or_jul = month_start[(month_break == 1) | (month_break == 7)] + format[jan_or_jul] = '%b' + format[year_start] = '%b\n%Y' + # Case 5. Less than 11 years ................ + elif span <= 11 * periodsperyear: + year_start = period_break(dates,'year') + if aslocator: + quarter_start = period_break(dates, 'quarter') + major = default[year_start] + minor = default[quarter_start] + else: + format[year_start] = '%Y' + # Case 6. More than 12 years ................ else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(nmonths//12) - min_spacing = min_anndef * 12 - maj_spacing = maj_anndef * 12 - return (min_spacing, maj_spacing) + year_start = period_break(dates,'year') + year_break = dates[year_start].years + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + major_idx = year_start[(year_break % maj_anndef == 0)] + if aslocator: + major = default[major_idx] + minor_idx = year_start[(year_break % min_anndef == 0)] + minor = default[minor_idx] + else: + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(default[formatted],format[formatted])]) +#............................................................................... +def _monthly_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_MTH: + raise ValueError("unexpected frequency") + periodsperyear = 12 + (vmin, vmax) = (int(vmin), int(vmax)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + year_start = (dates % 12 == 1).nonzero()[0] + #............................................ + if span <= 1.15 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + + format[:] = '%b' + format[year_start] = '%b\n%Y' + + if not has_level_label(year_start): + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = '%b\n%Y' + #........................ + elif span <= 2.5 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + quarter_start = (dates % 3 == 1).nonzero() + format[quarter_start] = '%b' + format[year_start] = '%b\n%Y' + #....................... + elif span <= 4 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + jan_or_jul = (dates % 12 == 1) | (dates % 12 == 7) + format[jan_or_jul] = '%b' + format[year_start] = '%b\n%Y' + #........................ + elif span <= 11 * periodsperyear: + if aslocator: + quarter_start = (dates % 3 == 1).nonzero() + major = dates[year_start] + minor = dates[quarter_start] + else: + format[year_start] = '%Y' + #......................... + else: + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + years = dates[year_start]//12 + 1 + major_idx = year_start[(years % maj_anndef == 0)] + if aslocator: + major = dates[major_idx] + minor = dates[year_start[(years % min_anndef == 0)]] + else: + format[major_idx] = '%Y' + #........................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) #............................................................................... +def _quarterly_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_QTR: + raise ValueError("unexpected frequency") + periodsperyear = 4 + (vmin, vmax) = (int(vmin), int(vmax)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + year_start = (dates % 4 == 1).nonzero()[0] + #............................................ + if span <= 3.5 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + format[:] = 'Q%q' + format[year_start] = 'Q%q\n%Y' + if not has_level_label(year_start): + if dates.size > 1: + idx = 1 + else: + idx = 0 + format[idx] = 'Q%q\n%Y' + #............................................ + elif span <= 11 * periodsperyear: + if aslocator: + major = dates[year_start] + minor = dates + else: + format[year_start] = '%Y' + #............................................ + else: + years = dates[year_start]//4 + 1 + nyears = span/periodsperyear + (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) + major_idx = year_start[(years % maj_anndef == 0)] + if aslocator: + major = dates[major_idx] + minor = dates[year_start[(years % min_anndef == 0)]] + else: + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) +#............................................................................... +def _annual_finder(vmin, vmax, freq, aslocator): + if freq != TS.FR_ANN: + raise ValueError("unexpected frequency") + (vmin, vmax) = (int(vmin), int(vmax+1)) + span = vmax - vmin + 1 + #............................................ + dates = N.arange(vmin, vmax+1) + format = N.empty(span, dtype="|S8") + format.flat = '' + #............................................ + (min_anndef, maj_anndef) = _get_default_annual_spacing(span) + major_idx = dates % maj_anndef == 0 + if aslocator: + major = dates[major_idx] + minor = dates[(dates % min_anndef == 0)] + else: + format[major_idx] = '%Y' + #............................................ + if aslocator: + return minor, major + else: + formatted = (format != '') + return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) + +#............................................................................... class TimeSeries_DateLocator(Locator): "Locates the ticks along an axis controlled by a DateArray." def __init__(self, freq, minor_locator=False, dynamic_mode=True, base=1, quarter=1, month=1, day=1): - self.freqstr = freq + self.freq = freq self.base = base (self.quarter, self.month, self.day) = (quarter, month, day) self.isminor = minor_locator self.isdynamic = dynamic_mode self.offset = 0 + #..... + if freq == TS.FR_ANN: + self.finder = _annual_finder + elif freq == TS.FR_QTR: + self.finder = _quarterly_finder + elif freq == TS.FR_MTH: + self.finder = _monthly_finder + elif freq in (TS.FR_BUS, TS.FR_DAY): + self.finder = _daily_finder - def _initialize_dates(self, start_val, end_val): - "Returns a DateArray for the current frequency." - freq = self.freqstr - dates = date_array(start_date=Date(freq, value=int(start_val)), - end_date=Date(freq, value=int(end_val)), - freq=freq) - return dates - - def _get_default_spacing(self, span): - "Returns the default ticks spacing." - raise NotImplementedError('Derived must override') + def asminor(self): + "Returns the locator set to minor mode." + self.isminor = True + return self - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - base = self._get_default_spacing(vmax-vmin+1) - else: - base = self.base - d = vmin // base - vmin = (d+1) * base + self.offset - locs = range(vmin, vmax+1, base) - return locs + def asmajor(self): + "Returns the locator set to major mode." + self.isminor = False + return self - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - if self.isdynamic: - base = self._get_default_spacing(dmax-dmin+1) - else: - base = self.base - (d,m) = divmod(dmin, base) - if m < base/2: - vmin = d * base - else: - vmin = (d+1) * base - (d,m) = divmod(dmax, base) - vmax = (d+1) * base - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#............................................................................... -class TimeSeries_AnnualLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by an annual DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'A', minor_locator, dynamic_mode, - base, quarter, month, day) - - def _get_default_spacing(self, span): - "Returns the default tick spacing for annual data." - (minor, major) = _get_default_annual_spacing(span) + def _get_default_locs(self, vmin, vmax): + "Returns the default locations of ticks." + (minor, major) = self.finder(vmin, vmax, self.freq, True) if self.isminor: return minor return major -#............................................................................... -class TimeSeries_QuarterlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a quarterly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'Q', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset=1 - def _get_default_spacing(self, span): - "Returns the default tick spacing for quarterly data." - (minor, major) = _get_default_quarterly_spacing(span) - if self.isminor: - return minor - return major -#............................................................................... -class TimeSeries_MonthlyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a monthly DateArray." - - def __init__(self, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self,'M', minor_locator, dynamic_mode, - base, quarter, month, day) - self.offset = 1 - - def _get_default_spacing(self, span): - "Returns the default tick spacing for monthly data." - (minor, major) = _get_default_monthly_spacing(span) - if self.isminor: - return minor - return major - -#............................................................................... -class TimeSeries_DailyLocator(TimeSeries_DateLocator): - "Locates the ticks along an axis controlled by a daily DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, dynamic_mode, - base, quarter, month, day) - if self.freqstr == 'B': - self.daysinyear = 261 - else: - self.daysinyear = 365 - self._cacheddates = None - - def _get_default_locs(self, vmin, vmax): - "Returns the default tick locations for daily data." - daysperyear = self.daysinyear - span = vmax - vmin + 1 - dates = self._initialize_dates(vmin, vmax) - default = N.arange(vmin, vmax+1) - # - if span <= daysperyear//12: - minor = default - major = default[(dates.day_of_week == 1)] - elif span <= daysperyear//3: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 1.5 * daysperyear: - minor = default[(dates.day_of_week == 1)] - major = default[(dates.day == 1)] - elif span <= 3 * daysperyear: - minor = default[(dates.day == 1)] - major = default[(dates.day_of_year == 1)] - elif span <= 11 * daysperyear: - minor = default[(dates.quarter != (dates-1).quarter)] - major = default[(dates.day_of_year == 1)] - else: - (min_anndef, maj_anndef) = _get_default_annual_spacing(span/daysperyear) - annual = (dates.day_of_year == 1) - minor = default[annual & (dates.years % min_anndef == 0)] - major = default[annual & (dates.years % maj_anndef == 0)] - if self.isminor: - return minor - return major - def __call__(self): - 'Return the locations of the ticks' + 'Return the locations of the ticks.' self.verify_intervals() vmin, vmax = self.viewInterval.get_bounds() if vmax < vmin: @@ -342,11 +498,11 @@ locs = self._get_default_locs(vmin, vmax) else: base = self.base - (d,m) = divmod(vmin, base) + (d, m) = divmod(vmin, base) vmin = (d+1) * base locs = range(vmin, vmax+1, base) return locs - + def autoscale(self): """Sets the view limits to the nearest multiples of base that contain the data. @@ -354,137 +510,72 @@ self.verify_intervals() dmin, dmax = self.dataInterval.get_bounds() locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0,-1]] + (vmin, vmax) = locs[[0, -1]] if vmin == vmax: vmin -= 1 vmax += 1 - return nonsingular(vmin, vmax) + return nonsingular(vmin, vmax) -#............................................................................... -class TimeSeries_YearLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) year. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive annual ticks. - - `quarter` : Integer *[1]* - Tells on which quarter the ticks should be. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - if freq == 'Q': - locs = (dates.quarters == self.quarter) - elif freq == 'M': - locs = (dates.months == self.month) - elif freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return dates.tovalue()[locs] -#............................................... -class TimeSeries_QuarterLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) quarter. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'A': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'Q': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq == 'M': - locs = (dates.months % 4 == self.month) - elif freq in 'BDU': - locs = (dates.months % 4 == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] -#............................................................................... -class TimeSeries_MonthLocator(TimeSeries_DateLocator): - """Locates ticks along a Date axis, for each (multiple of) month. - -:Ivariables: - - `base` : Integer - Gives the spacing between two consecutive quarter ticks. - - `month` : Integer *[1]* - Tells on which month the ticks should be. - - `day` : Integer *[1]* - Tells on which day the ticks should be. - """ - - def __init__(self, freq, minor_locator=False, - base=1, quarter=1, month=1, day=1): - TimeSeries_DateLocator.__init__(self, freq, minor_locator, False, - base, quarter, month, day) - - def __call__(self): - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - freq = self.freqstr - if freq == 'AQ': - msg = "The current frequency ('%s') is too coarse!" % freq - raise ValueError, msg - elif freq == 'M': - return range(vmin, vmax+1, self.base) - else: - dates = self._initialize_dates() - values = dates.tovalue() - if freq in 'BDU': - locs = (dates.months == self.month) & (dates.day == self.day) - if self.base > 1: - locs &= (locs.cumsum() % self.base == 1) - return values[locs] - #####--------------------------------------------------------------------------- #---- --- Formatter --- #####--------------------------------------------------------------------------- class TimeSeries_DateFormatter(Formatter): """Formats the ticks along a DateArray axis.""" - def __init__(self, freq, fmt=None): - if fmt is None: - fmt = Date.default_fmtstr[freq] - self.fmt = fmt - self.freqstr = freq + def __init__(self, freq, minor_locator=False, dynamic_mode=True,): + self.format = None + self.freq = freq + self.locs = [] + self.formatdict = {} + self.isminor = minor_locator + self.isdynamic = dynamic_mode + self.offset = 0 + #..... + if freq == TS.FR_ANN: + self.finder = _annual_finder + elif freq == TS.FR_QTR: + self.finder = _quarterly_finder + elif freq == TS.FR_MTH: + self.finder = _monthly_finder + elif freq in (TS.FR_BUS, TS.FR_DAY): + self.finder = _daily_finder + + def asminor(self): + "Returns the formatter set to minor mode." + self.isminor = True + return self + def asmajor(self): + "Returns the fromatter set to major mode." + self.isminor = False + return self + + def _set_default_format(self, vmin, vmax): + "Returns the default ticks spacing." + self.formatdict = self.finder(vmin, vmax, self.freq, False) + return self.formatdict + + def set_locs(self, locs): + 'Sets the locations of the ticks' + self.locs = locs + if len(self.locs) > 0: + self.verify_intervals() + self._set_default_format(locs[0], locs[-1]) + # def __call__(self, x, pos=0): - return Date(self.freqstr, value=int(x)).strfmt(self.fmt) + if self.isminor: + fmt = self.formatdict.pop(x, '') + if fmt is not '': + retval = Date(self.freq, value=int(x)).strfmt(fmt) + else: + retval = '' + else: + retval = '' + return retval + + #####-------------------------------------------------------------------------- #---- --- TimeSeries plots --- #####-------------------------------------------------------------------------- @@ -514,13 +605,13 @@ assert hasattr(_series, "dates") self._series = _series.ravel() self.xdata = _series.dates - self.freqstr = _series.dates.freqstr + self.freq = _series.dates.freq self.xaxis.set_major_locator else: self._series = None self.xdata = None - self.freqstr = None + self.freq = None self._austoscale = False # Get the data to plot self.legendsymbols = [] @@ -567,9 +658,9 @@ # The argument is a DateArray............ elif isinstance(a, (Date, DateArray)): # Force to current freq - if self.freqstr is not None: - if a.freqstr != self.freqstr: - a = a.asfreq(self.freqstr) + if self.freq is not None: + if a.freq != self.freq: + a = a.asfreq(self.freq) # There's an argument after if len(remaining) > 0: #...and it's a format string @@ -594,12 +685,9 @@ output.extend([a,b,c]) else: output.extend([a,b]) - # continue else: if self.ydata is None: raise ValueError, "No data information available!" - #else: - # break # Otherwise.............................. elif len(remaining) > 0: if isinstance(remaining[0], str): @@ -608,20 +696,18 @@ raise ValueError, "No date information available!" else: output.extend([self.xdata, a, b]) - #continue elif self.xdata is None: raise ValueError, "No date information available!" else: output.extend([self.xdata, a]) - #continue # Reinitialize the plot if needed ........... if self.xdata is None: self.xdata = output[0] - self.freqstr = self.xdata.freqstr + self.freq = self.xdata.freq # Force the xdata to the current frequency - elif output[0].freqstr != self.freqstr: + elif output[0].freq != self.freq: output = list(output) - output[0] = output[0].asfreq(self.freqstr) + output[0] = output[0].asfreq(self.freq) return output #............................................ def tsplot(self,*parms,**kwargs): @@ -631,11 +717,6 @@ parms = self._check_plot_params(*parms) self.legendlabels.append(kwargs.get('label',None)) Subplot.plot(self, *parms,**kwargs) - pylab.draw_if_interactive() -# #............................................ -# def ybaseline(self,ybase,**kwargs): -# """Plots an horizontal baseline on each subplot.""" -# self.axhline(ybase,**kwargs) #............................................ def format_dateaxis(self,maj_spacing=None, min_spacing=None, strformat="%Y", rotate=True): @@ -649,49 +730,28 @@ `strformat` : String *['%Y']* String format for major ticks ("%Y"). """ - # Get the locator class ................. - if self.freqstr in 'BDU': - locator = TimeSeries_DailyLocator - self.xaxis.set_major_locator(locator(self.freqstr, - minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(self.freqstr, - minor_locator=True, - dynamic_mode=True)) - else: - if self.freqstr == 'A': - locator = TimeSeries_AnnualLocator - elif self.freqstr == 'Q': - locator = TimeSeries_QuarterlyLocator - elif self.freqstr == 'M': - locator = TimeSeries_MonthlyLocator - self.xaxis.set_major_locator(locator(minor_locator=False, - dynamic_mode=True)) - self.xaxis.set_minor_locator(locator(minor_locator=True, - dynamic_mode=True)) + # Get the locator class ................. + majlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, + minor_locator=False) + minlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, + minor_locator=True) + self.xaxis.set_major_locator(majlocator) + self.xaxis.set_minor_locator(minlocator) + # Get the formatter ..................... + majformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, + minor_locator=False) + minformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, + minor_locator=True) + self.xaxis.set_major_formatter(majformatter) + self.xaxis.set_minor_formatter(minformatter) #........................................ - self.xaxis.set_major_formatter(TimeSeries_DateFormatter(self.freqstr)) - if rcParams['backend'] == 'PS': - rotate = False - warnings.warn("dateplot: PS backend detected, rotate disabled") - if self.is_last_row(): - if rotate: - setp(self.get_xticklabels(),rotation=45) -# self.xaxis.set_major_formatter(FuncFormatter(self.dateticks_formatter)) -# self.xaxis.set_minor_formatter(FuncFormatter(self.dateticks_formatter)) -# else: -# self.set_xticklabels([]) -# self.set_xlabel('') -# #............................................ -# def plot_shifts(self,shifts,**kwargs): -# """Plots regime shifts. -#:param shifts: Shifts/trends to plot. -#:type shifts: `RegimeShift` -# """ -# self.tsplot(self.xdata,shifts.regimes,**kwargs) -# for x in shifts.xshifts[0]: -# self.axvline(self.xdata[x],ls=':',c='#999999',lw=0.5) - #............................................ +# if rcParams['backend'] == 'PS': +# rotate = False +# warnings.warn("dateplot: PS backend detected, rotate disabled") +# if self.is_last_row(): +# if rotate: +# setp(self.get_xticklabels(),rotation=45) + TSPlot = TimeSeriesPlot @@ -715,7 +775,6 @@ return add_generic_subplot(self, *args, **kwargs) add_plot = add_tsplot TSFigure = TimeSeriesFigure -#Figure.add_tsplot = #................................................ def tsfigure(series, **figargs): """Creates a new `TimeSeriesFigure` object. @@ -728,8 +787,6 @@ """ figargs.update(FigureClass=TSFigure) figargs.update(series=series) -# print "figargs:",figargs -# num = figargs.pop('num',None) fig = pylab.figure(**figargs) return fig @@ -760,16 +817,16 @@ ################################################################################ if __name__ == '__main__': - da = date_array(start_date=Date(freq='D', year=2003, quarter=3, month=1, day=17), - length=51) + da = date_array(start_date=Date(freq='B', year=2003, quarter=3, month=1, day=17), + length=10) ser = timeseries.time_series(MA.arange(len(da)), dates=da) - ser[4] = MA.masked - ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('M')) +# ser[4] = MA.masked +# ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) pylab.figure() pylab.gcf().add_tsplot(111) pylab.gca().tsplot(ser, 'ko-') pylab.gca().format_dateaxis() - pylab.gca().tsplot(ser_2, 'rs') +# pylab.gca().tsplot(ser_2, 'rs') pylab.show() \ No newline at end of file From scipy-svn at scipy.org Tue Feb 27 09:05:20 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 08:05:20 -0600 (CST) Subject: [Scipy-svn] r2770 - in trunk/Lib/sandbox/timeseries: archived_version plotlib Message-ID: <20070227140520.1D9E839C2B9@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 08:05:18 -0600 (Tue, 27 Feb 2007) New Revision: 2770 Added: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_pgm.py Removed: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py Log: Moved remotely Copied: trunk/Lib/sandbox/timeseries/archived_version/mpl_timeseries_pgm.py (from rev 2769, trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py) Deleted: trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py =================================================================== --- trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-27 14:05:02 UTC (rev 2769) +++ trunk/Lib/sandbox/timeseries/plotlib/mpl_timeseries_pgm.py 2007-02-27 14:05:18 UTC (rev 2770) @@ -1,832 +0,0 @@ -""" -Classes to plot TimeSeries w/ matplotlib. - -:author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com -:date: $Date$ -:version: $Id$ -""" -__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" -__version__ = '1.0' -__revision__ = "$Revision$" -__date__ = '$Date$' - - -import matplotlib -from matplotlib import pylab, rcParams -from matplotlib.artist import setp -from matplotlib.axes import Subplot, PolarSubplot -from matplotlib.cbook import flatten -from matplotlib.collections import LineCollection -from matplotlib.contour import ContourSet -from matplotlib.dates import DayLocator, MonthLocator, YearLocator, \ - DateFormatter -from matplotlib.figure import Figure -from matplotlib.legend import Legend -from matplotlib.mlab import meshgrid -from matplotlib.ticker import Formatter, ScalarFormatter, FuncFormatter, \ - Locator, FixedLocator -#from matplotlib.transforms import nonsingular - -import numpy as N -import maskedarray as MA - -import timeseries -import timeseries as TS -from timeseries import date_array, Date, DateArray, TimeSeries - -import warnings - -#####--------------------------------------------------------------------------- -#---- --- Matplotlib extensions --- -#####--------------------------------------------------------------------------- - -def add_generic_subplot(figure_instance, *args, **kwargs): - """Generalizes the `add_subplot` figure method to generic subplots. -The specific Subplot object class to add is given through the keywords -`SubplotClass` or `class`. - -:Parameters: - `figure_instance` : Figure object - Figure to which the generic subplot should be attached. - `args` : Misc - Miscellaneous arguments to the subplot. - `kwargs` : Dictionary - Keywords. Same keywords as `Subplot`, with the addition of - - `SubplotClass` : Type of subplot - - `subclass` : Shortcut to `SubplotClass`. - - any keyword required by the `SubplotClass` subclass. - """ - - key = figure_instance._make_key(*args, **kwargs) - #TODO: Find why, sometimes, key is not hashable (even if tuple) - # else, there's a fix below - try: - key.__hash__() - except TypeError: - key = str(key) - # - if figure_instance._seen.has_key(key): - ax = figure_instance._seen[key] - figure_instance.sca(ax) - return ax - # - if not len(args): - return -# if hasattr(args[0], '__array__'): -# fixedargs = args[1:] -# else: -# fixedargs = args - # - SubplotClass = kwargs.pop("SubplotClass", Subplot) - SubplotClass = kwargs.pop("subclass",SubplotClass) - if isinstance(args[0], Subplot) or isinstance(args[0], PolarSubplot): - a = args[0] - assert(a.get_figure() is figure_instance) -# a.set_figure(figure_instance) - else: - ispolar = kwargs.pop('polar', False) - if ispolar: - a = PolarSubplot(figure_instance, *args, **kwargs) - else: - a = SubplotClass(figure_instance, *args, **kwargs) - - figure_instance.axes.append(a) - figure_instance._axstack.push(a) - figure_instance.sca(a) - figure_instance._seen[key] = a - return a - - -def nonsingular(vmin, vmax, expander=0.001, tiny=1e-15, increasing=True): - ''' - Ensure the endpoints of a range are not too close together. - - "too close" means the interval is smaller than 'tiny' times - the maximum absolute value. - - If they are too close, each will be moved by the 'expander'. - If 'increasing' is True and vmin > vmax, they will be swapped. - ''' - #TODO: Remove that when matplotlib incorporate it by default - swapped = False - if vmax < vmin: - vmin, vmax = vmax, vmin - swapped = True - if vmax - vmin <= max(abs(vmin), abs(vmax)) * tiny: - if vmin == 0.0: - vmin = -expander - vmax = expander - else: - vmin -= expander*abs(vmin) - vmax += expander*abs(vmax) - if swapped and not increasing: - vmin, vmax = vmax, vmin - return vmin, vmax - -##### ------------------------------------------------------------------------- -#---- --- Locators --- -##### ------------------------------------------------------------------------- - -def _get_default_annual_spacing(nyears): - """Returns a default spacing between consecutive ticks for annual data.""" - if nyears < 11: - (min_spacing, maj_spacing) = (1, 1) - elif nyears < 20: - (min_spacing, maj_spacing) = (1, 2) - elif nyears < 50: - (min_spacing, maj_spacing) = (1, 5) - elif nyears < 100: - (min_spacing, maj_spacing) = (5, 10) - elif nyears < 200: - (min_spacing, maj_spacing) = (5, 25) - elif nyears < 600: - (min_spacing, maj_spacing) = (10, 50) - else: - factor = nyears // 1000 + 1 - (min_spacing, maj_spacing) = (factor*20, factor*100) - return (min_spacing, maj_spacing) - - -def period_break(dates, period): - """Returns the indices where the given period changes. - -:Parameters: - dates : DateArray - Array of dates to monitor. - period : string - Name of the period to monitor. - """ - current = getattr(dates, period) - previous = getattr(dates-1, period) - return (current - previous).nonzero()[0] - -def has_level_label(label_flags): - """returns true if the label_flags indicate there is at least -one label for this level""" - if label_flags.size == 0 or \ - (label_flags.size == 1 and label_flags[0] == 0): - return False - else: - return True - -def _daily_finder(vmin, vmax, freq, aslocator): - - if freq == TS.FR_BUS: - periodsperyear = 261 - elif freq == TS.FR_DAY: - periodsperyear = 365 - else: - raise ValueError("unexpected frequency") - - (vmin, vmax) = (int(vmin), int(vmax)) - span = vmax - vmin + 1 - dates = date_array(start_date=Date(freq,vmin), - end_date=Date(freq, vmax)) - default = N.arange(vmin, vmax+1) - # Initialize the output - if not aslocator: - format = N.empty(default.shape, dtype="|S10") - format.flat = '' - - def first_label(label_flags): - if label_flags[0] == 0: return label_flags[1] - else: return label_flags[0] - - # Case 1. Less than a month - if span <= (periodsperyear//12 - 2): - month_start = period_break(dates,'month') - if aslocator: - major = default[month_start] - minor = default - else: - year_start = period_break(dates,'year') - format[:] = '%d' - format[month_start] = '%d\n%b' - format[year_start] = '%d\n%b\n%Y' - if not has_level_label(year_start): - if not has_level_label(month_start): - if dates.size > 1: - idx = 1 - else: - idx = 0 - format[idx] = '%d\n%b\n%Y' - else: - format[first_label(month_start)] = '%d\n%b\n%Y' - # Case 2. Less than three months - elif span <= periodsperyear//4: - month_start = period_break(dates,'month') - if aslocator: - major = default[month_start] - minor = default - else: - week_start = period_break(dates,'week') - year_start = period_break(dates,'year') - - format[week_start] = '%d' - format[month_start] = '\n\n%b' - format[year_start] = '\n\n%b\n%Y' - if not has_level_label(year_start): - if not has_level_label(month_start): - format[first_label(week_start)] = '\n\n%b\n%Y' - else: - format[first_label(month_start)] = '\n\n%b\n%Y' - # Case 3. Less than 14 months ............... - elif span <= 1.15 * periodsperyear: - - if aslocator: - d_minus_1 = dates-1 - - month_diff = N.abs(dates.month - d_minus_1.month) - week_diff = N.abs(dates.week - d_minus_1.week) - minor_idx = (month_diff + week_diff).nonzero()[0] - - major = default[month_diff != 0] - minor = default[minor_idx] - else: - year_start = period_break(dates,'year') - month_start = period_break(dates,'month') - - format[month_start] = '%b' - format[year_start] = '%b\n%Y' - if not has_level_label(year_start): - format[first_label(month_start)] = '%b\n%Y' - # Case 4. Less than 2.5 years ............... - elif span <= 2.5 * periodsperyear: - year_start = period_break(dates,'year') - if aslocator: - month_start = period_break(dates, 'quarter') - major = default[year_start] - minor = default[month_start] - else: - quarter_start = period_break(dates, 'quarter') - format[quarter_start] = '%b' - format[year_start] = '%b\n%Y' - # Case 4. Less than 4 years ................. - elif span <= 4 * periodsperyear: - year_start = period_break(dates,'year') - month_start = period_break(dates, 'month') - if aslocator: - major = default[year_start] - minor = default[month_start] - else: - month_break = dates[month_start].month - jan_or_jul = month_start[(month_break == 1) | (month_break == 7)] - format[jan_or_jul] = '%b' - format[year_start] = '%b\n%Y' - # Case 5. Less than 11 years ................ - elif span <= 11 * periodsperyear: - year_start = period_break(dates,'year') - if aslocator: - quarter_start = period_break(dates, 'quarter') - major = default[year_start] - minor = default[quarter_start] - else: - format[year_start] = '%Y' - # Case 6. More than 12 years ................ - else: - year_start = period_break(dates,'year') - year_break = dates[year_start].years - nyears = span/periodsperyear - (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) - major_idx = year_start[(year_break % maj_anndef == 0)] - if aslocator: - major = default[major_idx] - minor_idx = year_start[(year_break % min_anndef == 0)] - minor = default[minor_idx] - else: - format[major_idx] = '%Y' - #............................................ - if aslocator: - return minor, major - else: - formatted = (format != '') - return dict([(d,f) for (d,f) in zip(default[formatted],format[formatted])]) -#............................................................................... -def _monthly_finder(vmin, vmax, freq, aslocator): - if freq != TS.FR_MTH: - raise ValueError("unexpected frequency") - periodsperyear = 12 - - (vmin, vmax) = (int(vmin), int(vmax)) - span = vmax - vmin + 1 - #............................................ - dates = N.arange(vmin, vmax+1) - format = N.empty(span, dtype="|S8") - format.flat = '' - year_start = (dates % 12 == 1).nonzero()[0] - #............................................ - if span <= 1.15 * periodsperyear: - if aslocator: - major = dates[year_start] - minor = dates - else: - - format[:] = '%b' - format[year_start] = '%b\n%Y' - - if not has_level_label(year_start): - if dates.size > 1: - idx = 1 - else: - idx = 0 - format[idx] = '%b\n%Y' - #........................ - elif span <= 2.5 * periodsperyear: - if aslocator: - major = dates[year_start] - minor = dates - else: - quarter_start = (dates % 3 == 1).nonzero() - format[quarter_start] = '%b' - format[year_start] = '%b\n%Y' - #....................... - elif span <= 4 * periodsperyear: - if aslocator: - major = dates[year_start] - minor = dates - else: - jan_or_jul = (dates % 12 == 1) | (dates % 12 == 7) - format[jan_or_jul] = '%b' - format[year_start] = '%b\n%Y' - #........................ - elif span <= 11 * periodsperyear: - if aslocator: - quarter_start = (dates % 3 == 1).nonzero() - major = dates[year_start] - minor = dates[quarter_start] - else: - format[year_start] = '%Y' - #......................... - else: - nyears = span/periodsperyear - (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) - years = dates[year_start]//12 + 1 - major_idx = year_start[(years % maj_anndef == 0)] - if aslocator: - major = dates[major_idx] - minor = dates[year_start[(years % min_anndef == 0)]] - else: - format[major_idx] = '%Y' - #........................ - if aslocator: - return minor, major - else: - formatted = (format != '') - return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) -#............................................................................... -def _quarterly_finder(vmin, vmax, freq, aslocator): - if freq != TS.FR_QTR: - raise ValueError("unexpected frequency") - periodsperyear = 4 - (vmin, vmax) = (int(vmin), int(vmax)) - span = vmax - vmin + 1 - #............................................ - dates = N.arange(vmin, vmax+1) - format = N.empty(span, dtype="|S8") - format.flat = '' - year_start = (dates % 4 == 1).nonzero()[0] - #............................................ - if span <= 3.5 * periodsperyear: - if aslocator: - major = dates[year_start] - minor = dates - else: - format[:] = 'Q%q' - format[year_start] = 'Q%q\n%Y' - if not has_level_label(year_start): - if dates.size > 1: - idx = 1 - else: - idx = 0 - format[idx] = 'Q%q\n%Y' - #............................................ - elif span <= 11 * periodsperyear: - if aslocator: - major = dates[year_start] - minor = dates - else: - format[year_start] = '%Y' - #............................................ - else: - years = dates[year_start]//4 + 1 - nyears = span/periodsperyear - (min_anndef, maj_anndef) = _get_default_annual_spacing(nyears) - major_idx = year_start[(years % maj_anndef == 0)] - if aslocator: - major = dates[major_idx] - minor = dates[year_start[(years % min_anndef == 0)]] - else: - format[major_idx] = '%Y' - #............................................ - if aslocator: - return minor, major - else: - formatted = (format != '') - return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) -#............................................................................... -def _annual_finder(vmin, vmax, freq, aslocator): - if freq != TS.FR_ANN: - raise ValueError("unexpected frequency") - (vmin, vmax) = (int(vmin), int(vmax+1)) - span = vmax - vmin + 1 - #............................................ - dates = N.arange(vmin, vmax+1) - format = N.empty(span, dtype="|S8") - format.flat = '' - #............................................ - (min_anndef, maj_anndef) = _get_default_annual_spacing(span) - major_idx = dates % maj_anndef == 0 - if aslocator: - major = dates[major_idx] - minor = dates[(dates % min_anndef == 0)] - else: - format[major_idx] = '%Y' - #............................................ - if aslocator: - return minor, major - else: - formatted = (format != '') - return dict([(d,f) for (d,f) in zip(dates[formatted],format[formatted])]) - -#............................................................................... -class TimeSeries_DateLocator(Locator): - "Locates the ticks along an axis controlled by a DateArray." - - def __init__(self, freq, minor_locator=False, dynamic_mode=True, - base=1, quarter=1, month=1, day=1): - self.freq = freq - self.base = base - (self.quarter, self.month, self.day) = (quarter, month, day) - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - #..... - if freq == TS.FR_ANN: - self.finder = _annual_finder - elif freq == TS.FR_QTR: - self.finder = _quarterly_finder - elif freq == TS.FR_MTH: - self.finder = _monthly_finder - elif freq in (TS.FR_BUS, TS.FR_DAY): - self.finder = _daily_finder - - def asminor(self): - "Returns the locator set to minor mode." - self.isminor = True - return self - - def asmajor(self): - "Returns the locator set to major mode." - self.isminor = False - return self - - def _get_default_locs(self, vmin, vmax): - "Returns the default locations of ticks." - (minor, major) = self.finder(vmin, vmax, self.freq, True) - if self.isminor: - return minor - return major - - def __call__(self): - 'Return the locations of the ticks.' - self.verify_intervals() - vmin, vmax = self.viewInterval.get_bounds() - if vmax < vmin: - vmin, vmax = vmax, vmin - if self.isdynamic: - locs = self._get_default_locs(vmin, vmax) - else: - base = self.base - (d, m) = divmod(vmin, base) - vmin = (d+1) * base - locs = range(vmin, vmax+1, base) - return locs - - def autoscale(self): - """Sets the view limits to the nearest multiples of base that contain - the data. - """ - self.verify_intervals() - dmin, dmax = self.dataInterval.get_bounds() - locs = self._get_default_locs(dmin, dmax) - (vmin, vmax) = locs[[0, -1]] - if vmin == vmax: - vmin -= 1 - vmax += 1 - return nonsingular(vmin, vmax) - -#####--------------------------------------------------------------------------- -#---- --- Formatter --- -#####--------------------------------------------------------------------------- -class TimeSeries_DateFormatter(Formatter): - """Formats the ticks along a DateArray axis.""" - - def __init__(self, freq, minor_locator=False, dynamic_mode=True,): - self.format = None - self.freq = freq - self.locs = [] - self.formatdict = {} - self.isminor = minor_locator - self.isdynamic = dynamic_mode - self.offset = 0 - #..... - if freq == TS.FR_ANN: - self.finder = _annual_finder - elif freq == TS.FR_QTR: - self.finder = _quarterly_finder - elif freq == TS.FR_MTH: - self.finder = _monthly_finder - elif freq in (TS.FR_BUS, TS.FR_DAY): - self.finder = _daily_finder - - def asminor(self): - "Returns the formatter set to minor mode." - self.isminor = True - return self - - def asmajor(self): - "Returns the fromatter set to major mode." - self.isminor = False - return self - - def _set_default_format(self, vmin, vmax): - "Returns the default ticks spacing." - self.formatdict = self.finder(vmin, vmax, self.freq, False) - return self.formatdict - - def set_locs(self, locs): - 'Sets the locations of the ticks' - self.locs = locs - if len(self.locs) > 0: - self.verify_intervals() - self._set_default_format(locs[0], locs[-1]) - # - def __call__(self, x, pos=0): - if self.isminor: - fmt = self.formatdict.pop(x, '') - if fmt is not '': - retval = Date(self.freq, value=int(x)).strfmt(fmt) - else: - retval = '' - else: - retval = '' - return retval - - - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries plots --- -#####-------------------------------------------------------------------------- -class TimeSeriesPlot(Subplot, object): - """Defines a time series based subclass of Subplot.""" - def __init__(self, fig=None, *args, **kwargs): - """ -Accepts the same keywords as a standard subplot, plus a specific `series` keyword. - -:Parameters: - `fig` : Figure - Base figure. - -:Keywords: - `series` : TimeSeries - Data to plot - - """ - # Retrieve the series ................... - _series = kwargs.pop('series',None) - Subplot.__init__(self,fig,*args,**kwargs) -# # Force fig to be defined ..... -# if fig is None: -# fig = TSFigure(_series) - # Process options ....................... - if _series is not None: - assert hasattr(_series, "dates") - self._series = _series.ravel() - self.xdata = _series.dates - self.freq = _series.dates.freq - self.xaxis.set_major_locator - - else: - self._series = None - self.xdata = None - self.freq = None - self._austoscale = False - # Get the data to plot - self.legendsymbols = [] - self.legendlabels = [] - #............................................ - def set_ydata(self, series=None): - """Sets the base time series.""" - if self._series is not None: - print "WARNING ! Base series is being changed.""" - self._series = series.ravel() - if isinstance(series, TimeSeries): - self.xdata = self.series.dates - #.... - def get_ydata(self): - """Gets the base time series.""" - return self._series - ydata = property(fget=get_ydata, fset=set_ydata, doc='Time series') - #............................................ - def _check_plot_params(self,*args): - """Defines the plot coordinates (and basic plotting arguments).""" - remaining = list(args) - # No args ? Use defaults, if any - if len(args) == 0: - if self.xdata is None: - raise ValueError, "No date information available!" - return (self.xdata, self.ydata) - output = [] - while len(remaining) > 0: - a = remaining.pop(0) - # The argument is a format: use default dates/ - if isinstance(a,str): - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, self.ydata, a]) - # The argument is a TimeSeries: use its dates for x - elif isinstance(a, TimeSeries): - (x,y) = (a._dates, a._series) - if len(remaining) > 0 and isinstance(remaining[0], str): - b = remaining.pop(0) - output.extend([x,y,b]) - else: - output.extend([x,y]) - # The argument is a DateArray............ - elif isinstance(a, (Date, DateArray)): - # Force to current freq - if self.freq is not None: - if a.freq != self.freq: - a = a.asfreq(self.freq) - # There's an argument after - if len(remaining) > 0: - #...and it's a format string - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata, b]) - #... and it's another date: use the default - elif isinstance(remaining[0], DateArray): - if self.ydata is None: - raise ValueError, "No data information available!" - else: - output.extend([a, self.ydata]) - #... and it must be some data - else: - b = remaining.pop(0) - if len(remaining) > 0: - if isinstance(remaining[0], str): - c = remaining.pop(0) - output.extend([a,b,c]) - else: - output.extend([a,b]) - else: - if self.ydata is None: - raise ValueError, "No data information available!" - # Otherwise.............................. - elif len(remaining) > 0: - if isinstance(remaining[0], str): - b = remaining.pop(0) - if self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a, b]) - elif self.xdata is None: - raise ValueError, "No date information available!" - else: - output.extend([self.xdata, a]) - # Reinitialize the plot if needed ........... - if self.xdata is None: - self.xdata = output[0] - self.freq = self.xdata.freq - # Force the xdata to the current frequency - elif output[0].freq != self.freq: - output = list(output) - output[0] = output[0].asfreq(self.freq) - return output - #............................................ - def tsplot(self,*parms,**kwargs): - """Plots the data parsed in argument. -This command accepts the same keywords as `matplotlib.plot`.""" - #print "Parameters: %s - %i" % (parms, len(parms)) - parms = self._check_plot_params(*parms) - self.legendlabels.append(kwargs.get('label',None)) - Subplot.plot(self, *parms,**kwargs) - #............................................ - def format_dateaxis(self,maj_spacing=None, min_spacing=None, - strformat="%Y", rotate=True): - """Pretty-formats the date axis (x-axis). - -:Parameters: - `major` : Integer *[5]* - Major tick locator, in years (major tick every `major` years). - `minor` : Integer *[12]* - Minor tick locator, in months (minor ticks every `minor` months). - `strformat` : String *['%Y']* - String format for major ticks ("%Y"). - """ - # Get the locator class ................. - majlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, - minor_locator=False) - minlocator = TimeSeries_DateLocator(self.freq, dynamic_mode=True, - minor_locator=True) - self.xaxis.set_major_locator(majlocator) - self.xaxis.set_minor_locator(minlocator) - # Get the formatter ..................... - majformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, - minor_locator=False) - minformatter = TimeSeries_DateFormatter(self.freq, dynamic_mode=True, - minor_locator=True) - self.xaxis.set_major_formatter(majformatter) - self.xaxis.set_minor_formatter(minformatter) - #........................................ -# if rcParams['backend'] == 'PS': -# rotate = False -# warnings.warn("dateplot: PS backend detected, rotate disabled") -# if self.is_last_row(): -# if rotate: -# setp(self.get_xticklabels(),rotation=45) - -TSPlot = TimeSeriesPlot - - -#####-------------------------------------------------------------------------- -#---- --- TimeSeries Figures --- -#####-------------------------------------------------------------------------- -class TimeSeriesFigure(Figure): - """Time Series Figure: all subplots share the same time series. - """ - def __init__(self, series=None, **kwargs): - self._series = series - Figure.__init__(self,**kwargs) - fspnum = kwargs.pop('fspnum',None) - if fspnum is not None: - self.add_tsplot(fspnum, series=series) - #......... - def add_tsplot(self, *args, **kwargs): - """Adds a `TimeSeriesPlot` subplot to the figure.""" - kwargs.update(SubplotClass=TimeSeriesPlot, - series=self._series) - return add_generic_subplot(self, *args, **kwargs) - add_plot = add_tsplot -TSFigure = TimeSeriesFigure -#................................................ -def tsfigure(series, **figargs): - """Creates a new `TimeSeriesFigure` object. - -:Parameters: - `series` : TimeSeries object - Input data. - `figargs` : Dictionary - Figure options [`figsize`, `dpi`, `facecolor`, `edgecolor`, `frameon`]. - """ - figargs.update(FigureClass=TSFigure) - figargs.update(series=series) - fig = pylab.figure(**figargs) - return fig - -def add_tsplot(axes, *args, **kwargs): - kwargs.update(SubplotClass=TimeSeriesPlot) - if 'series' not in kwargs.keys(): - kwargs['series'] = None - return add_generic_subplot(axes, *args, **kwargs) -Figure.add_tsplot = add_tsplot - - -def tsplot(*args, **kwargs): - # allow callers to override the hold state by passing hold=True|False - b = pylab.ishold() - h = kwargs.pop('hold', None) - if h is not None: - pylab.hold(h) - try: - ret = pylab.gca().add_tsplot(*args, **kwargs) - pylab.draw_if_interactive() - except: - pylab.hold(b) - raise - - pylab.hold(b) - return ret - -################################################################################ -if __name__ == '__main__': - - da = date_array(start_date=Date(freq='B', year=2003, quarter=3, month=1, day=17), - length=10) - ser = timeseries.time_series(MA.arange(len(da)), dates=da) -# ser[4] = MA.masked -# ser_2 = timeseries.time_series(MA.arange(len(da)), dates=da.asfreq('Q')) - - pylab.figure() - pylab.gcf().add_tsplot(111) - pylab.gca().tsplot(ser, 'ko-') - pylab.gca().format_dateaxis() -# pylab.gca().tsplot(ser_2, 'rs') - pylab.show() - \ No newline at end of file From scipy-svn at scipy.org Tue Feb 27 10:04:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 09:04:13 -0600 (CST) Subject: [Scipy-svn] r2771 - trunk/Lib/sandbox/timeseries Message-ID: <20070227150413.EF20139C2BC@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 09:04:10 -0600 (Tue, 27 Feb 2007) New Revision: 2771 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: changed "ValueError" to "IndexError" for "Date out of Bounds" errors in DateArray Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-27 14:05:18 UTC (rev 2770) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-27 15:04:10 UTC (rev 2771) @@ -733,7 +733,7 @@ c += (self == d.value) c = c.nonzero() if fromnumeric.size(c) == 0: - raise ValueError, "Date out of bounds!" + raise IndexError, "Date out of bounds!" return c def date_to_index(self, date): @@ -741,12 +741,12 @@ if self.isvalid(): index = date.value - self[0].value if index < 0 or index > self.size: - raise ValueError, "Date out of bounds!" + raise IndexError, "Date out of bounds!" return index else: index_asarray = (self == date.value).nonzero() if fromnumeric.size(index_asarray) == 0: - raise ValueError, "Date out of bounds!" + raise IndexError, "Date out of bounds!" return index_asarray[0][0] #...................................................... def get_steps(self): From scipy-svn at scipy.org Tue Feb 27 10:09:23 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 09:09:23 -0600 (CST) Subject: [Scipy-svn] r2772 - trunk/Lib/sandbox/timeseries Message-ID: <20070227150923.C4D6C39C2BC@new.scipy.org> Author: mattknox_ca Date: 2007-02-27 09:09:20 -0600 (Tue, 27 Feb 2007) New Revision: 2772 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: fixed problem causing unit tests to fail (for printing when DateArray is data portion of maskedarray Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-27 15:04:10 UTC (rev 2771) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-27 15:09:20 UTC (rev 2772) @@ -599,10 +599,11 @@ # behaviour return Date(self.freq, value=r.item()) else: - r._cachedinfo.update(dict(steps=None, full=None, hasdups=None)) - for attr in ('tostr','toobj','toord'): - if r._cachedinfo[attr] is not None: - r._cachedinfo[attr] = r._cachedinfo[attr][indx] + if hasattr(r, '_cachedinfo'): + r._cachedinfo.update(dict(steps=None, full=None, hasdups=None)) + for attr in ('tostr','toobj','toord'): + if r._cachedinfo[attr] is not None: + r._cachedinfo[attr] = r._cachedinfo[attr][indx] return r def __repr__(self): From scipy-svn at scipy.org Tue Feb 27 16:41:23 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 15:41:23 -0600 (CST) Subject: [Scipy-svn] r2773 - trunk/Lib/sandbox/maskedarray Message-ID: <20070227214123.1D51C39C2A1@new.scipy.org> Author: pierregm Date: 2007-02-27 15:41:19 -0600 (Tue, 27 Feb 2007) New Revision: 2773 Modified: trunk/Lib/sandbox/maskedarray/__init__.py trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/extras.py trunk/Lib/sandbox/maskedarray/mrecords.py trunk/Lib/sandbox/maskedarray/testutils.py Log: testutils : fixed a pb in assert_equal (a bad indentation prevented assert_array_equal to be called) core : masked_where now returns a subclass of MaskedArray when needed Modified: trunk/Lib/sandbox/maskedarray/__init__.py =================================================================== --- trunk/Lib/sandbox/maskedarray/__init__.py 2007-02-27 15:09:20 UTC (rev 2772) +++ trunk/Lib/sandbox/maskedarray/__init__.py 2007-02-27 21:41:19 UTC (rev 2773) @@ -12,11 +12,9 @@ __date__ = '$Date$' import core -#reload(core) from core import * import extras -#reload(extras) from extras import * Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-02-27 15:09:20 UTC (rev 2772) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-02-27 21:41:19 UTC (rev 2773) @@ -372,6 +372,8 @@ return masked d1 = filled(a, self.fillx) d2 = filled(b, self.filly) +# CHECK : Do we really need to fill the arguments ? Pro'ly not +# result = self.f(a, b, *args, **kwargs).view(get_masked_subclass(a,b)) result = self.f(d1, d2, *args, **kwargs).view(get_masked_subclass(a,b)) if result.ndim > 0: result._mask = m @@ -643,7 +645,7 @@ #####-------------------------------------------------------------------------- #--- --- Masking functions --- #####-------------------------------------------------------------------------- -def masked_where(condition, x, copy=True): +def masked_where(condition, a, copy=True): """Returns `x` as an array masked where `condition` is true. Masked values of `x` or `condition` are kept. @@ -652,12 +654,16 @@ - `x` (ndarray) : Array to mask. - `copy` (boolean, *[False]*) : Returns a copy of `m` if true. """ - cm = filled(condition,1) - if isinstance(x,MaskedArray): - m = mask_or(x._mask, cm) - return x.__class__(x._data, mask=m, copy=copy) + cond = filled(condition,1) + a = numeric.array(a, copy=copy, subok=True) + if hasattr(a, '_mask'): + cond = mask_or(cond, a._mask) + cls = type(a) else: - return MaskedArray(fromnumeric.asarray(x), copy=copy, mask=cm) + cls = MaskedArray + result = a.view(cls) + result._mask = cond + return result def masked_greater(x, value, copy=1): "Shortcut to `masked_where`, with ``condition = (x > value)``." Modified: trunk/Lib/sandbox/maskedarray/extras.py =================================================================== --- trunk/Lib/sandbox/maskedarray/extras.py 2007-02-27 15:09:20 UTC (rev 2772) +++ trunk/Lib/sandbox/maskedarray/extras.py 2007-02-27 21:41:19 UTC (rev 2773) @@ -25,7 +25,6 @@ from itertools import groupby import core -#reload(core) from core import * import numpy Modified: trunk/Lib/sandbox/maskedarray/mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-27 15:09:20 UTC (rev 2772) +++ trunk/Lib/sandbox/maskedarray/mrecords.py 2007-02-27 21:41:19 UTC (rev 2773) @@ -28,7 +28,6 @@ _typestr = ntypes._typestr import maskedarray as MA -#reload(MA) from maskedarray import masked, nomask, mask_or, filled, getmask, getmaskarray, \ masked_array, make_mask from maskedarray import MaskedArray Modified: trunk/Lib/sandbox/maskedarray/testutils.py =================================================================== --- trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-27 15:09:20 UTC (rev 2772) +++ trunk/Lib/sandbox/maskedarray/testutils.py 2007-02-27 21:41:19 UTC (rev 2773) @@ -73,7 +73,7 @@ return _assert_equal_on_sequences(actual.tolist(), desired.tolist(), err_msg='') - return assert_array_equal(actual, desired, err_msg) + return assert_array_equal(actual, desired, err_msg) #............................. def fail_if_equal(actual,desired,err_msg='',): """Raises an assertion error if two items are equal. From scipy-svn at scipy.org Tue Feb 27 16:45:42 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Tue, 27 Feb 2007 15:45:42 -0600 (CST) Subject: [Scipy-svn] r2774 - in trunk/Lib/sandbox/timeseries: . tests Message-ID: <20070227214542.9951339C271@new.scipy.org> Author: pierregm Date: 2007-02-27 15:45:40 -0600 (Tue, 27 Feb 2007) New Revision: 2774 Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries : fixed a but in _tsmathmethods.__call__ tests/time_series : updated Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-27 21:41:19 UTC (rev 2773) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-02-27 21:45:40 UTC (rev 2774) @@ -275,7 +275,7 @@ series = time_series(N.arange(10).reshape(5,2), start_date=hodie) assert_equal(len(series), 5) assert_equal(series[0], [[0,1]]) - assert_equal(series[0]._dates, (hodie)) + assert_equal(series[0]._dates[0], (hodie)) assert_equal(series[:,0], [0,2,4,6,8]) assert_equal(series[:,0]._dates, series._dates) # Case 2D + mask @@ -284,7 +284,7 @@ assert_equal(len(series), 5) assert_equal(series[0], [[0,1]]) assert_equal(series[0]._mask, [[1,1]]) - assert_equal(series[0]._dates, (hodie)) + assert_equal(series[0]._dates[0], (hodie)) assert_equal(series[:,0]._data, [0,2,4,6,8]) assert_equal(series[:,0]._mask, [1,0,0,0,0]) assert_equal(series[:,0]._dates, series._dates) @@ -293,7 +293,7 @@ x = series[0] assert_equal(len(series), 5) assert_equal(series[0], [[[0,1],[2,3],[4,5]]]) - assert_equal(series[0]._dates, (hodie)) + assert_equal(series[0]._dates[0], (hodie)) assert_equal(series[:,0], series._data[:,0]) assert_equal(series[:,0]._dates, series._dates) x = series[:,:,0] Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-27 21:41:19 UTC (rev 2773) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-27 21:45:40 UTC (rev 2774) @@ -55,10 +55,12 @@ __all__ = [ 'TimeSeriesError','TimeSeriesCompatibilityError','TimeSeries','isTimeSeries', 'time_series', 'tsmasked', -'day_of_week','day_of_year','day','month','quarter','year','hour','minute','second', -'tofile','asrecords','flatten','adjust_endpoints','align_series','aligned', -'mask_period','mask_inside_period','mask_outside_period', -'convert','fill_missing_dates', 'stack' +'mask_period','mask_inside_period','mask_outside_period','compressed', +'adjust_endpoints','align_series','aligned','convert','group_byperiod', +'tshift','fill_missing_dates', 'stack', 'concatenate_series','empty_like', +'day_of_week','day_of_year','day','month','quarter','year', +'hour','minute','second', +'tofile','asrecords','flatten', ] #............................................................................... @@ -209,7 +211,7 @@ if isinstance(other, TimeSeries): assert(_timeseriescompat(instance, other)) func = getattr(super(TimeSeries, instance), self._name) - result = func(other, *args) #.view(type(instance)) + result = func(other, *args).view(type(instance)) result._dates = instance._dates return result @@ -288,6 +290,7 @@ """ options = None _defaultobserved = None + _genattributes = ['fill_value', 'observed'] def __new__(cls, data, dates=None, mask=nomask, freq=None, observed=None, start_date=None, dtype=None, copy=False, fill_value=None, @@ -678,7 +681,7 @@ #...................................................... def copy_attributes(self, oldseries, exclude=[]): "Copies the attributes from oldseries if they are not in the exclude list." - attrlist = ['fill_value', 'observed'] + attrlist = type(self)._genattributes if not isinstance(oldseries, TimeSeries): msg = "Series should be a valid TimeSeries object! (got <%s> instead)" raise TimeSeriesError, msg % type(oldseries) @@ -1410,4 +1413,18 @@ pass assert_equal(ser3d.transpose(0,2,1).shape, (5,2,3)) + if 1: + data = dates + series = time_series(data, dates) + assert(isinstance(series, TimeSeries)) + assert_equal(series._dates, dates) + assert_equal(series._data, data) + assert_equal(series.freqstr, 'D') + + series[5] = MA.masked + + # ensure that series can be represented by a string after masking a value + # (there was a bug before that prevented this from working when using a + # DateArray for the data) + strrep = str(series) From scipy-svn at scipy.org Wed Feb 28 09:04:20 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 08:04:20 -0600 (CST) Subject: [Scipy-svn] r2775 - trunk/Lib/sandbox/timeseries Message-ID: <20070228140420.F2AD439C016@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 08:04:16 -0600 (Wed, 28 Feb 2007) New Revision: 2775 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: added ability to handle series that have missing dates Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-27 21:45:40 UTC (rev 2774) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-28 14:04:16 UTC (rev 2775) @@ -60,6 +60,7 @@ import sys import operator, types, copy import timeseries as ts +import maskedarray as ma __all__ = [ 'Report', 'wrap_onspace', 'wrap_onspace_strict', @@ -359,20 +360,12 @@ else: col_width = [col_width for x in range(len(tseries)+1)] - ############################################################ - # temporary hack to handle singletons for different types of - # behaviour until we finalize how they will be handled - ############################################################ - if ts.time_series([1,2], start_date=ts.thisday('b'))[0].ndim == 0: - def getval(series, date): return series[date] - else: - def getval(series, date): - temp = series[date] - if temp is ts.tsmasked: - return temp - else: - return temp.series[0] - ############################################################ + def getval(series, date): + try: + val = series[date] + except IndexError: + val = ma.masked + return val for d in dates: rows.append([datefmt_func(d)]+[fmtfunc[i](getval(ser, d)) for i, ser in enumerate(tseries)]) From scipy-svn at scipy.org Wed Feb 28 11:50:07 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 10:50:07 -0600 (CST) Subject: [Scipy-svn] r2776 - trunk/Lib/sandbox/timeseries Message-ID: <20070228165007.A9CA039C324@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 10:50:02 -0600 (Wed, 28 Feb 2007) New Revision: 2776 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: eliminated most usage of mx.DateTime Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 14:04:16 UTC (rev 2775) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 16:50:02 UTC (rev 2776) @@ -10,7 +10,8 @@ __revision__ = "$Revision$" __date__ = '$Date$' -import datetime +import datetime as dt + import itertools import warnings import types @@ -26,7 +27,12 @@ import maskedarray as MA -import mx.DateTime as mxD +try: + from mx.DateTime import DateTimeType +except ImportError: + class DateTimeType: pass + + from mx.DateTime.Parser import DateFromString as mxDFromString import tcore as corelib @@ -47,11 +53,13 @@ #####--------------------------------------------------------------------------- #---- --- Date Info --- #####--------------------------------------------------------------------------- -OriginDate = mxD.Date(1970) -secondlyOriginDate = OriginDate - mxD.DateTimeDeltaFrom(seconds=1) -minutelyOriginDate = OriginDate - mxD.DateTimeDeltaFrom(minutes=1) -hourlyOriginDate = OriginDate - mxD.DateTimeDeltaFrom(hours=1) +OriginDate = dt.datetime(1970, 1, 1) +secondlyOriginDate = OriginDate - dt.timedelta(seconds=1) +minutelyOriginDate = OriginDate - dt.timedelta(minutes=1) +hourlyOriginDate = OriginDate - dt.timedelta(hours=1) + + #####--------------------------------------------------------------------------- #---- --- Date Exceptions --- #####--------------------------------------------------------------------------- @@ -107,16 +115,14 @@ >>> ts.Date('D', '2007-01-01') - - Use the `mxDate` keyword with an existing mx.DateTime.DateTime object, or - even a datetime.datetime object. - - >>> td.Date('D', mxDate=mx.DateTime.now()) - >>> td.Date('D', mxDate=datetime.datetime.now()) + - Use the `datetime` keyword with an existing datetime.datetime object. + + >>> td.Date('D', datetime=datetime.datetime.now()) """ default_fmtstr = {'A': "%Y", 'Q': "%YQ%q", 'M': "%b-%Y", - 'W': "%d-%b-%y", + 'W': "%d-%b-%Y", 'B': "%d-%b-%Y", 'D': "%d-%b-%Y", 'U': "%d-%b-%Y", @@ -128,7 +134,7 @@ def __init__(self, freq, value=None, string=None, year=None, month=None, day=None, quarter=None, hour=None, minute=None, second=None, - mxDate=None): + datetime=None): if hasattr(freq, 'freq'): self.freq = corelib.check_freq(freq.freq) @@ -138,40 +144,43 @@ if value is not None: + if isinstance(value, ndarray): + value = int(value) + if isinstance(value, str): - self.mxDate = mxDFromString(value) + self.datetime = mx_to_datetime(mxDFromString(value)) elif self.freqstr == 'A': - self.mxDate = mxD.Date(value, -1, -1) + self.datetime = dt.datetime(value, 1, 1) elif self.freqstr == 'B': valtmp = (value - 1)//5 - #... (value + valtmp*7 - valtmp*5) - self.mxDate = mxD.DateTimeFromAbsDateTime(value + valtmp*2) + self.datetime = dt.datetime.fromordinal(value + valtmp*2) elif self.freqstr in ['D','U']: - self.mxDate = mxD.DateTimeFromAbsDateTime(value) + self.datetime = dt.datetime.fromordinal(value) elif self.freqstr == 'H': - self.mxDate = hourlyOriginDate + mxD.DateTimeDeltaFrom(hours=value) + self.datetime = hourlyOriginDate + dt.timedelta(hours=value) elif self.freqstr == 'M': - self.mxDate = mxD.DateTimeFromAbsDateTime(1) + \ - mxD.RelativeDateTime(months=value-1, day=-1) + year = (value - 1)//12 + 1 + month = value - (year - 1)*12 + self.datetime = dt.datetime(year, month, 1) elif self.freqstr == 'Q': - self.mxDate = mxD.DateTimeFromAbsDateTime(1) + \ - mxD.RelativeDateTime(years=(value // 4), - month=((value * 3) % 12), day=-1) + year = (value - 1)//4 + 1 + month = (value - (year - 1)*4)*3 + self.datetime = dt.datetime(year, month, 1) elif self.freqstr == 'S': - self.mxDate = secondlyOriginDate + mxD.DateTimeDeltaFromSeconds(value) + self.datetime = secondlyOriginDate + dt.timedelta(seconds=value) elif self.freqstr == 'T': - self.mxDate = minutelyOriginDate + mxD.DateTimeDeltaFrom(minutes=value) + self.datetime = minutelyOriginDate + dt.timedelta(minutes=value) elif self.freqstr == 'W': - self.mxDate = mxD.Date(1,1,7) + \ - mxD.RelativeDateTime(weeks=value-1) + self.datetime = dt.datetime(1,1,7) + \ + dt.timedelta(days=(value-1)*7) elif string is not None: - self.mxDate = mxDFromString(string) + self.datetime = mx_to_datetime(mxDFromString(string)) - elif mxDate is not None: - if isinstance(mxDate, datetime.datetime): - mxDate = mxD.strptime(mxDate.isoformat()[:19], "%Y-%m-%dT%H:%M:%S") - self.mxDate = truncateDate(self.freq, mxDate) + elif datetime is not None: + if isinstance(datetime, DateTimeType): + datetime = mx_to_datetime(datetime) + self.datetime = truncateDate(self.freq, datetime) else: # First, some basic checks..... @@ -183,23 +192,23 @@ elif self.freqstr == 'M': if month is None: raise InsufficientDateError - day = -1 + day = 1 elif self.freqstr == 'Q': if quarter is None: raise InsufficientDateError month = quarter * 3 - day = -1 + day = 1 elif self.freqstr == 'A': - month = -1 - day = -1 + month = 1 + day = 1 elif self.freqstr == 'S': if month is None or day is None or second is None: raise InsufficientDateError if self.freqstr in ['A','B','D','M','Q','W']: - self.mxDate = truncateDate(self.freq, mxD.Date(year, month, day)) + self.datetime = truncateDate(self.freq, dt.datetime(year, month, day)) if self.freqstr == 'B': - if self.mxDate.day_of_week in [5,6]: + if self.datetime.isoweekday() in [6,7]: raise ValueError("Weekend passed as business day") elif self.freqstr in 'HTS': if hour is None: @@ -219,9 +228,9 @@ second = 0 else: second = second % 60 - self.mxDate = truncateDate(self.freqstr, - mxD.Date(year, month, day, - hour, minute, second)) + self.datetime = truncateDate(self.freqstr, + dt.datetime(year, month, day, + hour, minute, second)) self.value = self.__value() def __getitem__(self, indx): @@ -321,34 +330,36 @@ "Converts the date to an integer, depending on the current frequency." # Annual ....... if self.freqstr == 'A': - val = self.mxDate.year + val = self.datetime.year + # Quarterly..... + elif self.freqstr == 'Q': + val = (self.datetime.year-1)*4 + self.datetime.month//3 + # Monthly....... + elif self.freqstr == 'M': + val = (self.datetime.year-1)*12 + self.datetime.month + # Weekly........ + elif self.freqstr == 'W': + val = self.datetime.toordinal()//7 # Business days. elif self.freqstr == 'B': - days = self.mxDate.absdate + days = self.datetime.toordinal() weeks = days // 7 val = days - weeks*2 - # (weeks*5) + (days - weeks*7) # Daily/undefined elif self.freqstr in 'DU': - val = self.mxDate.absdate + val = self.datetime.toordinal() # Hourly........ elif self.freqstr == 'H': - val = (self.mxDate - hourlyOriginDate).hours - # Monthly....... - elif self.freqstr == 'M': - val = (self.mxDate.year-1)*12 + self.mxDate.month - # Quarterly..... - elif self.freqstr == 'Q': - val = (self.mxDate.year-1)*4 + self.mxDate.month//3 + delta = (self.datetime - hourlyOriginDate) + val = delta.days*24 + delta.seconds/(3600) + # Minutely...... + elif self.freqstr == 'T': + delta = (self.datetime - minutelyOriginDate) + val = delta.days*1440 + delta.seconds/(60) # Secondly...... elif self.freqstr == 'S': - val = (self.mxDate - secondlyOriginDate).seconds - # Minutely...... - elif self.freqstr == 'T': - val = (self.mxDate - minutelyOriginDate).minutes - # Weekly........ - elif self.freqstr == 'W': - val = self.mxDate.absdate//7 + delta = (self.datetime - secondlyOriginDate) + val = delta.days*86400 + delta.seconds return int(val) #...................................................... def strfmt(self, fmt): @@ -356,7 +367,7 @@ if fmt is None: fmt = self.default_fmtstr[self.freqstr] qFmt = fmt.replace("%q", "XXXX") - tmpStr = self.mxDate.strftime(qFmt) + tmpStr = self.datetime.strftime(qFmt) if "XXXX" in tmpStr: tmpStr = tmpStr.replace("XXXX", str(self.quarter)) return tmpStr @@ -369,11 +380,11 @@ #...................................................... def toordinal(self): "Returns the date as an ordinal." - return self.mxDate.absdays + return self.datetime.toordinal() def fromordinal(self, ordinal): "Returns the date as an ordinal." - return Date(self.freq, mxDate=mxD.DateTimeFromAbsDays(ordinal)) + return Date(self.freq, datetime=dt.datetime.fromordinal(ordinal)) def tostring(self): "Returns the date as a string." @@ -394,30 +405,39 @@ #####--------------------------------------------------------------------------- #---- --- Functions --- #####--------------------------------------------------------------------------- -def truncateDate(freq, mxDate): - """Chops off the irrelevant information from the mxDate passed in.""" + +def mx_to_datetime(mxDate): + microsecond = 1000000*(mxDate.second % 1) + return dt.datetime(mxDate.year, mxDate.month, + mxDate.day, mxDate.hour, + mxDate.minute, + int(mxDate.second), microsecond) + + +def truncateDate(freq, datetime): + "Chops off the irrelevant information from the datetime object passed in." freqstr = corelib.check_freqstr(freq) if freqstr == 'A': - return mxD.Date(mxDate.year) + return dt.datetime(datetime.year, 1, 1) elif freqstr == 'Q': - return mxD.Date(mxDate.year, monthToQuarter(mxDate.month)*3) + return dt.datetime(datetime.year, monthToQuarter(datetime.month)*3, 1) elif freqstr == 'M': - return mxD.Date(mxDate.year, mxDate.month) + return dt.datetime(datetime.year, datetime.month, 1) elif freqstr == 'W': - d = mxDate.absdate - return mxD.DateTimeFromAbsDateTime(d + (7 - d % 7) % 7) + d = datetime.toordinal() + return dt.datetime.fromordinal(d + (7 - d % 7) % 7) elif freqstr in 'BD': - if freq == 'B' and mxDate.day_of_week in [5,6]: + if freq == 'B' and datetime.isoweekday() in (6,7): raise ValueError("Weekend passed as business day") - return mxD.Date(mxDate.year, mxDate.month, mxDate.day) + return dt.datetime(datetime.year, datetime.month, datetime.day) elif freqstr == 'H': - return mxD.Date(mxDate.year, mxDate.month, mxDate.day, \ - mxDate.hour) + return dt.datetime(datetime.year, datetime.month, datetime.day, \ + datetime.hour) elif freqstr == 'T': - return mxD.Date(mxDate.year, mxDate.month, mxDate.day, \ - mxDate.hour, mxDate.minute) + return dt.datetime(datetime.year, datetime.month, datetime.day, \ + datetime.hour, datetime.minute) else: - return mxDate + return datetime def monthToQuarter(monthNum): """Returns the quarter corresponding to the month `monthnum`. @@ -427,12 +447,12 @@ def thisday(freq): "Returns today's date, at the given frequency `freq`." freqstr = corelib.check_freqstr(freq) - tempDate = mxD.now() + tempDate = dt.datetime.now() # if it is Saturday or Sunday currently, freq==B, then we want to use Friday - if freqstr == 'B' and tempDate.day_of_week >= 5: - tempDate -= (tempDate.day_of_week - 4) + if freqstr == 'B' and tempDate.isoweekday() >= 6: + tempDate = tempDate - dt.timedelta(days=(tempDate.isoweekday() - 5)) if freqstr in ('B','D','H','S','T','W','U'): - return Date(freq, mxDate=tempDate) + return Date(freq, datetime=tempDate) elif freqstr == 'M': return Date(freq, year=tempDate.year, month=tempDate.month) elif freqstr == 'Q': @@ -443,7 +463,7 @@ def prevbusday(day_end_hour=18, day_end_min=0): "Returns the previous business day." - tempDate = mxD.localtime() + tempDate = dt.datetime.now() dateNum = tempDate.hour + float(tempDate.minute)/60 checkNum = day_end_hour + float(day_end_min)/60 if dateNum < checkNum: @@ -874,13 +894,13 @@ ords = numpy.fromiter((s.absdays for s in dlist), float_) ords += 1 freq = guess_freq(ords) - dates = [Date(freq, mxDate=m) for m in dlist] + dates = [Date(freq, datetime=m) for m in dlist] #...as datetime objects elif hasattr(template, 'toordinal'): ords = numpy.fromiter((d.toordinal() for d in dlist), float_) if freq is None: freq = guess_freq(ords) - dates = [Date(freq, mxDate=mxD.DateTimeFromAbsDays(a)) for a in ords] + dates = [Date(freq, datetime=dt.datetime.fromordinal(a)) for a in ords] # result = DateArray(dates, freq) return result From scipy-svn at scipy.org Wed Feb 28 15:21:14 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 14:21:14 -0600 (CST) Subject: [Scipy-svn] r2777 - trunk/Lib/sandbox/timeseries Message-ID: <20070228202114.9FBDA39C31F@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 14:21:10 -0600 (Wed, 28 Feb 2007) New Revision: 2777 Modified: trunk/Lib/sandbox/timeseries/license.txt Log: added info about parser.py Modified: trunk/Lib/sandbox/timeseries/license.txt =================================================================== --- trunk/Lib/sandbox/timeseries/license.txt 2007-02-28 16:50:02 UTC (rev 2776) +++ trunk/Lib/sandbox/timeseries/license.txt 2007-02-28 20:21:10 UTC (rev 2777) @@ -7,6 +7,8 @@ are slightly modified versions of functions found in mxDateTime.c in the mx.DateTime source code. +parser.py is a slightly modified version of Parser.py found in mx.DateTime + ================================================================= EGENIX.COM PUBLIC LICENSE AGREEMENT VERSION 1.0.0 From scipy-svn at scipy.org Wed Feb 28 15:21:38 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 14:21:38 -0600 (CST) Subject: [Scipy-svn] r2778 - trunk/Lib/sandbox/timeseries Message-ID: <20070228202138.487AE39C31F@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 14:21:35 -0600 (Wed, 28 Feb 2007) New Revision: 2778 Added: trunk/Lib/sandbox/timeseries/parser.py Log: Added: trunk/Lib/sandbox/timeseries/parser.py =================================================================== --- trunk/Lib/sandbox/timeseries/parser.py 2007-02-28 20:21:10 UTC (rev 2777) +++ trunk/Lib/sandbox/timeseries/parser.py 2007-02-28 20:21:35 UTC (rev 2778) @@ -0,0 +1,877 @@ +""" Date/Time string parsing module. + +This code is a slightly modified version of Parser.py found in mx.DateTime + +As such, it is subject to the terms of the eGenix public license. Please see +license.txt for more details. +""" +import re,string +import datetime as dt +from string import atoi, atof, lower, upper + +# Enable to produce debugging output +_debug = 0 + +class RangeError(Exception): pass + +# REs for matching date and time parts in a string; These REs +# parse a superset of ARPA, ISO, American and European style dates. +# Timezones are supported via the Timezone submodule. + +_year = '(?P-?\d+\d(?!:))' +_fullyear = '(?P-?\d+\d\d(?!:))' +_year_epoch = '(?:' + _year + '(?P *[ABCDE\.]+)?)' +_fullyear_epoch = '(?:' + _fullyear + '(?P *[ABCDE\.]+)?)' +_relyear = '(?:\((?P[-+]?\d+)\))' + +_month = '(?P\d?\d(?!:))' +_fullmonth = '(?P\d\d(?!:))' +_litmonth = ('(?P' + 'jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|' + 'm?r|mae|mrz|mai|okt|dez|' + 'fev|avr|juin|juil|aou|ao?|d?c|' + 'ene|abr|ago|dic|' + 'out' + ')[a-z,\.;]*') +litmonthtable = { + # English + 'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6, + 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12, + # German + 'm?r':3, 'mae':3, 'mrz':3, 'mai':5, 'okt':10, 'dez':12, + # French + 'fev':2, 'avr':4, 'juin':6, 'juil':7, 'aou':8, 'ao?':8, + 'd?c':12, + # Spanish + 'ene':1, 'abr':4, 'ago':8, 'dic':12, + # Portuguese + 'out':10, + } +_relmonth = '(?:\((?P[-+]?\d+)\))' + +_day = '(?P\d?\d(?!:))' +_usday = '(?P\d?\d(?!:))(?:st|nd|rd|th|[,\.;])?' +_fullday = '(?P\d\d(?!:))' +_litday = ('(?P' + 'mon|tue|wed|thu|fri|sat|sun|' + 'die|mit|don|fre|sam|son|' + 'lun|mar|mer|jeu|ven|sam|dim|' + 'mie|jue|vie|sab|dom|' + 'pri|seg|ter|cua|qui' + ')[a-z]*') +litdaytable = { + # English + 'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6, + # German + 'die':1, 'mit':2, 'don':3, 'fre':4, 'sam':5, 'son':6, + # French + 'lun':0, 'mar':1, 'mer':2, 'jeu':3, 'ven':4, 'sam':5, 'dim':6, + # Spanish + 'mie':2, 'jue':3, 'vie':4, 'sab':5, 'dom':6, + # Portuguese + 'pri':0, 'seg':1, 'ter':2, 'cua':3, 'qui':4, + } +_relday = '(?:\((?P[-+]?\d+)\))' + +_hour = '(?P[012]?\d)' +_minute = '(?P[0-6]\d)' +_second = '(?P[0-6]\d(?:\.\d+)?)' + +_days = '(?P\d*\d(?:\.\d+)?)' +_hours = '(?P\d*\d(?:\.\d+)?)' +_minutes = '(?P\d*\d(?:\.\d+)?)' +_seconds = '(?P\d*\d(?:\.\d+)?)' + +_reldays = '(?:\((?P[-+]?\d+(?:\.\d+)?)\))' +_relhours = '(?:\((?P[-+]?\d+(?:\.\d+)?)\))' +_relminutes = '(?:\((?P[-+]?\d+(?:\.\d+)?)\))' +_relseconds = '(?:\((?P[-+]?\d+(?:\.\d+)?)\))' + +_sign = '(?:(?P[-+]) *)' +_week = 'W(?P\d?\d)' +_zone = ('(?P[A-Z]+|[+-]\d\d?:?(?:\d\d)?)') +_ampm = '(?P[ap][m.]+)' + +_time = (_hour + ':' + _minute + '(?::' + _second + ')? *' + + _ampm + '? *' + _zone + '?') +_isotime = _hour + ':?' + _minute + ':?' + _second + '? *' + _zone + '?' + +_weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?' +_eurodate = _day + '\.' + _month + '\.' + _year_epoch + '?' +_usdate = _month + '/' + _day + '(?:/' + _year_epoch + ')?' +_altusdate = _month + '-' + _day + '-' + _fullyear_epoch +_isodate = _year + '-' + _fullmonth + '-?' + _fullday + '?(?!:)' +_altisodate = _year + _fullmonth + _fullday + '(?!:)' +_litdate = ('(?:'+ _litday + ',? )? *' + + _usday + ' *' + + '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ] *' + + _year_epoch + '?') +_altlitdate = ('(?:'+ _litday + ',? )? *' + + _litmonth + '[ ,.a-z]+' + + _usday + + '(?:[ a-z]+' + _year_epoch + ')?') +_eurlitdate = ('(?:'+ _litday + ',?[ a-z]+)? *' + + '(?:'+ _usday + '[ a-z]+)? *' + + _litmonth + + '(?:[ ,.a-z]+' + _year_epoch + ')?') + +_relany = '[*%?a-zA-Z]+' + +_relisodate = ('(?:(?:' + _relany + '|' + _year + '|' + _relyear + ')-' + + '(?:' + _relany + '|' + _month + '|' + _relmonth + ')-' + + '(?:' + _relany + '|' + _day + '|' + _relday + '))') + +_asctime = ('(?:'+ _litday + ',? )? *' + + _usday + ' *' + + '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ]' + + '(?:[0-9: ]+)' + + _year_epoch + '?') + +_relisotime = ('(?:(?:' + _relany + '|' + _hour + '|' + _relhours + '):' + + '(?:' + _relany + '|' + _minute + '|' + _relminutes + ')' + + '(?::(?:' + _relany + '|' + _second + '|' + _relseconds + '))?)') + +_isodelta1 = (_sign + '?' + + _days + ':' + _hours + ':' + _minutes + ':' + _seconds) +_isodelta2 = (_sign + '?' + + _hours + ':' + _minutes + ':' + _seconds) +_isodelta3 = (_sign + '?' + + _hours + ':' + _minutes) +_litdelta = (_sign + '?' + + '(?:' + _days + ' *d[a-z]*[,; ]*)?' + + '(?:' + _hours + ' *h[a-z]*[,; ]*)?' + + '(?:' + _minutes + ' *m[a-z]*[,; ]*)?' + + '(?:' + _seconds + ' *s[a-z]*[,; ]*)?') + +_timeRE = re.compile(_time, re.I) +_isotimeRE = re.compile(_isotime, re.I) +_isodateRE = re.compile(_isodate, re.I) +_altisodateRE = re.compile(_altisodate, re.I) +_eurodateRE = re.compile(_eurodate, re.I) +_usdateRE = re.compile(_usdate, re.I) +_altusdateRE = re.compile(_altusdate, re.I) +_litdateRE = re.compile(_litdate, re.I) +_altlitdateRE = re.compile(_altlitdate, re.I) +_eurlitdateRE = re.compile(_eurlitdate, re.I) +_relisodateRE = re.compile(_relisodate, re.I) +_asctimeRE = re.compile(_asctime, re.I) +_isodelta1RE = re.compile(_isodelta1) +_isodelta2RE = re.compile(_isodelta2) +_isodelta3RE = re.compile(_isodelta3) +_litdeltaRE = re.compile(_litdelta) +_relisotimeRE = re.compile(_relisotime, re.I) + +# Available date parsers +_date_formats = ('euro', + 'us', 'altus', + 'iso', 'altiso', + 'lit', 'altlit', 'eurlit', + 'unknown') + + +# time zone parsing + +_zoneoffset = '(?:(?P[+-])?(?P\d\d?):?(?P\d\d)?)' + +# Compiled RE objects +_zoneoffsetRE = re.compile(_zoneoffset) + + +### Time zone offset table +# +# The offset given here represent the difference between UTC and the +# given time zone. +# +_zonetable = { + # Timezone abbreviations + # Std Summer + + # Standards + 'UT':0, + 'UTC':0, + 'GMT':0, + + # A few common timezone abbreviations + 'CET':1, 'CEST':2, 'CETDST':2, # Central European + 'MET':1, 'MEST':2, 'METDST':2, # Mean European + 'MEZ':1, 'MESZ':2, # Mitteleurop?ische Zeit + 'EET':2, 'EEST':3, 'EETDST':3, # Eastern Europe + 'WET':0, 'WEST':1, 'WETDST':1, # Western Europe + 'MSK':3, 'MSD':4, # Moscow + 'IST':5.5, # India + 'JST':9, # Japan + 'KST':9, # Korea + 'HKT':8, # Hong Kong + + # US time zones + 'AST':-4, 'ADT':-3, # Atlantic + 'EST':-5, 'EDT':-4, # Eastern + 'CST':-6, 'CDT':-5, # Central + 'MST':-7, 'MDT':-6, # Midwestern + 'PST':-8, 'PDT':-7, # Pacific + + # Australian time zones + 'CAST':9.5, 'CADT':10.5, # Central + 'EAST':10, 'EADT':11, # Eastern + 'WAST':8, 'WADT':9, # Western + + # US military time zones + 'Z': 0, + 'A': 1, + 'B': 2, + 'C': 3, + 'D': 4, + 'E': 5, + 'F': 6, + 'G': 7, + 'H': 8, + 'I': 9, + 'K': 10, + 'L': 11, + 'M': 12, + 'N':-1, + 'O':-2, + 'P':-3, + 'Q':-4, + 'R':-5, + 'S':-6, + 'T':-7, + 'U':-8, + 'V':-9, + 'W':-10, + 'X':-11, + 'Y':-12 + } + +def utc_offset(zone): + """ utc_offset(zonestring) + + Return the UTC time zone offset in minutes. + + zone must be string and can either be given as +-HH:MM, + +-HHMM, +-HH numeric offset or as time zone + abbreviation. Daylight saving time must be encoded into the + zone offset. + + Timezone abbreviations are treated case-insensitive. + + """ + if not zone: + return 0 + uzone = upper(zone) + if _zonetable.has_key(uzone): + return _zonetable[uzone]*60 + offset = _zoneoffsetRE.match(zone) + if not offset: + raise ValueError,'wrong format or unkown time zone: "%s"' % zone + zonesign,hours,minutes = offset.groups() + offset = int(hours or 0) * 60 + int(minutes or 0) + if zonesign == '-': + offset = -offset + return offset + +def add_century(year): + """ Sliding window approach to the Y2K problem: adds a suitable + century to the given year and returns it as integer. + + The window used depends on the current year (at import time). + If adding the current century to the given year gives a year + within the range current_year-70...current_year+30 [both + inclusive], then the current century is added. Otherwise the + century (current + 1 or - 1) producing the least difference is + chosen. + + """ + + current_year=dt.datetime.now().year + current_century=(dt.datetime.now().year / 100) * 100 + + if year > 99: + # Take it as-is + return year + year = year + current_century + diff = year - current_year + if diff >= -70 and diff <= 30: + return year + elif diff < -70: + return year + 100 + else: + return year - 100 + +def _parse_date(text, formats=_date_formats, defaultdate=None, + now=dt.datetime.now): + + """ Parses the date part given in text and returns a tuple + (text,day,month,year,style) with the following + meanings: + + * text gives the original text without the date part + + * day,month,year give the parsed date + + * style gives information about which parser was successful: + 'euro' - the European date parser + 'us' - the US date parser + 'altus' - the alternative US date parser (with '-' instead of '/') + 'iso' - the ISO date parser + 'altiso' - the alternative ISO date parser (without '-') + 'lit' - the US literal date parser + 'altlit' - the alternative US literal date parser + 'eurlit' - the Eurpean literal date parser + 'unknown' - no date part was found, defaultdate was used + + formats may be set to a tuple of style strings specifying + which of the above parsers to use and in which order to try + them. Default is to try all of them in the above order. + + defaultdate provides the defaults to use in case no date part + is found. Most other parsers default to the current year + January 1 if some of these date parts are missing. + + If 'unknown' is not given in formats and the date cannot be + parsed, a ValueError is raised. + + """ + match = None + style = '' + + # Apply parsers in the order given in formats + for format in formats: + + if format == 'euro': + # European style date + match = _eurodateRE.search(text) + if match is not None: + day,month,year,epoch = match.groups() + if year: + if len(year) == 2: + # Y2K problem: + year = add_century(atoi(year)) + else: + year = atoi(year) + else: + if defaultdate is None: + defaultdate = dt.datetime.now() + year = defaultdate.year + if epoch and 'B' in epoch: + year = -year + 1 + month = atoi(month) + # Could have mistaken euro format for us style date + # which uses month, day order + if month > 12 or month == 0: + continue + day = atoi(day) + break + + elif format == 'us' or format == 'altus': + # US style date + if format == 'us': + match = _usdateRE.search(text) + else: + match = _altusdateRE.search(text) + if match is not None: + month,day,year,epoch = match.groups() + if year: + if len(year) == 2: + # Y2K problem: + year = add_century(atoi(year)) + else: + year = atoi(year) + else: + if defaultdate is None: + defaultdate = dt.datetime.now() + year = defaultdate.year + if epoch and 'B' in epoch: + year = -year + 1 + month = atoi(month) + # Could have mistaken us format for euro style date + # which uses day, month order + if month > 12 or month == 0: + continue + # Default to 1 if no day is given + if day: + day = atoi(day) + else: + day = 1 + break + + elif format == 'iso' or format == 'altiso': + # ISO style date + if format == 'iso': + match = _isodateRE.search(text) + else: + match = _altisodateRE.search(text) + # Avoid mistaking ISO time parts ('Thhmmss') for dates + if match is not None: + left, right = match.span() + if left > 0 and \ + text[left - 1:left] == 'T': + continue + if match is not None: + year,month,day = match.groups() + if len(year) == 2: + # Y2K problem: + year = add_century(atoi(year)) + else: + year = atoi(year) + # Default to January 1st + if not month: + month = 1 + else: + month = atoi(month) + if not day: + day = 1 + else: + day = atoi(day) + break + + elif format == 'lit': + # US style literal date + match = _litdateRE.search(text) + if match is not None: + litday,day,litmonth,month,year,epoch = match.groups() + break + + elif format == 'altlit': + # Alternative US style literal date + match = _altlitdateRE.search(text) + if match is not None: + litday,litmonth,day,year,epoch = match.groups() + month = '' + break + + elif format == 'eurlit': + # European style literal date + match = _eurlitdateRE.search(text) + if match is not None: + litday,day,litmonth,year,epoch = match.groups() + month = '' + break + + elif format == 'unknown': + # No date part: use defaultdate + if defaultdate is None: + defaultdate = dt.datetime.now() + year = defaultdate.year + month = defaultdate.month + day = defaultdate.day + style = format + break + + # Check success + if match is not None: + # Remove date from text + left, right = match.span() + if 0 and _debug: + print 'parsed date:',repr(text[left:right]),\ + 'giving:',year,month,day + text = text[:left] + text[right:] + style = format + + elif not style: + # Not recognized: raise an error + raise ValueError, 'unknown date format: "%s"' % text + + # Literal date post-processing + if style in ('lit', 'altlit', 'eurlit'): + if 0 and _debug: print match.groups() + # Default to current year, January 1st + if not year: + if defaultdate is None: + defaultdate = dt.datetime.now() + year = defaultdate.year + else: + if len(year) == 2: + # Y2K problem: + year = add_century(atoi(year)) + else: + year = atoi(year) + if epoch and 'B' in epoch: + year = -year + 1 + if litmonth: + litmonth = lower(litmonth) + try: + month = litmonthtable[litmonth] + except KeyError: + raise ValueError,\ + 'wrong month name: "%s"' % litmonth + elif month: + month = atoi(month) + else: + month = 1 + if day: + day = atoi(day) + else: + day = 1 + + return text,day,month,year,style + +def _parse_time(text, formats=('iso','unknown')): + + """ Parses a time part given in text and returns a tuple + (text,hour,minute,second,offset,style) with the following + meanings: + + * text gives the original text without the time part + * hour,minute,second give the parsed time + * offset gives the time zone UTC offset + * style gives information about which parser was successful: + 'standard' - the standard parser + 'iso' - the ISO time format parser + 'unknown' - no time part was found + + formats may be set to a tuple specifying the parsers to use: + 'standard' - standard time format with ':' delimiter + 'iso' - ISO time format (superset of 'standard') + 'unknown' - default to 0:00:00, 0 zone offset + + If 'unknown' is not given in formats and the time cannot be + parsed, a ValueError is raised. + + """ + match = None + style = '' + + # Apply parsers in the order given in formats + for format in formats: + + # Standard format + if format == 'standard': + match = _timeRE.search(text) + if match is not None: + hour,minute,second,ampm,zone = match.groups() + style = 'standard' + break + + # ISO format + if format == 'iso': + match = _isotimeRE.search(text) + if match is not None: + hour,minute,second,zone = match.groups() + ampm = None + style = 'iso' + break + + # Default handling + elif format == 'unknown': + hour,minute,second,offset = 0,0,0.0,0 + style = 'unknown' + break + + if not style: + # If no default handling should be applied, raise an error + raise ValueError, 'unknown time format: "%s"' % text + + # Post-processing + if match is not None: + if zone: + # Convert to UTC offset + offset = utc_offset(zone) + else: + offset = 0 + hour = atoi(hour) + if ampm: + if ampm[0] in ('p', 'P'): + hour = hour + 12 + if minute: + minute = atoi(minute) + else: + minute = 0 + if not second: + second = 0.0 + else: + second = atof(second) + + # Remove time from text + left,right = match.span() + if 0 and _debug: + print 'parsed time:',repr(text[left:right]),\ + 'giving:',hour,minute,second,offset + text = text[:left] + text[right:] + + return text,hour,minute,second,offset,style + +### + +def DateTimeFromString(text, formats=_date_formats, defaultdate=None): + + """ DateTimeFromString(text, [formats, defaultdate]) + + Returns a datetime instance reflecting the date and time given + in text. In case a timezone is given, the returned instance + will point to the corresponding UTC time value. Otherwise, the + value is set as given in the string. + + formats may be set to a tuple of strings specifying which of + the following parsers to use and in which order to try + them. Default is to try all of them in the order given below: + + 'euro' - the European date parser + 'us' - the US date parser + 'altus' - the alternative US date parser (with '-' instead of '/') + 'iso' - the ISO date parser + 'altiso' - the alternative ISO date parser (without '-') + 'lit' - the US literal date parser + 'altlit' - the alternative US literal date parser + 'eurlit' - the Eurpean literal date parser + 'unknown' - if no date part is found, use defaultdate + + defaultdate provides the defaults to use in case no date part + is found. Most other parsers default to the current year + January 1 if some of these date parts are missing. + + If 'unknown' is not given in formats and the date/time cannot + be parsed, a ValueError is raised. + + """ + origtext = text + formats = tuple(formats) + + if formats is _date_formats or \ + 'iso' in formats or \ + 'altiso' in formats: + # First try standard order (parse time, then date) + if formats[0] not in ('iso', 'altiso'): + text,hour,minute,second,offset,timestyle = _parse_time( + origtext, + ('standard', 'iso', 'unknown')) + text,day,month,year,datestyle = _parse_date( + text, + formats + ('unknown',), + defaultdate) + if 0 and _debug: + print 'tried time/date on %s, date=%s, time=%s' % (origtext, + datestyle, + timestyle) + else: + timestyle = 'iso' + + # If this fails, try the ISO order + if timestyle in ('iso', 'unknown'): + text,day,month,year,datestyle = _parse_date( + origtext, + formats, + defaultdate) + text,hour,minute,second,offset,timestyle = _parse_time( + text, + ('iso', 'unknown')) + if 0 and _debug: + print 'tried ISO on %s, date=%s, time=%s' % (origtext, + datestyle, + timestyle) + else: + # Standard order: time part, then date part + text,hour,minute,second,offset,timestyle = _parse_time( + origtext, + ('standard', 'unknown')) + text,day,month,year,datestyle = _parse_date( + text, + formats, + defaultdate) + + if (datestyle == 'unknown' or \ + timestyle == 'unknown') and \ + 'unknown' not in formats: + raise ValueError,\ + 'Failed to parse "%s": found "%s" date, "%s" time' % \ + (origtext, datestyle, timestyle) + + try: + microsecond = int(1000000 * (second % 1)) + second = int(second) + return dt.datetime(year,month,day,hour,minute,second, microsecond) - \ + dt.timedelta(minutes=offset) + except ValueError, why: + raise RangeError,\ + 'Failed to parse "%s": %s' % (origtext, why) + +def DateFromString(text, formats=_date_formats, defaultdate=None): + + """ DateFromString(text, [formats, defaultdate]) + + Returns a datetime instance reflecting the date given in + text. A possibly included time part is ignored. + + formats and defaultdate work just like for + DateTimeFromString(). + + """ + _text,day,month,year,datestyle = _parse_date(text, formats, defaultdate) + + if datestyle == 'unknown' and \ + 'unknown' not in formats: + raise ValueError,\ + 'Failed to parse "%s": found "%s" date' % \ + (origtext, datestyle) + + try: + return dt.datetime(year,month,day) + except ValueError, why: + raise RangeError,\ + 'Failed to parse "%s": %s' % (text, why) + +def validateDateTimeString(text, formats=_date_formats): + + """ validateDateTimeString(text, [formats, defaultdate]) + + Validates the given text and returns 1/0 depending on whether + text includes parseable date and time values or not. + + formats works just like for DateTimeFromString() and defines + the order of date/time parsers to apply. It defaults to the + same list of parsers as for DateTimeFromString(). + + XXX Undocumented ! + + """ + formats = list(formats) + if 'unknown' in formats: + formats.remove('unknown') + try: + DateTimeFromString(text, formats) + except ValueError, why: + return 0 + return 1 + +def validateDateString(text, formats=_date_formats): + + """ validateDateString(text, [formats, defaultdate]) + + Validates the given text and returns 1/0 depending on whether + text includes a parseable date value or not. + + formats works just like for DateTimeFromString() and defines + the order of date/time parsers to apply. It defaults to the + same list of parsers as for DateTimeFromString(). + + XXX Undocumented ! + + """ + formats = list(formats) + if 'unknown' in formats: + formats.remove('unknown') + try: + DateFromString(text, formats) + except ValueError, why: + return 0 + return 1 + + +### Tests + +def _test(): + + import sys + + t = dt.datetime.now() + + print 'Testing DateTime Parser...' + + l = [ + + # Literal formats + ('Sun Nov 6 08:49:37 1994', '1994-11-06 08:49:37.00'), + ('sun nov 6 08:49:37 1994', '1994-11-06 08:49:37.00'), + ('sUN NOV 6 08:49:37 1994', '1994-11-06 08:49:37.00'), + ('Sunday, 06-Nov-94 08:49:37 GMT', '1994-11-06 08:49:37.00'), + ('Sun, 06 Nov 1994 08:49:37 GMT', '1994-11-06 08:49:37.00'), + ('06-Nov-94 08:49:37', '1994-11-06 08:49:37.00'), + ('06-Nov-94', '1994-11-06 00:00:00.00'), + ('06-NOV-94', '1994-11-06 00:00:00.00'), + ('November 19 08:49:37', '%s-11-19 08:49:37.00' % t.year), + ('Nov. 9', '%s-11-09 00:00:00.00' % t.year), + ('Sonntag, der 6. November 1994, 08:49:37 GMT', '1994-11-06 08:49:37.00'), + ('6. November 2001, 08:49:37', '2001-11-06 08:49:37.00'), + ('sep 6', '%s-09-06 00:00:00.00' % t.year), + ('September 29', '%s-09-29 00:00:00.00' % t.year), + ('Sep. 29', '%s-09-29 00:00:00.00' % t.year), + ('6 sep', '%s-09-06 00:00:00.00' % t.year), + ('29 September', '%s-09-29 00:00:00.00' % t.year), + ('29 Sep.', '%s-09-29 00:00:00.00' % t.year), + ('sep 6 2001', '2001-09-06 00:00:00.00'), + ('Sep 6, 2001', '2001-09-06 00:00:00.00'), + ('September 6, 2001', '2001-09-06 00:00:00.00'), + ('sep 6 01', '2001-09-06 00:00:00.00'), + ('Sep 6, 01', '2001-09-06 00:00:00.00'), + ('September 6, 01', '2001-09-06 00:00:00.00'), + + # ISO formats + ('1994-11-06 08:49:37', '1994-11-06 08:49:37.00'), + ('010203', '2001-02-03 00:00:00.00'), + ('2001-02-03 00:00:00.00', '2001-02-03 00:00:00.00'), + ('2001-02 00:00:00.00', '2001-02-01 00:00:00.00'), + ('2001-02-03', '2001-02-03 00:00:00.00'), + ('2001-02', '2001-02-01 00:00:00.00'), + ('20000824/2300', '2000-08-24 23:00:00.00'), + ('20000824/0102', '2000-08-24 01:02:00.00'), + ('20000824', '2000-08-24 00:00:00.00'), + ('20000824/020301', '2000-08-24 02:03:01.00'), + ('20000824 020301', '2000-08-24 02:03:01.00'), + ('20000824T020301', '2000-08-24 02:03:01.00'), + ('20000824 020301', '2000-08-24 02:03:01.00'), + ('2000-08-24 02:03:01.00', '2000-08-24 02:03:01.00'), + ('T020311', '%s 02:03:11.00' % t.strftime('%Y-%m-%d')), + + # US formats + ('06/11/94 08:49:37', '1994-06-11 08:49:37.00'), + ('11/06/94 08:49:37', '1994-11-06 08:49:37.00'), + ('9/23/2001', '2001-09-23 00:00:00.00'), + ('9-23-2001', '2001-09-23 00:00:00.00'), + ('9/6', '%s-09-06 00:00:00.00' % t.year), + ('09/6', '%s-09-06 00:00:00.00' % t.year), + ('9/06', '%s-09-06 00:00:00.00' % t.year), + ('09/06', '%s-09-06 00:00:00.00' % t.year), + ('9/6/2001', '2001-09-06 00:00:00.00'), + ('09/6/2001', '2001-09-06 00:00:00.00'), + ('9/06/2001', '2001-09-06 00:00:00.00'), + ('09/06/2001', '2001-09-06 00:00:00.00'), + ('9-6-2001', '2001-09-06 00:00:00.00'), + ('09-6-2001', '2001-09-06 00:00:00.00'), + ('9-06-2001', '2001-09-06 00:00:00.00'), + ('09-06-2001', '2001-09-06 00:00:00.00'), + + # European formats + ('6.11.2001, 08:49:37', '2001-11-06 08:49:37.00'), + ('06.11.2001, 08:49:37', '2001-11-06 08:49:37.00'), + ('06.11. 08:49:37', '%s-11-06 08:49:37.00' % t.year), + + # Time only formats + ('01:03', '%s 01:03:00.00' % t.strftime('%Y-%m-%d')), + ('01:03:11', '%s 01:03:11.00' % t.strftime('%Y-%m-%d')), + ('01:03:11.50', '%s 01:03:11.50' % t.strftime('%Y-%m-%d')), + ('01:03:11.50 AM', '%s 01:03:11.50' % t.strftime('%Y-%m-%d')), + ('01:03:11.50 PM', '%s 13:03:11.50' % t.strftime('%Y-%m-%d')), + ('01:03:11.50 a.m.', '%s 01:03:11.50' % t.strftime('%Y-%m-%d')), + ('01:03:11.50 p.m.', '%s 13:03:11.50' % t.strftime('%Y-%m-%d')), + ] + + for text, reference in l: + try: + value = DateTimeFromString(text) + except: + if reference is None: + continue + else: + value = str(sys.exc_info()[1]) + valid_datetime = validateDateTimeString(text) + valid_date = validateDateString(text) + + if reference[-3:] == '.00': reference = reference[:-3] + elif reference[-3:] == '.50': reference = reference + '0000' + + if str(value) != reference and \ + not reference == 'ignore': + print 'Failed to parse "%s"' % text + print ' expected: %s' % (reference or '') + print ' parsed: %s' % value + elif _debug: + print 'Parsed "%s" successfully' % text + if _debug: + if not valid_datetime: + print ' "%s" failed date/time validation' % text + if not valid_date: + print ' "%s" failed date validation' % text + + +if __name__ == '__main__': + _test() From scipy-svn at scipy.org Wed Feb 28 15:24:30 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 14:24:30 -0600 (CST) Subject: [Scipy-svn] r2779 - trunk/Lib/sandbox/timeseries Message-ID: <20070228202430.B258739C2E6@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 14:24:21 -0600 (Wed, 28 Feb 2007) New Revision: 2779 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: - eliminated requirement of mx.DateTime - renamed mxDate parameter in Date constructor to datetime - still accepts mx.DateTime instances for the datetime parameter, but these are converted to regular datetime objects internally Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 20:21:35 UTC (rev 2778) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 20:24:21 UTC (rev 2779) @@ -31,10 +31,9 @@ from mx.DateTime import DateTimeType except ImportError: class DateTimeType: pass - - -from mx.DateTime.Parser import DateFromString as mxDFromString +from parser import DateFromString, DateTimeFromString + import tcore as corelib import cseries @@ -148,7 +147,10 @@ value = int(value) if isinstance(value, str): - self.datetime = mx_to_datetime(mxDFromString(value)) + if self.freqstr in ('H', 'T', 'S'): + self.datetime = DateTimeFromString(value) + else: + self.datetime = DateFromString(value) elif self.freqstr == 'A': self.datetime = dt.datetime(value, 1, 1) elif self.freqstr == 'B': @@ -175,7 +177,10 @@ dt.timedelta(days=(value-1)*7) elif string is not None: - self.datetime = mx_to_datetime(mxDFromString(string)) + if self.freqstr in ('H', 'T', 'S'): + self.datetime = DateTimeFromString(string) + else: + self.datetime = DateFromString(string) elif datetime is not None: if isinstance(datetime, DateTimeType): @@ -867,7 +872,7 @@ # Case #1: dates as strings ................. if dlist.dtype.kind == 'S': #...construct a list of ordinals - ords = numpy.fromiter((mxDFromString(s).absdays for s in dlist), + ords = numpy.fromiter((DateTimeFromString(s).toordinal() for s in dlist), float_) ords += 1 #...try to guess the frequency From scipy-svn at scipy.org Wed Feb 28 16:04:33 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:04:33 -0600 (CST) Subject: [Scipy-svn] r2780 - trunk/Lib/sandbox/timeseries Message-ID: <20070228210433.86ABF39C031@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:04:30 -0600 (Wed, 28 Feb 2007) New Revision: 2780 Modified: trunk/Lib/sandbox/timeseries/readme.txt Log: updated to reflect current state of things Modified: trunk/Lib/sandbox/timeseries/readme.txt =================================================================== --- trunk/Lib/sandbox/timeseries/readme.txt 2007-02-28 20:24:21 UTC (rev 2779) +++ trunk/Lib/sandbox/timeseries/readme.txt 2007-02-28 21:04:30 UTC (rev 2780) @@ -1,22 +1,12 @@ Requirements and warnings: -1. version 2.0.x of the mx DateTime module MUST be installed. Only "tested" with 2.0.3. The cseries - code requires access to a couple of the header files included with this module when compiling -2. Only tested with numpy 1.0.1 -3. Only tested with Python 2.4.x -4. Only tested on Windows and x86_64 Platform -5. Requires maskedarray from the sandbox (not the standard masked array module with numpy) +1. Only tested with numpy 1.0.1 +2. Only tested with Python 2.4.x +3. Only tested on Windows and x86_64 Platform +4. Requires maskedarray from the sandbox (not the standard masked array module with numpy) Instructions: -1. read through the included example.py script in the examples subfolder. This illustrates - the basic functionality of the module. I recommend placing print statements after each - variable assignment, one at a time, to see the result of each item in the examples. - - Documentation is very limited, so the examples really are the best starting point. - -2. Before you get too crazy and start modifying the examples and writing your own scripts, - please read todo.txt in the doc subdirectory for an outline of limitations in the current - module. - +1. Check out the wiki. That is the main source of documentation for now. + http://www.scipy.org/TimeSeriesPackage From scipy-svn at scipy.org Wed Feb 28 16:07:04 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:07:04 -0600 (CST) Subject: [Scipy-svn] r2781 - trunk/Lib/sandbox/timeseries/doc Message-ID: <20070228210704.B1ABF39C031@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:07:01 -0600 (Wed, 28 Feb 2007) New Revision: 2781 Modified: trunk/Lib/sandbox/timeseries/doc/todo.txt Log: updated to reflect current state of things Modified: trunk/Lib/sandbox/timeseries/doc/todo.txt =================================================================== --- trunk/Lib/sandbox/timeseries/doc/todo.txt 2007-02-28 21:04:30 UTC (rev 2780) +++ trunk/Lib/sandbox/timeseries/doc/todo.txt 2007-02-28 21:07:01 UTC (rev 2781) @@ -4,9 +4,6 @@ but would be nice to have none-the-less :) =================================================================== - - create an actual Date data type so dates can be stored in arrays without - resorting to object_ dtype - - add a "basis" option to the convert method. Possible values are 'business' or 'daily'. This would be used in determining the weights of the months, quarters, etc when converting things with @@ -16,7 +13,7 @@ provide the default behaviour if the basis option was not specified. - add time series specific concepts/functions like moving averages, MACD, moving - standard deviation, etc... + standard deviation, seasonal analysis, etc... - add support for more frequencies: semi-annual, decade, and maybe even sub-frequencies like quarterly with quarters ending on months other than @@ -28,16 +25,5 @@ Wishlist: - - currently, the code relies on the mx.DateTime module (both the python, - and c extension code). For obvious reasons, it would be nice if - external dependencies could be eliminated as much as possible. Not sure - if the built in datetime module is up to the task though, particularly - where the c-api is concerned. - - pytables submodule for reading and writing timeseries objects directly to/from HDF5 databases - - - plotting sub-module so timeseries objects can be easily plotted using - matplotlib - - - reporting sub-module From scipy-svn at scipy.org Wed Feb 28 16:10:16 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:10:16 -0600 (CST) Subject: [Scipy-svn] r2782 - trunk/Lib/sandbox/timeseries/archived_version Message-ID: <20070228211016.7402639C031@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:10:14 -0600 (Wed, 28 Feb 2007) New Revision: 2782 Removed: trunk/Lib/sandbox/timeseries/archived_version/.project Log: Removed file/folder Deleted: trunk/Lib/sandbox/timeseries/archived_version/.project =================================================================== --- trunk/Lib/sandbox/timeseries/archived_version/.project 2007-02-28 21:07:01 UTC (rev 2781) +++ trunk/Lib/sandbox/timeseries/archived_version/.project 2007-02-28 21:10:14 UTC (rev 2782) @@ -1,17 +0,0 @@ - - - scipy_svn_timeseries - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - - From scipy-svn at scipy.org Wed Feb 28 16:10:26 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:10:26 -0600 (CST) Subject: [Scipy-svn] r2783 - in trunk/Lib/sandbox/timeseries: . archived_version Message-ID: <20070228211026.92B5439C031@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:10:23 -0600 (Wed, 28 Feb 2007) New Revision: 2783 Added: trunk/Lib/sandbox/timeseries/archived_version/.project Removed: trunk/Lib/sandbox/timeseries/.project Log: Moved remotely Deleted: trunk/Lib/sandbox/timeseries/.project =================================================================== --- trunk/Lib/sandbox/timeseries/.project 2007-02-28 21:10:14 UTC (rev 2782) +++ trunk/Lib/sandbox/timeseries/.project 2007-02-28 21:10:23 UTC (rev 2783) @@ -1,17 +0,0 @@ - - - scipy_svn_timeseries - - - - - - org.python.pydev.PyDevBuilder - - - - - - org.python.pydev.pythonNature - - Copied: trunk/Lib/sandbox/timeseries/archived_version/.project (from rev 2782, trunk/Lib/sandbox/timeseries/.project) From scipy-svn at scipy.org Wed Feb 28 16:19:24 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:19:24 -0600 (CST) Subject: [Scipy-svn] r2784 - trunk/Lib/sandbox/timeseries Message-ID: <20070228211924.A5ED839C2C5@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:19:21 -0600 (Wed, 28 Feb 2007) New Revision: 2784 Modified: trunk/Lib/sandbox/timeseries/tseries.py Log: changed tsmasked constant to use the value 1 instead of 0 (zero was valid for mx.DateTime.DateTime objects, but is not for datetime.datetime objects) Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-02-28 21:10:23 UTC (rev 2783) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-02-28 21:19:21 UTC (rev 2784) @@ -924,7 +924,7 @@ "Returns whether the series is a valid TimeSeries object." return isinstance(series, TimeSeries) -tsmasked = TimeSeries(masked,dates=Date('D',0)) +tsmasked = TimeSeries(masked,dates=Date('D',1)) ##### -------------------------------------------------------------------------- #---- ... Additional functions ... From scipy-svn at scipy.org Wed Feb 28 16:20:06 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:20:06 -0600 (CST) Subject: [Scipy-svn] r2785 - trunk/Lib/sandbox/timeseries Message-ID: <20070228212006.39A8739C282@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:20:03 -0600 (Wed, 28 Feb 2007) New Revision: 2785 Modified: trunk/Lib/sandbox/timeseries/tmulti.py Log: fixed typo in Matt's email address Modified: trunk/Lib/sandbox/timeseries/tmulti.py =================================================================== --- trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-28 21:19:21 UTC (rev 2784) +++ trunk/Lib/sandbox/timeseries/tmulti.py 2007-02-28 21:20:03 UTC (rev 2785) @@ -3,7 +3,7 @@ Support for multi-variable time series, through masked recarrays. :author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com +:contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com :version: $Id$ """ __author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" From scipy-svn at scipy.org Wed Feb 28 16:20:29 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:20:29 -0600 (CST) Subject: [Scipy-svn] r2786 - trunk/Lib/sandbox/timeseries Message-ID: <20070228212029.982E839C2C5@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:20:26 -0600 (Wed, 28 Feb 2007) New Revision: 2786 Modified: trunk/Lib/sandbox/timeseries/tdates.py Log: fixed typo with Matt's email address Modified: trunk/Lib/sandbox/timeseries/tdates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 21:20:03 UTC (rev 2785) +++ trunk/Lib/sandbox/timeseries/tdates.py 2007-02-28 21:20:26 UTC (rev 2786) @@ -2,7 +2,7 @@ Classes definition for the support of individual dates and array of dates. :author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com +:contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com :version: $Id$ """ __author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" From scipy-svn at scipy.org Wed Feb 28 16:20:55 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:20:55 -0600 (CST) Subject: [Scipy-svn] r2787 - trunk/Lib/sandbox/timeseries Message-ID: <20070228212055.F28E839C27B@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:20:50 -0600 (Wed, 28 Feb 2007) New Revision: 2787 Modified: trunk/Lib/sandbox/timeseries/tcore.py Log: fixed typo with Matt's email address Modified: trunk/Lib/sandbox/timeseries/tcore.py =================================================================== --- trunk/Lib/sandbox/timeseries/tcore.py 2007-02-28 21:20:26 UTC (rev 2786) +++ trunk/Lib/sandbox/timeseries/tcore.py 2007-02-28 21:20:50 UTC (rev 2787) @@ -2,7 +2,7 @@ A collection of tools for timeseries :author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com +:contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com :version: $Id$ """ __author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" From scipy-svn at scipy.org Wed Feb 28 16:21:14 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:21:14 -0600 (CST) Subject: [Scipy-svn] r2788 - trunk/Lib/sandbox/timeseries Message-ID: <20070228212114.68D7639C279@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:21:11 -0600 (Wed, 28 Feb 2007) New Revision: 2788 Modified: trunk/Lib/sandbox/timeseries/reportlib.py Log: fixed typo in Matt's email address Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-28 21:20:50 UTC (rev 2787) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-02-28 21:21:11 UTC (rev 2788) @@ -2,7 +2,7 @@ Reporting functions :author: Pierre GF Gerard-Marchant & Matt Knox -:contact: pierregm_at_uga_dot_edu - mattknow_ca_at_hotmail_dot_com +:contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com :version: $Id: tdates.py 2641 2007-01-30 18:40:17Z mattknox_ca $ Ideas borrowed from: From scipy-svn at scipy.org Wed Feb 28 16:25:21 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:25:21 -0600 (CST) Subject: [Scipy-svn] r2789 - trunk/Lib/sandbox/timeseries Message-ID: <20070228212521.48E2A39C094@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:25:16 -0600 (Wed, 28 Feb 2007) New Revision: 2789 Modified: trunk/Lib/sandbox/timeseries/parser.py Log: fixed encoding specification problem Modified: trunk/Lib/sandbox/timeseries/parser.py =================================================================== --- trunk/Lib/sandbox/timeseries/parser.py 2007-02-28 21:21:11 UTC (rev 2788) +++ trunk/Lib/sandbox/timeseries/parser.py 2007-02-28 21:25:16 UTC (rev 2789) @@ -1,3 +1,4 @@ +#-*- coding: latin-1 -*- """ Date/Time string parsing module. This code is a slightly modified version of Parser.py found in mx.DateTime @@ -9,6 +10,10 @@ import datetime as dt from string import atoi, atof, lower, upper +__all__ = [ +'DateFromString', 'DateTimeFromString' + ] + # Enable to produce debugging output _debug = 0 From scipy-svn at scipy.org Wed Feb 28 16:46:13 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:46:13 -0600 (CST) Subject: [Scipy-svn] r2790 - trunk/Lib/sandbox/timeseries Message-ID: <20070228214613.B60A139C279@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:46:10 -0600 (Wed, 28 Feb 2007) New Revision: 2790 Added: trunk/Lib/sandbox/timeseries/extras.py Log: new file for "extra" functions Added: trunk/Lib/sandbox/timeseries/extras.py =================================================================== --- trunk/Lib/sandbox/timeseries/extras.py 2007-02-28 21:25:16 UTC (rev 2789) +++ trunk/Lib/sandbox/timeseries/extras.py 2007-02-28 21:46:10 UTC (rev 2790) @@ -0,0 +1,108 @@ +"""Time Series add-ons. + +A collection of utilities for timeseries + +:author: Pierre GF Gerard-Marchant & Matt Knox +:contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com +:version: $Id: tcore.py 2752 2007-02-22 20:50:12Z mattknox_ca $ +""" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" +__version__ = '1.0' +__revision__ = "$Revision: 2752 $" +__date__ = '$Date: 2007-02-22 15:50:12 -0500 (Thu, 22 Feb 2007) $' + +__all__ = [ + 'forward_fill', 'backward_fill', 'interp_masked1d' + ] + +from scipy.interpolate import fitpack +import maskedarray as MA +from maskedarray import masked, nomask, getmask +import numpy.core.numeric as numeric + +#####--------------------------------------------------------------------------- +#---- --- Functions for filling in masked values in a masked array --- +#####--------------------------------------------------------------------------- + +def forward_fill(marr, maxgap=None): + """forward_fill(marr, maxgap=None) + +Forward fill masked values in a 1-d array when there are <= maxgap +consecutive masked values. If maxgap is None, then forward fill all +masked values.""" + + if numeric.ndim(marr) > 1: + raise ValueError,"The input array should be 1D only!" + + marr = MA.array(marr, copy=True) + if getmask(marr) is nomask or marr.size == 0: + return marr + + currGap = 0 + + if maxgap is not None: + for i in range(1, marr.size): + if marr._mask[i]: + currGap += 1 + if currGap <= maxgap and not marr._mask[i-1]: + marr._data[i] = marr._data[i-1] + marr._mask[i] = False + elif currGap == maxgap + 1: + marr._mask[i-maxgap:i] = True + else: + currGap = 0 + else: + for i in range(1, marr.size): + if marr._mask[i] and not marr._mask[i-1]: + marr._data[i] = marr._data[i-1] + marr._mask[i] = False + return marr + + +def backward_fill(marr, maxgap=None): + """backward_fill(marr, maxgap=None) + +backward fill masked values in a 1-d array when there are <= maxgap +consecutive masked values. If maxgap is None, then backward fill all +masked values.""" + return forward_fill(marr[::-1], maxgap=maxgap)[::-1] + + +def interp_masked1d(marr, kind='linear'): + """interp_masked1d(marr, king='linear') + +Interpolate masked values in marr according to method kind. +kind must be one of 'constant', 'linear', 'cubic', quintic' +""" + if numeric.ndim(marr) > 1: + raise ValueError("array must be 1 dimensional!") + # + marr = MA.array(marr, copy=True) + if getmask(marr) is nomask: + return marr + # + unmaskedIndices = (~marr._mask).nonzero()[0] + if unmaskedIndices.size < 2: + return marr + # + kind = kind.lower() + if kind == 'constant': + return forward_fill(marr) + try: + k = {'linear' : 1, + 'cubic' : 3, + 'quintic' : 5}[kind.lower()] + except KeyError: + raise ValueError("Unsupported interpolation type.") + + first_unmasked, last_unmasked = MA.extras.flatnotmasked_edges(marr) + + vals = marr.data[unmaskedIndices] + + tck = fitpack.splrep(unmaskedIndices, vals, k=k) + + maskedIndices = marr._mask.nonzero()[0] + interpIndices = maskedIndices[(maskedIndices > first_unmasked) & \ + (maskedIndices < last_unmasked)] + marr[interpIndices] = fitpack.splev(interpIndices, tck).astype(marr.dtype) + return marr From scipy-svn at scipy.org Wed Feb 28 16:46:33 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:46:33 -0600 (CST) Subject: [Scipy-svn] r2791 - trunk/Lib/sandbox/timeseries Message-ID: <20070228214633.928D539C279@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:46:29 -0600 (Wed, 28 Feb 2007) New Revision: 2791 Modified: trunk/Lib/sandbox/timeseries/tcore.py Log: moved some functions over to "extras.py" Modified: trunk/Lib/sandbox/timeseries/tcore.py =================================================================== --- trunk/Lib/sandbox/timeseries/tcore.py 2007-02-28 21:46:10 UTC (rev 2790) +++ trunk/Lib/sandbox/timeseries/tcore.py 2007-02-28 21:46:29 UTC (rev 2791) @@ -16,7 +16,6 @@ from scipy.interpolate import fitpack import maskedarray as MA -from maskedarray import masked, nomask, getmask from cseries import TSER_CONSTANTS @@ -143,60 +142,6 @@ raise ValueError("Invalid frequency: %s " % str(freq)) fmtFreq = check_freqstr -#class DateSpec: -# "Fake data type for date variables." -# def __init__(self, freq): -# self.freq = fmtFreq(freq) -# -# def __hash__(self): -# return hash(self.freq) -# -# def __eq__(self, other): -# if hasattr(other, "freq"): -# return self.freq == other.freq -# else: -# return False -# def __str__(self): -# return "Date(%s)" % str(self.freq) -# -## define custom numpy types. -## Note: A more robust approach would register these as actual valid numpy types -## this is just a hack for now -#numpy.dateA = DateSpec("Annual") -#numpy.dateB = DateSpec("Business") -#numpy.dateD = DateSpec("Daily") -#numpy.dateH = DateSpec("Hourly") -#numpy.dateM = DateSpec("Monthly") -#numpy.dateQ = DateSpec("Quarterly") -#numpy.dateS = DateSpec("Secondly") -#numpy.dateT = DateSpec("Minutely") -#numpy.dateW = DateSpec("Weekly") -#numpy.dateU = DateSpec("Undefined") -# -# -#freq_type_mapping = {'A': numpy.dateA, -# 'B': numpy.dateB, -# 'D': numpy.dateD, -# 'H': numpy.dateH, -# 'M': numpy.dateM, -# 'Q': numpy.dateQ, -# 'S': numpy.dateS, -# 'T': numpy.dateT, -# 'W': numpy.dateW, -# 'U': numpy.dateU, -# } -# -#def freqToType(freq): -# "Returns the Date dtype corresponding to the given frequency." -# return freq_type_mapping[fmtFreq(freq)] -# -#def isDateType(dtype): -# "Returns True whether the argument is the dtype of a Date." -# #TODO: That looks messy. We should simplify that -# if len([x for x in freq_type_mapping.values() if x == dtype]) > 0: -# return True -# else: -# return False #####--------------------------------------------------------------------------- #---- --- Misc functions --- @@ -219,90 +164,3 @@ else: return flatten_sequence(args) - -#####--------------------------------------------------------------------------- -#---- --- Functions for filling in masked values in a masked array --- -#####--------------------------------------------------------------------------- - -def forward_fill(marr, maxgap=None): - """forward_fill(marr, maxgap=None) - -Forward fill masked values in a 1-d array when there are <= maxgap -consecutive masked values. If maxgap is None, then forward fill all -masked values.""" - - if numeric.ndim(marr) > 1: - raise ValueError,"The input array should be 1D only!" - - marr = MA.array(marr, copy=True) - if getmask(marr) is nomask or marr.size == 0: - return marr - - currGap = 0 - - if maxgap is not None: - for i in range(1, marr.size): - if marr._mask[i]: - currGap += 1 - if currGap <= maxgap and not marr._mask[i-1]: - marr._data[i] = marr._data[i-1] - marr._mask[i] = False - elif currGap == maxgap + 1: - marr._mask[i-maxgap:i] = True - else: - currGap = 0 - else: - for i in range(1, marr.size): - if marr._mask[i] and not marr._mask[i-1]: - marr._data[i] = marr._data[i-1] - marr._mask[i] = False - return marr - - -def backward_fill(marr, maxgap=None): - """backward_fill(marr, maxgap=None) - -backward fill masked values in a 1-d array when there are <= maxgap -consecutive masked values. If maxgap is None, then backward fill all -masked values.""" - return forward_fill(marr[::-1], maxgap=maxgap)[::-1] - - -def interp_masked1d(marr, kind='linear'): - """interp_masked1d(marr, king='linear') - -Interpolate masked values in marr according to method kind. -kind must be one of 'constant', 'linear', 'cubic', quintic' -""" - if numeric.ndim(marr) > 1: - raise ValueError("array must be 1 dimensional!") - # - marr = MA.array(marr, copy=True) - if getmask(marr) is nomask: - return marr - # - unmaskedIndices = (~marr._mask).nonzero()[0] - if unmaskedIndices.size < 2: - return marr - # - kind = kind.lower() - if kind == 'constant': - return forward_fill(marr) - try: - k = {'linear' : 1, - 'cubic' : 3, - 'quintic' : 5}[kind.lower()] - except KeyError: - raise ValueError("Unsupported interpolation type.") - - first_unmasked, last_unmasked = MA.extras.flatnotmasked_edges(marr) - - vals = marr.data[unmaskedIndices] - - tck = fitpack.splrep(unmaskedIndices, vals, k=k) - - maskedIndices = marr._mask.nonzero()[0] - interpIndices = maskedIndices[(maskedIndices > first_unmasked) & \ - (maskedIndices < last_unmasked)] - marr[interpIndices] = fitpack.splev(interpIndices, tck).astype(marr.dtype) - return marr From scipy-svn at scipy.org Wed Feb 28 16:47:25 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:47:25 -0600 (CST) Subject: [Scipy-svn] r2792 - trunk/Lib/sandbox/timeseries Message-ID: <20070228214725.B10B539C279@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:47:22 -0600 (Wed, 28 Feb 2007) New Revision: 2792 Modified: trunk/Lib/sandbox/timeseries/__init__.py Log: updated to include extras.py (also removed plotlib reference which shouldn't be in here) Modified: trunk/Lib/sandbox/timeseries/__init__.py =================================================================== --- trunk/Lib/sandbox/timeseries/__init__.py 2007-02-28 21:46:29 UTC (rev 2791) +++ trunk/Lib/sandbox/timeseries/__init__.py 2007-02-28 21:47:22 UTC (rev 2792) @@ -23,10 +23,12 @@ from tmulti import * import reportlib from reportlib import * -import plotlib +import extras +from extras import * __all__ = ['tdates', 'tseries','tmulti','reportlib'] __all__ += tdates.__all__ __all__ += tseries.__all__ __all__ += tmulti.__all__ __all__ += reportlib.__all__ +__all__ += extras.__all__ From scipy-svn at scipy.org Wed Feb 28 16:49:59 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:49:59 -0600 (CST) Subject: [Scipy-svn] r2793 - trunk/Lib/sandbox/timeseries/tests Message-ID: <20070228214959.033EE39C282@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:49:56 -0600 (Wed, 28 Feb 2007) New Revision: 2793 Modified: trunk/Lib/sandbox/timeseries/tests/test_dates.py Log: updated to reflect recent changes to tdates.py Modified: trunk/Lib/sandbox/timeseries/tests/test_dates.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-28 21:47:22 UTC (rev 2792) +++ trunk/Lib/sandbox/timeseries/tests/test_dates.py 2007-02-28 21:49:56 UTC (rev 2793) @@ -26,11 +26,8 @@ from maskedarray.testutils import assert_equal, assert_array_equal import timeseries.tdates as tdates -#from timeseries import tdates -#from timeseries.tdates import date_array_fromlist, Date, DateArray, date_array,\ -# mxDFromString, today from timeseries.tdates import * -from timeseries.tdates import mxDFromString +from timeseries.parser import DateFromString from timeseries import tcore @@ -103,7 +100,7 @@ dobj = [datetime.datetime.fromordinal(d) for d in dates.toordinal()] odates = date_array_fromlist(dobj) assert_equal(dates,odates) - dobj = [mxDFromString(d) for d in dlist] + dobj = [DateFromString(d) for d in dlist] odates = date_array_fromlist(dobj) assert_equal(dates,odates) From scipy-svn at scipy.org Wed Feb 28 16:50:34 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:50:34 -0600 (CST) Subject: [Scipy-svn] r2794 - trunk/Lib/sandbox/timeseries/tests Message-ID: <20070228215034.EDB5039C285@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:50:29 -0600 (Wed, 28 Feb 2007) New Revision: 2794 Modified: trunk/Lib/sandbox/timeseries/tests/test_misc.py Log: updated to reflect moving some functions from tcore to extras Modified: trunk/Lib/sandbox/timeseries/tests/test_misc.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_misc.py 2007-02-28 21:49:56 UTC (rev 2793) +++ trunk/Lib/sandbox/timeseries/tests/test_misc.py 2007-02-28 21:50:29 UTC (rev 2794) @@ -24,7 +24,7 @@ import maskedarray.testutils from maskedarray.testutils import assert_equal, assert_array_equal, approx -from timeseries import tcore +from timeseries import extras class test_funcs(NumpyTestCase): @@ -39,29 +39,29 @@ result[0] = 1 result[2] = 3 - assert_equal(tcore.backward_fill(self.test_array, maxgap=1), result) + assert_equal(extras.backward_fill(self.test_array, maxgap=1), result) result[5] = 7 result[6] = 7 - assert_equal(tcore.backward_fill(self.test_array), result) + assert_equal(extras.backward_fill(self.test_array), result) def test_forward_fill (self): result = masked_array(self.data, mask=self.mask) result[2] = 1 - assert_equal(tcore.forward_fill(self.test_array, maxgap=1), result) + assert_equal(extras.forward_fill(self.test_array, maxgap=1), result) result[5] = 4 result[6] = 4 - assert_equal(tcore.forward_fill(self.test_array), result) + assert_equal(extras.forward_fill(self.test_array), result) def test_interp_fill(self): result_lin = masked_array(self.data).astype(float_) result_lin[0] = masked - approx(tcore.interp_masked1d(self.test_array.astype(float_), kind='linear'), result_lin) + approx(extras.interp_masked1d(self.test_array.astype(float_), kind='linear'), result_lin) ############################################################################### From scipy-svn at scipy.org Wed Feb 28 16:50:52 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 15:50:52 -0600 (CST) Subject: [Scipy-svn] r2795 - trunk/Lib/sandbox/timeseries/io/fame/tests Message-ID: <20070228215052.44C3439C285@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 15:50:48 -0600 (Wed, 28 Feb 2007) New Revision: 2795 Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py Log: Modified: trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py =================================================================== --- trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-28 21:50:29 UTC (rev 2794) +++ trunk/Lib/sandbox/timeseries/io/fame/tests/test_fame.py 2007-02-28 21:50:48 UTC (rev 2795) @@ -18,6 +18,7 @@ from numpy.testing.utils import build_err_msg from timeseries.io import fame +from timeseries.io.fame import const from timeseries import Report import timeseries as ts import maskedarray as ma @@ -478,10 +479,10 @@ assert_equal(desc, _desc) assert_equal(doc, _doc) - self.db.set_obj_basis("$freq_b", fame.HBSDAY) - assert_equal(self.db.obj_basis("$freq_b"), fame.HBSDAY) - self.db.set_obj_basis("$freq_b", fame.HBSBUS) - assert_equal(self.db.obj_basis("$freq_b"), fame.HBSBUS) + self.db.set_obj_basis("$freq_b", const.HBSDAY) + assert_equal(self.db.obj_basis("$freq_b"), const.HBSDAY) + self.db.set_obj_basis("$freq_b", const.HBSBUS) + assert_equal(self.db.obj_basis("$freq_b"), const.HBSBUS) self.db.set_obj_observed("$freq_b", "END") assert_equal(self.db.obj_observed("$freq_b"), "ENDING") From scipy-svn at scipy.org Wed Feb 28 17:33:00 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 16:33:00 -0600 (CST) Subject: [Scipy-svn] r2796 - trunk/Lib/sandbox/timeseries Message-ID: <20070228223300.94E6939C282@new.scipy.org> Author: mattknox_ca Date: 2007-02-28 16:32:44 -0600 (Wed, 28 Feb 2007) New Revision: 2796 Modified: trunk/Lib/sandbox/timeseries/extras.py Log: added expmave function Modified: trunk/Lib/sandbox/timeseries/extras.py =================================================================== --- trunk/Lib/sandbox/timeseries/extras.py 2007-02-28 21:50:48 UTC (rev 2795) +++ trunk/Lib/sandbox/timeseries/extras.py 2007-02-28 22:32:44 UTC (rev 2796) @@ -12,12 +12,14 @@ __date__ = '$Date: 2007-02-22 15:50:12 -0500 (Thu, 22 Feb 2007) $' __all__ = [ - 'forward_fill', 'backward_fill', 'interp_masked1d' + 'forward_fill', 'backward_fill', 'interp_masked1d', + 'expmave' ] +import numpy as N from scipy.interpolate import fitpack import maskedarray as MA -from maskedarray import masked, nomask, getmask +from maskedarray import masked, nomask, getmask, MaskedArray import numpy.core.numeric as numeric #####--------------------------------------------------------------------------- @@ -106,3 +108,41 @@ (maskedIndices < last_unmasked)] marr[interpIndices] = fitpack.splev(interpIndices, tck).astype(marr.dtype) return marr + + +#####--------------------------------------------------------------------------- +#---- --- Moving average functions --- +#####--------------------------------------------------------------------------- + +def expmave(data, n, tol=1e-6): + """calculate exponential moving average of a series. + +:Parameters: + - `data` (ndarray, MaskedArray) : data is a valid ndarray or MaskedArray + or an instance of a subclass of these types. In particular, TimeSeries + objects are valid here. + - `n` (int) : time periods. Where the smoothing factor is 2/(n + 1) + - `tol` (float, *[1e-6]*) : when `data` contains masked values, this + parameter will determine what points in the result should be masked. + Values in the result that would not be "significantly" impacted (as + determined by this parameter) by the masked values are left unmasked. +""" + if isinstance(data, MaskedArray): + ismasked = (data._mask is not nomask) + else: + ismasked = False + + k = 2./float(n + 1) + def expmave_sub(a, b): + return b + k * (a - b) + + if ismasked: + data = data.filled(0) + + result = N.frompyfunc(expmave_sub, 2, 1).accumulate(data).astype(data.dtype) + if ismasked: + _unmasked = N.logical_not(MA.getmask(data)).astype(N.float_) + marker = 1 - N.frompyfunc(expmave_sub, 2, 1).accumulate(_unmasked) + result[marker > tol] = masked + + return result From scipy-svn at scipy.org Wed Feb 28 21:49:58 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 20:49:58 -0600 (CST) Subject: [Scipy-svn] r2797 - trunk/Lib/weave Message-ID: <20070301024958.B50B339C045@new.scipy.org> Author: timl Date: 2007-02-28 20:49:48 -0600 (Wed, 28 Feb 2007) New Revision: 2797 Modified: trunk/Lib/weave/ext_tools.py Log: small cleanup Modified: trunk/Lib/weave/ext_tools.py =================================================================== --- trunk/Lib/weave/ext_tools.py 2007-02-28 22:32:44 UTC (rev 2796) +++ trunk/Lib/weave/ext_tools.py 2007-03-01 02:49:48 UTC (rev 2797) @@ -376,13 +376,13 @@ """ file_changed = 1 if os.path.exists(module_file): - f =open(module_file,'r') + f = open(module_file,'r') old_string = f.read() f.close() if old_string == module_string: file_changed = 0 if file_changed: - f =open(module_file,'w') + f = open(module_file,'w') f.write(module_string) f.close() return module_file From scipy-svn at scipy.org Wed Feb 28 23:05:37 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 22:05:37 -0600 (CST) Subject: [Scipy-svn] r2798 - in trunk/Lib/sandbox/maskedarray: . tests Message-ID: <20070301040537.3901E39C03B@new.scipy.org> Author: pierregm Date: 2007-02-28 22:05:32 -0600 (Wed, 28 Feb 2007) New Revision: 2798 Modified: trunk/Lib/sandbox/maskedarray/core.py trunk/Lib/sandbox/maskedarray/tests/test_core.py trunk/Lib/sandbox/maskedarray/tests/test_extras.py trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py Log: core: fixed pickling with _data a subclass of ndarray Modified: trunk/Lib/sandbox/maskedarray/core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/core.py 2007-03-01 02:49:48 UTC (rev 2797) +++ trunk/Lib/sandbox/maskedarray/core.py 2007-03-01 04:05:32 UTC (rev 2798) @@ -1884,6 +1884,52 @@ """Returns the `_data` part of the MaskedArray. You should really use `data` instead...""" return self._data + #-------------------------------------------- + # Pickling + def __getstate__(self): + "Returns the internal state of the masked array, for pickling purposes." + state = (1, + self.shape, + self.dtype, + self.flags.fnc, + self._data.tostring(), + getmaskarray(self).tostring(), + self._fill_value, + ) + return state + # + def __setstate__(self, state): + """Restores the internal state of the masked array, for pickling purposes. + `state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: + + - class name + - a tuple giving the shape of the data + - a typecode for the data + - a binary string for the data + - a binary string for the mask. + """ + (ver, shp, typ, isf, raw, msk, flv) = state + ndarray.__setstate__(self, (shp, typ, isf, raw)) + self._mask.__setstate__((shp, dtype(bool), isf, msk)) + self.fill_value = flv + # + def __reduce__(self): + """Returns a 3-tuple for pickling a MaskedArray.""" + return (_mareconstruct, + (self.__class__, self._baseclass, (0,), 'b', ), + self.__getstate__()) + + +def _mareconstruct(subtype, baseclass, baseshape, basetype,): + """Internal function that builds a new MaskedArray from the information stored +in a pickle.""" + _data = ndarray.__new__(baseclass, baseshape, basetype) + _mask = ndarray.__new__(ndarray, baseshape, 'b1') + return subtype.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False) +#MaskedArray.__dump__ = dump +#MaskedArray.__dumps__ = dumps + + #####-------------------------------------------------------------------------- #---- --- Shortcuts --- @@ -2531,44 +2577,6 @@ #####-------------------------------------------------------------------------- #---- --- Pickling --- #####-------------------------------------------------------------------------- -#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data -def _mareconstruct(subtype, baseshape, basetype,): - """Internal function that builds a new MaskedArray from the information stored -in a pickle.""" - _data = ndarray.__new__(ndarray, baseshape, basetype) - _mask = ndarray.__new__(ndarray, baseshape, basetype) - return MaskedArray.__new__(subtype, _data, mask=_mask, dtype=basetype, small_mask=False) - -def _getstate(a): - "Returns the internal state of the masked array, for pickling purposes." - state = (1, - a.shape, - a.dtype, - a.flags.fnc, - a.tostring(), - getmaskarray(a).tostring()) - return state - -def _setstate(a, state): - """Restores the internal state of the masked array, for pickling purposes. -`state` is typically the output of the ``__getstate__`` output, and is a 5-tuple: - - - class name - - a tuple giving the shape of the data - - a typecode for the data - - a binary string for the data - - a binary string for the mask. - """ - (ver, shp, typ, isf, raw, msk) = state - super(MaskedArray, a).__setstate__((shp, typ, isf, raw)) - (a._mask).__setstate__((shp, dtype('|b1'), isf, msk)) - -def _reduce(a): - """Returns a 3-tuple for pickling a MaskedArray.""" - return (_mareconstruct, - (a.__class__, (0,), 'b', ), - a.__getstate__()) - def dump(a,F): """Pickles the MaskedArray `a` to the file `F`. `F` can either be the handle of an exiting file, or a string representing a file name. @@ -2592,15 +2600,24 @@ "Loads a pickle from the current string.""" return cPickle.loads(strg) -MaskedArray.__getstate__ = _getstate -MaskedArray.__setstate__ = _setstate -MaskedArray.__reduce__ = _reduce -MaskedArray.__dump__ = dump -MaskedArray.__dumps__ = dumps ################################################################################ -#if __name__ == '__main__': -# import numpy as N -# from maskedarray.testutils import assert_equal, assert_array_equal -# pi = N.pi +if __name__ == '__main__': + import numpy as N + from maskedarray.testutils import assert_equal, assert_array_equal + # + a = arange(10) + a[::3] = masked + a.fill_value = 999 + a_pickled = cPickle.loads(a.dumps()) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled._data, a._data) + assert_equal(a_pickled.fill_value, 999) + # + a = array(numpy.matrix(range(10)), mask=[1,0,1,0,0]*2) + a_pickled = cPickle.loads(a.dumps()) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled, a) + assert(isinstance(a_pickled._data,numpy.matrix)) + Modified: trunk/Lib/sandbox/maskedarray/tests/test_core.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-03-01 02:49:48 UTC (rev 2797) +++ trunk/Lib/sandbox/maskedarray/tests/test_core.py 2007-03-01 04:05:32 UTC (rev 2798) @@ -18,11 +18,9 @@ from numpy.testing.utils import build_err_msg import maskedarray.testutils -reload(maskedarray.testutils) from maskedarray.testutils import * import maskedarray.core as coremodule -reload(coremodule) from maskedarray.core import * pi = N.pi @@ -600,16 +598,6 @@ assert t[0] == 'abc' assert t[1] == 2 assert t[2] == 3 - #........................ - def check_pickling(self): - "Test of pickling" - import pickle - x = arange(12) - x[4:10:2] = masked - x = x.reshape(4,3) - s = pickle.dumps(x) - y = pickle.loads(s) - assert_equal(x,y) #....................... def check_maskedelement(self): "Test of masked element" @@ -700,6 +688,23 @@ assert_equal(X._mask, x.mask) assert_equal(getmask(x), [0,0,1,0,0]) + def check_pickling(self): + "Tests pickling" + import cPickle + a = arange(10) + a[::3] = masked + a.fill_value = 999 + a_pickled = cPickle.loads(a.dumps()) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled._data, a._data) + assert_equal(a_pickled.fill_value, 999) + # + a = array(N.matrix(range(10)), mask=[1,0,1,0,0]*2) + a_pickled = cPickle.loads(a.dumps()) + assert_equal(a_pickled._mask, a._mask) + assert_equal(a_pickled, a) + assert(isinstance(a_pickled._data,N.matrix)) + #............................................................................... class test_ufuncs(NumpyTestCase): Modified: trunk/Lib/sandbox/maskedarray/tests/test_extras.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_extras.py 2007-03-01 02:49:48 UTC (rev 2797) +++ trunk/Lib/sandbox/maskedarray/tests/test_extras.py 2007-03-01 04:05:32 UTC (rev 2798) @@ -16,14 +16,11 @@ from numpy.testing.utils import build_err_msg import maskedarray.testutils -reload(maskedarray.testutils) from maskedarray.testutils import * import maskedarray.core -reload(maskedarray.core) from maskedarray.core import * import maskedarray.extras -reload(maskedarray.extras) from maskedarray.extras import * class test_average(NumpyTestCase): Modified: trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py 2007-03-01 02:49:48 UTC (rev 2797) +++ trunk/Lib/sandbox/maskedarray/tests/test_mrecords.py 2007-03-01 04:05:32 UTC (rev 2798) @@ -18,16 +18,12 @@ from numpy.testing.utils import build_err_msg import maskedarray.testutils -reload(maskedarray.testutils) from maskedarray.testutils import * import maskedarray.core as MA -##reload(MA) #import maskedarray.mrecords -##reload(maskedarray.mrecords) #from maskedarray.mrecords import mrecarray, fromarrays, fromtextfile, fromrecords import maskedarray.mrecords -reload(maskedarray.mrecords) from maskedarray.mrecords import MaskedRecords, \ fromarrays, fromtextfile, fromrecords, addfield Modified: trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py =================================================================== --- trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-03-01 02:49:48 UTC (rev 2797) +++ trunk/Lib/sandbox/maskedarray/tests/test_subclassing.py 2007-03-01 04:05:32 UTC (rev 2798) @@ -16,11 +16,9 @@ from numpy.testing import NumpyTest, NumpyTestCase import maskedarray.testutils -#reload(maskedarray.testutils) from maskedarray.testutils import * import maskedarray.core as coremodule -#reload(coremodule) from maskedarray.core import * @@ -125,5 +123,15 @@ ################################################################################ if __name__ == '__main__': NumpyTest().run() + if 1: + x = N.arange(5) + m = [0,0,1,0,0] + xinfo = [(i,j) for (i,j) in zip(x,m)] + xsub = MSubArray(x, mask=m, info={'xsub':xinfo}) + # + xsub_low = less(xsub,3) + assert isinstance(xsub, MSubArray) + assert_equal(xsub_low.info, xinfo) + From scipy-svn at scipy.org Wed Feb 28 23:43:15 2007 From: scipy-svn at scipy.org (scipy-svn at scipy.org) Date: Wed, 28 Feb 2007 22:43:15 -0600 (CST) Subject: [Scipy-svn] r2799 - in trunk/Lib/sandbox/timeseries: . io plotlib tests Message-ID: <20070301044315.F0F8839C0E8@new.scipy.org> Author: pierregm Date: 2007-02-28 22:43:08 -0600 (Wed, 28 Feb 2007) New Revision: 2799 Added: trunk/Lib/sandbox/timeseries/.project Modified: trunk/Lib/sandbox/timeseries/extras.py trunk/Lib/sandbox/timeseries/io/__init__.py trunk/Lib/sandbox/timeseries/parser.py trunk/Lib/sandbox/timeseries/plotlib/__init__.py trunk/Lib/sandbox/timeseries/readme.txt trunk/Lib/sandbox/timeseries/reportlib.py trunk/Lib/sandbox/timeseries/setup.py trunk/Lib/sandbox/timeseries/tests/test_misc.py trunk/Lib/sandbox/timeseries/tests/test_timeseries.py trunk/Lib/sandbox/timeseries/tseries.py Log: tseries : fixed pickling w/ multi-variables Added: trunk/Lib/sandbox/timeseries/.project =================================================================== --- trunk/Lib/sandbox/timeseries/.project 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/.project 2007-03-01 04:43:08 UTC (rev 2799) @@ -0,0 +1,17 @@ + + + scipy_svn_timeseries + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + Modified: trunk/Lib/sandbox/timeseries/extras.py =================================================================== --- trunk/Lib/sandbox/timeseries/extras.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/extras.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -4,12 +4,12 @@ :author: Pierre GF Gerard-Marchant & Matt Knox :contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com -:version: $Id: tcore.py 2752 2007-02-22 20:50:12Z mattknox_ca $ +:version: $Id$ """ -__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" __version__ = '1.0' -__revision__ = "$Revision: 2752 $" -__date__ = '$Date: 2007-02-22 15:50:12 -0500 (Thu, 22 Feb 2007) $' +__revision__ = "$Revision$" +__date__ = '$Date$' __all__ = [ 'forward_fill', 'backward_fill', 'interp_masked1d', Property changes on: trunk/Lib/sandbox/timeseries/extras.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Property changes on: trunk/Lib/sandbox/timeseries/io/__init__.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Property changes on: trunk/Lib/sandbox/timeseries/parser.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Property changes on: trunk/Lib/sandbox/timeseries/plotlib/__init__.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Property changes on: trunk/Lib/sandbox/timeseries/readme.txt ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/timeseries/reportlib.py =================================================================== --- trunk/Lib/sandbox/timeseries/reportlib.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/reportlib.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -3,7 +3,7 @@ :author: Pierre GF Gerard-Marchant & Matt Knox :contact: pierregm_at_uga_dot_edu - mattknox_ca_at_hotmail_dot_com -:version: $Id: tdates.py 2641 2007-01-30 18:40:17Z mattknox_ca $ +:version: $Id$ Ideas borrowed from: @@ -52,10 +52,10 @@ html_o.write("") """ -__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" __version__ = '1.0' -__revision__ = "$Revision: 2641 $" -__date__ = '$Date: 2007-01-30 13:40:17 -0500 (Tue, 30 Jan 2007) $' +__revision__ = "$Revision$" +__date__ = '$Date$' import sys import operator, types, copy Property changes on: trunk/Lib/sandbox/timeseries/reportlib.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/timeseries/setup.py =================================================================== --- trunk/Lib/sandbox/timeseries/setup.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/setup.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -1,7 +1,7 @@ #!/usr/bin/env python __version__ = '1.0' -__revision__ = "$Revision: 37 $" -__date__ = '$Date: 2006-12-08 14:30:29 -0500 (Fri, 08 Dec 2006) $' +__revision__ = "$Revision$" +__date__ = '$Date$' import os from os.path import join Property changes on: trunk/Lib/sandbox/timeseries/setup.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/timeseries/tests/test_misc.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_misc.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/tests/test_misc.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -4,12 +4,12 @@ :author: Pierre Gerard-Marchant & Matt Knox :contact: pierregm_at_uga_dot_edu & mattknox_ca_at_hotmail_dot_com -:version: $Id: test_timeseries.py 2578 2007-01-17 19:25:10Z mattknox_ca $ +:version: $Id$ """ -__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author: mattknox_ca $)" +__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)" __version__ = '1.0' -__revision__ = "$Revision: 2578 $" -__date__ = '$Date: 2007-01-17 14:25:10 -0500 (Wed, 17 Jan 2007) $' +__revision__ = "$Revision$" +__date__ = '$Date$' import numpy as N from numpy import bool_, complex_, float_, int_, object_ Property changes on: trunk/Lib/sandbox/timeseries/tests/test_misc.py ___________________________________________________________________ Name: svn:keywords + Date Author Revision Id Modified: trunk/Lib/sandbox/timeseries/tests/test_timeseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/tests/test_timeseries.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -348,7 +348,7 @@ assert_array_equal(shift_negative, shift_negative_result) assert_array_equal(shift_positive, shift_positive_result) - + # def test_convert(self): """Test convert function @@ -390,10 +390,9 @@ Date(freq='b', year=2005, month=6, day=1).asfreq('M')) assert_equal(highToLow.end_date, (Date(freq='b', year=2005, month=6, day=1) + 99).asfreq('M')) - + # def test_fill_missing_dates(self): """Test fill_missing_dates function""" - _start = Date(freq='m', year=2005, month=1) _end = Date(freq='m', year=2005, month=4) @@ -406,7 +405,6 @@ assert(filled_ser.isfull()) assert(not filled_ser.has_duplicated_dates()) assert_equal(filled_ser.size, _end - _start + 1) - # def test_maskperiod(self): "Test mask_period" @@ -426,13 +424,24 @@ inplace=False) assert_equal(mask._mask, [1,1,1,1,1,0,0,0,0,0,0,0,1,1,1]) # - def pickling(self): + def test_pickling(self): "Tests pickling/unpickling" (series, data, dates) = self.d - tmp = maskedarray.loads(series.dumps()) - assert_equal(tmp._data, series._data) - assert_equal(tmp._dates, series._dates) - assert_equal(tmp._mask, series._mask) + import cPickle + series_pickled = cPickle.loads(series.dumps()) + assert_equal(series_pickled._dates, series._dates) + assert_equal(series_pickled._data, series._data) + assert_equal(series_pickled._mask, series._mask) + # + data = masked_array(N.matrix(range(10)).T, mask=[1,0,0,0,0]*2) + dates = date_array(start_date=thisday('D'), length=10) + series = time_series(data,dates=dates) + series_pickled = cPickle.loads(series.dumps()) + assert_equal(series_pickled._dates, series._dates) + assert_equal(series_pickled._data, series._data) + assert_equal(series_pickled._mask, series._mask) + assert(isinstance(series_pickled._data, N.matrix)) + def test_empty_timeseries(self): "Tests that empty TimeSeries are handled properly" Modified: trunk/Lib/sandbox/timeseries/tseries.py =================================================================== --- trunk/Lib/sandbox/timeseries/tseries.py 2007-03-01 04:05:32 UTC (rev 2798) +++ trunk/Lib/sandbox/timeseries/tseries.py 2007-03-01 04:43:08 UTC (rev 2799) @@ -29,6 +29,7 @@ import numpy from numpy import ndarray from numpy.core import bool_, complex_, float_, int_, object_ +from numpy.core.multiarray import dtype import numpy.core.fromnumeric as fromnumeric import numpy.core.numeric as numeric import numpy.core.umath as umath @@ -292,7 +293,7 @@ _defaultobserved = None _genattributes = ['fill_value', 'observed'] def __new__(cls, data, dates=None, mask=nomask, - freq=None, observed=None, start_date=None, + freq=None, observed=None, start_date=None, length=None, dtype=None, copy=False, fill_value=None, keep_mask=True, small_mask=True, hard_mask=False, **options): maparms = dict(copy=copy, dtype=dtype, fill_value=fill_value, @@ -315,6 +316,8 @@ else: dshape = _data.shape if len(dshape) > 0: + if length is None: + length = dshape[0] newdates = date_array(start_date=start_date, length=dshape[0], freq=freq) else: @@ -688,7 +691,55 @@ for attr in attrlist: if not attr in exclude: setattr(self, attr, getattr(oldseries, attr)) + #...................................................... + # Pickling + def __getstate__(self): + "Returns the internal state of the TimeSeries, for pickling purposes." + # raise NotImplementedError,"Please use timeseries.archive/unarchive instead.""" + state = (1, + self.shape, + self.dtype, + self.flags.fnc, + self._data.tostring(), + getmaskarray(self).tostring(), + self._fill_value, + self._dates.shape, + numeric.asarray(self._dates).tostring(), + self.freq, + ) + return state + # + def __setstate__(self, state): + """Restores the internal state of the TimeSeries, 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, dsh, dtm, frq) = state + super(TimeSeries, self).__setstate__((ver, shp, typ, isf, raw, msk, flv)) + self._dates.__setstate__((dsh, dtype(int_), isf, dtm)) + self._dates.freq = frq +# + def __reduce__(self): + """Returns a 3-tuple for pickling a MaskedArray.""" + return (_tsreconstruct, + (self.__class__, self._baseclass, + self.shape, self._dates.shape, self.dtype, self._fill_value), + self.__getstate__()) + +def _tsreconstruct(genclass, baseclass, baseshape, dateshape, basetype, fill_value): + """Internal function that builds a new TimeSeries from the information stored + in a pickle.""" + # raise NotImplementedError,"Please use timeseries.archive/unarchive instead.""" + _series = ndarray.__new__(baseclass, baseshape, basetype) + _dates = ndarray.__new__(DateArray, dateshape, int_) + _mask = ndarray.__new__(ndarray, baseshape, bool_) + return genclass.__new__(genclass, _series, dates=_dates, mask=_mask, + dtype=basetype, fill_value=fill_value) def _attrib_dict(series, exclude=[]): """this function is used for passing through attributes of one @@ -828,61 +879,7 @@ #####--------------------------------------------------------------------------- #---- --- Archiving --- #####--------------------------------------------------------------------------- -def _tsreconstruct(baseclass, datesclass, baseshape, basetype, fill_value): - """Internal function that builds a new TimeSeries from the information stored -in a pickle.""" -# raise NotImplementedError,"Please use timeseries.archive/unarchive instead.""" - _series = ndarray.__new__(ndarray, baseshape, basetype) - _dates = ndarray.__new__(datesclass, baseshape, int_) - _mask = ndarray.__new__(ndarray, baseshape, bool_) - return baseclass.__new__(baseclass, _series, dates=_dates, mask=_mask, - dtype=basetype, fill_value=fill_value) -# -def _tsgetstate(a): - "Returns the internal state of the TimeSeries, for pickling purposes." -# raise NotImplementedError,"Please use timeseries.archive/unarchive instead.""" - records = a.asrecords() - state = (1, - a.shape, - a.dtype, - a.freq, - records.flags.fnc, - a.fill_value, - records - ) - return state -# -def _tssetstate(a, state): - """Restores the internal state of the TimeSeries, 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, frq, isf, flv, rec) = state - a.fill_value = flv - a._dates = a._dates.__class__(rec['_dates'], freq=frq) - (a._dates).__tostr = None - _data = rec['_series'].view(typ) - _mask = rec['_mask'].view(MA.MaskType) - a._series = masked_array(_data, mask=_mask) -# a._data.shape = shp -# a._dates.shape = shp -# a._mask = rec['_mask'].view(MA.MaskType) -# a._mask.shape = shp -# -def _tsreduce(a): - """Returns a 3-tuple for pickling a MaskedArray.""" - return (_tsreconstruct, - (a.__class__, a.dates.__class__, (0,), 'b', -9999), - a.__getstate__()) -# -TimeSeries.__getstate__ = _tsgetstate -TimeSeries.__setstate__ = _tssetstate -TimeSeries.__reduce__ = _tsreduce #TimeSeries.__dump__ = dump #TimeSeries.__dumps__ = dumps @@ -1413,18 +1410,25 @@ pass assert_equal(ser3d.transpose(0,2,1).shape, (5,2,3)) - if 1: - data = dates - - series = time_series(data, dates) - assert(isinstance(series, TimeSeries)) - assert_equal(series._dates, dates) - assert_equal(series._data, data) - assert_equal(series.freqstr, 'D') + if 1: + dlist = ['2007-01-%02i' % i for i in range(1,11)] + dates = date_array_fromlist(dlist) + data = masked_array(numeric.arange(10), mask=[1,0,0,0,0]*2, dtype=float_) + series = time_series(data, dlist) + # + import cPickle + series_pickled = cPickle.loads(series.dumps()) + assert_equal(series_pickled._dates, series._dates) + assert_equal(series_pickled._data, series._data) + assert_equal(series_pickled._mask, series._mask) + # + data = masked_array(N.matrix(range(10)).T, mask=[1,0,0,0,0]*2) + dates = date_array(start_date=thisday('D'), length=10) + series = time_series(data,dates=dates) + series_pickled = cPickle.loads(series.dumps()) + assert_equal(series_pickled._dates, series._dates) + assert_equal(series_pickled._data, series._data) + assert_equal(series_pickled._mask, series._mask) + assert(isinstance(series_pickled._data, N.matrix)) - series[5] = MA.masked - - # ensure that series can be represented by a string after masking a value - # (there was a bug before that prevented this from working when using a - # DateArray for the data) - strrep = str(series) + \ No newline at end of file