[Scipy-svn] r2370 - in trunk/Lib/sandbox: . timeseries

scipy-svn at scipy.org scipy-svn at scipy.org
Fri Dec 8 11:06:10 EST 2006


Author: mattknox_ca
Date: 2006-12-08 10:05:35 -0600 (Fri, 08 Dec 2006)
New Revision: 2370

Added:
   trunk/Lib/sandbox/timeseries/
   trunk/Lib/sandbox/timeseries/README
   trunk/Lib/sandbox/timeseries/__init__.py
   trunk/Lib/sandbox/timeseries/__init__.pyc
   trunk/Lib/sandbox/timeseries/corelib.py
   trunk/Lib/sandbox/timeseries/corelib.pyc
   trunk/Lib/sandbox/timeseries/cseries.pyd
   trunk/Lib/sandbox/timeseries/cseriesmodule.c
   trunk/Lib/sandbox/timeseries/shiftingarray.py
   trunk/Lib/sandbox/timeseries/shiftingarray.pyc
   trunk/Lib/sandbox/timeseries/timeseries.py
   trunk/Lib/sandbox/timeseries/timeseries.pyc
   trunk/Lib/sandbox/timeseries/tsdate.py
   trunk/Lib/sandbox/timeseries/tsdate.pyc
Log:
uploaded timeseries module to sandbox

Added: trunk/Lib/sandbox/timeseries/README
===================================================================
--- trunk/Lib/sandbox/timeseries/README	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/README	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,22 @@
+Requirements and warnings:
+
+1. version 2.0.x of the mx DateTime module MUST be installed. Only "tested" with 2.0.3
+2. Only tested with numpy 1.0.1
+3. Only tested with Python 2.4.x
+4. Only tested on Windows Platform
+5. the included cseries.pyd file was compiled for 32-bit windows, so if you are trying
+   this on another platform, the first thing you need to do is recompile it
+   
+
+Instructions:
+
+1. read through the included example.py script in the examples subfolder. This illustrates
+   the basic functionality of the module. I recommend placing print statements after each
+   variable assignment, one at a time, to see the result of each item in the examples.
+   
+   Documentation is very limited, so the examples really are the best starting point.
+   
+2. Before you get too crazy and start modifying the examples and writing your own scripts,
+   please read todo.txt in the doc subdirectory for an outline of limitations in the current
+   module.
+   

Added: trunk/Lib/sandbox/timeseries/__init__.py
===================================================================
--- trunk/Lib/sandbox/timeseries/__init__.py	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/__init__.py	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,5 @@
+from timeseries import *
+from tsdate import *
+from corelib import *
+from numpy import ma
+masked = ma.masked
\ No newline at end of file

Added: trunk/Lib/sandbox/timeseries/__init__.pyc
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/__init__.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/Lib/sandbox/timeseries/corelib.py
===================================================================
--- trunk/Lib/sandbox/timeseries/corelib.py	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/corelib.py	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,79 @@
+import numpy
+
+#converts possible strings for frequency into acceptable values             
+def fmtFreq (freqStr):
+    if freqStr is None:
+        return None    
+    elif freqStr.upper() in ("A","ANNUAL","B","BUSINESS","D","DAILY","M","MONTHLY","Q","QUARTERLY","S","SECONDLY"):
+        return freqStr[0].upper()
+    else:
+        raise ValueError("Invalid frequency: "+str(freqStr))
+        
+
+#converts possible strings for observed into acceptable values
+def fmtObserv(obStr):
+
+    obsVals = (   "UNDEFINED",
+                  "BEGINNING",
+                  "END",
+                  "AVERAGED",
+                  "SUMMED",
+                  "ANNUALIZED",
+                  "FORMULA",
+                  "HIGH",
+                  "LOW")
+
+    if obStr is None:
+        return None
+    elif obStr.upper() in obsVals:
+        return obStr.upper()    
+    elif obStr.upper() in ("UNDEFINED", "BEGIN", "END", "AVERAGE", "SUM", "ANNUAL" , "FORMULA", "HIGH", "LOW"):
+        obStr = obStr.upper()
+        for x in obsVals:
+            if obStr[:2] == x[:2]:
+                return x       
+    else:
+        raise ValueError("Invalid value for observed attribute: "+str(obStr))
+        
+def freqToType(freq):
+    return freqTypeMapping[fmtFreq(freq)]
+
+
+# fake data type for date variables
+class DateSpec:
+    def __init__(self, freq):
+        self.freq = fmtFreq(freq)
+        
+    def __hash__(self): return hash(self.freq)
+    
+    def __eq__(self, other):
+        if hasattr(other, "freq"): return self.freq == other.freq
+        else: return False
+        
+    def __str__(self): return "Date(" + str(self.freq) + ")"
+    
+    
+
+# define custom numpy types.
+# Note: A more robust approach would register these as actual valid numpy types
+# this is just a hack for now
+numpy.dateS = DateSpec("Secondly")
+numpy.dateD = DateSpec("Daily")
+numpy.dateB = DateSpec("Business")
+numpy.dateM = DateSpec("Monthly")
+numpy.dateQ = DateSpec("Quarterly")
+numpy.dateA = DateSpec("Annual")
+
+freqTypeMapping = {
+    'S':numpy.dateS,
+    'D':numpy.dateD,
+    'B':numpy.dateB,
+    'M':numpy.dateM,
+    'Q':numpy.dateQ,
+    'A':numpy.dateA
+}
+
+def isDateType(dtype):
+    if len([x for x in (numpy.dateS,numpy.dateD,numpy.dateB,numpy.dateM,numpy.dateQ,numpy.dateA) if x == dtype]) > 0: return True
+    else: return False
+

