/[escript]/temp_trunk_copy/escript/py_src/timeseries.py
ViewVC logotype

Contents of /temp_trunk_copy/escript/py_src/timeseries.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1384 - (show annotations)
Fri Jan 11 02:29:38 2008 UTC (11 years, 10 months ago) by phornby
File MIME type: text/x-python
File size: 43080 byte(s)
Make a temp copy of the trunk before checking in the windows changes


1 #
2 # $Id$
3 #
4 #######################################################
5 #
6 # Copyright 2003-2007 by ACceSS MNRF
7 # Copyright 2007 by University of Queensland
8 #
9 # http://esscc.uq.edu.au
10 # Primary Business: Queensland, Australia
11 # Licensed under the Open Software License version 3.0
12 # http://www.opensource.org/licenses/osl-3.0.php
13 #
14 #######################################################
15 #
16
17 """
18 Time serieas analysis
19
20 @var __author__: name of author
21 @var __copyright__: copyrights
22 @var __license__: licence agreement
23 @var __url__: url entry point on documentation
24 @var __version__: version
25 @var __date__: date of the version
26 """
27
28
29 __author__="Lutz Gross, l.gross@uq.edu.au"
30 __copyright__=""" Copyright (c) 2006 by ACcESS MNRF
31 http://www.access.edu.au
32 Primary Business: Queensland, Australia"""
33 __license__="""Licensed under the Open Software License version 3.0
34 http://www.opensource.org/licenses/osl-3.0.php"""
35 __url__="http://www.iservo.edu.au/esys/escript"
36 __version__="$Revision$"
37 __date__="$Date$"
38
39
40 import numarray
41 from types import SliceType
42 DEFAULT_BUFFER_SIZE=1000
43 DEFAULT_FLOAT_TYPE=numarray.Float64
44
45 class TimeSeriesBase:
46 """The TimeSeriesBase class is the base class for all class of the TimeSeries module."""
47
48 def __init__(self,debug=False,description="TimeSeriesBase"):
49 self.__debug=debug
50 self.setDescription(description)
51
52 def __str__(self):
53 return self.__description
54
55 def setDescription(self,text):
56 self.__description=text
57
58 def setDebugOn(self):
59 """switch on degugging mode"""
60 self.__debug=True
61
62 def setDebugOff(self):
63 """switch off degugging mode"""
64 self.__debug=False
65
66 def setDebug(self,flag=False):
67 """sets debug mode to flag"""
68 if flag:
69 self.setDebugOn()
70 else:
71 self.setDebugOff()
72
73 def debug(self):
74 """returns true if debug mode is on"""
75 return self.__debug
76
77 #============================================================================================================
78 class TimeSeriesBaseDataset(TimeSeriesBase):
79 """provides an interface for accessing a set of linearly ordered data."""
80 def __init__(self,buffer,offset=0,debug=False,description="TimeSeriesDataset"):
81 TimeSeriesBase.__init__(self,debug,description)
82 self.__buffer=buffer
83 self.__offset=offset
84 if self.debug(): print "Debug: %s: offset %d to buffer"%(self,self.getOffset())
85
86 def __len__(self):
87 """needed to handle negative indexing in slicing"""
88 return 0
89
90 def getNumComponents(self):
91 """returns the number of components of the data (may be overwritten by subclass)"""
92 return self.getBaseBuffer().getNumComponents()
93
94 def getIdOfLastDatum(self):
95 """returns the identification number of the last datum in the data set (may be overwritten by subclass)"""
96 return self.getBaseBuffer().getIdOfLastDatum()-self.getOffset()
97
98 def getIdOfFirstDatum(self):
99 """returns the identification number of the first datum (may be overwritten by subclass)"""
100 return self.getBaseBuffer().getIdOfFirstDatum()-self.getOffset()
101
102 def getIdOfFirstAvailableDatum(self):
103 """returns the identification number of the first avaiable datum (may be overwritten by subclass)"""
104 return self.getBaseBuffer().getIdOfFirstAvailableDatum()-self.getOffset()
105
106 def getOffsetInBaseBuffer(self):
107 """returns the offset to access elements in getBaseBuffer() (may be overwritten by subclass)"""
108 return self.getOffset()
109
110 def getIdOfLastUnreferencedDatum(self):
111 """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (may be overwritten by subclass)"""
112 return self.getBaseBuffer().getIdOfLastUnreferencedDatum()-self.getOffset()
113
114 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
115 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
116 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum+self.getOffset())
117
118 def append(self,values):
119 """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (to be overwritten by subclass)"""
120 self.getBaseBuffer().append(values)
121
122 def getBaseBufferSize(self):
123 """returns the size of the buffer (to be overwritten by subclass)"""
124 return self.getBaseBuffer().getBaseBufferSize()
125
126 def needsRearrangement(self,num_new_data=0):
127 """returns True if the buffer will be full after num_new_data have been appended (to be overwritten by subclass)"""
128 return self.getBaseBuffer().needsRearrangement(num_new_data)
129
130 def isEmpty(self):
131 """returns true if no data are appeneded to buffer"""
132 return self.getNumData()<=0
133
134 def getNumData(self):
135 """returns the number of data (not all of them are accessible)"""
136 return self.getIdOfLastDatum()-self.getIdOfFirstDatum()+1
137
138 def getBaseBuffer(self):
139 """return the buffer referenced by the TimeSeriesBaseDataset"""
140 return self.__buffer
141
142 def getOffset(self):
143 """return the offset when referring to dataset elements"""
144 return self.__offset
145
146 def __getitem__(self,index):
147 """returns the datum index"""
148 if type(index)==SliceType:
149 start=index.start
150 end=index.stop
151 if start==end:
152 return self[start]
153 else:
154 if start<self.getIdOfFirstDatum() or start>self.getIdOfLastDatum() or \
155 end-1<self.getIdOfFirstDatum() or end-1>self.getIdOfLastDatum(): raise IndexError,"%s: Index [%d:%d] out of range"%(self,start,end)
156 return self.getBaseBuffer()[start+self.getOffsetInBaseBuffer():end+self.getOffsetInBaseBuffer()]
157 else:
158 if index<self.getIdOfFirstDatum() or index>self.getIdOfLastDatum(): raise IndexError,"%s: Index %d out of range"%(self,index)
159 return self.getBaseBuffer()[index+self.getOffsetInBaseBuffer()]
160
161 class TimeSeriesBaseBuffer(TimeSeriesBaseDataset):
162 """An inplementation of TimeSeriesBaseDataset which actually is storing data into a numarray buffer"""
163 def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,numComponents=1,type=DEFAULT_FLOAT_TYPE,id_of_first_datum=0,debug=False,description="TimeSeriesBaseBuffer"):
164 if numComponents<2:
165 buffer=numarray.zeros((buffer_size,),type)
166 else:
167 buffer=numarray.zeros((buffer_size,numComponents),type)
168 TimeSeriesBaseDataset.__init__(self,buffer,id_of_first_datum-1,debug,description)
169 self.__num_data_in_buffer=0
170 self.__id_last_unreferenced_datum=id_of_first_datum-1
171 self.__id_last_datum=id_of_first_datum-1
172 self.__id_first_datum=id_of_first_datum
173 if self.debug(): print "Debug: %s : buffer of size %d with %d components allocated (first datum is %d)."% \
174 (self,self.getBaseBufferSize(),self.getNumComponents(),id_of_first_datum)
175
176
177 def getBaseBufferSize(self):
178 """returns the size of the buffer"""
179 return self.getBaseBuffer().shape[0]
180
181 def getNumComponents(self):
182 """returns the number of components of the data (overwrites TimeSeriesBaseDataset method)"""
183 if self.getBaseBuffer().rank==1:
184 return 1
185 else:
186 self.getBaseBuffer().shape[1]
187
188 def getNumDataInBaseBuffer(self):
189 """returns the number of data currently in the buffer"""
190 return self.__num_data_in_buffer
191
192 def getIdOfLastDatum(self):
193 """returns the identification number of the last datum in the data set (overwrites method from TimeSeriesBaseDataset)"""
194 return self.__id_last_datum
195
196 def getIdOfFirstDatum(self):
197 """returns the identification number of the first datum (overwrites method from TimeSeriesBaseDataset)"""
198 return self.__id_first_datum
199
200 def getOffsetInBaseBuffer(self):
201 """returns the offset to access elements in the buffer (overwrites method from TimeSeriesBaseDataset)"""
202 return -self.getIdOfLastDatum()+self.getNumDataInBaseBuffer()-1
203
204 def getIdOfLastUnreferencedDatum(self):
205 """returns the identification number of the last datum which has been unused by all TimeSeries refering to the TimeSeriesBaseDataset (overwrites method from TimeSeriesBaseDataset)"""
206 return self.__id_last_unreferenced_datum
207
208 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
209 """updates the identification number of the last unused datum (to be overwritten by subclass)"""
210 self.getBaseBuffer().updateIdOfLastUnreferencedDatum(last_unreferenced_datum-self.getOffset())
211
212 def updateIdOfLastUnreferencedDatum(self,last_unreferenced_datum):
213 """updates the identification number of the last unused datum (overwrites TimeSeriesBaseDataset method)"""
214 if self.__id_last_unreferenced_datum>last_unreferenced_datum:
215 self.__id_last_unreferenced_datum=last_unreferenced_datum
216 if self.debug(): print "Debug: %s: last unused datum is now %s"%(self,last_unreferenced_datum)
217
218 def needsRearrangement(self,num_new_data=0):
219 """returns True if the buffer will be full after num_new_data have been appended"""
220 return self.getNumDataInBaseBuffer()+num_new_data>self.getBaseBufferSize()
221
222 def getIdOfFirstAvailableDatum(self):
223 """returns the identification number of the first avaiable datum (overwrites TimeSeriesBaseDataset method)"""
224 return self.getIdOfLastDatum()-self.__num_data_in_buffer+1
225
226 def append(self,data):
227 """appends data to the buffer. If the buffer would be full the buffer is rearranged before the data are appended (overwrites TimeSeriesBaseDataset method)"""
228 data=numarray.array(data)
229 nc=self.getNumComponents()
230 if data.rank==0:
231 if nc==1:
232 num_new_data=1
233 else:
234 raise ValueError,"%s: illegal data shape"%self
235 elif data.rank==1:
236 if nc==1:
237 num_new_data=data.shape[0]
238 else:
239 num_new_data=1
240 elif data.rank==2:
241 if not nc==data.shape[1]: raise ValueError,"%s: illegal data shape"%self
242 num_new_data=data.shape[0]
243 else:
244 raise ValueError,"%s: illegal rank"%self
245
246 # check is buffer will be overflown when data are appended:
247 if self.needsRearrangement(num_new_data):
248 nn=self.getNumDataInBaseBuffer()
249 num_protected_data=self.getIdOfLastDatum()-self.getIdOfLastUnreferencedDatum()
250 if num_protected_data+num_new_data>self.getBaseBufferSize():
251 raise ValueError,"%s: buffer overflow: buffer size has to be bigger than %d"%(self,num_protected_data+num_new_data)
252 if num_protected_data>0: self.getBaseBuffer()[0:num_protected_data]=self.getBaseBuffer()[nn-num_protected_data:nn]
253 self.__num_data_in_buffer=num_protected_data
254 self.__id_last_unreferenced_datum=self.__id_last_datum
255 if self.debug():
256 print "Debug: %s: rearrangement: first data in buffer is %d."%(self,self.getIdOfLastDatum()-self.getNumDataInBaseBuffer()+1)
257 # copy data over:
258 nn=self.getNumDataInBaseBuffer()
259 self.getBaseBuffer()[nn:nn+num_new_data]=data
260 self.__num_data_in_buffer+=num_new_data
261 self.__id_last_datum+=num_new_data
262 self.__id_last_unreferenced_datum+=num_new_data
263 if self.debug(): print "Debug: %s: %d data appended. Last unreferenced datum is now %d."%(self,num_new_data,self.__id_last_unreferenced_datum)
264
265 # ======================================
266 class TimeSeriesControlerView(TimeSeriesBase):
267 """A TimeSeriesControlerView is attached to a Controler and moves forward in time by increasing the id of the last processed datum.
268 Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
269 def __init__(self,id_first_datum=0,debug=False,description="TimeSeries"):
270 TimeSeriesBase.__init__(self,debug,description)
271 self.__id_last_processed_datum=id_first_datum-1
272 if self.debug(): print "Debug: %s created with first datum %d"%(str(self),id_first_datum)
273
274 def getIdOfLastProcessedDatum(self):
275 return self.__id_last_processed_datum
276
277 def updateIdOfLastProcessedDatum(self,id_last_processed_datum):
278 self.__id_last_processed_datum=id_last_processed_datum
279
280 # def getControler(self):
281 # """returns the Controler of the time series (to be overwritten by subclass)"""
282 # pass
283
284 class TimeSeries(TimeSeriesBaseDataset,TimeSeriesControlerView):
285 """makes TimeSeriesBaseDataset look like a TimeSeries and introduces operations
286 Any implementation of a TimeSeriesControlerView must provide the getControler method which returns the controler"""
287 def __init__(self,dataset,debug=False,description="TimeSeries"):
288 TimeSeriesControlerView.__init__(self,dataset.getIdOfFirstDatum(),debug,description)
289 TimeSeriesBaseDataset.__init__(self,dataset,0,debug,description)
290
291 def getDataset(self):
292 """returns the TimeSeriesBaseDataset of the time series"""
293 return self.getBaseBuffer()
294
295 # def getControler(self):
296 # """returns the Controler of the time series (to be overwritten by subclass)"""
297 # pass
298
299 def __add__(self,arg):
300 if isinstance(arg,TimeSeriesBaseDataset):
301 return TimeSeriesAdd(self,arg)
302 else:
303 return TimeSeriesAddScalar(self,arg)
304
305 def __sub__(self,arg):
306 return self+(-1.)*arg
307
308 def __mul__(self,arg):
309 if isinstance(arg,TimeSeriesBaseDataset):
310 return TimeSeriesMult(self,arg)
311 else:
312 return TimeSeriesMultScalar(self,arg)
313
314 def __div__(self,arg):
315 if isinstance(arg,TimeSeriesBaseDataset):
316 return TimeSeriesDiv(self,arg)
317 else:
318 return TimeSeriesMultScalar(self,1./arg)
319
320 def __pow__(self,arg):
321 if isinstance(arg,TimeSeriesBaseDataset):
322 return TimeSeriesPower(self,arg)
323 else:
324 return TimeSeriesPowerScalar(self,arg)
325
326 def __radd__(self,arg):
327 return self.__add__(arg)
328
329 def __rsub__(self,arg):
330 return arg+(-1.)*self
331
332 def __rmul__(self,arg):
333 return self.__mul__(arg)
334
335 def __rdiv__(self,arg):
336 if isinstance(arg,TimeSeriesBaseDataset):
337 return TimeSeriesDiv(arg,self)
338 else:
339 return TimeSeriesDivScalar(self,arg)
340
341 def __rpow__(self,arg):
342 if isinstance(arg,TimeSeriesBaseDataset):
343 return TimeSeriesPower(arg,self)
344 else:
345 return Exp(numarray.log(arg)*self)
346
347 def __lshift__(self,arg):
348 return TimeSeriesShift(self,-arg)
349
350 def __rshift__(self,arg):
351 return TimeSeriesShift(self,arg)
352
353 def __neg__(self):
354 return (-1.0)*self
355
356 def __pos__(self):
357 return (1.0)*self
358
359 class TimeSeriesOperator(TimeSeriesControlerView):
360 """a TimeSeriesOperator decribes an operation acting on list of TimeSeries time_series_args. It allows to update its output (if there is any)
361 through the update method which is overwritten by a particular implementation of the class. The update method is called to process the data [start:end] using
362 [start-left_wing_size:end+right_wing_size] of its arguments"""
363 def __init__(self,controler,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesOperator"):
364 id_first_datum=controler.getIdOfFirstDatum()
365 for i in time_series_args: id_first_datum=max(id_first_datum,i.getIdOfFirstDatum())
366 TimeSeriesControlerView.__init__(self,id_first_datum+left_wing_size,debug,description)
367 self.__left_wing_size=left_wing_size
368 self.__right_wing_size=right_wing_size
369 self.__time_series_args=time_series_args
370 self.__controler=controler
371 controler.appendOperatorToUpdateList(self)
372 if self.debug(): print "Debug: %s: with left/right wing size %d/%d and %d arguments."%(str(self),left_wing_size,right_wing_size,len(time_series_args))
373
374 def __del__(self):
375 self.getControler().removeOperatorFromUpdateList(self)
376
377 def getControler(self):
378 """returns the Controler updating the TimeSeriesOperator"""
379 return self.__controler
380
381 def getLeftWingSize(self):
382 """returns the left wing size"""
383 return self.__left_wing_size
384
385 def getRightWingSize(self):
386 """returns the right wing size"""
387 return self.__right_wing_size
388
389 def getArguments(self,index=None):
390 """returns the list of arguments or, index is present, the argument with index index. In the latter case None is returned if no arguments are present"""
391 if index==None:
392 return self.__time_series_args
393 else:
394 if len(self.__time_series_args)>0:
395 return self.__time_series_args[index]
396 else:
397 return None
398
399 def getArgumentDataset(self,index):
400 """returns the dataset of in the argument with index index"""
401 arg=self.getArguments(index)
402 if arg==None:
403 return None
404 else:
405 return self.getArguments(index).getDataset()
406
407 def flush(self):
408 """calls the update method with all the maximum processable range. It also updates the id of unused datum for all arguments"""
409 start=self.getIdOfLastProcessedDatum()+1
410 end=self.getControler().getIdOfLastDatum()
411 for i in self.getArguments(): end=min(end,i.getIdOfLastDatum())
412 if start<=end-self.getRightWingSize():
413 if self.debug(): print "Debug: %s: range [%d:%d] is updated."%(self,start,end-self.getRightWingSize())
414 self.update(start,end-self.getRightWingSize()+1)
415 for i in self.getArguments(): i.updateIdOfLastUnreferencedDatum(end-self.getLeftWingSize())
416 self.updateIdOfLastProcessedDatum(end)
417
418 def update(self,start,end):
419 """updates the the data [start:end] using [start-left_wing_size:end+right_wing_size] of its arguments (is overwritten by a particular TimeSeriesOperator)"""
420 pass
421
422
423 class TimeSeriesFilter(TimeSeries,TimeSeriesOperator):
424 """a TimeSeriesFilter is a TimeSeries taht is created trough a TimeSeriesOperator"""
425 def __init__(self,controler,dataset,time_series_args=[],left_wing_size=0,right_wing_size=0,debug=False,description="TimeSeriesFilter"):
426 TimeSeriesOperator.__init__(self,controler,time_series_args,left_wing_size,right_wing_size,debug,description)
427 TimeSeries.__init__(self,dataset,debug,description)
428
429 def update(self,start,end):
430 """appends zeros to the dataset. This method should be overwritten by a particular TimeSeriesFilter"""
431 nc=self.getNumComponents()
432 if nc>1:
433 self.getDataset().append(numarray.zeros([nc,end-start]))
434 else:
435 self.getDataset().append(numarray.zeros(end-start))
436
437 class Controler(TimeSeries):
438 """controls a set of TimeSeries"""
439 def __init__(self,buffer_size=DEFAULT_BUFFER_SIZE,debug=False,description="TimeSeriesControler"):
440 TimeSeries.__init__(self,TimeSeriesBaseBuffer(buffer_size,1,DEFAULT_FLOAT_TYPE,0,debug,"node buffer of "+description),debug,"nodes of "+description)
441 self.setFlushRate()
442 self.__update_time_series=list()
443
444 def getControler(self):
445 """returns the Controler of the time series (overwrites method of by TimeSeries)"""
446 return self
447
448 def setFlushRate(self,rate=50):
449 """set the flush rate, i.e. after rate new time nodes have been checked in the flush method is called."""
450 self.__flush_rate=rate
451 if self.debug(): print "Debug: %s: flush rate is set to %d"%(self,rate)
452
453 def needsFlushing(self):
454 """returns true if the depending TimeSeriesFilters needs to be flushed becuase the time nodes buffer is full or because of the set flush rate"""
455 return self.needsRearrangement(1) or (self.getNumData()+1)%self.__flush_rate==0
456
457 def flush(self):
458 """flushes all dependend TimeSeriesFilters by processing their flush method"""
459 if self.debug(): print "Debug: %s: start flushing"%self
460 for time_serie in self.__update_time_series: time_serie.flush()
461
462 def appendOperatorToUpdateList(self,time_serie):
463 if not time_serie.getControler()==self: raise ValueError,"%s: TimeSeries %s is not defined on this controler."%(self,time_serie)
464 if not self.isEmpty(): raise ValueError,"%s: you can only check in a time series time_serie is controler is empty."%self
465 self.__update_time_series.append(time_serie)
466 if self.debug(): print "Debug: %s: %s has been added to update list."%(self,time_serie)
467
468 def removeOperatorFromUpdateList(self,time_serie):
469 self.__update_time_series.remove(time_serie)
470 if self.debug(): print "Debug: %s: %s has been removed from update list."%(self,time_serie)
471
472 def nextTime(self,value):
473 if self.needsFlushing(): self.flush()
474 self.getDataset().append(value)
475 if self.debug(): print "Debug: %s: new time node %e has been added."%(self,value)
476
477 class TimeSeriesShift(TimeSeries):
478 """creates a shift of the time series, i.e. if d[n] is the datum at time t[n], the value at t[n] becomes v[n+shift] on the output"""
479 def __init__(self,time_serie,shift=1):
480 if shift<0:
481 dsc="(%s)<<%d"%(time_serie,-shift)
482 else:
483 dsc="(%s)>>%d"%(time_serie,shift)
484 self.__controler=time_serie.getControler()
485 TimeSeries.__init__(self,TimeSeriesBaseDataset(time_serie.getDataset(),-shift,time_serie.debug(),"buffer view to "+dsc),time_serie.debug(),dsc)
486
487 def getControler(self):
488 return self.__controler
489
490 class TimeSeriesAdd(TimeSeriesFilter):
491 """adds two TimeSeries"""
492 def __init__(self,time_serie_1,time_serie_2):
493 dsc="(%s)+(%s)"%(time_serie_1,time_serie_2)
494 dbg=time_serie_1.debug() or time_serie_2.debug()
495 cntrl=time_serie_1.getControler()
496 if not cntrl==time_serie_2.getControler():
497 raise ValueError("TimeSeriesAdd: %s and %s have different controler."%(time_serie_1,time_serie_2))
498 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
499 TimeSeriesFilter.__init__(self,cntrl, \
500 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
501 [time_serie_1,time_serie_2],0,0,dbg,dsc)
502
503 def update(self,start,end):
504 self.append(self.getArgumentDataset(0)[start:end]+self.getArgumentDataset(1)[start:end])
505
506 class TimeSeriesAddScalar(TimeSeriesFilter):
507 """adds a single value to a TimeSeries"""
508 def __init__(self,time_serie,scalar):
509 dsc="(%s)+(%s)"%(time_serie,scalar)
510 dbg=time_serie.debug()
511 cntrl=time_serie.getControler()
512 id_first_datum=time_serie.getIdOfFirstDatum()
513 TimeSeriesFilter.__init__(self,cntrl, \
514 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
515 [time_serie],0,0,dbg,dsc)
516 self.__scalar=scalar
517
518 def update(self,start,end):
519 self.append(self.getArgumentDataset(0)[start:end]+self.__scalar)
520
521 class TimeSeriesMult(TimeSeriesFilter):
522 """multiplies two TimeSeries"""
523 def __init__(self,time_serie_1,time_serie_2):
524 dsc="(%s)*(%s)"%(time_serie_1,time_serie_2)
525 dbg=time_serie_1.debug() or time_serie_2.debug()
526 cntrl=time_serie_1.getControler()
527 if not cntrl==time_serie_2.getControler():
528 raise ValueError("TimeSeriesMult: %s and %s have different controler."%(time_serie_1,time_serie_2))
529 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
530 TimeSeriesFilter.__init__(self,cntrl, \
531 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
532 [time_serie_1,time_serie_2],0,0,dbg,dsc)
533
534 def update(self,start,end):
535 self.append(self.getArgumentDataset(0)[start:end]*self.getArgumentDataset(1)[start:end])
536
537 class TimeSeriesMultScalar(TimeSeriesFilter):
538 """multiplies a TimeSeries with a single value"""
539 def __init__(self,time_serie,scalar):
540 dsc="(%s)*%s"%(time_serie,scalar)
541 dbg=time_serie.debug()
542 cntrl=time_serie.getControler()
543 id_first_datum=time_serie.getIdOfFirstDatum()
544 TimeSeriesFilter.__init__(self,cntrl, \
545 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
546 [time_serie],0,0,dbg,dsc)
547 self.__scalar=scalar
548
549 def update(self,start,end):
550 self.append(self.getArgumentDataset(0)[start:end]*self.__scalar)
551
552 class TimeSeriesDiv(TimeSeriesFilter):
553 """divides two TimeSeries"""
554 def __init__(self,time_serie_1,time_serie_2):
555 dsc="(%s)/(%s)"%(time_serie_1,time_serie_2)
556 dbg=time_serie_1.debug() or time_serie_2.debug()
557 cntrl=time_serie_1.getControler()
558 if not cntrl==time_serie_2.getControler():
559 raise ValueError("TimeSeriesDiv: %s and %s have different controler."%(time_serie_1,time_serie_2))
560 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
561 TimeSeriesFilter.__init__(self,cntrl, \
562 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
563 [time_serie_1,time_serie_2],0,0,dbg,dsc)
564
565 def update(self,start,end):
566 self.append(self.getArgumentDataset(0)[start:end]/self.getArgumentDataset(1)[start:end])
567
568 class TimeSeriesDivScalar(TimeSeriesFilter):
569 """divides a scalar be a TimeSerie"""
570 def __init__(self,time_serie,scalar):
571 dsc="(%s)/(%s)"%(scalar,time_serie)
572 dbg=time_serie.debug()
573 cntrl=time_serie.getControler()
574 id_first_datum=time_serie.getIdOfFirstDatum()
575 TimeSeriesFilter.__init__(self,cntrl, \
576 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
577 [time_serie],0,0,dbg,dsc)
578 self.__scalar=scalar
579
580 def update(self,start,end):
581 self.append(self.__scalar/self.getArgumentDataset(0)[start:end])
582
583 class TimeSeriesPower(TimeSeriesFilter):
584 """raise one TimeSeries to the power of an other TimeSeries"""
585 def __init__(self,time_serie_1,time_serie_2):
586 dsc="(%s)**(%s)"%(time_serie_1,time_serie_2)
587 dbg=time_serie_1.debug() or time_serie_2.debug()
588 cntrl=time_serie_1.getControler()
589 if not cntrl==time_serie_2.getControler():
590 raise ValueError("TimeSeriesPower: %s and %s have different controler."%(time_serie_1,time_serie_2))
591 id_first_datum=max(time_serie_1.getIdOfFirstDatum(),time_serie_2.getIdOfFirstDatum())
592 TimeSeriesFilter.__init__(self,cntrl, \
593 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie_1.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
594 [time_serie_1,time_serie_2],0,0,dbg,dsc)
595
596 def update(self,start,end):
597 self.append(self.getArgumentDataset(0)[start:end]**self.getArgumentDataset(1)[start:end])
598
599 class TimeSeriesPowerScalar(TimeSeriesFilter):
600 """raises a TimeSerie to the power of a scalar"""
601 def __init__(self,time_serie,scalar):
602 dsc="(%s)**(%s)"%(time_serie,scalar)
603 dbg=time_serie.debug()
604 cntrl=time_serie.getControler()
605 id_first_datum=time_serie.getIdOfFirstDatum()
606 TimeSeriesFilter.__init__(self,cntrl, \
607 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
608 [time_serie],0,0,dbg,dsc)
609 self.__scalar=scalar
610
611 def update(self,start,end):
612 self.append(self.getArgumentDataset(0)[start:end]**self.__scalar)
613
614 class Exp(TimeSeriesFilter):
615 """"""
616 def __init__(self,time_serie):
617 dsc="exp(%s)"%(time_serie)
618 dbg=time_serie.debug()
619 cntrl=time_serie.getControler()
620 id_first_datum=time_serie.getIdOfFirstDatum()
621 TimeSeriesFilter.__init__(self,cntrl, \
622 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
623 [time_serie],0,0,dbg,dsc)
624
625 def update(self,start,end):
626 self.append(numarray.exp(self.getArgumentDataset(0)[start:end]))
627
628 class Writer(TimeSeriesOperator):
629 """writes the time series into an output strim ostream which mast have the writeline method. The values are seperated by the string seperator."""
630 def __init__(self,time_serie,ostream,seperator=",",commend_tag="#"):
631 dsc="write %s to %s"%(time_serie,ostream)
632 dbg=time_serie.debug()
633 cntrl=time_serie.getControler()
634 self.__ostream=ostream
635 self.__seperator=seperator
636 TimeSeriesOperator.__init__(self,cntrl,[time_serie],0,0,dbg,dsc)
637 ostream.writelines("%s time series %s\n"%(commend_tag,str(self)))
638
639 def update(self,start,end):
640 cntrl=self.getControler()
641 arg=self.getArguments(0)
642 n=arg.getNumComponents()
643 if n<2:
644 for i in range(start,end): self.__ostream.writelines("%s%s%s\n"%(cntrl[i],self.__seperator,arg[i]))
645 else:
646 for i in range(start,end):
647 l="%s"%cntrl[i]
648 for j in range(n): l=l+"%s%s"(self.__seperator,arg[i][j])
649 self.__ostream.writelines("%s\n"%l)
650
651 class DataCatcher(TimeSeries):
652 """collects data into a time series."""
653 def __init__(self,controler,numComponents=1,description="DataCatcher"):
654 self.__controler=controler
655 dbg=controler.debug()
656 TimeSeries.__init__(self,TimeSeriesBaseBuffer(controler.getBaseBufferSize(),numComponents,DEFAULT_FLOAT_TYPE,controler.getIdOfFirstDatum(),dbg,"buffer for "+description),dbg,description)
657
658 def getControler(self):
659 return self.__controler
660
661 def nextValue(self,value):
662 """append a value to the time series"""
663 id_last=self.getIdOfLastDatum()
664 id_current=self.getControler().getIdOfLastDatum()
665 if id_last+1==id_current:
666 self.getDataset().append(value)
667 elif id_last+1<id_current:
668 if self.isEmpty():
669 self.getDataset().append(value)
670 id_last+=1
671 t_last=self.getControler()[id_last]
672 t_current=self.getControler()[id_current]
673 value_last=self[id_last]
674 out=(value_last-value)/(t_last-t_current)*(self.getControler()[id_last+1:id_current+1]-t_current)+value
675 self.getDataset().append(out)
676 else :
677 raise ValueError,"%s: a new time node must be introduced before a new value can be added."
678 self.updateIdOfLastUnreferencedDatum(id_last)
679
680
681 class TimeSeriesCumulativeSum(TimeSeriesFilter):
682 """cummulative sum of the time series values"""
683 def __init__(self,time_serie):
684 dsc="cumsum(%s)"%(time_serie)
685 dbg=time_serie.debug()
686 cntrl=time_serie.getControler()
687 id_first_datum=time_serie.getIdOfFirstDatum()
688 TimeSeriesFilter.__init__(self,cntrl, \
689 TimeSeriesBaseBuffer(cntrl.getBaseBufferSize(),time_serie.getNumComponents(),DEFAULT_FLOAT_TYPE,id_first_datum,dbg,"buffer for "+dsc), \
690 [time_serie],0,0,dbg,dsc)
691 self.__last_value=0
692
693 def update(self,start,end):
694 out=numarray.cumsum(self.getArgumentDataset(0)[start:end])+self.__last_value
695 self.__last_value=out[end-start-1]
696 self.append(out)
697
698
699 class Reader(TimeSeriesBase):
700 """reads a list of input streams and creates a time series for each input stream but on the same Controler where the first column
701 is used to create the time nodes"""
702 def __init__(self,list_of_istreams,buffer_size=DEFAULT_BUFFER_SIZE,seperator=",",commend_tag="#",debug=False):
703 TimeSeriesBase.__init__(self,debug=debug,description="reader")
704 if not isinstance(list_of_istreams,list):
705 self.__list_of_istreams=[list_of_istreams]
706 else:
707 self.__list_of_istreams=list_of_istreams
708 self.__cntrl=Controler(buffer_size,debug,"reader controler")
709 self.__seperator=seperator
710 self.__commend_tag=commend_tag
711 self.__time_series={}
712 self.__t={}
713 self.__v={}
714 # set up the time series:
715 for i in self.__list_of_istreams:
716 line=self.__commend_tag
717 while not line=="" and line[0]==self.__commend_tag:
718 line=i.readline().strip()
719 if line=="":
720 list_of_istreams.remove(i)
721 else:
722 d=line.split(self.__seperator)
723 self.__t[i]=float(d[0])
724 tmp=[]
725 for j in d[1:]: tmp.append(float(j))
726 self.__v[i]=numarray.array(tmp)
727 self.__time_series[i]=DataCatcher(self.__cntrl,len(d)-1,str(i))
728
729 #
730 def run(self):
731 while len(self.__list_of_istreams)>0:
732 if len(self.__time_series)>0:
733 # find list all times with minumum time node:
734 tminargs=[]
735 for i in self.__time_series:
736 if len(tminargs)==0:
737 tminargs.append(i)
738 elif abs(t[tminargs[0]]-self.__t[i])<1.e-8*abs(self.__t[i]):
739 tminargs.append(i)
740 elif self.__t[i]<t[tminargs[0]]:
741 tminargs=[i]
742 # find list all times with minumum time node:
743 self.__cntrl.nextTime(self.__t[tminargs[0]])
744 for i in tminargs:
745 self.__time_series[i].nextValue(self.__v[i])
746 # find next line without leading "#"
747 line="#"
748 while not line=="" and line[0]==self.__commend_tag:
749 line=i.readline().strip()
750 # if eof reached iostream is removed for searching
751 if line=="":
752 self.__list_of_istreams.remove(i)
753 else:
754 d=line.split(self.__seperator)
755 self.__t[i]=float(d[0])
756 tmp=[]
757 for j in d[1:]: tmp.append(float(j))
758 self.__v[i]=numarray.array(tmp)
759
760 def getControler(self):
761 """returns the controler shared by all time series created through the input streams"""
762 return self.__cntrl
763
764 def getTimeSeries(self,istream=None):
765 """returns the time series as a tuple. If istream is present its time series is returned"""
766 if istream==None:
767 out=self.__time_series.values()
768 if len(out)>1:
769 return tuple(out)
770 elif len(out)>0:
771 return out[0]
772 else:
773 return None
774 else:
775 return self.__time_series[istream]
776
777
778 class Plotter(TimeSeriesOperator):
779 def __init__(self,time_series,window_size=DEFAULT_BUFFER_SIZE/4,file_name=None,format=None):
780 if isinstance(time_series,list):
781 dbg=time_series[0].getControler().debug()
782 text=""
783 for i in time_series:
784 if len(text)==0:
785 text=str(i)
786 else:
787 text=text+","+str(i)
788 TimeSeriesOperator.__init__(self,time_series[0].getControler(),time_series,window_size,0,dbg,"plot(%s)"%text)
789 else:
790 dbg=time_series.getControler().debug()
791 text=str(time_series)
792 TimeSeriesOperator.__init__(self,time_series.getControler(),[time_series],window_size,0,dbg,"plot(%s)"%text)
793 from pyvisi.renderers.gnuplot import LinePlot,Scene,PsImage
794 self.__renderer=Scene()
795 self.__line_plot=LinePlot(self.__renderer)
796 self.__line_plot.setTitle(text)
797 self.__line_plot.setLineStyle("lines")
798 self.__line_plot.setXLabel("time")
799 self.__line_plot.setYLabel("values")
800 self.__file_name=file_name
801 if format==None:
802 self.__format=PsImage()
803 else:
804 self.__format=format
805 self.__window_size=window_size
806
807 def update(self,start,end):
808 s=max(end-self.__window_size,self.getControler().getIdOfFirstAvailableDatum())
809 args=[self.getControler()[s:end]]
810 for arg in self.getArguments(): args.append(arg[s:end])
811 self.__line_plot.setData(*args)
812 self.__line_plot.render()
813 if self.__file_name==None:
814 raise SystemError,"Online viewing is not avilabel yet!"
815 else:
816 self.__renderer.save(fname=self.__file_name, format=self.__format)
817
818
819 def viewer(time_serie,seperator=","):
820 """creates a viewer for a time series"""
821 import sys
822 return Writer(time_serie,sys.stdout,seperator)
823
824 def differential(time_serie):
825 """calculates the derivative Dv of the time series v:
826
827 Dv[n]=(v[n]-v[n-1])/(t[n]-t[n-1])
828
829 """
830 out=(((time_serie<<1)-time_serie)/((time_serie.getControler()<<1)-time_serie.getControler())+ \
831 ((time_serie>>1)-time_serie)/((time_serie.getControler()>>1)-time_serie.getControler()))/2.
832 out.setDescription("d(%s)/dt"%str(time_serie))
833 out.setDebug(time_serie.debug())
834 return out
835
836 def integral(time_serie):
837 """calculates the intagral Iv of the time series v using the trapozidal rule:
838
839 Iv[n]=int_{t_0}^{t_n} v ~ sum_{0<i<=n} n (v[i]+v[i-1])/2*(t[i]-t[i-1])
840
841 """
842 out=TimeSeriesCumulativeSum(((time_serie>>1)+time_serie)/2.*(time_serie.getControler()-(time_serie.getControler()>>1)))
843 out.setDescription("I (%s) dt"%str(time_serie))
844 out.setDebug(time_serie.debug())
845 return out
846
847 def smooth(time_serie,range=5):
848 """smoothes a time series using the at each time the previous and next range values"""
849 i=integral(time_serie)
850 out=((i>>range)-(i<<range))/((time_serie.getControler()>>range)-(time_serie.getControler()<<range))
851 out.setDescription("smooth(%s,-%d:%d) dt"%(str(time_serie),range,range))
852 out.setDebug(time_serie.debug())
853 return out
854
855 def leakySmooth(time_serie,l=0.99):
856 """leaky smoother: s(t)=int_{t_0}^{t} v(r) l^{t-r} dr/ int_{t_0}^{t} l^{t-r} dr """
857 w=l**(-time_serie.getControler())
858 out=integrate(time_serie*w)/integrate(w)
859 out.setDescription("leaky smoother(%s)"%str(time_serie))
860 return out
861
862 # test
863
864 if __name__=="__main__":
865 # tests the interfaces to data sets:
866 print "Test of Datasets:"
867 print "================="
868 bf=TimeSeriesBaseBuffer(buffer_size=5,numComponents=1,debug=True,description="TestBaseBuffer")
869 bfv_l=TimeSeriesBaseDataset(bf,offset=1,debug=True,description="offset 1")
870 bfv_r=TimeSeriesBaseDataset(bf,offset=-1,debug=True,description="offset -1")
871 bf.append([1.,2.,3.,4.])
872 print "should be all 2. :",bfv_l[0]
873 print bf[1]
874 print bfv_r[2]
875 bf.append([5.,6.,7.])
876 print "should be all 5. :",bfv_l[3],bf[4],bfv_r[5]
877 print "should be all 6. :",bfv_l[4],bf[5],bfv_r[6]
878 print "should be all 7. :",bfv_l[5],bf[6],bfv_r[7]
879 print "should be all [6., 7.] :",bfv_l[4:6],bf[5:7],bfv_r[6:8]
880
881 print "Test of Controler"
882 print "================="
883 b=Controler(buffer_size=15,debug=True)
884 s3=b>>3
885 s1=b>>1
886 s_3=b<<3
887 print s_3
888 print b
889 print b+s3
890 sum=(s_3+b)+(b+s3)
891
892 for i in range(30):
893 b.nextTime(i*1.)
894 b.flush()
895 print "should be all 28. :",s_3.getDataset()[25],b.getDataset()[28],s3.getDataset()[31]
896 print "should be all 29. :",s_3.getDataset()[26],b.getDataset()[29],s3.getDataset()[32]
897 print "should be all 96. :",sum.getDataset()[24]
898
899 print "Test of operators"
900 print "================="
901 b=Controler(buffer_size=15,debug=True)
902 b.setFlushRate(2)
903 q=DataCatcher(b)
904 b1=b<<1
905 a=b+b1
906 a_s=b1+1.
907 s_a=1.+b1
908 d=b-b1
909 d_s=b1-1.
910 s_d=1.-b1
911 m=b*b1
912 m_s=b1*2.
913 s_m=2.*b1
914 dv=b/b1
915 dv_s=b1/2.
916 s_dv=2./b1
917 p=b**b1
918 p_s=b1**2.
919 s_p=2.**b1
920 pb=+b
921 mb=-b
922 sum=TimeSeriesCumulativeSum(b)
923 diff=differential(b)
924 smt=smooth(b,2)
925 int=integral(b*2)
926 fl=file("/tmp/test.csv","w")
927 w=Writer(q,fl)
928 v=viewer(q)
929 plo=Plotter([a,a_s],window_size=4,file_name="s.ps")
930 for i in range(30):
931 b.nextTime(i*1.)
932 if i%2==1: q.nextValue(i*28.)
933 b.flush()
934 print "a[28] should be %e: %e"%(28.+29.,a[28])
935 print "a_s[28] should be %e: %e"%(29.+1.,a_s[28])
936 print "s_a[28] should be %e: %e"%(29.+1.,s_a[28])
937 print "d[28] should be %e: %e"%(28.-29.,d[28])
938 print "d_s[28] should %e: %e"%(29.-1.,d_s[28])
939 print "s_d[28] should %e: %e"%(1.-29.,s_d[28])
940 print "m[28] should be %e: %e"%(28.*29.,m[28])
941 print "m_s[28] should be %e: %e"%(29.*2.,m_s[28])
942 print "s_m[28] should be %e: %e"%(29.*2.,s_m[28])
943 print "dv[28] should be %e: %e"%(28./29.,dv[28])
944 print "dv_s[28] should be %e: %e"%(29./2.,dv_s[28])
945 print "s_dv[28] should be %e: %e"%(2./29.,s_dv[28])
946 print "p[28] should be %e: %e"%(28.**29.,p[28])
947 print "p_s[28] should be %e: %e"%(29.**2,p_s[28])
948 print "s_p[28] should be %e: %e"%(2.**29.,s_p[28])
949 print "pb[28] should be %e: %e"%(28.,pb[28])
950 print "mb[28] should be %e: %e"%(-28.,mb[28])
951 print "sum[28] should be %e: %e"%(28*29./2,sum[28])
952 print "diff[28] should be %e: %e"%(1.,diff[28])
953 print "smt[27] should be %e: %e"%(27.,smt[27])
954 print "int[28] should be %e: %e"%(28.**2,int[28])
955 print "q[27] should be %e: %e"%(27*28.,q[27])
956 print "q[28] should be %e: %e"%(28*28.,q[28])
957 print "q[29] should be %e: %e"%(29*28.,q[29])
958 fl.flush()
959
960 rin=Reader(file("/tmp/test.csv","r+"),buffer_size=15,debug=True)
961 rin.run()
962 inp=rin.getTimeSeries()
963 print "inp[27] should be %e: %e"%(27*28.,inp[27])
964 print "inp[28] should be %e: %e"%(28*28.,inp[28])
965 print "inp[29] should be %e: %e"%(29*28.,inp[29])
966

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.26