[Scipy-svn] r2808 - trunk/Lib/sandbox/timeseries
scipy-svn at scipy.org
scipy-svn at scipy.org
Fri Mar 2 03:48:46 EST 2007
Author: pierregm
Date: 2007-03-02 02:48:36 -0600 (Fri, 02 Mar 2007)
New Revision: 2808
Removed:
trunk/Lib/sandbox/timeseries/extras.py
trunk/Lib/sandbox/timeseries/tdates.py
Log:
extras : deleted, redispatched to addons/filters and addons/interpolate
Deleted: trunk/Lib/sandbox/timeseries/extras.py
===================================================================
--- trunk/Lib/sandbox/timeseries/extras.py 2007-03-02 08:48:04 UTC (rev 2807)
+++ trunk/Lib/sandbox/timeseries/extras.py 2007-03-02 08:48:36 UTC (rev 2808)
@@ -1,148 +0,0 @@
-"""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$
-"""
-__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)"
-__version__ = '1.0'
-__revision__ = "$Revision$"
-__date__ = '$Date$'
-
-__all__ = [
- '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, MaskedArray
-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
-
-
-#####---------------------------------------------------------------------------
-#---- --- 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
Deleted: trunk/Lib/sandbox/timeseries/tdates.py
===================================================================
--- trunk/Lib/sandbox/timeseries/tdates.py 2007-03-02 08:48:04 UTC (rev 2807)
+++ trunk/Lib/sandbox/timeseries/tdates.py 2007-03-02 08:48:36 UTC (rev 2808)
@@ -1,1064 +0,0 @@
-"""
-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 - mattknox_ca_at_hotmail_dot_com
-:version: $Id$
-"""
-__author__ = "Pierre GF Gerard-Marchant & Matt Knox ($Author$)"
-__version__ = '1.0'
-__revision__ = "$Revision$"
-__date__ = '$Date$'
-
-import datetime as dt
-
-import itertools
-import warnings
-import types
-
-
-import numpy
-from numpy import bool_, float_, int_, object_
-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
-
-try:
- from mx.DateTime import DateTimeType
-except ImportError:
- class DateTimeType: pass
-
-from parser import DateFromString, DateTimeFromString
-
-import tcore as corelib
-import cseries
-
-
-
-__all__ = [
-'Date', 'DateArray','isDate','isDateArray',
-'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',
-'period_break'
- ]
-
-
-#####---------------------------------------------------------------------------
-#---- --- Date Info ---
-#####---------------------------------------------------------------------------
-
-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 ---
-#####---------------------------------------------------------------------------
-class DateError(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 InsufficientDateError(DateError):
- """Defines the exception raised when there is not enough information
- to create a Date object."""
- def __init__(self, msg=None):
- if msg is None:
- msg = "Insufficient parameters given to create a date at the given frequency"
- DateError.__init__(self, msg)
-
-class FrequencyDateError(DateError):
- """Defines the exception raised when the frequencies are incompatible."""
- def __init__(self, msg, freql=None, freqr=None):
- msg += " : Incompatible frequencies!"
- if not (freql is None or freqr is None):
- msg += " (%s<>%s)" % (freql, freqr)
- DateError.__init__(self, msg)
-
-class ArithmeticDateError(DateError):
- """Defines the exception raised when dates are used in arithmetic expressions."""
- def __init__(self, msg=''):
- msg += " Cannot use dates for arithmetics!"
- DateError.__init__(self, msg)
-
-#####---------------------------------------------------------------------------
-#---- --- Date Class ---
-#####---------------------------------------------------------------------------
-
-class Date:
- """Defines a Date object, as the combination of a date and a frequency.
- Several options are available to construct a Date object explicitly:
-
- - Give appropriate values to the `year`, `month`, `day`, `quarter`, `hours`,
- `minutes`, `seconds` arguments.
-
- >>> td.Date(freq='Q',year=2004,quarter=3)
- >>> td.Date(freq='D',year=2001,month=1,day=1)
-
- - Use the `string` keyword. This method calls the `mx.DateTime.Parser`
- submodule, more information is available in its documentation.
-
- >>> ts.Date('D', '2007-01-01')
-
- - 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",
- 'B': "%d-%b-%Y",
- 'D': "%d-%b-%Y",
- 'U': "%d-%b-%Y",
- 'H': "%d-%b-%Y %H:00",
- 'T': "%d-%b-%Y %H:%M",
- 'S': "%d-%b-%Y %H:%M:%S"
- }
-
- def __init__(self, freq, value=None, string=None,
- year=None, month=None, day=None, quarter=None,
- hour=None, minute=None, second=None,
- datetime=None):
-
- if hasattr(freq, 'freq'):
- self.freq = corelib.check_freq(freq.freq)
- else:
- self.freq = corelib.check_freq(freq)
- self.freqstr = corelib.freq_tostr(self.freq)
-
-
- if value is not None:
- if isinstance(value, ndarray):
- value = int(value)
-
- if isinstance(value, str):
- 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':
- valtmp = (value - 1)//5
- self.datetime = dt.datetime.fromordinal(value + valtmp*2)
- elif self.freqstr in ['D','U']:
- self.datetime = dt.datetime.fromordinal(value)
- elif self.freqstr == 'H':
- self.datetime = hourlyOriginDate + dt.timedelta(hours=value)
- elif self.freqstr == 'M':
- year = (value - 1)//12 + 1
- month = value - (year - 1)*12
- self.datetime = dt.datetime(year, month, 1)
- elif self.freqstr == 'Q':
- year = (value - 1)//4 + 1
- month = (value - (year - 1)*4)*3
- self.datetime = dt.datetime(year, month, 1)
- elif self.freqstr == 'S':
- self.datetime = secondlyOriginDate + dt.timedelta(seconds=value)
- elif self.freqstr == 'T':
- self.datetime = minutelyOriginDate + dt.timedelta(minutes=value)
- elif self.freqstr == 'W':
- self.datetime = dt.datetime(1,1,7) + \
- dt.timedelta(days=(value-1)*7)
-
- elif string is not None:
- 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):
- datetime = mx_to_datetime(datetime)
- self.datetime = truncateDate(self.freq, datetime)
-
- else:
- # First, some basic checks.....
- if year is None:
- raise InsufficientDateError
- if self.freqstr in 'BDWU':
- if month is None or day is None:
- raise InsufficientDateError
- elif self.freqstr == 'M':
- if month is None:
- raise InsufficientDateError
- day = 1
- elif self.freqstr == 'Q':
- if quarter is None:
- raise InsufficientDateError
- month = quarter * 3
- day = 1
- elif self.freqstr == 'A':
- 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.datetime = truncateDate(self.freq, dt.datetime(year, month, day))
- if self.freqstr == 'B':
- if self.datetime.isoweekday() in [6,7]:
- raise ValueError("Weekend passed as business day")
- elif self.freqstr in 'HTS':
- if hour is None:
- if minute is None:
- if second is None:
- hour = 0
- else:
- hour = second//3600
- else:
- hour = minute // 60
- if minute is None:
- if second is None:
- minute = 0
- else:
- minute = (second-hour*3600)//60
- if second is None:
- second = 0
- else:
- second = second % 60
- self.datetime = truncateDate(self.freqstr,
- dt.datetime(year, month, day,
- hour, minute, second))
- self.value = self.__value()
-
- def __getitem__(self, indx):
- return self
-
- @property
- def day(self):
- "Returns the day of month."
- return self.__getdateinfo__('D')
- @property
- def day_of_week(self):
- "Returns the day of week."
- return self.__getdateinfo__('W')
- @property
- def day_of_year(self):
- "Returns the day of year."
- return self.__getdateinfo__('R')
- @property
- def month(self):
- "Returns the month."
- return self.__getdateinfo__('M')
- @property
- def quarter(self):
- "Returns the quarter."
- return self.__getdateinfo__('Q')
- @property
- def year(self):
- "Returns the year."
- return self.__getdateinfo__('Y')
- @property
- def second(self):
- "Returns the seconds."
- return self.__getdateinfo__('S')
- @property
- def minute(self):
- "Returns the minutes."
- return self.__getdateinfo__('T')
- @property
- def hour(self):
- "Returns the hour."
- return self.__getdateinfo__('H')
- @property
- def week(self):
- "Returns the week."
- return self.__getdateinfo__('I')
-
- 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):
- raise FrequencyDateError("Cannot add dates",
- self.freqstr, other.freqstr)
- return Date(freq=self.freq, value=int(self) + other)
-
- def __radd__(self, other):
- return self+other
-
- def __sub__(self, other):
- if isinstance(other, Date):
- if self.freq != other.freq:
- raise FrequencyDateError("Cannot subtract dates", \
- self.freqstr, other.freqstr)
- else:
- return int(self) - int(other)
- else:
- return self + (-1) * int(other)
-
- def __eq__(self, other):
- if not hasattr(other, 'freq'):
- return False
- elif self.freq != other.freq:
- raise FrequencyDateError("Cannot compare dates", \
- self.freqstr, other.freqstr)
- return int(self) == int(other)
-
- def __cmp__(self, other):
- if not hasattr(other, 'freq'):
- return False
- elif self.freq != other.freq:
- raise FrequencyDateError("Cannot compare dates", \
- self.freqstr, other.freqstr)
- return int(self)-int(other)
-
- def __hash__(self):
- return hash(int(self)) ^ hash(self.freq)
-
- def __int__(self):
- return self.value
-
- def __float__(self):
- return float(self.value)
-
- def __value(self):
- "Converts the date to an integer, depending on the current frequency."
- # Annual .......
- if self.freqstr == 'A':
- 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.datetime.toordinal()
- weeks = days // 7
- val = days - weeks*2
- # Daily/undefined
- elif self.freqstr in 'DU':
- val = self.datetime.toordinal()
- # Hourly........
- elif self.freqstr == 'H':
- 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':
- delta = (self.datetime - secondlyOriginDate)
- val = delta.days*86400 + delta.seconds
- return int(val)
- #......................................................
- def strfmt(self, fmt):
- "Formats the date"
- if fmt is None:
- fmt = self.default_fmtstr[self.freqstr]
- return cseries.strfmt(self.datetime, fmt)
-
- def __str__(self):
- return self.strfmt(self.default_fmtstr[self.freqstr])
-
- def __repr__(self):
- return "<%s : %s>" % (str(self.freqstr), str(self))
- #......................................................
- def toordinal(self):
- "Returns the date as an ordinal."
- return self.datetime.toordinal()
-
- def fromordinal(self, ordinal):
- "Returns the date as an ordinal."
- return Date(self.freq, datetime=dt.datetime.fromordinal(ordinal))
-
- def tostring(self):
- "Returns the date as a string."
- return str(self)
-
- def toobject(self):
- "Returns the date as itself."
- return self
-
- def isvalid(self):
- "Returns whether the DateArray is valid: no missing/duplicated dates."
- # A date is always valid by itself, but we need the object to support the function
- # when we're working with singletons.
- return True
- #......................................................
-
-
-#####---------------------------------------------------------------------------
-#---- --- Functions ---
-#####---------------------------------------------------------------------------
-
-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 dt.datetime(datetime.year, 1, 1)
- elif freqstr == 'Q':
- return dt.datetime(datetime.year, monthToQuarter(datetime.month)*3, 1)
- elif freqstr == 'M':
- return dt.datetime(datetime.year, datetime.month, 1)
- elif freqstr == 'W':
- d = datetime.toordinal()
- return dt.datetime.fromordinal(d + (7 - d % 7) % 7)
- elif freqstr in 'BD':
- if freq == 'B' and datetime.isoweekday() in (6,7):
- raise ValueError("Weekend passed as business day")
- return dt.datetime(datetime.year, datetime.month, datetime.day)
- elif freqstr == 'H':
- return dt.datetime(datetime.year, datetime.month, datetime.day, \
- datetime.hour)
- elif freqstr == 'T':
- return dt.datetime(datetime.year, datetime.month, datetime.day, \
- datetime.hour, datetime.minute)
- else:
- return datetime
-
-def monthToQuarter(monthNum):
- """Returns the quarter corresponding to the month `monthnum`.
- For example, December is the 4th quarter, Januray the first."""
- return int((monthNum-1)/3)+1
-
-def thisday(freq):
- "Returns today's date, at the given frequency `freq`."
- freqstr = corelib.check_freqstr(freq)
- tempDate = dt.datetime.now()
- # if it is Saturday or Sunday currently, freq==B, then we want to use Friday
- 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, datetime=tempDate)
- elif freqstr == 'M':
- return Date(freq, year=tempDate.year, month=tempDate.month)
- elif freqstr == 'Q':
- return Date(freq, year=tempDate.year, quarter=monthToQuarter(tempDate.month))
- elif freqstr == 'A':
- return Date(freq, year=tempDate.year)
-today = thisday
-
-def prevbusday(day_end_hour=18, day_end_min=0):
- "Returns the previous business day."
- tempDate = dt.datetime.now()
- dateNum = tempDate.hour + float(tempDate.minute)/60
- checkNum = day_end_hour + float(day_end_min)/60
- if dateNum < checkNum:
- return thisday('B') - 1
- else:
- return thisday('B')
-
-def asfreq(date, toFreq, relation="BEFORE"):
- """Returns a date converted to another frequency `toFreq`, according to the
- relation `relation` ."""
- tofreq = corelib.check_freq(toFreq)
- _rel = relation.upper()[0]
- if _rel not in ['B', 'A']:
- msg = "Invalid relation '%s': Should be in ['before', 'after']"
- raise ValueError, msg % relation
-
- if not isinstance(date, Date):
- raise DateError, "Date should be a valid Date instance!"
-
- if date.freqstr == 'U':
- warnings.warn("Undefined frequency: assuming daily!")
- fromfreq = corelib.freq_revdict['D']
- else:
- fromfreq = date.freq
-
- if fromfreq == tofreq:
- return date
- else:
- value = cseries.asfreq(numeric.asarray(date.value), fromfreq, tofreq, _rel)
- if value > 0:
- return Date(freq=tofreq, value=value)
- else:
- return None
-Date.asfreq = asfreq
-
-def isDate(data):
- "Returns whether `data` is an instance of Date."
- return isinstance(data, Date) or \
- (hasattr(data,'freq') and hasattr(data,'value'))
-
-
-#####---------------------------------------------------------------------------
-#---- --- DateArray ---
-#####---------------------------------------------------------------------------
-ufunc_dateOK = ['add','subtract',
- '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.
-
-When viewed globally (array-wise), DateArray is an array of integers.
-When viewed element-wise, DateArray is a sequence of dates.
-For example, a test such as :
->>> DateArray(...) = value
-will be valid only if value is an integer, not a Date
-However, a loop such as :
->>> for d in DateArray(...):
-accesses the array element by element. Therefore, `d` is a Date object.
- """
- _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:
- _freq = getattr(dates, 'freq', -9999)
- else:
- _freq = corelib.check_freq(freq)
- cls._defaultfreq = corelib.check_freq(_freq)
- # Get the dates ..........
- _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):
- if context is None:
- return self
- elif context[0].__name__ not in ufunc_dateOK:
- raise ArithmeticDateError, "(function %s)" % context[0].__name__
-
- def __array_finalize__(self, obj):
- 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):
- if isinstance(indx, Date):
- 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)
- if isinstance(r, (generic, int)):
- return Date(self.freq, value=r)
- 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:
- 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):
- 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 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')
- @property
- def day_of_week(self):
- "Returns the day of week."
- return self.__getdateinfo__('W')
- @property
- def day_of_year(self):
- "Returns the day of year."
- return self.__getdateinfo__('R')
- @property
- def month(self):
- "Returns the month."
- return self.__getdateinfo__('M')
- @property
- def quarter(self):
- "Returns the quarter."
- return self.__getdateinfo__('Q')
- @property
- def year(self):
- "Returns the year."
- return self.__getdateinfo__('Y')
- @property
- def second(self):
- "Returns the seconds."
- return self.__getdateinfo__('S')
- @property
- def minute(self):
- "Returns the minutes."
- return self.__getdateinfo__('T')
- @property
- def hour(self):
- "Returns the hour."
- return self.__getdateinfo__('H')
- @property
- def week(self):
- "Returns the week."
- return self.__getdateinfo__('I')
-
- days = day
- weekdays = day_of_week
- yeardays = day_of_year
- months = month
- quarters = quarter
- years = year
- seconds = second
- minutes = minute
- hours = hour
- weeks = week
-
- def __getdateinfo__(self, info):
- return numeric.asarray(cseries.getDateInfo(numeric.asarray(self),
- self.freq, info),
- dtype=int_)
- __getDateInfo = __getdateinfo__
- #.... Conversion methods ....................
- #
- def tovalue(self):
- "Converts the dates to integer values."
- return numeric.asarray(self)
- #
- def toordinal(self):
- "Converts the dates from values to ordinals."
- # Note: we better try to cache the result
- 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._cachedinfo['toord'] = toord
- return self._cachedinfo['toord']
- #
- def tostring(self):
- "Converts the dates to strings."
- # Note: we better cache the result
- if self._cachedinfo['tostr'] is None:
- firststr = str(self[0])
- if self.size > 0:
- ncharsize = len(firststr)
- tostr = numpy.fromiter((str(d) for d in self),
- dtype='|S%i' % ncharsize)
- else:
- tostr = firststr
- self._cachedinfo['tostr'] = tostr
- return self._cachedinfo['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 or freq == -9999:
- return self
- tofreq = corelib.check_freq(freq)
- if tofreq == self.freq:
- return self
- fromfreq = self.freq
- _rel = relation.upper()[0]
- new = cseries.asfreq(numeric.asarray(self), fromfreq, tofreq, _rel)
- return DateArray(new, freq=freq)
- #......................................................
- def find_dates(self, *dates):
- "Returns the indices corresponding to given dates, as an array."
- ifreq = self.freq
- c = numpy.zeros(self.shape, bool_)
- for d in corelib.flatargs(*dates):
- if d.freq != ifreq:
- d = d.asfreq(ifreq)
- c += (self == d.value)
- c = c.nonzero()
- if fromnumeric.size(c) == 0:
- raise IndexError, "Date out of bounds!"
- return c
-
- 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 IndexError, "Date out of bounds!"
- return index
- else:
- index_asarray = (self == date.value).nonzero()
- if fromnumeric.size(index_asarray) == 0:
- raise IndexError, "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 integers!")
- if self._cachedinfo['steps'] is None:
- _cached = self._cachedinfo
- val = numeric.asarray(self).ravel()
- if val.size > 1:
- steps = val[1:] - val[:-1]
- if _cached['full'] is None:
- _cached['full'] = (steps.max() == 1)
- if _cached['hasdups'] is None:
- _cached['hasdups'] = (steps.min() == 0)
- else:
- _cached['full'] = True
- _cached['hasdups'] = False
- steps = numeric.array([], dtype=int_)
- self._cachedinfo['steps'] = steps
- return self._cachedinfo['steps']
-
- def has_missing_dates(self):
- "Returns whether the DateArray have missing dates."
- if self._cachedinfo['full'] is None:
- steps = self.get_steps()
- return not(self._cachedinfo['full'])
-
- def isfull(self):
- "Returns whether the DateArray has no missing dates."
- if self._cachedinfo['full'] is None:
- steps = self.get_steps()
- return self._cachedinfo['full']
-
- def has_duplicated_dates(self):
- "Returns whether the DateArray has duplicated dates."
- if self._cachedinfo['hasdups'] is None:
- steps = self.get_steps()
- return self._cachedinfo['hasdups']
-
- def isvalid(self):
- "Returns whether the DateArray is valid: no missing/duplicated dates."
- return (self.isfull() and not self.has_duplicated_dates())
- #......................................................
-
-#............................
-
-
-#####---------------------------------------------------------------------------
-#---- --- DateArray functions ---
-#####---------------------------------------------------------------------------
-def isDateArray(a):
- "Tests whether an array is a DateArray object."
- return isinstance(a,DateArray)
-
-def guess_freq(dates):
- """Tries to estimate the frequency of a list of dates, by checking the steps
- between consecutive dates The steps should be in days.
- Returns a frequency code (alpha character)."""
- ddif = numeric.asarray(numpy.diff(dates))
- ddif.sort()
- if ddif[0] == ddif[-1] == 1.:
- fcode = 'D'
- elif (ddif[0] == 1.) and (ddif[-1] == 3.):
- fcode = 'B'
- elif (ddif[0] > 3.) and (ddif[-1] == 7.):
- fcode = 'W'
- elif (ddif[0] >= 28.) and (ddif[-1] <= 31.):
- fcode = 'M'
- elif (ddif[0] >= 90.) and (ddif[-1] <= 92.):
- fcode = 'Q'
- elif (ddif[0] >= 365.) and (ddif[-1] <= 366.):
- fcode = 'A'
- elif numpy.abs(24.*ddif[0] - 1) <= 1e-5 and \
- numpy.abs(24.*ddif[-1] - 1) <= 1e-5:
- fcode = 'H'
- elif numpy.abs(1440.*ddif[0] - 1) <= 1e-5 and \
- numpy.abs(1440.*ddif[-1] - 1) <= 1e-5:
- fcode = 'T'
- elif numpy.abs(86400.*ddif[0] - 1) <= 1e-5 and \
- numpy.abs(86400.*ddif[-1] - 1) <= 1e-5:
- fcode = 'S'
- else:
- warnings.warn("Unable to estimate the frequency! %.3f<>%.3f" %\
- (ddif[0], ddif[-1]))
- fcode = 'U'
- return fcode
-
-
-def _listparser(dlist, freq=None):
- "Constructs a DateArray from a list."
- dlist = numeric.asarray(dlist)
- dlist.sort()
- if dlist.ndim == 0:
- dlist.shape = (1,)
- # Case #1: dates as strings .................
- if dlist.dtype.kind == 'S':
- #...construct a list of ordinals
- ords = numpy.fromiter((DateTimeFromString(s).toordinal() for s in dlist),
- float_)
- ords += 1
- #...try to guess the frequency
- if freq is None:
- freq = guess_freq(ords)
- #...construct a list of dates
- dates = [Date(freq, string=s) for s in dlist]
- # Case #2: dates as numbers .................
- elif dlist.dtype.kind in 'if':
- #...hopefully, they are values
- if freq is None:
- freq = guess_freq(dlist)
- dates = dlist
- # Case #3: dates as objects .................
- elif dlist.dtype.kind == 'O':
- template = dlist[0]
- #...as Date objects
- if isinstance(template, Date):
- dates = numpy.fromiter((d.value for d in dlist), int_)
- #...as mx.DateTime objects
- elif hasattr(template,'absdays'):
- # no freq given: try to guess it from absdays
- if freq is None:
- ords = numpy.fromiter((s.absdays for s in dlist), float_)
- ords += 1
- freq = guess_freq(ords)
- 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, datetime=dt.datetime.fromordinal(a)) for a in ords]
- #
- result = DateArray(dates, freq)
- return result
-
-
-def date_array(dlist=None, start_date=None, end_date=None, length=None,
- include_last=True, freq=None):
- """Constructs a DateArray from:
- - a starting date and either an ending date or a given length.
- - a list of dates.
- """
- freq = corelib.check_freq(freq)
- # Case #1: we have a list ...................
- if dlist is not None:
- # Already a DateArray....................
- if isinstance(dlist, DateArray):
- if (freq is not None) and (dlist.freq != corelib.check_freq(freq)):
- return dlist.asfreq(freq)
- else:
- return dlist
- # Make sure it's a sequence, else that's a start_date
- if hasattr(dlist,'__len__'):
- return _listparser(dlist, freq)
- elif start_date is not None:
- if end_date is not None:
- dmsg = "What starting date should be used ? '%s' or '%s' ?"
- raise DateError, dmsg % (dlist, start_date)
- else:
- (start_date, end_date) = (dlist, start_date)
- else:
- 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! "
- dmsg += "(got '%s' instead)" % type(start_date)
- raise DateError, dmsg
- # Check if we have an end_date
- if end_date is None:
- if length is None:
-# raise ValueError,"No length precised!"
- length = 1
- else:
- if not isDate(end_date):
- raise DateError, "Ending date should be a valid Date instance!"
- length = int(end_date - start_date)
- if include_last:
- length += 1
-# dlist = [(start_date+i).value for i in range(length)]
- dlist = numeric.arange(length, dtype=int_)
- dlist += start_date.value
- if freq is None:
- freq = start_date.freq
- return DateArray(dlist, freq=freq)
-datearray = date_array
-
-def date_array_fromlist(dlist, freq=None):
- "Constructs a DateArray from a list of dates."
- return date_array(dlist=dlist, freq=freq)
-
-def date_array_fromrange(start_date, end_date=None, length=None,
- include_last=True, freq=None):
- """Constructs a DateArray from a starting date and either an ending date or
- a length."""
- return date_array(start_date=start_date, end_date=end_date,
- length=length, include_last=include_last, freq=freq)
-
-#####---------------------------------------------------------------------------
-#---- --- Definition of functions from the corresponding methods ---
-#####---------------------------------------------------------------------------
-class _frommethod(object):
- """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(DateArray, self._methodname).__doc__
- except AttributeError:
- return "???"
- #
- def __call__(self, caller, *args, **params):
- if hasattr(caller, self._methodname):
- method = getattr(caller, self._methodname)
- # If method is not callable, it's a property, and don't call it
- if hasattr(method, '__call__'):
- return method.__call__(*args, **params)
- return method
- method = getattr(fromnumeric.asarray(caller), self._methodname)
- try:
- return method(*args, **params)
- except SystemError:
- return getattr(numpy,self._methodname).__call__(caller, *args, **params)
-#............................
-day_of_week = _frommethod('day_of_week')
-day_of_year = _frommethod('day_of_year')
-year = _frommethod('year')
-quarter = _frommethod('quarter')
-month = _frommethod('month')
-day = _frommethod('day')
-hour = _frommethod('hour')
-minute = _frommethod('minute')
-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__':
- import maskedarray.testutils
- 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])
- 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)
-
- 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
More information about the Scipy-svn
mailing list