Added: trunk/Lib/sandbox/timeseries/corelib.pyc
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/corelib.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/Lib/sandbox/timeseries/cseries.pyd
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/cseries.pyd
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/Lib/sandbox/timeseries/cseriesmodule.c
===================================================================
--- trunk/Lib/sandbox/timeseries/cseriesmodule.c	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/cseriesmodule.c	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,538 @@
+#include <Python.h>
+//#include <datetime.h>
+#include <structmember.h>
+#include <stdio.h>
+#include <string.h>
+#include "mxDateTime.h"
+#include "arrayobject.h"
+
+static char cseries_doc[] = "Speed sensitive time series operations";
+
+///////////////////////////////////////////////////////////////////////
+
+
+static int
+freqVal(char freq)
+{
+	switch(freq)
+	{
+		case 'A':
+			//annual
+			return 1;
+		case 'Q':
+			//quarterly
+			return 2;
+		case 'M':
+			//monthly
+			return 3;
+		case 'B':
+			//business
+			return 4;
+		case 'D':
+			//daily
+			return 5;
+		default:
+			return 0;
+	}
+}
+
+
+//fromDate is periods since Dec 31, 1849
+static long
+convert(long fromDate, char fromFreq, char toFreq, int notStartInd, int atEnd)
+{
+    long absdate, origin, secondorigin, secsInDay;
+    long converted;
+    int rem;
+    int y,m,d,s;
+
+	mxDateTimeObject *theDate;
+	mxDateTimeObject *convDate;
+
+    origin = 675333;
+    secondorigin = 722814;
+    secsInDay = 86400;
+
+	//convert fromDate to days since Dec 31, 1849 (Jan 1, 1850 would have absdate of 1)
+    switch(fromFreq)
+    {
+        case 'D':
+            absdate = fromDate;
+            break;
+        case 'B':
+            absdate = (fromDate/5)*7 + fromDate%5;
+            break;
+        case 'M':
+			y = fromDate/12 + 1;
+			m = fromDate%12;
+			if (atEnd) m++;
+			if (m == 0)
+			{
+				m = 12;
+				y--;
+			}
+			d=1;
+			break;
+        case 'Q':
+        	y = fromDate/4 + 1;
+        	m = (fromDate%4) * 3;
+			if (!atEnd) m -= 2;	//change to first month of quarter
+			else m += 1;
+			if (m < 1)
+			{
+				m += 12;
+				y--;
+			}
+			else if (m == 12)
+			{
+				m = 1;
+				y++;
+			}
+			d=1;
+			break;
+        case 'A':
+        	y = fromDate-1;
+        	if (atEnd == 1) y++;
+        	m = 1;
+        	d = 1;
+        	break;
+        default:
+            return -1;
+    }
+
+	if (freqVal(fromFreq) < 4)
+	{
+		//switch to years from 0 for mxDateTime
+		y+= 1849;
+
+		theDate = (mxDateTimeObject *)mxDateTime.DateTime_FromDateAndTime(y,m,d,0,0,0);
+		absdate = (long)(theDate->absdate);
+		if (atEnd == 1) absdate--;
+	}
+	else
+	{
+		//days from 0 for mxDateTime
+		absdate += origin;
+	}
+
+	if (atEnd) s = secsInDay-1;
+	else s = 0;
+
+	convDate = (mxDateTimeObject *)mxDateTime.DateTime_FromAbsDateAndTime(absdate,s);
+
+	//switch back to days and years since 1849 for pyTSA Date
+	absdate -= origin;
+	y = convDate->year - 1849;
+	m = convDate->month;
+
+	//convert convDate to appropriate # of periods according to toFreq
+    switch(toFreq)
+    {
+        case 'D':
+            converted = absdate;
+            break;
+        case 'B':
+        	rem = absdate%7;
+            if (rem > 4) //is weekend day
+            {
+				if (notStartInd == 1 && freqVal(fromFreq) > 4)
+				{
+					return -1;
+				}
+				else
+				{
+					d = convDate->day;
+					d -= rem - 4;	//change to friday before weekend
+					if (d < 1) d += 3; //if friday was prev. month, change to monday instead
+					absdate = absdate - convDate->day + d;
+					converted = (long)((absdate / 7 * 5.0) + absdate%7);
+				}
+			}
+			else
+			{
+				converted = (long)((absdate / 7 * 5.0) + rem);
+			}
+            break;
+        case 'M':
+        	converted = (long)((y-1)*12 + m);
+        	break;
+       	case 'Q':
+       		converted = (long)((y-1)*4 + ((m-1)/3) + 1);
+       		break;
+       	case 'A':
+       		converted = (long)(y+1);
+       		break;
+        default:
+            return -1;
+    }
+
+    return converted;
+}
+
+
+static long
+expand(long oldSize, char fromFr, char toFr)
+{
+	long newSize;
+	int fromFreq, toFreq;
+
+	if (fromFr == toFr) return oldSize;
+
+	fromFreq = freqVal(fromFr);
+	toFreq = freqVal(toFr);
+	if (fromFreq*toFreq == 0) return oldSize; //invalid frequency
+
+	newSize = oldSize;
+
+	while (toFreq > fromFreq)
+	{
+		if (fromFreq == 1)	//Annual
+		{
+			newSize *= 4; //quarters in year
+			fromFreq++;
+		}
+		else if (fromFreq == 2) //Quarterly
+		{
+			newSize *= 3; //months in quarter
+			fromFreq++;
+		}
+		else if (fromFreq == 3)	//Monthly
+		{
+			newSize *= 31; //max days in month
+			fromFreq++;
+		}
+		else if (fromFreq == 4)	//Business
+		{
+			newSize *= 2; //max d days for each b days
+			fromFreq++;
+		}
+	}
+
+
+	return newSize;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+/*
+OBSERVED
+
+from lower freq to higher freq
+----------------------
+
+summed --	all values in period set as lower freq's value / # of values
+
+rest --		all values in period set as lower freq's value
+
+from higher freq to lower freq
+----------------------
+begin - 	lower freq's value set as first value in period
+end - 		lower freq's value set as end value in period
+summed -	lower freq's value set as sum of all values in period
+averaged -	lower freq's value set as average of all values in period
+high - 		lower freq's value set as largest value in period
+low - 		lower freq's value set as smallest value in period
+
+*/
+///////////////////////////////////////////////////////////////////////
+
+static void
+adjValForObsSet(PyArrayObject *theArray, char obs, PyObject **newVal, PyObject **newValMask, PyObject *val, PyObject *valMask,  long curPerLen)
+{
+	double dblVal;
+	long lngValMask, lngAllMasked;
+
+	lngValMask = PyInt_AsLong(valMask);
+	lngAllMasked = PyInt_AsLong(*newValMask);
+
+	if (!lngValMask) {
+
+		// if any value is not masked, then we shall not mask the aggregated result
+		*newValMask = valMask;
+
+		if (obs == 'B')
+		{
+			if (lngAllMasked) {
+				*newVal = val;
+			}
+		}
+		else if ( PyArray_ISFLOAT(theArray) && (obs=='S' || obs=='A') )
+		{
+
+			if (obs == 'S')
+			{
+				//observed is summed
+
+				dblVal = PyFloat_AsDouble(*newVal);
+				dblVal += PyFloat_AsDouble(val);
+				*newVal = PyFloat_FromDouble(dblVal);
+			}
+			else
+			{
+				//observed is averaged
+
+				dblVal = PyFloat_AsDouble(*newVal);
+				dblVal *= (curPerLen-1);
+				dblVal += PyFloat_AsDouble(val);
+				dblVal /= curPerLen;
+				*newVal = PyFloat_FromDouble(dblVal);
+			}
+
+		}
+		else if ( PyArray_ISNUMBER(theArray) && (obs=='H' || obs=='L') )
+		{
+
+			if (obs == 'H')
+			{
+				//observed is high
+
+				if (PyFloat_AsDouble(val) > PyFloat_AsDouble(*newVal)) *newVal = val;
+			}
+			else if (obs == 'L')
+			{
+				//observed is low
+
+				if (PyFloat_AsDouble(val) < PyFloat_AsDouble(*newVal)) *newVal = val;
+			}
+
+		}
+		else
+		{
+			//observed is not beginning and
+			//val is string or (val is date and observed is summed/averaged)
+			//or observed is end or not supported
+
+			*newVal = val;
+		}
+	}
+
+}
+
+
+static //PyArrayObject *
+setArrayItem(PyArrayObject **theArray, long index, PyObject *newVal)
+{
+	char *setptr;
+
+	if (index >= 0)
+	{
+		//set value in array
+		setptr = (*theArray)->data + (index) * (*theArray)->strides[0];
+		PyArray_SETITEM(*theArray,setptr,newVal);
+	}
+
+	//return theArray;
+}
+
+
+static char cseries_reindex_doc[] = "";
+static PyObject *
+cseries_reindex(PyObject *self, PyObject *args)
+{
+    PyArrayObject *array;
+    PyArrayObject *tempArray;
+    PyArrayObject *newArray;
+
+    PyArrayObject *mask;
+    PyArrayObject *tempMask;
+    PyArrayObject *newMask;
+
+    PyObject *returnVal = NULL;
+
+    int notStartInd, atEnd;
+    long startIndex, newStart;
+    long i, curPerInd, nextPerInd, prevIndex, curIndex;
+    long dim;
+    long curPerLen;
+    long lngValMask;
+    char *fromFreq, *toFreq, *observed;
+
+    char *getptr;
+    PyObject *val, *newVal;
+
+    char *getptrMask;
+    PyObject *valMask, *newValMask;
+
+    int toFrVal, fromFrVal;
+
+	returnVal = PyDict_New();
+
+	if (!PyArg_ParseTuple(args, "OssslO:reindex(array, fromfreq, tofreq, observed, startIndex,mask)", &tempArray, &fromFreq, &toFreq, &observed, &startIndex, &tempMask)) return NULL;
+
+    if (toFreq[0] == fromFreq[0])
+    {
+
+        PyDict_SetItemString(returnVal, "values", (PyObject*)tempArray);
+        PyDict_SetItemString(returnVal, "mask", (PyObject*)tempMask);
+
+        return returnVal;
+    }
+
+    array = PyArray_GETCONTIGUOUS(tempArray);
+    mask = PyArray_GETCONTIGUOUS(tempMask);
+
+	//expand size to fit new values if needed
+	dim = expand(array->dimensions[0], fromFreq[0], toFreq[0]);
+
+	//initialize new array
+    newArray = (PyArrayObject*)PyArray_SimpleNew(array->nd, &dim, array->descr->type_num);
+    newMask  = (PyArrayObject*)PyArray_SimpleNew(mask->nd, &dim, mask->descr->type_num);
+
+    for (i = 0; i < dim; i++)
+    {
+		setArrayItem(&newArray, i, PyInt_FromLong(1));
+		setArrayItem(&newMask, i, PyInt_FromLong(1));
+	}
+
+	//convert start index to new frequency
+	notStartInd = 0;
+	atEnd = 0;
+    newStart = convert(startIndex, fromFreq[0], toFreq[0], notStartInd, atEnd);
+
+	//initialize prevIndex
+	prevIndex = newStart - 1;
+
+	notStartInd = 1;
+	atEnd = 0;
+
+	//set values in the new array
+    for (i = 0; i < array->dimensions[0]; i++)
+    {
+		//find index for start of current period in new frequency
+        curPerInd = convert(startIndex + i, fromFreq[0], toFreq[0], notStartInd, atEnd);
+
+		//get frequency numeric mapping
+		fromFrVal = freqVal(fromFreq[0]);
+		toFrVal = freqVal(toFreq[0]);
+
+		//get value from old array
+		getptr = array->data + i*array->strides[0];
+		val = PyArray_GETITEM(array,getptr);
+
+		//get the mask corresponding to the old value
+		getptrMask = mask->data + i*mask->strides[0];
+		valMask = PyArray_GETITEM(mask,getptrMask);
+
+        if (fromFrVal < toFrVal)
+        {
+			//from lower freq to higher freq
+
+			newVal = val;
+			newValMask = valMask;
+
+			//find index for start of next period in new frequency
+			nextPerInd = convert(startIndex + i + 1, fromFreq[0], toFreq[0], notStartInd, atEnd);
+
+			//adjust for observed setting
+			if (observed[0] == 'S' && PyArray_ISFLOAT(array) && !( (fromFrVal == 4 && toFrVal == 5) || (fromFrVal == 5 && toFrVal == 4) ) )
+			{
+				//summed
+
+				//all values in period set as old array's value / # of values
+				newVal = PyFloat_FromDouble( PyFloat_AsDouble(val) / (nextPerInd - curPerInd) );
+			}
+
+			//set each value in period
+			for (curIndex = curPerInd; curIndex < nextPerInd; curIndex++)
+			{
+				setArrayItem(&newArray, curIndex-newStart, newVal);
+				setArrayItem(&newMask, curIndex-newStart, newValMask);
+			}
+		}
+		else
+		{
+
+			lngValMask = PyInt_AsLong(valMask);
+
+			//from higher freq to lower freq
+
+			if (curPerInd != prevIndex)
+			{
+				//starting new period in old array
+
+
+				//set value in the new array
+				setArrayItem(&newArray, prevIndex-newStart, newVal);
+				setArrayItem(&newMask, prevIndex-newStart, newValMask);
+
+				//reset period length
+				curPerLen = 0;
+
+
+
+				if (!lngValMask) {
+					curPerLen++;
+				}
+
+
+
+				//store current index and value
+				prevIndex = curPerInd;
+				newVal = val;
+				newValMask = valMask;
+
+			}
+			else
+			{
+				//still in same period
+
+
+
+				if (!lngValMask) {
+					curPerLen++;
+				}
+
+				//adjust new value according to observed setting
+				adjValForObsSet(array, observed[0], &newVal, &newValMask, val, valMask, curPerLen);
+			}
+
+		}
+
+    }
+
+	//set value of last item in the new array
+	setArrayItem(&newArray, curPerInd-newStart, newVal);
+	setArrayItem(&newMask, curPerInd-newStart, newValMask);
+
+	PyDict_SetItemString(returnVal, "values", (PyObject*)newArray);
+	PyDict_SetItemString(returnVal, "mask", (PyObject*)newMask);
+
+	return returnVal;
+
+}
+
+
+static char cseries_convert_doc[] = "";
+static PyObject *
+cseries_convert(PyObject *self, PyObject *args)
+{
+    long fromDate;
+    char* fromFreq;
+    char* toFreq;
+    int notStartInd, atEnd;
+
+    if (!PyArg_ParseTuple(args, "lss:convert(fromDate, fromfreq, tofreq)", &fromDate, &fromFreq, &toFreq)) return NULL;
+
+	//always want start of period (only matters when converting from lower freq to higher freq ie. m -> d)
+	atEnd = 0;
+	notStartInd = 0;
+
+    return PyInt_FromLong(convert(fromDate, fromFreq[0], toFreq[0], notStartInd, atEnd));
+}
+
+
+///////////////////////////////////////////////////////////////////////
+
+static PyMethodDef cseries_methods[] = {
+    {"reindex", cseries_reindex, METH_VARARGS, cseries_reindex_doc},
+    {"convert", cseries_convert, METH_VARARGS, cseries_convert_doc},
+    {NULL, NULL}
+};
+
+PyMODINIT_FUNC
+initcseries(void)
+{
+    Py_InitModule3("cseries", cseries_methods, cseries_doc);
+    mxDateTime_ImportModuleAndAPI();
+    import_array();
+}
\ No newline at end of file

Added: trunk/Lib/sandbox/timeseries/shiftingarray.py
===================================================================
--- trunk/Lib/sandbox/timeseries/shiftingarray.py	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/shiftingarray.py	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,302 @@
+import numpy, types , corelib
+import copy
+from numpy import ma
+
+class ShiftingArray(object):
+    def __init__(self, values, dtype=None, startIndex=None, mask=ma.nomask):
+
+        # hack to convert our fake date data types to real data types
+        if corelib.isDateType(dtype):
+            self.dtype = numpy.int_
+        else:
+            self.dtype = dtype
+            
+        if self.dtype is None:
+            self.dtype = values.dtype
+
+        # need to use the empty function instead of passing an empty list
+        # because that won't work when type=numpy.object_
+        if len(values) == 0 and dtype is numpy.object_: 
+            tempData = ma.array(numpy.empty((0,), self.dtype))
+        else:
+            tempData = ma.array(values, self.dtype)
+        
+        newSize = tempData.size*2       
+
+        firstIndex = newSize//4
+        lastIndex = firstIndex + tempData.size - 1
+        if startIndex is None:
+            self.indexZeroRepresents = None
+        else:    
+            self.indexZeroRepresents = int(startIndex)-firstIndex
+        
+        if mask is not ma.nomask:
+            tempMask = ma.make_mask(mask)
+            tempData[tempMask] = ma.masked
+
+        self.data = ma.array(numpy.empty(newSize,self.dtype))
+
+        if firstIndex > 0:
+            self.data[0:firstIndex] = ma.masked
+            if self.data.size > lastIndex+1: self.data[lastIndex+1:self.data.size] = ma.masked
+
+        self.data[firstIndex:lastIndex+1] = tempData[:]
+
+
+    def shift(self, n):
+        self.indexZeroRepresents += n
+
+
+    #DATA ACCESS        
+
+    def __setitem__(self, index, value):
+        self.__expandToFit(self.__minIndex(index),self.__maxIndex(index))
+        convIndex = self.__convIndex(index)
+        self.data[convIndex] = value
+
+
+    def __getitem__(self, index):
+        self.__expandToFit(self.__minIndex(index),self.__maxIndex(index))
+        convIndex = self.__convIndex(index)
+        return self.data[convIndex]
+
+    def _shift(self, startIndex, endIndex):
+        self.__expandToFit(startIndex, endIndex)
+        return self.data[startIndex-self.indexZeroRepresents:endIndex-self.indexZeroRepresents+1]
+
+        
+    #PRIVATE FUNCTIONS
+
+    def __convIndex(self,index):
+
+        if self.indexZeroRepresents is not None:
+            if isinstance(index,ShiftingArray):
+
+                if index.indexZeroRepresents > self.indexZeroRepresents:
+                    #expand index to the left
+                    originalSize = index.data.size
+                    shiftAmt = index.indexZeroRepresents - self.indexZeroRepresents
+                    newSize = originalSize + shiftAmt
+                    temp = ma.array(numpy.empty(newSize, index.data.dtype))
+                    temp[newSize-originalSize:] = index.data
+                    temp[0:shiftAmt] = False
+                    temp = temp.filled(False)
+                else:
+                    #chop off first portion of data
+                    temp = index.data[self.indexZeroRepresents - index.indexZeroRepresents:].filled(False)
+
+                # chop off extra values on right hand side
+                if temp.size > self.data.size: return temp[:self.data.size]
+                else: return temp
+
+            elif type(index) == types.SliceType:
+                if index.start is None: tempStart = None
+                else: tempStart = index.start - self.indexZeroRepresents
+                if index.stop is None: tempStop = None
+                else: tempStop = index.stop - self.indexZeroRepresents
+                tempStep = index.step
+                
+                return slice(tempStart,tempStop,tempStep)
+            else:
+                return index - self.indexZeroRepresents
+                
+        else:
+            return index
+
+    def __maxIndex(self,index):
+        if type(index) == types.IntType: return index
+        if type(index) == types.SliceType: return index.stop
+        elif isinstance(index,ShiftingArray): return index.lastValue()
+        elif hasattr(index,'__len__'): return max(index)
+        else: return int(index)
+    
+    def __minIndex(self,index):
+        if type(index) == types.IntType: return index
+        if type(index) == types.SliceType: return index.start
+        elif isinstance(index,ShiftingArray): return index.firstValue()
+        elif hasattr(index,'__len__'): return min(index)
+        else: return int(index)
+
+    def __expandToFit(self, minRange, maxRange=None):
+
+        if self.indexZeroRepresents is None:
+            self.indexZeroRepresents = minRange
+
+        if maxRange is None:
+            maxRange = minRange
+        if maxRange < minRange:
+            raise ValueError("invalid range: " + str(minRange) + " to " + str(maxRange))
+
+        minRange -= self.indexZeroRepresents
+        maxRange -= self.indexZeroRepresents
+
+        if maxRange > self.data.size-1:   #expand to the right
+            originalSize = self.data.size
+            newSize = originalSize
+            while maxRange > newSize-1:
+                newSize = expandAmt(newSize)
+            
+            self.data = self.data.resize(numpy.shape(numpy.empty(newSize)))
+            self.data[originalSize:] = ma.masked
+
+
+        if minRange < 0:                  #expand to the left
+            originalSize = self.data.size
+            newSize = originalSize
+            shiftAmt = int(0)
+            while minRange + shiftAmt < 0:
+                newSize = expandAmt(newSize)
+                shiftAmt = int(newSize - originalSize)
+
+            temp = ma.array(numpy.empty(newSize, self.data.dtype))
+            temp[newSize-originalSize:] = self.data
+            self.data = temp
+            self.data[0:shiftAmt] = ma.masked
+
+            self.indexZeroRepresents -= shiftAmt
+
+
+
+    #MATH FUNCTIONS
+
+    def __add__(self, other,fill_value=ma.masked):  return doFunc(self,other, ma.add,fill_value=fill_value)
+    def __radd__(self, other):                      return self+other
+    def __sub__(self, other,fill_value=ma.masked):  return doFunc(self,other, ma.subtract,fill_value=fill_value)
+    def __rsub__(self, other):                      return doFunc((self*-1),other, ma.add)
+    def __mul__(self, other,fill_value=ma.masked):  return doFunc(self,other, ma.multiply,fill_value=fill_value)
+    def __rmul__(self, other):                      return self*other
+    def __div__(self, other,fill_value=ma.masked):  return doFunc(self,other, ma.divide,fill_value=fill_value)
+    def __rdiv__(self, other):                      return doFunc(pow(self,-1),other, ma.multiply)
+    def __pow__(self, other,fill_value=ma.masked):  return doFunc(self,other, ma.power,fill_value=fill_value)
+
+    def __eq__(self, other):                        return doFunc(self,other, ma.equal)
+    def __le__(self, other):                        return doFunc(self,other, ma.less_equal)
+    def __lt__(self, other):                        return doFunc(self,other, ma.less)
+    def __ge__(self, other):                        return doFunc(self,other, ma.greater_equal)
+    def __gt__(self, other):                        return doFunc(self,other, ma.greater)
+
+    def max(self,other):                            return doFunc(self,other, ma.maximum)
+    def min(self,other):                            return doFunc(self,other, ma.minimum)
+
+    #INFO FUNCTIONS
+
+    def __len__(self):
+        fv = self.firstValue()
+        if fv is not None:
+            return self.lastValue()-fv+1
+        else:
+            return 0
+
+    def firstValue(self):
+        firstIndex = first_unmasked(self.data)
+        if self.indexZeroRepresents is None or firstIndex is None:
+            return None
+        else:
+            return firstIndex+self.indexZeroRepresents
+
+
+    def lastValue(self):
+        lastIndex = last_unmasked(self.data)
+        if self.indexZeroRepresents is None or lastIndex is None:
+            return None
+        else:
+            return lastIndex+self.indexZeroRepresents
+
+
+    #DISPLAY FUNCTIONS
+
+    def __str__(self):
+        retVal = ""
+        if self.firstValue() is not None:
+            for i in range(first_unmasked(self.data), last_unmasked(self.data)+1):
+                index = str(i+self.indexZeroRepresents)
+                index = index + (" " * (6-len(index)))
+                retVal += index + "---> " + str(self.data[i]) + "\n"
+            return retVal
+        else:
+            return "<no data>"
+
+    def show(self, showLists=True):
+        print "indexZeroRepresents = ", self.indexZeroRepresents
+        print self.data
+
+
+
+#apply func to ser1 and ser2, replacing masked values with fill_value
+def doFunc(ser1, ser2, func,fill_value=ma.masked):
+    if not isinstance(ser2, ShiftingArray):
+        if ser1.indexZeroRepresents is None:
+            return ShiftingArray([],ser1.data.dtype)        
+        else:
+            ser2 = ShiftingArray([ser2]*len(ser1),ser1.data.dtype, ser1.firstValue())
+    
+    sFV, sLV = ser1.firstValue(), ser1.lastValue()
+    oFV, oLV = ser2.firstValue(), ser2.lastValue()
+
+    if ser1.indexZeroRepresents is not None and ser2.indexZeroRepresents is not None:
+        if fill_value is not ma.masked:
+            minVal = min(sFV, oFV)
+            maxVal = max(sLV, oLV)
+        else:
+            minVal = max(sFV, oFV)
+            maxVal = min(sLV, oLV)
+    elif ser1.indexZeroRepresents is None and ser2.indexZeroRepresents is None:
+            return ShiftingArray([],ser1.data.dtype)
+    elif ser1.indexZeroRepresents is None:
+        minVal = oFV
+        maxVal = oLV
+    else: #ser2.indexZeroRepresents is None:
+        minVal = sFV
+        maxVal = sLV
+
+    if maxVal < minVal:
+        return ShiftingArray([],ser1.data.dtype, startIndex=minVal)
+
+    data1 = ser1._shift(minVal, maxVal)
+    data2 = ser2._shift(minVal, maxVal)
+
+    if fill_value is ma.masked:
+        mask = data1.mask | data2.mask
+    else:
+        mask = data1.mask & data2.mask
+
+        data1 = data1.filled(fill_value)
+        data2 = data2.filled(fill_value)
+
+    data = func(data1,data2)
+
+    return ShiftingArray(data,data.dtype,minVal, mask)
+    
+    
+def doFunc_oneseries(ser,func):
+    
+    sFV = ser.firstValue()
+    
+    if sFV is None:
+        return ser
+    else:
+        result = func(ser.data)
+        return ShiftingArray(result,result.dtype,sFV,result.mask)
+
+
+#MISC GLOBAL FUNCTIONS
+
+def expandAmt(size):
+    EXPAND_MULT = 1.2
+    EXPAND_ADD  = 5
+    return round(size*EXPAND_MULT) + EXPAND_ADD
+
+
+def first_unmasked(m):
+    idx = numpy.where(m.mask == False)
+    if len(idx) != 0 and len(idx[0]) != 0:
+        return idx[0][0]
+    else:
+        return None
+    
+def last_unmasked(m):
+    idx = numpy.where(m.mask == False)
+    if len(idx) != 0 and len(idx[0]) != 0:
+        return idx[0][-1]
+    else:
+        return None
\ No newline at end of file

Added: trunk/Lib/sandbox/timeseries/shiftingarray.pyc
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/shiftingarray.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/Lib/sandbox/timeseries/timeseries.py
===================================================================
--- trunk/Lib/sandbox/timeseries/timeseries.py	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/timeseries.py	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,232 @@
+import numpy
+from numpy import ma
+import types
+
+import corelib
+import shiftingarray as sa
+from shiftingarray import doFunc, doFunc_oneseries
+import cseries
+import tsdate
+import copy
+
+class TimeSeries(sa.ShiftingArray):
+    def __init__(self,values=[], dtype=numpy.float64, freq=None, observed='END', startIndex=None,mask=ma.nomask):
+    
+        if freq is None: raise ValueError("freq not specified")
+
+        super(TimeSeries, self).__init__(values, dtype, startIndex,mask)
+        self.freq = corelib.fmtFreq(freq)
+        self.observed = corelib.fmtObserv(observed)
+        self.dtype = dtype
+        
+    def __getitem__(self, key):
+        if isinstance(key,tsdate.Date):
+            if self.freq != key.freq:
+                raise "series of frequency "+str(self.freq)+" given date expression of type "+str(key.freq)
+            else:
+                key = int(key)
+        return super(TimeSeries, self).__getitem__(key)
+        
+    def __setitem__(self, key, value):
+        if isinstance(key, tsdate.Date):
+            key = int(key)
+        super(TimeSeries, self).__setitem__(key,value)
+
+    
+    def convert(self, freq, observed=None):
+        # return self converted to freq, method according to self.observed
+        toFreq = corelib.fmtFreq(freq)
+        fromFreq = self.freq
+        
+        if fromFreq != toFreq:
+            if observed is None: observed=self.observed
+            else: observed = corelib.fmtObserv(observed)
+
+            firstIndex = sa.first_unmasked(self.data)
+            if firstIndex is None:
+                return TimeSeries([],dtype=self.dtype,freq=toFreq,observed=observed)
+
+            startIndexAdj = self.firstValue()
+
+            lastIndex = sa.last_unmasked(self.data)
+
+            tempData = copy.deepcopy(self.data[firstIndex:lastIndex+1])
+            tempMask = tempData.mask
+            tempData = tempData.filled()
+
+            cRetVal = cseries.reindex(tempData, fromFreq, toFreq, observed, startIndexAdj,tempMask)
+            _values = cRetVal['values']
+            _mask = cRetVal['mask']
+
+            startIndex = cseries.convert(startIndexAdj, fromFreq, toFreq)
+
+            return TimeSeries(_values,dtype=self.data.dtype,freq=toFreq,observed=observed,startIndex=startIndex, mask=_mask)
+            
+        else:
+            return copy.deepcopy(self)
+
+        
+    def __str__(self):
+        retVal = ""
+        if self.firstValue() is not None:
+            for i in range(self.firstValue(),self.lastValue()+1):
+                index = str(tsdate.Date(freq=self.freq,val=i))
+                index = index + (" " * (6-len(index)))
+                retVal += index + "---> " + str(super(TimeSeries, self).__getitem__(i)) + "\n"
+            return retVal
+        else:
+            return "<no data>"
+            
+            
+    ### DATA 
+    
+    def __add__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__add__(other),self.freq,self.observed)
+        
+    def __radd__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__add__(other),self.freq,self.observed)
+        
+    def __sub__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__sub__(other),self.freq,self.observed)
+        
+    def __rsub__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__rsub__(other),self.freq,self.observed)
+        
+    def __mul__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__mul__(other),self.freq,self.observed)
+        
+    def __rmul__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__rmul__(other),self.freq,self.observed)
+        
+    def __div__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__div__(other),self.freq,self.observed)
+        
+    def __rdiv__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__rdiv__(other),self.freq,self.observed)
+        
+    def __pow__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__pow__(other),self.freq,self.observed)
+        
+    ### IN PLACE
+    
+    def __iadd__(self, other):
+        validOpInputs(self,other)
+        self = SAtoTS(super(TimeSeries, self).__add__(other),self.freq,self.observed)
+        return self
+    
+    def __isub__(self, other):
+        validOpInputs(self,other)
+        self = SAtoTS(super(TimeSeries, self).__sub__(other),self.freq,self.observed)
+        return self
+    
+    def __imul__(self, other):
+        validOpInputs(self,other)
+        self = SAtoTS(super(TimeSeries, self).__mul__(other),self.freq,self.observed)
+        return self
+    
+    def __idiv__(self, other):
+        validOpInputs(self,other)
+        self = SAtoTS(super(TimeSeries, self).__div__(other),self.freq,self.observed)
+        return self
+        
+    # this overrides & and should only be used by boolean series
+    def __and__(self, other):
+        validOpInputs(self,other)
+        return self * other
+
+    # this overrides | and should only be used by boolean series
+    def __or__(self, other):
+        validOpInputs(self,other)
+        return ~(~self & ~other)
+            
+    # this overrides ~ and should only be used by boolean series
+    # it is our "not" operator
+    def __invert__(self):
+        return self == False
+    
+    ### COMPARISON
+    
+    def __eq__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__eq__(other),self.freq,self.observed)
+        
+    def __le__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__le__(other),self.freq,self.observed)
+        
+    def __lt__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__lt__(other),self.freq,self.observed)
+        
+    def __ge__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__ge__(other),self.freq,self.observed)
+        
+    def __gt__(self, other):
+        validOpInputs(self,other)
+        return SAtoTS(super(TimeSeries, self).__gt__(other),self.freq,self.observed)
+
+def tser(start,end):
+    if start.freq != end.freq:
+        raise ValueError("start and end dates must have same frequency!")
+    return TimeSeries(numpy.arange(int(start),int(end)+1),dtype=corelib.freqTypeMapping[start.freq],freq=start.freq,observed='END',startIndex=int(start))
+        
+def validOpInputs(ser1,ser2):
+    if isinstance(ser1,TimeSeries) and isinstance(ser2,TimeSeries) and ser1.freq != ser2.freq:
+        raise "operation cannot be performed on series with different frequencies ("+str(ser1.freq) + " and " + str(ser2.freq)+")"
+
+    
+def SAtoTS(values,freq,observed,dtype=None):
+    if dtype is None: _dtype = values.dtype
+    else: _dtype = dtype
+    return TimeSeries(values.data,dtype=_dtype,freq=freq,observed=observed,startIndex=values.indexZeroRepresents)
+
+
+# math functions (two series)
+def add(ser1,ser2,fill_value=ma.masked):
+    return apply_func_twoseries(ma.add,ser1,ser2,fill_value)
+
+def multiply(ser1,ser2,fill_value=ma.masked):
+    return apply_func_twoseries(ma.multiply,ser1,ser2,fill_value)
+
+def divide(ser1,ser2,fill_value=ma.masked):
+    return apply_func_twoseries(ma.divide,ser1,ser2,fill_value)
+    
+def subtract(ser1,ser2,fill_value=ma.masked):
+    return apply_func_twoseries(ma.subtract,ser1,ser2,fill_value)
+    
+# math functions (one series, return series)
+def sqrt(ser):
+    return apply_func_oneseries(ma.sqrt,ser)
+    
+# math functions (one series, return scalar)
+def sum(ser):
+    return ma.sum(ser.data)
+
+def product(ser):
+    return ma.product(ser.data)
+    
+def average(ser):
+    return ma.average(ser.data)
+    
+def where(condition,x,y):
+    tempResult = ma.where(condition.data,x,y)
+    return TimeSeries(tempResult,dtype=numpy.bool_,freq=condition.freq,observed=condition.observed,startIndex=condition.indexZeroRepresents)
+
+# generic functions
+def apply_func_twoseries(func,ser1,ser2,fill_value=ma.masked):
+    validOpInputs(ser1,ser2)
+    return SAtoTS(doFunc(ser1,ser2,func,fill_value=fill_value),ser1.freq,ser1.observed)
+    
+def apply_func_oneseries(func,ser):
+    return SAtoTS(doFunc_oneseries(ser,func),ser.freq,ser.observed)
+    

