[Scipy-svn] r2481 - trunk/Lib/sandbox/timeseries/mtimeseries
scipy-svn at scipy.org
scipy-svn at scipy.org
Wed Jan 3 02:12:13 CST 2007
Author: pierregm
Date: 2007-01-03 02:12:10 -0600 (Wed, 03 Jan 2007)
New Revision: 2481
Added:
trunk/Lib/sandbox/timeseries/mtimeseries/CHANGELOG
Removed:
trunk/Lib/sandbox/timeseries/mtimeseries/timeseries_ini.py
Modified:
trunk/Lib/sandbox/timeseries/mtimeseries/test_timeseries.py
trunk/Lib/sandbox/timeseries/mtimeseries/tscore.py
trunk/Lib/sandbox/timeseries/mtimeseries/tsdate.py
trunk/Lib/sandbox/timeseries/mtimeseries/tseries.py
Log:
cf changelog
Added: trunk/Lib/sandbox/timeseries/mtimeseries/CHANGELOG
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/CHANGELOG 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/CHANGELOG 2007-01-03 08:12:10 UTC (rev 2481)
@@ -0,0 +1,6 @@
+#2007-01-03 : tseries
+# : - Allowed endpoints adjustment after convert
+# : - Put the estimation of the data length in its own function.
+# : - Added a timestep compatibility check.
+# : - The variables in a multi-series correspond now to the last axis.
+# : - Blocked transpose/swapaxes, temporarily.
\ No newline at end of file
Modified: trunk/Lib/sandbox/timeseries/mtimeseries/test_timeseries.py
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/test_timeseries.py 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/test_timeseries.py 2007-01-03 08:12:10 UTC (rev 2481)
@@ -266,6 +266,14 @@
mask = mask_period(tseries, start, end, inside=False, include_edges=False,
inplace=False)
assert_equal(mask._mask, [1,1,1,1,1,0,0,0,0,0,0,0,1,1,1])
+ #
+ def pickling(self):
+ "Tests pickling/unpickling"
+ (tseries, data, dates) = self.d
+ tmp = maskedarray.loads(tseries.dumps())
+ assert_equal(tmp._data, tseries._data)
+ assert_equal(tmp._dates, tseries._dates)
+ assert_equal(tmp._mask, tseries._mask)
###############################################################################
#------------------------------------------------------------------------------
Deleted: trunk/Lib/sandbox/timeseries/mtimeseries/timeseries_ini.py
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/timeseries_ini.py 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/timeseries_ini.py 2007-01-03 08:12:10 UTC (rev 2481)
@@ -1,1494 +0,0 @@
-# pylint: disable-msg=W0201, W0212
-"""
-Core classes for time/date related arrays.
-
-The `DateArray` class provides a base for the creation of date-based objects,
-using days as the base units. This class could be adapted easily to objects
-with a smaller temporal resolution (for example, using one hour, one second as the
-base unit).
-
-The `TimeSeries` class provides a base for the definition of time series.
-A time series is defined here as the combination of two arrays:
-
- - an array storing the time information (as a `DateArray` instance);
- - an array storing the data (as a `MaskedArray` instance.
-
-These two classes were liberally adapted from `MaskedArray` class.
-
-
-:author: Pierre GF Gerard-Marchant
-:contact: pierregm_at_uga_edu
-:date: $Date: 2006-12-05 20:40:46 -0500 (Tue, 05 Dec 2006) $
-:version: $Id: timeseries.py 25 2006-12-06 01:40:46Z backtopop $
-"""
-__author__ = "Pierre GF Gerard-Marchant ($Author: backtopop $)"
-__version__ = '1.0'
-__revision__ = "$Revision: 25 $"
-__date__ = '$Date: 2006-12-05 20:40:46 -0500 (Tue, 05 Dec 2006) $'
-
-
-#import numpy as N
-
-#from numpy.core import bool_, float_, int_
-import numpy.core.umath as umath
-import numpy.core.fromnumeric as fromnumeric
-import numpy.core.numeric as numeric
-from numpy import ndarray
-from numpy.core.records import recarray
-from numpy.core.records import fromarrays as recfromarrays
-
-#from cPickle import dump, dumps
-
-import core
-reload(core)
-from core import *
-
-
-import maskedarray as MA
-#reload(MA)
-from maskedarray.core import domain_check_interval, \
- domain_greater_equal, domain_greater, domain_safe_divide, domain_tan
-from maskedarray.core import MaskedArray, MAError, masked_array, isMaskedArray,\
- getmask, getmaskarray, filled, mask_or, make_mask
-from maskedarray.core import convert_typecode, masked_print_option, \
- masked_singleton
-from maskedarray.core import load, loads, dump, dumps
-
-import addons.numpyaddons
-reload(addons.numpyaddons)
-from addons.numpyaddons import ascondition
-
-#...............................................................................
-talog = logging.getLogger('TimeArray')
-tslog = logging.getLogger('TimeSeries')
-mtslog = logging.getLogger('MaskedTimeSeries')
-
-nomask = MA.nomask
-masked = MA.masked
-masked_array = MA.masked_array
-ufunc_domain = {}
-ufunc_fills = {}
-
-
-#### --------------------------------------------------------------------------
-#--- ... Date Tools ...
-#### --------------------------------------------------------------------------
-dtmdefault = dtm.datetime(2001,1,1)
-
-def isDate(obj):
- """Returns *True* if the argument is a `datetime.date` or `datetime.datetime`
-instance."""
- return isinstance(obj,dtm.date) or isinstance(obj,dtm.datetime)
-
-def fake_dates(count, start=dtmdefault):
- """fake_dates(count, start=dtmdefault)
-Returns a *count x 1* array of daily `datetime` objects.
-:Parameters:
- - `count` (Integer) : Number of dates to output
- - `start` (datetime object) : Starting date *[NOW]*."""
- dobj = list(dtmrule.rrule(DAILY,count=count,dtstart=start))
- return N.array(dobj)
-
-#### --------------------------------------------------------------------------
-#--- ... TimeArray class ...
-#### --------------------------------------------------------------------------
-class TimeArrayError(Exception):
- """Defines a generic DateArrayError."""
- def __init__ (self, args=None):
- "Create an exception"
- Exception.__init__(self)
- self.args = args
- def __str__(self):
- "Calculate the string representation"
- return str(self.args)
- __repr__ = __str__
-
-class TimeArray(ndarray):
- """Stores datetime.dateime objects in an array."""
- def __new__(subtype, series, copy=False):
- vlev = 3
- if isinstance(series, TimeArray):
-# verbose.report("DA __new__: data isDA types %s, %s, %i" % \
-# (type(series), series, len(series)), vlev)
- if not copy:
- return series
- else:
- return series.copy()
- else:
- data = N.array(series)
- if data.dtype.str == "|O8":
- new = data #.view(subtype)
- talog.debug("__new__: data isnotDA types %s, %s, %s, %i" % \
- (type(series), data.dtype, series, data.size),)
- else:
- new = N.empty(data.shape, dtype="|O8")
- newflat = new.flat
- talog.debug("DA __new__: data isnotDA types %s, %s, %s, %i" % \
- (type(series), data.dtype, series, data.size),)
- # Forces years (as integers) to be read as characters
- if N.all(data < 9999):
- data = data.astype("|S4")
- # Parse dates as characters
- if data.dtype.char == "S":
- for (k,s) in enumerate(data.flat):
- newflat[k] = dtmparser.parse(s, default=dtmdefault)
- else:
- # Parse dates as ordinals
- try:
- for k,s in enumerate(data.flat):
- newflat[k] = dtm.datetime.fromordinal(s)
- except TypeError:
- raise TypeError, "unable to process series !"
- subtype._asobject = new
- return new.view(subtype)
-# #............................................
-# def __array_wrap__(self, obj):
-# return TimeArray(obj)
- #............................................
- def __array_finalize__(self, obj):
- self._resolution = None
- (self._years, self._months, self._days, self._yeardays) = [None]*4
- self._asobjects = None
- self._asstrings = None
- self._asordinals = None
- return
- #............................................
- def __len__(self):
- "Returns the length of the object. Or zero if it fails."
- if self.ndim == 0:
- return 0
- return ndarray.__len__(self)
-
-
- def __getitem__(self,i):
- # Force a singleton to DateArray
- try:
- obj = ndarray.__getitem__(self,i)
- except IndexError:
- obj = self
- return TimeArray(obj)
-# if isDate(obj):
-# return DateArray(obj)
-# else:
-# return obj
- #............................................
- def __eq__(self, other):
- return N.asarray(self).__eq__(N.asarray(other))
-
- def __add__(self, other):
- if not isinstance(other, dtm.timedelta):
- raise TypeError, "Unsupported type for add operation"
- return TimeArray(N.asarray(self).__add__(other))
-
- def __sub__(self, other):
- if not isinstance(other, dtm.timedelta) and \
- not isinstance(other, TimeArray):
- raise TypeError, "Unsupported type for sub operation"
- return N.asarray(self).__sub__(N.asarray(other))
-
- def __mul__(self, other):
- raise TimeArrayError, "TimeArray objects cannot be multiplied!"
- __rmul__ = __mul__
- __imul__ = __mul__
-# def shape(self):
-# if self.size == 1:
-# return (1,)
-# else:
-# return self._data.shape
- #............................................
- def __str__(self):
- """x.__str__() <=> str(x)"""
- return str(self.asstrings())
- def __repr__(self):
- """Calculate the repr representation, using masked for fill if
- it is enabled. Otherwise fill with fill value.
- """
- template = """\
-timearray(
- %(data)s,)"""
- template_short = """\
-timearray(%(data)s)"""
- if self.ndim <= 1:
- return template_short % {'data': str(self)}
- else:
- return template % {'data': str(self)}
- #............................................
- @property
- def resolution(self):
- """Calculates the initial resolution, as the smallest difference
-between consecutive values (in days)."""
- if self._resolution is None:
- self._resolution = N.diff(self.asordinals().ravel()).min()
- if self._resolution <= 3:
- self._resolution = 1
- elif self._resolution <= 10:
- self._resolution = 7
- elif self._resolution <= 270:
- self._resolution = 30
- else:
- self._resolution = 365
- return self._resolution
- timestep = resolution
- #............................................
- def has_missing_dates(self, resolution=None):
- """Returns *True* there's a gap in the series, assuming a regular series
-with a constant timestep of `resolution`.
-If `resolution` is None, uses the default resolution of `self`.
-"""
- if self.size < 2:
- return False
- dt = N.diff(self.asordinals().ravel())
- if resolution is None:
- resolution = self.resolution
- if resolution == 1:
- return (dt.max() > 1)
- elif resolution == 30:
- return (dt.max() > 31)
- elif resolution == 365:
- return (dt.max() > 730)
- else:
- #FIXME: OK, there's probly something wrong here...
- return True
- #............................................
- def asobjects(self):
- """Transforms an array of ordinal dates to an array of date objects."""
- if self._asobjects is None:
- if self.size == 1:
- self._asobjects = self.item()
- else:
- self._asobjects = self
- return self._asobjects
- #............................................
- def asordinals(self):
- """Transforms an array of dates to an array of date objects."""
- if self._asordinals is None:
- # Build list of datetime objects
- self._asordinals = N.empty(self.shape,dtype=int_)
- _asordinalsflat = self._asordinals.flat
- if self.size == 0:
- self._asordinals = dtm.datetime.toordinal(dtmdefault)
- elif self.size == 1:
- self._asordinals = dtm.datetime.toordinal(self.item())
- else:
- itr = (dtm.datetime.toordinal(val) for val in self.flat)
- self._asordinals = N.fromiter(itr, float_)
- return self._asordinals
- #............................................
- def asstrings(self, stringsize=10):
- """Transforms a *N*-array of ordinal dates to a *N*-list of
-datestrings `YYYY-MM-DD`.
-
-:Parameters:
- `stringsize` : Integer *[10]*
- String size.
-
- - `< 4' outputs 4
- - `< 8' outputs 8
- - anything else outputs 10.
- """
- if not stringsize:
- strsize = 10
- elif stringsize <= 4:
- strsize = 4
- elif stringsize <= 8:
- strsize = 7
- else:
- strsize = 10
- if self._asstrings is None:
- deftype = "|S10"
- if self.size == 0:
- self._asstrings = N.array(dtmdefault.isoformat(), dtype=deftype)
- elif self.size == 1:
- self._asstrings = N.array(self.item().isoformat(), dtype=deftype)
- else:
- itr = (val.isoformat() for val in self.flat)
- self._asstrings = N.fromiter(itr,dtype=deftype).reshape(self.shape)
- return self._asstrings.getfield('S%i' % strsize)
- #............................................
- def astype(self, newtype):
- """Outputs the array in the type provided in argument."""
- newtype = N.dtype(newtype)
- if newtype.type == int_:
- return self.asordinals()
- elif newtype.type == str_:
- n = newtype.itemsize
- if n > 0 :
- return self.asstrings(stringsize=n)
- else:
- return self.asstrings(stringsize=n)
- elif newtype.type == object_:
- return self
- else:
- raise ValueError, "Invalid type: %s" % newtype
- #------------------------------------------------------
- @property
- def years(self):
- """Returns the corresponding year (as integer)."""
- if self._years is None:
- self._years = self.asstrings(1).astype(int_)
- return self._years
- @property
- def months(self):
- """Returns the corresponding month (as integer)."""
- if self._months is None:
- self._months = N.empty(self.shape, dtype=int_)
- _monthsflat = self._months.flat
- if self.size == 1:
- try:
- _monthsflat[:] = self.asobjects().month
- except AttributeError:
- _monthsflat[:] = 1
- else:
- for (k,val) in enumerate(self.asobjects().flat):
- _monthsflat[k] = val.month
- return self._months
- @property
- def days(self):
- """Returns the corresponding day of month (as integer)."""
- if self._days is None:
- self._days = N.empty(self.shape, dtype=int_)
- _daysflat = self._days.flat
- if self.size == 1:
- try:
- _daysflat[:] = self.asobjects().day
- except AttributeError:
- _daysflat[:] = 1
- else:
- for (k,val) in enumerate(self.asobjects().flat):
- _daysflat[k] = val.day
- return self._days
- @property
- def yeardays(self):
- """Returns the corresponding day of year (as integer)."""
- if self._yeardays is None:
- self._yeardays = N.empty(self.size, dtype=int_)
- _doyflat = self._yeardays.flat
- for (k,val,yyy) in N.broadcast(N.arange(len(self)),
- self.asordinals(),
- self.years.flat):
- _doyflat[k] = val - dtm.datetime(yyy-1,12,31).toordinal()
- self._yeardays.reshape(self.shape).astype(int_)
- return self._yeardays
-#................................................
-def isTimeArray(x):
- """Checks whether `x` is n array of times/dates (an instance of `TimeArray` )."""
- return isinstance(x,TimeArray)
-
-def time_array(t, copy=False):
- """Creates a date array from the series `t`.
- """
- if isinstance(t,TimeArray):
- dobj = t
- else:
- t = N.asarray(t)
- if t.dtype.str == "|O8":
- dobj = t
- else:
- dobj = N.empty(t.shape, dtype="|O8")
- dobjflat = dobj.flat
- if t.dtype.char == 'S':
- for k,s in enumerate(t.flat):
- dobjflat[k] = dtmparser.parse(s)
- else:
- try:
- for k,s in enumerate(t.flat):
- dobjflat[k] = dtm.datetime.fromordinal(s)
- except TypeError:
- raise TypeError, "unable to process series !"
- return TimeArray(dobj, copy=copy)
-
-#### --------------------------------------------------------------------------
-#--- ... Time Series ...
-#### --------------------------------------------------------------------------
-class TimeSeriesError(Exception):
- "Class for TS related errors."
- def __init__ (self, args=None):
- "Creates an exception."
- Exception.__init__(self)
- self.args = args
- def __str__(self):
- "Calculates the string representation."
- return str(self.args)
- __repr__ = __str__
-#......................................
-#TODO: __eq__, __cmp__ classes
-class TimeSeries(ndarray, object):
- """Base class for the definition of time series.
-A time series is here defined as the combination of two arrays:
-
- - an array storing the time information (as a `TimeArray` instance);
- - an array storing the data (as a `MaskedArray` instance.
- """
- def __new__(cls, data, dates=None, dtype=None, copy=True):
- tslog.debug("__new__: data types %s, %s" % (type(data), dtype))
-# if isinstance(data, TimeSeries):
- if hasattr(data,"_series") and hasattr(data,"_dates"):
- tslog.debug("__new__: data has _series and _dates")
- tslog.debug("__new__: setting basedates",)
- cls._basedates = data._dates
- tslog.debug("__new__: setting basedates done",)
- if (not copy) and (dtype == data.dtype):
- return N.asarray(data).view(cls)
- else:
- return N.array(data, dtype=dtype).view(cls)
- #..........
- dsize = N.size(data)
- tslog.debug("__new__: args dates type %s, %s" % (type(dates), dates),)
- if dates is None:
- _dates = TimeArray(fake_dates(N.size(data)))
- tslog.debug("__new__: args dates FAKED type %s, %s, %i" % \
- (type(dates), _dates, _dates.size),)
- else:
- _dates = TimeArray(dates)
- tslog.debug("__new__: dates set to %s" % _dates,)
- if _dates.size != dsize:
- msg = "Incompatible sizes between dates (%i) and data (%i) !"
- raise ValueError, msg % (_dates.size, dsize)
- _dates.shape = N.shape(data)
- cls._basedates = _dates
- return N.array(data, copy=copy, dtype=dtype).view(cls)
- #..................................
- def __array_wrap__(self, obj):
- return TimeSeries(obj, dates=self._dates)
- #..................................
- def __array_finalize__(self,obj):
- if not hasattr(self,"_series"):
- try:
- self._series = obj._series
- tslog.debug("__array_finalize__: obj._data => : %s - %s" % \
- (id(self._series), self._series.ravel() ),)
- except AttributeError:
- self._series = obj
- tslog.debug("__array_finalize__: obj => : %s - %s" % \
- (id(self._series), self._series.ravel() ),)
- if not hasattr(self,"_dates"):
- self._dates = self._basedates
- tslog.debug("__array_finalize__: dates set! => : %s - %s" % \
- (id(self._dates), self._dates.ravel() ))
- return
- #........................
- def _get_flat(self):
- """Calculate the flat value.
- """
- return self.__class__(self._series.ravel(),
- dates = self._dates.ravel(), copy=False)
- #....
- def _set_flat (self, value):
- "x.flat = value"
- y = self.ravel()
- y[:] = value
- flat = property(fget=_get_flat,fset=_set_flat,doc='Access array in flat form.')
- #........................
- def _get_shape(self):
- "Return the current shape."
- return self._series.shape
- #....
- def _set_shape (self, newshape):
- "Set the array's shape."
- self._series.shape = self._dates.shape = newshape
- #....
- shape = property(fget=_get_shape, fset=_set_shape, doc="Array shape")
- #....
- @property
- def ndim(self):
- """Returns the number of dimensions."""
- return self._series.ndim
- @property
- def size(self):
- """Returns the total number of elements."""
- return self._series.size
- #........................
- def __getitem__(self, i):
- "Get item described by i. Not a copy as in previous versions."
- (tout, dout) = (self._dates[i], self._series[i])
- return self.__class__(dout, dates=tout)
- #....
- def __setitem__(self, index, value):
- "Gets item described by i. Not a copy as in previous versions."
- self._series[index] = value
- return self
- #........................
- def __getslice__(self, i, j):
- "Gets slice described by i, j"
- (tout, dout) = (self._dates[i:j], self._series[i:j])
- return self.__class__(dout, dates=tout,)
- #....
- def __setslice__(self, i, j, value):
- "Gets item described by i. Not a copy as in previous versions."
- #TODO: Here, we should have a better test for self._dates==value._dates
- if hasattr(value,"_dates"):
- tslog.debug("__setslice__: value: %s" % str(value))
- tslog.debug("__setslice__: dates: %s" % str(value._dates),)
- if not (self._dates == value._dates).all():
- raise TimeSeriesError,"Can't force a change of dates !"
- self._series[i:j] = value
- return self
- #........................
- def ravel (self):
- """Returns a 1-D view of self."""
- return self.__class__(self._series.ravel(),
- dates=self._dates.ravel())
- #........................
- 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)
- def __repr__(self):
- """Calculates the repr representation, using masked for fill if
- it is enabled. Otherwise fill with fill value.
- """
- desc = """\
-timeseries(data =
- %(data)s,
- dates =
- %(time)s, )
-"""
- desc_short = """\
-timeseries(data = %(data)s,
- dates = %(time)s,)
-"""
- if self.ndim <= 1:
- return desc_short % {
- 'data': str(self._series),
- 'time': str(self.dates),
- }
- return desc % {
- 'data': str(self._series),
- 'time': str(self.dates),
- }
-
- def ids (self):
- """Return the ids of the data, dates and mask areas"""
- return (id(self._series), id(self.dates),)
-
- @property
- def series(self):
- "Returnhs the series."
- return self._series
-
- #------------------------------------------------------
- @property
- def dates(self):
- """Returns the dates"""
- return self._dates
-### def _set_dates(self, object):
-### """Returns the dates"""
-### self._dates = object
-### dates = property(fget=_get_dates, fset=_set_dates, doc="Dates")
- @property
- def years(self):
- """Returns the corresponding years."""
- return self.dates.years
- @property
- def months(self):
- """Returns the corresponding months."""
- return self._dates.months
- @property
- def yeardays(self):
- """Returns the corresponding days of yuear."""
- return self._dates.yeardays
-
- def has_missing_dates(self):
- """Returns whether there's a date gap in the series."""
- return self._dates.has_missing_dates()
-
-#### --------------------------------------------------------------------------
-#--- ... Additional methods ...
-#### --------------------------------------------------------------------------
-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):
- self.obj = obj
- return self
- #
- def __call__ (self, other, *args):
- "Execute the call behavior."
- instance = self.obj
- _dates = instance._dates
- tslog.debug("_tsmathmethod: series: %s" % instance,)
- tslog.debug("_tsmathmethod: other : %s" % other,)
- func = getattr(instance._series, self.f)
- if hasattr(instance,"_dates") and hasattr(other,"_dates"):
- tslog.debug("_tsmathmethod: instance : %s" % instance,)
- tslog.debug("_tsmathmethod: other : %s" % other,)
- if N.any(instance._dates != other._dates):
- tslog.debug("_tsmathmethod %s on %s and %s" % \
- (self.f, instance, other),)
- raise TimeSeriesError, "Method %s not yet implemented !" % self.f
- 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__')
-#................................................
-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)
-#......................................
-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 original 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."
- func = getattr(self.obj._series, self._name)
- return func(*args, **params)
-#.......................................
-TimeSeries.astype = _tsarraymethod('astype')
-TimeSeries.reshape = _tsarraymethod('reshape', ondates=True)
-TimeSeries.transpose = _tsarraymethod('transpose', ondates=True)
-TimeSeries.swapaxes = _tsarraymethod('swapaxes', ondates=True)
-TimeSeries.copy = _tsarraymethod('copy', ondates=True)
-TimeSeries.compress = _tsarraymethod('compress', ondates=True)
-#
-TimeSeries.sum = _tsaxismethod('sum')
-TimeSeries.cumsum = _tsaxismethod('cumsum')
-TimeSeries.prod = _tsaxismethod('prod')
-TimeSeries.cumprod = _tsaxismethod('cumprod')
-TimeSeries.mean = _tsaxismethod('mean')
-TimeSeries.var = _tsaxismethod('var')
-TimeSeries.varu = _tsaxismethod('varu')
-TimeSeries.std = _tsaxismethod('std')
-TimeSeries.stdu = _tsaxismethod('stdu')
-
-
-#..............................................................................
-def concatenate(arrays, axis=0):
- """Concatenates a sequence of time series."""
- concatenate.__doc__ = N.concatenate.__doc__
- for a in arrays:
- if hasattr(a,"_dates"):
- raise TimeSeriesError, "Not yet implemented for TimeSeries !"
-
-#### ---------------------------------------------------------------------------
-#--- ... Pickling ...
-#### ---------------------------------------------------------------------------
-#FIXME: We're kinda stuck with forcing the mask to have the same shape as the data
-def _tsreconstruct(baseclass, datesclass, baseshape, basetype):
- """Internal function that builds a new MaskedArray from the information stored
-in a pickle."""
- _series = ndarray.__new__(ndarray, baseshape, basetype)
- _dates = ndarray.__new__(datesclass, baseshape, basetype)
- return TimeSeries.__new__(baseclass, _series, dates=_dates, dtype=basetype)
-
-def _tsgetstate(a):
- "Returns the internal state of the TimeSeries, for pickling purposes."
- #TODO: We should prolly go through a recarray here as well.
- state = (1,
- a.shape,
- a.dtype,
- a.flags.fnc,
- (a._series).__reduce__()[-1][-1],
- (a._dates).__reduce__()[-1][-1])
- 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, isf, raw, dti) = state
- (a._series).__setstate__((shp, typ, isf, raw))
- (a._dates).__setstate__((shp, N.dtype('|O8'), isf, dti))
- (a._dates)._asstrings = None
-
-def _tsreduce(a):
- """Returns a 3-tuple for pickling a MaskedArray."""
- return (_tsreconstruct,
- (a.__class__, a._dates.__class__, (0,), 'b', ),
- a.__getstate__())
-
-TimeSeries.__getstate__ = _tsgetstate
-TimeSeries.__setstate__ = _tssetstate
-TimeSeries.__reduce__ = _tsreduce
-TimeSeries.__dump__ = dump
-TimeSeries.__dumps__ = dumps
-
-#................................................
-def tofile(self, output, sep='\t', format='%s', format_dates=None):
- """Writes the TimeSeries to a file.
-
-:Parameters:
- - `output` (String) : Name or handle of the output file.
- - `sep` (String) : Column separator *['\t']*.
- - `format` (String) : Data format *['%s']*.
- """
- if not hasattr(output, 'writeline'):
- ofile = open(output,'w')
- else:
- ofile = output
- oformat = "%%s%s%s" % (sep,format)
- for (_dates,_data) in N.broadcast(self._dates.ravel().asstrings(),
- filled(self)):
- ofile.write('%s\n' % sep.join([oformat % (_dates, _data) ]))
- ofile.close()
-TimeSeries.tofile = tofile
-
-#### --------------------------------------------------------------------------
-#--- ... Shortcuts ...
-#### --------------------------------------------------------------------------
-def isTimeSeries(x):
- """Checks whether `x` is a time series (an instance of `TimeSeries` )."""
- return isinstance(x, TimeSeries)
-
-#### --------------------------------------------------------------------------
-#--- ... MaskedTimeSeries class ...
-#### --------------------------------------------------------------------------
-class MaskedTimeSeries(MaskedArray, TimeSeries):
- """Base class for the definition of time series.
-A time series is here defined as the combination of two arrays:
-
- - an array storing the time information (as a `TimeArray` instance);
- - an array storing the data (as a `MaskedArray` instance.
- """
- def __new__(cls, data, dates=None, mask=nomask,
- dtype=None, copy=True, fill_value=-9999):
- mtslog.log(5, "__new__: data types %s, %s" % (type(data), dtype))
-# if isinstance(data, TimeSeries):
- #....................
- if isinstance(data, TimeSeries):
- if isinstance(data, MaskedTimeSeries):
- _data = data._data
- else:
- _data = data
- _dates = data._dates
- _series = data._series
- mtslog.log(5, "__new__ from TS: data %i - %s - %s" % \
- (id(_data._series), type(_data._series), _data.ravel()))
- mtslog.log(5,"__new__ from TS: dates %i - %s - %s" % \
- (id(_dates), type(_dates), _dates.ravel()))
- elif isinstance(data, recarray):
- assert(data.dtype.names == ('_dates', '_series', '_mask'),
- "Invalid fields names (got %s)" % (data.dtype.names,))
- _dates = data['_dates']
- _series = data['_series']
- _mask = data['_mask']
- else:
- if hasattr(data, "_data"):
- _data = TimeSeries(data._data, dates=dates,
- dtype=dtype, copy=copy)
- else:
- _data = TimeSeries(data, dates=dates,
- dtype=dtype, copy=copy)
- _dates = _data._dates
- _series = _data._series
- mtslog.log(5,"__new__ from scratch: data %i - %s - %s" % \
- (id(_data._series), type(_data._series), _data.ravel()))
- mtslog.log(5,"__new__ from TS: dates %i - %s - %s" % \
- (id(_dates), type(_dates), _dates.ravel()))
- #.....................
- if mask is nomask:
- if hasattr(data, "_mask"):
- _mask = data._mask
- else:
- _mask = nomask
- else:
- _mask = make_mask(mask, copy=copy, flag=True)
- #....Check shapes compatibility
- if _mask is not nomask:
- (nd, nm) = (_data.size, _mask.size)
- if (nm != nd):
- if nm == 1:
- _mask = N.resize(_mask, _data.shape)
- elif nd == 1:
- _data = N.resize(_data, _mask.shape)
- else:
- msg = "Mask and data not compatible (size issues: %i & %i)."
- raise MAError, msg % (nm, nd)
- elif (_mask.shape != _data.shape):
- mtslog.log(5,"__new__ from scratch: force _mask shape %s > %s" % \
- (_mask.shape, _data.shape))
- _mask.shape = _data.shape
- #....
- cls._fill_value = fill_value
- cls._basemask = _mask
- cls._basedates = _dates
- cls._baseseries = _series
- return _data.view(cls)
-# return _series.view(cls)
- #..............
- def __array_wrap__(self, obj, context=None):
- """Special hook for ufuncs.
-Wraps the numpy array and sets the mask according to context.
- """
-# return MaskedArray.__array_wrap__(obj, context=None)
- return MaskedTimeSeries(obj, dates=self._dates, mask=self._mask,
- fill_value=self._fill_value)
-
- #..............
- def __array_finalize__(self,obj):
- mtslog.log(5, "__array_finalize__: obj is %s" % (type(obj), ))
- if not hasattr(self, "_data"):
- self._data = obj
- if not hasattr(self, "_dates"):
- self._dates = self._basedates
- mtslog.log(5, "__array_finalize__: set dates to: %s - %s" % \
- (id(self._dates), self._dates.ravel() ))
- if not hasattr(self, "_mask"):
- self._mask = self._basemask
- mtslog.log(5, "__array_finalize__: set mask to: %s - %s" % \
- (id(self._mask), self._mask.ravel() ))
- if not hasattr(self, "_series"):
- if hasattr(obj, "_series"):
- self._series = obj._series
- else:
- self._series = obj
- self.fill_value = self._fill_value
- return
-
- #------------------------------------------------------
-# def __mul__(self):
- #------------------------------------------------------
- def __str__(self):
- """Calculate the str representation, using masked for fill if
- it is enabled. Otherwise fill with fill value.
- """
- if masked_print_option.enabled():
- f = masked_print_option
- # XXX: Without the following special case masked
- # XXX: would print as "[--]", not "--". Can we avoid
- # XXX: checks for masked by choosing a different value
- # XXX: for the masked singleton? 2005-01-05 -- sasha
- 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
- res = (self._series).astype("|O8")
- res[self._mask] = f
- else:
- res = self.filled(self.fill_value)
- return str(res)
-
- def __repr__(self):
- """Calculate the repr representation, using masked for fill if
- it is enabled. Otherwise fill with fill value.
- """
- desc = """\
-timeseries(data =
- %(data)s,
- mask =
- %(mask)s,
- date =
- %(time)s, )
-"""
- desc_short = """\
-timeseries(data = %(data)s,
- mask = %(mask)s,
- date = %(time)s,)
-"""
-# if (self._mask is nomask) and (not self._mask.any()):
-# if self.ndim <= 1:
-# return without_mask1 % {'data':str(self.filled()),
-# 'time':str(self._dates.asstrings())}
-# return without_mask % {'data':str(self.filled()),
-# 'time':str(self._dates.asstrings())}
-# else:
- if self.ndim <= 1:
- return desc_short % {
- 'data': str(self),
- 'mask': str(self._mask),
- 'time': str(self.dates),
- }
- return desc % {
- 'data': str(self),
- 'mask': str(self._mask),
- 'time': str(self.dates),
- }
- #............................................
- def ids (self):
- """Return the ids of the data, dates and mask areas"""
- return (id(self._series), id(self.dates), id(self._mask))
- #............................................
- @property
- def maskedseries(self):
- """Returns a masked array of the series (dates are omitteed)."""
- return masked_array(self._series, mask=self._mask)
- _mseries = maskedseries
- #............................................
- def filled(self, fill_value=None):
- """A numeric array with masked values filled. If fill_value is None,
- use self.fill_value().
-
- If mask is nomask, copy data only if not contiguous.
- Result is always a contiguous, numeric array.
-# Is contiguous really necessary now?
- """
- (d, m) = (self._data, 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:
- return numeric.array(value)
- #
- result = d.copy()
- try:
- result.__setitem__(m, value)
- except (TypeError, AttributeError):
- #ok, can't put that value in here
- 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 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:
- return self._data.sum(axis, dtype=dtype)
- else:
- if axis is None:
- return self.filled(0).sum(None, dtype=dtype)
- return MaskedArray(self.filled(0).sum(axis, dtype=dtype),
- mask=self._mask.all(axis))
-
- 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:
- return self._data.cumsum(axis=axis, dtype=dtype)
- else:
- if axis is None:
- return self.filled(0).cumsum(None, dtype=dtype)
- return MaskedArray(self.filled(0).cumsum(axis=axis, dtype=dtype),
- mask=self._mask)
-
- 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:
- return self._data.prod(axis=axis, dtype=dtype)
- else:
- if axis is None:
- return self.filled(1).prod(None, dtype=dtype)
- return MaskedArray(self.filled(1).prod(axis=axis, dtype=dtype),
- mask=self._mask.all(axis))
- 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:
- return self._data.cumprod(axis=axis, dtype=dtype)
- else:
- if axis is None:
- return self.filled(1).cumprod(None, dtype=dtype)
- return MaskedArray(self.filled(1).cumprod(axis=axis, dtype=dtype),
- mask=self._mask)
-
- def mean(self, axis=None, dtype=None):
- """mean(a, axis=None, dtype=None)
-Returns the arithmetic mean.
-
-The mean is the sum of the elements divided by the number of elements.
- """
- if self._mask is nomask:
- return self._data.mean(axis=axis, dtype=dtype)
- else:
- sum = N.sum(self.filled(0), axis=axis, dtype=dtype)
- cnt = self.count(axis=axis)
- if axis is None:
- if self._mask.all(None):
- return masked
- else:
- return sum*1./cnt
- return MaskedArray(sum*1./cnt, mask=self._mask.all(axis))
-
- 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 - N.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:
- return MaskedArray(self._data.var(axis=axis, dtype=dtype),
- mask=nomask)
- else:
- cnt = self.count(axis=axis)
- anom = self.anom(axis=axis, dtype=dtype)
- anom *= anom
- dvar = anom.sum(axis)
- dvar /= cnt
- if axis is None:
- return dvar
- return MaskedArray(dvar, mask=mask_or(self._mask.all(axis), (cnt==1)))
-
- 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)).
- """
- var = self.var(axis,dtype)
- if axis is None:
- if var is masked:
- return masked
- else:
- return N.sqrt(var)
- return MaskedArray(N.sqrt(var._data), mask=var._mask)
-
- def varu(self, axis=None, dtype=None):
- """a.var(axis=None, dtype=None)
-Returns an unbiased estimate of the variance.
-
-Instead of dividing the sum of squared anomalies by n, the number of elements,
-this sum is divided by n-1.
- """
- cnt = self.count(axis=axis)
- anom = self.anom(axis=axis, dtype=dtype)
- anom *= anom
- var = anom.sum(axis)
- var /= (cnt-1)
- if axis is None:
- return var
- return MaskedArray(var, mask=mask_or(self._mask.all(axis), (cnt==1)))
-
- def stdu(self, axis=None, dtype=None):
- """a.var(axis=None, dtype=None)
-Returns an unbiased estimate of the standard deviation.
- """
- var = self.varu(axis,dtype)
- if axis is None:
- if var is masked:
- return masked
- else:
- return N.sqrt(var)
- return MaskedArray(N.sqrt(var._data), mask=var._mask)
- #............................................
- def asrecords(self):
- """Returns the masked time series as a recarray.
-Fields are `_dates`, `_data` and _`mask`.
- """
- desctype = [('_dates','|O8'), ('_series',self.dtype), ('_mask',N.bool_)]
- flat = self.ravel()
- if flat.size > 0:
- return recfromarrays([flat._dates, flat._series, getmaskarray(flat)],
- dtype=desctype,
- shape = (flat.size,),
- )
- else:
- return recfromarrays([[], [], []], dtype=desctype,
- shape = (flat.size,),
- )
-
-
-# def reshape (self, *s):
-# """This array reshaped to shape s"""
-# self._data = self._data.reshape(*s)
-# self._dates = self._dates.reshape(*s)
-# if self._mask is not nomask:
-# self._mask = self._mask.reshape(*s)
-# return self.view()
-#### --------------------------------------------------------------------------
-#--- ... Pickling ...
-#### --------------------------------------------------------------------------
-def _mtsreconstruct(baseclass, datesclass, baseshape, basetype, fill_value):
- """Internal function that builds a new MaskedArray 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, '|O8')
- _mask = ndarray.__new__(ndarray, baseshape, '|O8')
- return baseclass.__new__(baseclass, _series, dates=_dates, mask=_mask,
- dtype=basetype, fill_value=fill_value)
-#
-def _mtsgetstate(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,
- records.flags.fnc,
- a.fill_value,
- records
- )
- return state
-#
-def _mtssetstate(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, isf, flv, rec) = state
- a.fill_value = flv
- a._data._series = a._series = N.asarray(rec['_series'])
- a._data._series.shape = a._series.shape = shp
- a._data._dates = a._dates = a._dates.__class__(rec['_dates'])
- a._data._dates.shape = a._dates.shape = shp
- (a._dates)._asstrings = None
- a._mask = N.array(rec['_mask'], dtype=MA.MaskType)
- a._mask.shape = shp
-#
-def _mtsreduce(a):
- """Returns a 3-tuple for pickling a MaskedArray."""
- return (_mtsreconstruct,
- (a.__class__, a.dates.__class__, (0,), 'b', -9999),
- a.__getstate__())
-#
-MaskedTimeSeries.__getstate__ = _mtsgetstate
-MaskedTimeSeries.__setstate__ = _mtssetstate
-MaskedTimeSeries.__reduce__ = _mtsreduce
-MaskedTimeSeries.__dump__ = dump
-MaskedTimeSeries.__dumps__ = dumps
-
-##### -------------------------------------------------------------------------
-#---- --- TimeSeries creator ---
-##### -------------------------------------------------------------------------
-def time_series(data, dates=None, mask=nomask, copy=False, fill_value=None):
- """Creates a TimeSeries object
-
-:Parameters:
- `dates` : ndarray
- Array of dates.
- `data` :
- Array of data.
- """
- if isinstance(data, MaskedTimeSeries):
- if not copy:
- data._mask = mask_or(data._mask, mask)
- return data
- _data = data._data
- _mask = mask_or(data._mask, mask)
- _dates = _data.dates
- elif isinstance(data, TimeSeries):
- _data = data._series
- _mask = make_mask(mask)
- _dates = data.dates
- else:
- data = masked_array(data, copy=False)
- _data = data._data
- _mask = mask_or(data._mask, mask)
- if dates is None:
- _dates = fake_dates(data.size)
- else:
- _dates = time_array(dates)
- return MaskedTimeSeries(_data, dates=_dates, mask=_mask, copy=copy,
- fill_value=fill_value)
-
-
-#
-
-#### --------------------------------------------------------------------------
-#--- ... Additional functions ...
-#### --------------------------------------------------------------------------
-def check_dates(a,b):
- """Returns the array of dates from the two objects `a` or `b` (or None)."""
- if isTimeSeries(a):
- if isTimeSeries(b) and (a._dates == b._dates).all() is False:
- raise ValueError, "Incompatible dates !"
- return a._dates
- elif isTimeSeries(b):
- return b._dates
- else:
- return
-
-def parse_period(period):
- """Returns a TimeArray couple (starting date; ending date) from the arguments."""
-#### print "........DEBUG PARSE DATES: period %s is %s" % (period, type(period))
-# if isinstance(period,TimeArray) or isinstance(period,Dates):
-#### print "........DEBUG PARSE_PERIOD: OK"
- if isinstance(period,TimeArray):
- return (period[0],period[-1])
- elif hasattr(period,"__len__"):
- if not isinstance(period[0], TimeArray):
- tstart = TimeArray(period[0])
- else:
- tstart = period[0]
- if not isinstance(period[-1], TimeArray):
- tend = TimeArray(period[-1])
- else:
- tend = period[-1]
- return (tstart, tend)
- else:
- p = N.asarray(period)
- if N.all(p < 9999):
- p = N.array(period,dtype="|S4")
- p = time_array(p)
- return (p[0], p[-1])
-
-def where_period(period, dates, *choices):
- """Returns choices fro True/False, whether dates fall during a given period.
-If no choices are given, outputs the array indices for the dates falling in the
-period.
-
-:Parameters:
- `period` : Sequence
- Selection period, as a sequence (starting date, ending date).
- `dates` : TimeArray
- Array of dates.
- `choices` : *(optional)*
- Arrays to select from when the condition is True/False.
- """
- (tstart, tend) = parse_period(period)
- condition = ascondition((dates>=tstart)&(dates<=tend))
- condition = (dates>=tstart)&(dates<=tend)
- return N.where(condition, *choices)
-
-def masked_inside_period(data, period, dates=None):
- """Returns x as an array masked where dates fall inside the selection period,
-as well as where data are initially missing (masked)."""
- (tstart, tend) = parse_period(period)
- # Get dates ..................
- if hasattr(data, "_dates"):
- dates = data._dates
- elif dates is None:
- raise ValueError,"Undefined dates !"
- else:
- assert(N.size(dates)==N.size(data),
- "Inconsistent data and dates sizes!")
- # where_period yields True inside the period, when mask should yield False
- condition = ascondition(N.logical_and((dates>=tstart), (dates<=tend)))
- cm = filled(condition,True).reshape(data.shape)
- mask = mask_or(MA.getmaskarray(data), cm, copy=True)
- if isinstance(data, MaskedTimeSeries):
- return data.__class__(data._data, dates=dates, mask=mask, copy=True)
- if isinstance(data, TimeSeries):
- return MaskedTimeSeries(data, dates=dates, mask=mask, copy=True)
- else:
- return masked_array(data, mask=mask, copy=True)
-
-def masked_outside_period(data, period, dates=None):
- """Returns x as an array masked where dates fall outside the selection period,
-as well as where data are initially missing (masked)."""
- (tstart, tend) = parse_period(period)
- if hasattr(data, "_dates"):
- dates = data._dates
- elif dates is None:
- raise ValueError,"Undefined dates !"
- else:
- assert(N.size(dates)==N.size(data),
- "Inconsistent data and dates sizes!")
- #................
- condition = ascondition(N.logical_or((dates<tstart),(dates>tend)))
- cm = filled(condition,True).reshape(data.shape)
- mask = mask_or(MA.getmaskarray(data), cm, copy=True)
- if isinstance(data, MaskedTimeSeries):
- return data.__class__(data._data, dates=dates, mask=mask, copy=True)
- if isinstance(data, TimeSeries):
- return MaskedTimeSeries(data, dates=dates, mask=mask, copy=True)
- else:
- return masked_array(data, mask=mask, copy=True)
-
-#..............................................................................
-def fill_missing_dates(dates,data,resolution=None,fill_value=None):
- """Finds and fills the missing dates in a time series, and allocates a
-default filling value to the data corresponding to the newly added dates.
-
-:Parameters:
- `dates`
- Initial array of dates.
- `data`
- Initial array of data.
- `resolution` : float *[None]*
- New date resolutions, in years. For example, a value of 1/365.25 indicates
- a daily resolution. If *None*, the initial resolution is used instead.
- `fill_value` : float
- Default value for missing data.
- """
- if not isinstance(dates, TimeArray):
- print "DEBUG FILL_MISSING_DATES: dates was %s" % type(dates)
- dates = TimeArray(dates)
- print "DEBUG FILL_MISSING_DATES: dates is %s" % type(dates)
- dflat = dates.ravel()
- n = len(dflat)
- # Get data ressolution .......
- if resolution is None:
- resolution = dflat.resolution
- if resolution >= 28 and resolution <= 31:
- resolution = 30
- else:
- resolution = int(1./float(resolution))
- # Find on what to fill .......
- if resolution == 1:
- (resol, freq, refdelta) = (DAILY, 1, dtmdelta.relativedelta(days=+1))
- elif resolution == 7:
- (resol, freq, refdelta) = (WEEKLY, 1, dtmdelta.relativedelta(days=+7))
- elif resolution == 30:
- (resol, freq, refdelta) = (MONTHLY, 1, dtmdelta.relativedelta(months=+1))
- elif resolution == 365:
- (resol, freq, refdelta) = (YEARLY, 1, dtmdelta.relativedelta(years=+1))
- else:
- raise ValueError,\
- "Unable to define a proper date resolution (found %s)." % resolution
- # ...and now, fill it ! ......
- (tstart, tend) = dflat.asobjects()[[0,-1]].tolist()
- gaprule = dtmrule.rrule(resol, interval=freq, dtstart=tstart, until=tend)
- newdates = dates.__class__(list(gaprule))
- #.............................
- # Get the steps between consecutive data. We need relativedelta to deal w/ months
- delta = N.array([dtmdelta.relativedelta(b,a)
- for (b,a) in N.broadcast(dflat[1:],dflat[:-1])])
- dOK = N.equal(delta,refdelta)
- slcid = N.r_[[0,], N.arange(1,n).compress(-dOK), [n,]]
- oldslc = N.array([slice(i,e) for (i,e) in N.broadcast(slcid[:-1],slcid[1:])])
- if resolution == 1:
- addidx = N.cumsum([d.days for d in N.diff(dflat).compress(-dOK)])
- elif resolution == 30:
- addidx = N.cumsum([d.years*12+d.months for d in delta.compress(-dOK)])
- elif resolution == 365:
- addidx = N.cumsum([d.years for d in delta.compress(-dOK)])
- addidx -= N.arange(len(addidx))
- newslc = N.r_[[oldslc[0]],
- [slice(i+d-1,e+d-1) for (i,e,d) in \
- N.broadcast(slcid[1:-1],slcid[2:],addidx)]
- ]
-# misslc = [slice(i,i+d-1)
-# for (i,d) in N.broadcast(slcid[1:-1],addidx)]
- #.............................
- # Just a quick check
- for (osl,nsl) in zip(oldslc,newslc):
- assert N.equal(dflat[osl],newdates[nsl]).all(),\
- "Slicing mishap ! Please check %s (old) and %s (new)" % (osl,nsl)
- #.............................
- data = MA.asarray(data)
- oldmask = MA.getmaskarray(data)
- newdata = N.empty(newdates.size,data.dtype)
- newmask = N.ones(newdates.size, 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)
- newdata.fill(fill_value)
- #....
- for (new,old) in zip(newslc,oldslc):
- newdata[new] = data[old]
- newmask[new] = oldmask[old]
-# for mis in misslc:
-# newdata[mis].fill(fill_value)
- # Get new shape ..............
- if data.ndim == 1:
- nshp = (newdates.size,)
- else:
- nshp = tuple([-1,] + list(data.shape[1:]))
- return MaskedTimeSeries(newdata.reshape(nshp),
- dates=newdates.reshape(nshp),
- mask=newmask.reshape(nshp),
- fill_value=fill_value)
-
-######--------------------------------------------------------------------------
-##---- --- Archiving ---
-######--------------------------------------------------------------------------
-#import iodata.iotools as iotools
-#def archive(timeseries,filename,compression=None):
-# data = timeseries.asrecords()
-# iotools.archive(data, filename, compression)
-#
-#def unarchive(filename):
-# raise NotImplementedError
-
-
-###############################################################################
\ No newline at end of file
Modified: trunk/Lib/sandbox/timeseries/mtimeseries/tscore.py
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/tscore.py 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/tscore.py 2007-01-03 08:12:10 UTC (rev 2481)
@@ -136,15 +136,13 @@
#####---------------------------------------------------------------------------
#---- --- Misc functions ---
#####---------------------------------------------------------------------------
-#def flatten(listOfLists):
-# return list(chain(*listOfLists))
#http://aspn.activestate.com/ASPN/Mail/Message/python-tutor/2302348
-def flatten(iterable):
+def flatten_sequence(iterable):
"""Flattens a compound of nested iterables."""
itm = iter(iterable)
for elm in itm:
if hasattr(elm,'__iter__') and not isinstance(elm, basestring):
- for f in flatten(elm):
+ for f in flatten_sequence(elm):
yield f
else:
yield elm
@@ -154,6 +152,6 @@
if not hasattr(args, '__iter__'):
return args
else:
- return flatten(args)
+ return flatten_sequence(args)
Modified: trunk/Lib/sandbox/timeseries/mtimeseries/tsdate.py
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/tsdate.py 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/tsdate.py 2007-01-03 08:12:10 UTC (rev 2481)
@@ -703,6 +703,22 @@
self.__tostr = tostr
return self.__tostr
#
+# def asfreq_ini(self, freq=None):
+# "Converts the dates to another frequency."
+# # Note: As we define a new object, we don't need caching
+# if freq is None:
+# return self
+# freq = corelib.fmtFreq(freq)
+# if freq == self.freq:
+# return self
+# if self.isvalid():
+# new = numeric.arange(self.size, dtype=int_)
+# new += self[0].asfreq(freq).value
+# else:
+# new = numpy.fromiter((d.asfreq(freq).value for d in self),
+# dtype=float_)
+# return DateArray(new, freq=freq)
+
def asfreq(self, freq=None):
"Converts the dates to another frequency."
# Note: As we define a new object, we don't need caching
@@ -711,12 +727,8 @@
freq = corelib.fmtFreq(freq)
if freq == self.freq:
return self
- if self.isvalid():
- new = numeric.arange(self.size, dtype=int_)
- new += self[0].asfreq(freq).value
- else:
- new = numpy.fromiter((d.asfreq(freq).value for d in self),
- dtype=float_)
+ new = numpy.fromiter((d.asfreq(freq).value for d in self),
+ dtype=float_)
return DateArray(new, freq=freq)
#......................................................
def find_dates(self, *dates):
@@ -1052,3 +1064,7 @@
myDateD = Date(freq='D',year=1985,month=10,day=4)
#------------------------------------------------
+ if 1:
+ dlist = ['2007-01-%02i' % i for i in range(1,15)]
+ dates = date_array_fromlist(dlist)
+ dates_2 = dates.asfreq('M')
\ No newline at end of file
Modified: trunk/Lib/sandbox/timeseries/mtimeseries/tseries.py
===================================================================
--- trunk/Lib/sandbox/timeseries/mtimeseries/tseries.py 2007-01-03 06:08:11 UTC (rev 2480)
+++ trunk/Lib/sandbox/timeseries/mtimeseries/tseries.py 2007-01-03 08:12:10 UTC (rev 2481)
@@ -119,9 +119,14 @@
if a.freq != b.freq:
raise TimeSeriesCompatibilityError('freq', a.freq, b.freq)
elif a.start_date() != b.start_date():
- raise TimeSeriesCompatibilityError('start_date', a.start_date(), b.start_date())
+ raise TimeSeriesCompatibilityError('start_date',
+ a.start_date(), b.start_date())
+ elif (a._dates.get_steps() != b._dates.get_steps()).any():
+ raise TimeSeriesCompatibilityError('time_steps',
+ a._dates.get_steps(), b._dates.get_steps())
elif a.shape != b.shape:
- raise TimeSeriesCompatibilityError('size', str(a.shape), str(b.shape))
+ raise TimeSeriesCompatibilityError('size', "1: %s" % str(a.shape),
+ "2: %s" % str(b.shape))
return True
def _datadatescompat(data,dates):
@@ -134,12 +139,19 @@
if dsize == tsize:
return True
elif data.ndim > 1:
- dsize = numeric.asarray(data.shape[1:]).prod()
+ dsize = numeric.asarray(data.shape)[:-1].prod()
if dsize == tsize:
return True
- raise TimeSeriesCompatibilityError('size', dsize, tsize)
-
+ raise TimeSeriesCompatibilityError('size', "data: %s" % dsize,
+ "dates: %s" % tsize)
+def _getdatalength(data):
+ "Estimates the length of a series (size/nb of variables)."
+ if numeric.ndim(data) >= 2:
+ return numeric.asarray(numeric.shape(data))[:-1].prod()
+ else:
+ return numeric.size(data)
+
##### --------------------------------------------------------------------------
##--- ... Time Series ...
##### --------------------------------------------------------------------------
@@ -196,10 +208,7 @@
else:
# Check dates ........
if dates is None:
- if numeric.ndim(data) >= 2:
- length = numeric.asarray(numeric.shape(data))[1:].prod()
- else:
- length = numeric.size(data)
+ length = _getdatalength(data)
newdates = date_array(start_date=start_date, length=length,
freq=freq)
elif not hasattr(dates, 'freq'):
@@ -606,10 +615,9 @@
dates=instance._dates)
#TimeSeries.astype = _tsarraymethod('astype')
TimeSeries.reshape = _tsarraymethod('reshape', ondates=True)
-TimeSeries.transpose = _tsarraymethod('transpose', ondates=True)
-TimeSeries.swapaxes = _tsarraymethod('swapaxes', 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)
@@ -640,7 +648,7 @@
else:
try:
axis = params.get('axis', args[0])
- if axis == 0:
+ if axis in [-1, _series.ndim-1]:
result = TimeSeries(result, dates=_dates)
except IndexError:
pass
@@ -654,6 +662,25 @@
TimeSeries.std = _tsaxismethod('std')
TimeSeries.stdu = _tsaxismethod('stdu')
+class _tsblockedmethods(object):
+ """Defines a wrapper for array methods that should be temporarily disabled.
+ """
+ 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):
+ raise NotImplementedError
+TimeSeries.transpose = _tsarraymethod('transpose', ondates=True)
+TimeSeries.swapaxes = _tsarraymethod('swapaxes', ondates=True)
+
+
#####---------------------------------------------------------------------------
#---- --- Definition of functions from the corresponding methods ---
#####---------------------------------------------------------------------------
@@ -697,7 +724,7 @@
##### ---------------------------------------------------------------------------
#---- ... Additional methods ...
##### ---------------------------------------------------------------------------
-def tofile(self, output, sep='\t', format='%s', format_dates=None):
+def tofile(self, output, sep='\t', format_dates=None):
"""Writes the TimeSeries to a file.
:Parameters:
@@ -718,328 +745,105 @@
ofile.close()
TimeSeries.tofile = tofile
-##### ---------------------------------------------------------------------------
-##--- ... Pickling ...
-##### ---------------------------------------------------------------------------
-##FIXME: We're kinda stuck with forcing the mask to have the same shape as the data
-#def _tsreconstruct(baseclass, datesclass, baseshape, basetype):
-# """Internal function that builds a new MaskedArray from the information stored
-#in a pickle."""
-# _series = ndarray.__new__(ndarray, baseshape, basetype)
-# _dates = ndarray.__new__(datesclass, baseshape, basetype)
-# return TimeSeries.__new__(baseclass, _series, dates=_dates, dtype=basetype)
-#
-#def _tsgetstate(a):
-# "Returns the internal state of the TimeSeries, for pickling purposes."
-# #TODO: We should prolly go through a recarray here as well.
-# state = (1,
-# a.shape,
-# a.dtype,
-# a.flags.fnc,
-# (a._series).__reduce__()[-1][-1],
-# (a._dates).__reduce__()[-1][-1])
-# return state
+#............................................
+def asrecords(series):
+ """Returns the masked time series as a recarray.
+Fields are `_dates`, `_data` and _`mask`.
+ """
+ desctype = [('_dates',int_), ('_series',series.dtype), ('_mask', bool_)]
+ flat = series.ravel()
+ _dates = numeric.asarray(flat._dates)
+ if flat.size > 0:
+ return recfromarrays([_dates, flat._data, getmaskarray(flat)],
+ dtype=desctype,
+ shape = (flat.size,),
+ )
+ else:
+ return recfromarrays([[], [], []], dtype=desctype,
+ shape = (flat.size,),
+ )
+TimeSeries.asrecords = asrecords
+
+def flatten(series):
+ """Flattens a (multi-) time series to 1D series."""
+ shp_ini = series.shape
+ # Already flat time series....
+ if len(shp_ini) == 1:
+ return series
+ # Folded single time series ..
+ newdates = series._dates.ravel()
+ if series._dates.size == series._series.size:
+ newshape = (series._series.size,)
+ else:
+ newshape = (numeric.asarray(shp_ini[:-1]).prod(), shp_ini[-1])
+ newseries = series._series.reshape(newshape)
+ return time_series(newseries, newdates)
+TimeSeries.flatten = flatten
+
+
+
+#####---------------------------------------------------------------------------
+#---- --- 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 _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, isf, raw, dti) = state
-# (a._series).__setstate__((shp, typ, isf, raw))
-# (a._dates).__setstate__((shp, N.dtype('|O8'), isf, dti))
-# (a._dates)._asstrings = None
+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', ),
-# a.__getstate__())
-#
-#TimeSeries.__getstate__ = _tsgetstate
-#TimeSeries.__setstate__ = _tssetstate
-#TimeSeries.__reduce__ = _tsreduce
+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
-#
-##................................................
-#
-##### --------------------------------------------------------------------------
-##--- ... Shortcuts ...
-##### --------------------------------------------------------------------------
-#def isTimeSeries(x):
-# """Checks whether `x` is a time series (an instance of `TimeSeries` )."""
-# return isinstance(x, TimeSeries)
-#
-##### --------------------------------------------------------------------------
-##--- ... MaskedTimeSeries class ...
-##### --------------------------------------------------------------------------
-#class MaskedTimeSeries(MaskedArray, TimeSeries):
-# """Base class for the definition of time series.
-#A time series is here defined as the combination of two arrays:
-#
-# - an array storing the time information (as a `TimeArray` instance);
-# - an array storing the data (as a `MaskedArray` instance.
-# """
-# def __new__(cls, data, dates=None, mask=nomask,
-# dtype=None, copy=True, fill_value=-9999):
-# mtslog.log(5, "__new__: data types %s, %s" % (type(data), dtype))
-## if isinstance(data, TimeSeries):
-# #....................
-# if isinstance(data, TimeSeries):
-# if isinstance(data, MaskedTimeSeries):
-# _data = data._data
-# else:
-# _data = data
-# _dates = data._dates
-# _series = data._series
-# mtslog.log(5, "__new__ from TS: data %i - %s - %s" % \
-# (id(_data._series), type(_data._series), _data.ravel()))
-# mtslog.log(5,"__new__ from TS: dates %i - %s - %s" % \
-# (id(_dates), type(_dates), _dates.ravel()))
-# elif isinstance(data, recarray):
-# assert(data.dtype.names == ('_dates', '_series', '_mask'),
-# "Invalid fields names (got %s)" % (data.dtype.names,))
-# _dates = data['_dates']
-# _series = data['_series']
-# _mask = data['_mask']
-# else:
-# if hasattr(data, "_data"):
-# _data = TimeSeries(data._data, dates=dates,
-# dtype=dtype, copy=copy)
-# else:
-# _data = TimeSeries(data, dates=dates,
-# dtype=dtype, copy=copy)
-# _dates = _data._dates
-# _series = _data._series
-# mtslog.log(5,"__new__ from scratch: data %i - %s - %s" % \
-# (id(_data._series), type(_data._series), _data.ravel()))
-# mtslog.log(5,"__new__ from TS: dates %i - %s - %s" % \
-# (id(_dates), type(_dates), _dates.ravel()))
-# #.....................
-# if mask is nomask:
-# if hasattr(data, "_mask"):
-# _mask = data._mask
-# else:
-# _mask = nomask
-# else:
-# _mask = make_mask(mask, copy=copy, flag=True)
-# #....Check shapes compatibility
-# if _mask is not nomask:
-# (nd, nm) = (_data.size, _mask.size)
-# if (nm != nd):
-# if nm == 1:
-# _mask = N.resize(_mask, _data.shape)
-# elif nd == 1:
-# _data = N.resize(_data, _mask.shape)
-# else:
-# msg = "Mask and data not compatible (size issues: %i & %i)."
-# raise MAError, msg % (nm, nd)
-# elif (_mask.shape != _data.shape):
-# mtslog.log(5,"__new__ from scratch: force _mask shape %s > %s" % \
-# (_mask.shape, _data.shape))
-# _mask.shape = _data.shape
-# #....
-# cls._fill_value = fill_value
-# cls._basemask = _mask
-# cls._basedates = _dates
-# cls._baseseries = _series
-# return _data.view(cls)
-## return _series.view(cls)
-# #..............
-# def __array_wrap__(self, obj, context=None):
-# """Special hook for ufuncs.
-#Wraps the numpy array and sets the mask according to context.
-# """
-## return MaskedArray.__array_wrap__(obj, context=None)
-# return MaskedTimeSeries(obj, dates=self._dates, mask=self._mask,
-# fill_value=self._fill_value)
-#
-# #..............
-# def __array_finalize__(self,obj):
-# mtslog.log(5, "__array_finalize__: obj is %s" % (type(obj), ))
-# if not hasattr(self, "_data"):
-# self._data = obj
-# if not hasattr(self, "_dates"):
-# self._dates = self._basedates
-# mtslog.log(5, "__array_finalize__: set dates to: %s - %s" % \
-# (id(self._dates), self._dates.ravel() ))
-# if not hasattr(self, "_mask"):
-# self._mask = self._basemask
-# mtslog.log(5, "__array_finalize__: set mask to: %s - %s" % \
-# (id(self._mask), self._mask.ravel() ))
-# if not hasattr(self, "_series"):
-# if hasattr(obj, "_series"):
-# self._series = obj._series
-# else:
-# self._series = obj
-# self.fill_value = self._fill_value
-# return
-# #------------------------------------------------------
-# def __str__(self):
-# """Calculate the str representation, using masked for fill if
-# it is enabled. Otherwise fill with fill value.
-# """
-# if masked_print_option.enabled():
-# f = masked_print_option
-# # XXX: Without the following special case masked
-# # XXX: would print as "[--]", not "--". Can we avoid
-# # XXX: checks for masked by choosing a different value
-# # XXX: for the masked singleton? 2005-01-05 -- sasha
-# 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
-# res = (self._series).astype("|O8")
-# res[self._mask] = f
-# else:
-# res = self.filled(self.fill_value)
-# return str(res)
-# #............................................
-# def ids (self):
-# """Return the ids of the data, dates and mask areas"""
-# return (id(self._series), id(self.dates), id(self._mask))
-# #............................................
-# @property
-# def maskedseries(self):
-# """Returns a masked array of the series (dates are omitteed)."""
-# return masked_array(self._series, mask=self._mask)
-# _mseries = maskedseries
-# #............................................
-# def filled(self, fill_value=None):
-# """A numeric array with masked values filled. If fill_value is None,
-# use self.fill_value().
-#
-# If mask is nomask, copy data only if not contiguous.
-# Result is always a contiguous, numeric array.
-## Is contiguous really necessary now?
-# """
-# (d, m) = (self._data, 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:
-# return numeric.array(value)
-# #
-# result = d.copy()
-# try:
-# result.__setitem__(m, value)
-# except (TypeError, AttributeError):
-# #ok, can't put that value in here
-# 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 asrecords(self):
-# """Returns the masked time series as a recarray.
-#Fields are `_dates`, `_data` and _`mask`.
-# """
-# desctype = [('_dates','|O8'), ('_series',self.dtype), ('_mask',N.bool_)]
-# flat = self.ravel()
-# if flat.size > 0:
-# return recfromarrays([flat._dates, flat._series, getmaskarray(flat)],
-# dtype=desctype,
-# shape = (flat.size,),
-# )
-# else:
-# return recfromarrays([[], [], []], dtype=desctype,
-# shape = (flat.size,),
-# )
-#
-#
-## def reshape (self, *s):
-## """This array reshaped to shape s"""
-## self._data = self._data.reshape(*s)
-## self._dates = self._dates.reshape(*s)
-## if self._mask is not nomask:
-## self._mask = self._mask.reshape(*s)
-## return self.view()
-##### --------------------------------------------------------------------------
-##--- ... Pickling ...
-##### --------------------------------------------------------------------------
-#def _mtsreconstruct(baseclass, datesclass, baseshape, basetype, fill_value):
-# """Internal function that builds a new MaskedArray 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, '|O8')
-# _mask = ndarray.__new__(ndarray, baseshape, '|O8')
-# return baseclass.__new__(baseclass, _series, dates=_dates, mask=_mask,
-# dtype=basetype, fill_value=fill_value)
-##
-#def _mtsgetstate(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,
-# records.flags.fnc,
-# a.fill_value,
-# records
-# )
-# return state
-##
-#def _mtssetstate(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, isf, flv, rec) = state
-# a.fill_value = flv
-# a._data._series = a._series = N.asarray(rec['_series'])
-# a._data._series.shape = a._series.shape = shp
-# a._data._dates = a._dates = a._dates.__class__(rec['_dates'])
-# a._data._dates.shape = a._dates.shape = shp
-# (a._dates)._asstrings = None
-# a._mask = N.array(rec['_mask'], dtype=MA.MaskType)
-# a._mask.shape = shp
-##
-#def _mtsreduce(a):
-# """Returns a 3-tuple for pickling a MaskedArray."""
-# return (_mtsreconstruct,
-# (a.__class__, a.dates.__class__, (0,), 'b', -9999),
-# a.__getstate__())
-##
-#MaskedTimeSeries.__getstate__ = _mtsgetstate
-#MaskedTimeSeries.__setstate__ = _mtssetstate
-#MaskedTimeSeries.__reduce__ = _mtsreduce
-#MaskedTimeSeries.__dump__ = dump
-#MaskedTimeSeries.__dumps__ = dumps
-
-
##### -------------------------------------------------------------------------
#---- --- TimeSeries creator ---
##### -------------------------------------------------------------------------
@@ -1057,10 +861,7 @@
Array of data.
"""
if dates is None:
- if numeric.ndim(data) >= 2:
- length = numeric.asarray(numeric.shape(data))[1:].prod()
- else:
- length = numeric.size(data)
+ length = _getdatalength(data)
dates = date_array(start_date=start_date, end_date=end_date,
length=length, include_last=include_last, freq=freq)
elif not isinstance(dates, DateArray):
@@ -1078,82 +879,6 @@
##### --------------------------------------------------------------------------
##--- ... Additional functions ...
##### --------------------------------------------------------------------------
-#def check_dates(a,b):
-# """Returns the array of dates from the two objects `a` or `b` (or None)."""
-# if isTimeSeries(a):
-# if isTimeSeries(b) and (a._dates == b._dates).all() is False:
-# raise ValueError, "Incompatible dates !"
-# return a._dates
-# elif isTimeSeries(b):
-# return b._dates
-# else:
-# return
-#
-#def parse_period(period):
-# """Returns a TimeArray couple (starting date; ending date) from the arguments."""
-##### print "........DEBUG PARSE DATES: period %s is %s" % (period, type(period))
-## if isinstance(period,TimeArray) or isinstance(period,Dates):
-##### print "........DEBUG PARSE_PERIOD: OK"
-# if isinstance(period,TimeArray):
-# return (period[0],period[-1])
-# elif hasattr(period,"__len__"):
-# if not isinstance(period[0], TimeArray):
-# tstart = TimeArray(period[0])
-# else:
-# tstart = period[0]
-# if not isinstance(period[-1], TimeArray):
-# tend = TimeArray(period[-1])
-# else:
-# tend = period[-1]
-# return (tstart, tend)
-# else:
-# p = N.asarray(period)
-# if N.all(p < 9999):
-# p = N.array(period,dtype="|S4")
-# p = time_array(p)
-# return (p[0], p[-1])
-#
-#def where_period(period, dates, *choices):
-# """Returns choices fro True/False, whether dates fall during a given period.
-#If no choices are given, outputs the array indices for the dates falling in the
-#period.
-#
-#:Parameters:
-# `period` : Sequence
-# Selection period, as a sequence (starting date, ending date).
-# `dates` : TimeArray
-# Array of dates.
-# `choices` : *(optional)*
-# Arrays to select from when the condition is True/False.
-# """
-# (tstart, tend) = parse_period(period)
-# condition = ascondition((dates>=tstart)&(dates<=tend))
-# condition = (dates>=tstart)&(dates<=tend)
-# return N.where(condition, *choices)
-#
-#def masked_inside_period(data, period, dates=None):
-# """Returns x as an array masked where dates fall inside the selection period,
-#as well as where data are initially missing (masked)."""
-# (tstart, tend) = parse_period(period)
-# # Get dates ..................
-# if hasattr(data, "_dates"):
-# dates = data._dates
-# elif dates is None:
-# raise ValueError,"Undefined dates !"
-# else:
-# assert(N.size(dates)==N.size(data),
-# "Inconsistent data and dates sizes!")
-# # where_period yields True inside the period, when mask should yield False
-# condition = ascondition(N.logical_and((dates>=tstart), (dates<=tend)))
-# cm = filled(condition,True).reshape(data.shape)
-# mask = mask_or(MA.getmaskarray(data), cm, copy=True)
-# if isinstance(data, MaskedTimeSeries):
-# return data.__class__(data._data, dates=dates, mask=mask, copy=True)
-# if isinstance(data, TimeSeries):
-# return MaskedTimeSeries(data, dates=dates, mask=mask, copy=True)
-# else:
-# return masked_array(data, mask=mask, copy=True)
-#
def mask_period(data, start_date=None, end_date=None,
inside=True, include_edges=True, inplace=True):
"""Returns x as an array masked where dates fall outside the selection period,
@@ -1223,7 +948,7 @@
"""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 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,
@@ -1238,6 +963,9 @@
if not a.dates.isvalid():
raise TimeSeriesError, \
"Cannot adjust a series with missing or duplicated dates."
+ # Flatten the series if needed ..............
+ a = a.flatten()
+ shp_flat = a.shape
# Dates validity checks .,...................
msg = "%s should be a valid Date instance! (got %s instead)"
(dstart, dend) = a.dates[[0,-1]]
@@ -1248,7 +976,7 @@
if not isDateType(start_date):
raise TypeError, msg % ('start_date', type(start_date))
start_lag = start_date - dstart
-
+ #....
if end_date is None:
end_date = dend
end_lag = 0
@@ -1256,14 +984,15 @@
if not isDateType(end_date):
raise TypeError, msg % ('end_date', type(end_date))
end_lag = end_date - dend
+ # Check if the new range is included in the old one
if start_lag >= 0:
if end_lag == 0:
return a[start_lag:]
elif end_lag < 0:
return a[start_lag:end_lag]
-
+ # Create a new series .......................
newdates = date_array(start_date=start_date, end_date=end_date)
- newshape = list(a.shape)
+ newshape = list(shp_flat)
newshape[0] = len(newdates)
newshape = tuple(newshape)
@@ -1273,8 +1002,7 @@
end_date = min(end_date, dend) + 1
newseries[start_date:end_date] = a[start_date:end_date]
return newseries
-
-
+#..........................................................
def align_series(*series, **kwargs):
"""Aligns several TimeSeries, so that their starting and ending dates match.
Series are resized and filled with mased values accordingly.
@@ -1286,8 +1014,7 @@
dates respectively.
"""
if len(series) < 2:
- return series
-
+ return series
unique_freqs = numpy.unique([x.freq for x in series])
try:
common_freq = unique_freqs.item()
@@ -1365,23 +1092,16 @@
tempData = masked_array(_values, mask=_mask)
- if tempData.ndim == 2:
-# tempData = tempData.T
- if func is not None:
- tempData = MA.apply_along_axis(func, 1, tempData)
- else:
- tempData = tempData.T
-
- #startIndex = cseries.convert(start_date, fromFreq, toFreq)
+ if tempData.ndim == 2 and func is not None:
+ tempData = MA.apply_along_axis(func, -1, tempData)
+
newStart = series._dates[0].asfreq(toFreq, "BEFORE")
newEnd = series._dates[-1].asfreq(toFreq, "AFTER")
newseries = TimeSeries(tempData, freq=toFreq,
observed=series.observed,
start_date=newStart)
- return newseries
-# return adjust_endpoints(newseries, end_date=newEnd)
-# return (tempData, newStart, toFreq, newseries, _values, _mask)
+ return adjust_endpoints(newseries, end_date=newEnd)
def fill_missing_dates(data, dates=None, freq=None,fill_value=None):
@@ -1457,14 +1177,7 @@
return time_series(newdata.reshape(nshp), newdates)
-#######--------------------------------------------------------------------------
-###---- --- Archiving ---
-#######--------------------------------------------------------------------------
-
-
-
-
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
from maskedarray.testutils import assert_equal
@@ -1498,9 +1211,25 @@
data = masked_array(numeric.arange(15)-6, mask=[1,0,0,0,0]*3)
print "."*50+"\nseries"
tseries = time_series(data, dlist)
-
+
if 1:
- mser3 = time_series(MA.mr_[malg1._series, malg2._series].reshape(2,-1),
+ 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 1:
+ mser3 = time_series(MA.mr_[malg1._series, malg2._series].reshape(-1,2),
dates=malg1.dates)
More information about the Scipy-svn
mailing list