Added: trunk/Lib/sandbox/timeseries/timeseries.pyc
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/timeseries.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: trunk/Lib/sandbox/timeseries/tsdate.py
===================================================================
--- trunk/Lib/sandbox/timeseries/tsdate.py	2006-12-08 05:13:33 UTC (rev 2369)
+++ trunk/Lib/sandbox/timeseries/tsdate.py	2006-12-08 16:05:35 UTC (rev 2370)
@@ -0,0 +1,290 @@
+import corelib
+import mx.DateTime
+import numpy
+
+class Date:
+    def __init__(self,freq,year=None, month=None, day=None, seconds=None,quarter=None, date=None, val=None):
+        
+        if hasattr(freq,'freq'):
+            self.freq = corelib.fmtFreq(freq.freq)
+        else:
+            self.freq = corelib.fmtFreq(freq)
+        self.type = corelib.freqToType(self.freq)
+        
+        if val is not None:
+            if self.freq == 'D':
+                self.__date = val+originDate
+            elif self.freq == 'B':
+                self.__date = originDate + val + (val//5)*7 - (val//5)*5
+            elif self.freq == 'S':
+                self.__date = secondlyOriginDate + mx.DateTime.DateTimeDeltaFromSeconds(val)
+            elif self.freq == 'M':
+                self.__date = originDate + mx.DateTime.RelativeDateTime(months=val, day=-1)
+            elif self.freq == 'A':
+                self.__date = originDate + mx.DateTime.RelativeDateTime(years=val, month=-1, day=-1)
+            elif self.freq == 'Q':
+                self.__date = originDate + 1 + mx.DateTime.RelativeDateTime(years=int(val/4), month=int(12 * (float(val)/4 - val/4)), day=-1)
+        elif date is not None:
+            self.__date = date
+        else:
+            error = ValueError("Insufficient parameters given to create a date at the given frequency")
+
+            if year is None:
+                raise error            
+            
+            if self.freq in ('B','D'):
+                if month is None or day is None: raise error
+            elif self.freq == 'M':
+                if month is None: raise error
+                day = -1
+            elif self.freq == 'Q':
+                if quarter is None: raise error
+                month = quarter * 3
+                day = -1
+            elif self.freq == 'A':
+                month = -1
+                day = -1
+            elif self.freq == 'S':
+                if month is None or day is None or seconds is None: raise error
+                
+            if self.freq != 'S':
+                self.__date = mx.DateTime.Date(year, month, day)
+                if self.freq == 'B':
+                    if self.__date.day_of_week == 5 or self.__date.day_of_week == 6:
+                        raise ValueError("Weekend passed as business day")
+            else:
+                _hours = int(seconds/3600)
+                _minutes = int((seconds - _hours*3600)/60)
+                _seconds = seconds % 60
+                
+                self.__date = mx.DateTime.Date(year, month, day, _hours, _minutes, _seconds)
+                        
+                
+    def day(self):          return self.getDate().day
+    def day_of_week(self):  return self.getDate().day_of_week
+    def month(self):        return self.getDate().month
+    def quarter(self):      return monthToQuarter(self.getDate().month)
+    def year(self):         return self.getDate().year
+    def seconds(self):      return int(self.getDate().second)
+    def minute(self):       return int(self.getDate().minute)
+    def hour(self):         return int(self.getDate().hour)
+    
+    def strfmt(self,fmt):
+        qFmt = fmt.replace("%q","XXXX")
+        tmpStr = self.__date.strftime(qFmt)
+        return tmpStr.replace("XXXX",str(self.quarter()))
+            
+    def __str__(self):
+        if self.freq in ("B","D"):
+            return self.__date.strftime("%d-%b-%y")
+        elif self.freq == "S":
+            return self.__date.strftime("%d-%b-%Y %H:%M:%S")
+        elif self.freq == "M":
+            return self.__date.strftime("%b-%Y")
+        elif self.freq == "Q":
+            return str(self.year())+"q"+str(self.quarter())
+        elif self.freq == "A":
+            return str(self.year())
+        else:
+            return self.__date.strftime("%d-%b-%y")
+
+        
+    def __add__(self, other):
+        if isinstance(other, Date):
+            raise TypeError("Cannot add dates")
+        return Date(freq=self.freq, val=int(self) + other)
+    
+    def __radd__(self, other): return self+other
+    
+    def __sub__(self, other):
+        try: return self + (-1) * other
+        except: pass
+        try:
+            if self.freq <> other.freq:
+                raise ValueError("Cannont subtract dates of different frequency (" + str(self.freq) + " <> " + str(other.freq) + ")")
+            return int(self) - int(other)
+        except TypeError: 
+            raise TypeError("Could not subtract types " + str(type(self)) + " and " + str(type(other)))
+
+    def __repr__(self): return "<" + str(self.freq) + ":" + str(self) + ">"
+    
+    def __eq__(self, other):
+        if self.freq <> other.freq:
+            raise TypeError("frequencies are not equal!")
+        return int(self) == int(other) 
+    
+    def __cmp__(self, other): 
+        if self.freq <> other.freq:
+            raise TypeError("frequencies are not equal!")
+        return int(self)-int(other)    
+        
+    def __hash__(self): return hash(int(self)) ^ hash(self.freq)
+    
+    def __int__(self):
+        return self.value()
+            
+    def value(self):
+        if self.freq == 'D':
+            return int((self.__date-originDate).days)
+        elif self.freq == 'B':
+            days = (self.__date-originDate).days
+            weeks = days // 7
+            return int((weeks*5) + (days - weeks*7))
+        elif self.freq == 'M':
+            return (self.__date.year - originDate.year)*12 + (self.__date.month - originDate.month)
+        elif self.freq == 'S':
+            return int((self.__date - secondlyOriginDate).seconds)
+        elif self.freq == 'A':
+            return int(self.__date.year - originDate.year + 1)
+        elif self.freq == 'Q':
+            return int ((self.__date.year - originDate.year)*4 + (self.__date.month - originDate.month)/3)
+            
+    def mxDate(self):
+        return self.__date
+    
+originDate = mx.DateTime.Date(1850)-1
+secondlyOriginDate = mx.DateTime.Date(1980) - mx.DateTime.DateTimeDeltaFromSeconds(1)
+
+    
+#######################
+# FUNCTIONS
+#######################
+def monthToQuarter(monthNum):
+    return int((monthNum-1)/3)+1
+
+def thisday(freq):
+
+    freq = corelib.fmtFreq(freq)
+
+    tempDate = mx.DateTime.now()
+    
+    # if it is Saturday or Sunday currently, freq==B, then we want to use Friday
+    if freq == 'B' and tempDate.day_of_week >= 5:
+        tempDate -= (tempDate.day_of_week - 4)
+    if freq == 'B' or freq == 'D' or freq == 'S':
+        return Date(freq, date=tempDate)
+    elif freq == 'M':
+        return Date(freq,tempDate.year,tempDate.month)
+    elif freq == 'Q':
+        return Date(freq,tempDate.year,quarter=monthToQuarter(tempDate.month))
+    elif freq == 'A':
+        return Date(freq,tempDate.year)
+
+def prevbusday(day_end_hour=18,day_end_min=0):
+    tempDate = mx.DateTime.localtime()
+
+    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')
+
+                
+#    returns _date converted to a date of _destFreq according to _relation
+#    _relation = "BEFORE" or "AFTER" (not case sensitive)
+def dateOf(_date,_destFreq,_relation="BEFORE"):
+
+    _destFreq = corelib.fmtFreq(_destFreq)
+    _rel = _relation.upper()[0]
+
+    if _date.freq == _destFreq:
+        return _date
+    elif _date.freq == 'D':
+
+        if _destFreq == 'B':
+            # BEFORE result: preceeding Friday if _date is a weekend, same day otherwise
+            # AFTER result: following Monday if _date is a weekend, same day otherwise
+            tempDate = _date.mxDate()
+            if _rel == "B":
+                if tempDate.day_of_week >= 5: tempDate -= (tempDate.day_of_week - 4)
+            elif _rel == "A":
+                if tempDate.day_of_week >= 5: tempDate += 7 - tempDate.day_of_week
+            return Date(freq='B',date=tempDate)
+            
+        elif _destFreq == 'M': return Date('M',_date.mxDate().year,_date.mxDate().month)
+
+        elif _destFreq == 'S':
+            if _rel == "B": return Date('S',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day,0)
+            elif _rel == "A": return Date('S',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day,24*60*60-1)
+            
+        elif _destFreq == 'Q': return Date('Q',_date.mxDate().year,quarter=monthToQuarter(_date.mxDate().month))
+        
+        elif _destFreq == 'A': return Date('A',_date.mxDate().year)
+        
+    elif _date.freq == 'B':
+
+        if _destFreq == 'D': return Date('D',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day)
+
+        elif _destFreq == 'M': return Date('M',_date.mxDate().year,_date.mxDate().month)
+
+        elif _destFreq == 'S':
+            if _rel == "B": return Date('S',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day,0)
+            elif _rel == "A": return Date('S',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day,24*60*60-1)
+            
+        elif _destFreq == 'Q': return Date('Q',_date.mxDate().year,quarter=monthToQuarter(_date.mxDate().month))
+                
+        elif _destFreq == 'A': return Date('A',_date.mxDate().year)
+
+    elif _date.freq == 'M':
+
+        if _destFreq == 'D':
+            tempDate = _date.mxDate()
+            if _rel == "B":
+                return Date('D',_date.mxDate().year,_date.mxDate().month,1)
+            elif _rel == "A":
+                if _date.mxDate().month == 12:
+                    tempMonth = 1
+                    tempYear = _date.mxDate().year + 1
+                else:
+                    tempMonth = _date.mxDate().month + 1
+                    tempYear = _date.mxDate().year
+                return Date('D',tempYear,tempMonth,1)-1
+
+        elif _destFreq == 'B':
+            if _rel == "B": return dateOf(dateOf(_date,'D',"BEFORE"),'B',"AFTER")
+            elif _rel == "A": return dateOf(dateOf(_date,'D',"AFTER"),'B',"BEFORE")
+
+        elif _destFreq == 'S':
+            if _rel == "B": return dateOf(dateOf(_date,'D',"BEFORE"),'S',"BEFORE")
+            elif _rel == "A": return dateOf(dateOf(_date,'D',"AFTER"),'S',"AFTER")
+            
+        elif _destFreq == 'Q': return Date('Q',_date.mxDate().year,quarter=monthToQuarter(_date.mxDate().month))
+                        
+        elif _destFreq == 'A': return Date('A',_date.mxDate().year)
+    
+    elif _date.freq == 'S':
+
+        if _destFreq == 'D':
+            return Date('D',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day)
+        elif _destFreq == 'B':
+            if _rel == "B": return dateOf(dateOf(_date,'D'),'B',"BEFORE")
+            elif _rel == "A": return dateOf(dateOf(_date,'D'),'B',"AFTER")
+        elif _destFreq == 'M':
+            return Date('M',_date.mxDate().year,_date.mxDate().month)
+            
+    elif _date.freq == 'Q':
+    
+        if _destFreq == 'D':
+            if _rel == "B": return dateOf(_date-1,'D',"AFTER")+1
+            elif _rel == "A": return Date('D',_date.mxDate().year,_date.mxDate().month,_date.mxDate().day)
+        elif _destFreq == 'B':
+            if _rel == "B": return dateOf(dateOf(_date,'D'),'B',"AFTER")
+            if _rel == "A": return dateOf(dateOf(_date,'D',"AFTER"),'B',"BEFORE")
+        elif _destFreq == 'M':
+            if _rel == "B": return dateOf(_date-1,'M',"AFTER")+1
+            elif _rel == "A": return Date('M',_date.mxDate().year,_date.mxDate().month)
+        elif _destFreq == 'A': return Date('A',_date.mxDate().year)
+    elif _date.freq == 'A':
+        
+        if _destFreq == 'D':
+            if _rel == "B": return Date('D',_date.mxDate().year, 1, 1)
+            elif _rel == "A": return Date('D',_date.mxDate().year,12,31)            
+        elif _destFreq == 'B':
+            if _rel == "B": return dateOf(dateOf(_date,'D'),'B',"AFTER")
+            if _rel == "A": return dateOf(dateOf(_date,'D',"AFTER"),'B',"BEFORE")
+        elif _destFreq == 'M':
+            if _rel == "B": return Date('M',_date.mxDate().year,1)
+            elif _rel == "A": return Date('M',_date.mxDate().year,12)
+        elif _destFreq == 'Q':
+            if _rel == "B": return Date('Q',_date.mxDate().year,quarter=1)
+            elif _rel == "A": return Date('Q',_date.mxDate().year,quarter=4)
\ No newline at end of file

Added: trunk/Lib/sandbox/timeseries/tsdate.pyc
===================================================================
(Binary files differ)


Property changes on: trunk/Lib/sandbox/timeseries/tsdate.pyc
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream




More information about the Scipy-svn mailing list