faif
TimeSeries.hpp
1 #ifndef FAIF_TIME_SERIES
2 #define FAIF_TIME_SERIES
3 
4 // the file with timeseries tools
5 
6 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
7 //msvc8.0 warnings for boost::date_time
8 #pragma warning(disable:4100)
9 #pragma warning(disable:4512)
10 #pragma warning(disable:4127)
11 #pragma warning(disable:4245)
12 //msvc8.0 warnings for std::transform
13 #pragma warning(disable:4996)
14 #endif
15 
16 #include <cmath>
17 #include <ostream>
18 #include <vector>
19 #include <algorithm>
20 #include <iterator>
21 #include <numeric>
22 #include <functional>
23 
24 #include <boost/bind.hpp>
25 #include <boost/tuple/tuple.hpp>
26 #include <boost/tuple/tuple_comparison.hpp>
27 #include <boost/date_time/posix_time/posix_time.hpp>
28 
29 namespace faif {
30  /** \brief TimeSeries (collection of triples<timestamp, value, quality>) tools
31  */
32  namespace timeseries {
33 
34  /** \brief the real time type */
35  typedef boost::posix_time::ptime RealTime;
36  /** \brief the real time duration */
37  typedef boost::posix_time::time_duration RealDuration;
38 
39  /** \brief convert from RealTime to posix_t (num of seconds from 1970) */
40  inline long to_time_t(const RealTime& real_time) {
41  boost::posix_time::ptime start(boost::gregorian::date(1970,1,1),boost::posix_time::time_duration(0,0,0));
42  return (real_time-start).total_seconds();
43  }
44 
45  /** \brief digit time type.
46  DigitTime < 0 past, DigitTime >= 0 future, DigitTime == 0 now. */
47  typedef int DigitTime;
48  /** \brief value - real number */
49  typedef double Value;
50  /** \brief quality - real 0..1 */
51  typedef double Quality;
52 
53  /** \brief timeseries value, single value in given real time. Plain old data */
54  struct TimeValueReal : public boost::tuple<RealTime,Value,Quality> {
55 
56  /** c-tor */
57  TimeValueReal(RealTime t, const Value& v, const Quality& q = 0.0)
58  : boost::tuple<RealTime,Value,Quality>(t,v,q)
59  {}
60  /** accessor */
61  const RealTime& getTime() const { return get<0>(); }
62  /** accessor */
63  const Value& getValue() const { return get<1>(); }
64  /** accessor */
65  const Quality& getQuality() const { return get<2>(); }
66 
67  };
68  /** (for debugging) */
69  inline std::ostream& operator<<(std::ostream& os, const TimeValueReal& time_val) {
70  os << '(' << time_val.getTime() << ',' << time_val.getValue() << ')';
71  return os;
72  }
73 
74 
75  /** \brief timeseries - time hold as RealTime */
76  class TimeSeriesReal : public std::vector<TimeValueReal> {
77  public:
78  typedef std::vector<TimeValueReal>::iterator iterator;
79  typedef std::vector<TimeValueReal>::const_iterator const_iterator;
80 
81  /** \brief empty collection */
83 
84  /** \brief copy c-tor */
85  TimeSeriesReal(const TimeSeriesReal& t) : std::vector<TimeValueReal>(t) {}
86 
87  /** \brief c-tor from a range */
88  TimeSeriesReal(const TimeValueReal* begin, const TimeValueReal* end)
89  : std::vector<TimeValueReal>(begin,end) {}
90  /** \brief c-tor from a range */
91  TimeSeriesReal(const_iterator begin, const_iterator end)
92  : std::vector<TimeValueReal>(begin,end) {}
93 
94  /** \brief c-tor from a C style table of timestamps (as long - posix time), and C style table of values */
95  TimeSeriesReal(const long* time_begin, const long* time_end, const Value* value_begin);
96 
97  /** \brief c-tor from C style table of values.
98  The timestamps are calculated from start_time and delta */
99  TimeSeriesReal(const RealTime& start_time, const RealDuration& delta,
100  const Value* value_begin, const Value* value_end );
101 
102  /** \brief c-tor from timeseries - the timestams are modified (the offset is added) */
103  TimeSeriesReal(const TimeSeriesReal& ts, const RealTime& offset);
104  /** operator = */
106  std::vector<TimeValueReal>::operator=(t);
107  return *this;
108  }
109  /** d-tor */
111 
112  /** the sum of values of timeseries */
113  double getSum() const {
114  return std::accumulate( begin(), end(), 0.0, boost::bind( std::plus<double>(), _1, boost::bind(&TimeValueReal::getValue, _2) ) );
115  }
116 
117  /** the average of values of timeseries, 0.0 if the timeseries is empty. */
118  double getAvg() const { return empty() ? 0.0 : getSum()/size(); }
119 
120  /** the integral (the area under the line) for timeseries */
121  double getIntegral() const;
122  };
123 
124  /** (for debugging) */
125  inline std::ostream& operator<<(std::ostream& os, const TimeSeriesReal& time_series) {
126  std::copy( time_series.begin(), time_series.end(), std::ostream_iterator<TimeValueReal>(os," ") );
127  return os;
128  }
129 
130  /** the timeseries value, the time is the offset (DigitTime), plain old data. */
131  struct TimeValueDigit : public boost::tuple<DigitTime,Value,Quality> {
132 
133  /** c-tor */
134  TimeValueDigit(DigitTime t = 0, const Value& v = 0.0, const Quality& q = 0.0)
135  : boost::tuple<DigitTime,Value,Quality>(t,v,q)
136  {}
137  /** accessor */
138  const DigitTime& getTime() const { return get<0>(); }
139  /** accessor */
140  const Value& getValue() const { return get<1>(); }
141  /** accessor */
142  const Quality& getQuality() const { return get<2>(); }
143  };
144  /** for debugging */
145  inline std::ostream& operator<<(std::ostream& os, const TimeValueDigit& time_val) {
146  os << '(' << time_val.getTime() << ',' << time_val.getValue() << ')';
147  return os;
148  }
149 
150  /** the timeseries (collection), time as DigitTime */
151  class TimeSeriesDigit : public std::vector<TimeValueDigit> {
152  public:
153  typedef std::vector<TimeValueDigit>::iterator iterator;
154  typedef std::vector<TimeValueDigit>::const_iterator const_iterator;
155 
156  /** \brief c-tor, empty timeseries */
158  /** \brief c-tor, from range */
160  : std::vector<TimeValueDigit>(begin,end) {}
161  /** \brief c-tor, from range */
162  TimeSeriesDigit(const_iterator begin, const_iterator end)
163  : std::vector<TimeValueDigit>(begin,end) {}
164  /** \brief, c-tor from C style table of values. The timestamps are 0,1,..,n */
165  TimeSeriesDigit(const Value* begin, const Value* end);
166 
167  /** \brief copy c-tor */
169 
170  /** \brief c-tor from timeseries, the timestamps are modified, the offset is added */
171  TimeSeriesDigit(const TimeSeriesDigit& ts, const DigitTime& offset);
172 
173  /** operator = */
175  std::vector<TimeValueDigit>::operator=(t);
176  return *this;
177  }
178  ~TimeSeriesDigit(){}
179 
180  /** the autocorelation of timeseries. The average is not substracted.
181  out(0) = sum(i=0, i<n) in(i)*in(i)
182  out(1) = sum(i=0, i<n-1) in(i)*in(i+1)
183  out(2) = sum(i=0, i<n-2) in(i)*in(i+2)
184  ...
185  */
186  TimeSeriesDigit autoCorrelationE(int scope) const;
187 
188  /** the sum of values of timeseries */
189  double getSum() const {
190  return std::accumulate( begin(), end(), 0.0,
191  boost::bind( std::plus<double>(), _1, boost::bind(&TimeValueDigit::getValue, _2) ) );
192  }
193 
194  /** the sum of square of values of timeseries */
195  double getSumSquared() const {
196  return std::accumulate( begin(), end(), 0.0,
197  boost::bind( std::plus<double>(), _1,
198  boost::bind( std::multiplies<double>(),
199  boost::bind(&TimeValueDigit::getValue, _2),
200  boost::bind(&TimeValueDigit::getValue, _2) )
201  )
202  );
203  }
204 
205  /** the average of values of timeseries or 0.0 if the timeseries is empty */
206  double getAvg() const { return empty() ? 0.0 : getSum()/size(); }
207 
208  /** the square of variation */
209  double getSigmaSquared() const { return size()*getSumSquared() - getSum()*getSum(); }
210 
211  /** the variation (slower calculation) */
212  double getSigma() const { return empty()? 0.0 : std::sqrt( getSigmaSquared() )/ size(); }
213 
214  // TimeSeriesDigit operator+(const TimeSeriesDigit& ts) const;
215 
216  };
217 
218 
219  /** the average of difference for two timeserieses */
220  inline double getAvgAbsDiff(const TimeSeriesDigit& ts1, const TimeSeriesDigit& ts2) {
221 
222  typedef TimeSeriesDigit::const_iterator TSIter;
223 
224  TSIter it1 = ts1.begin();
225  TSIter it2 = ts2.begin();
226 
227  double sum_abs = 0.0;
228  int count = 0;
229  //przynajmniej jeden do rozpatrzenia
230  while( it1 != ts1.end() || it2 != ts2.end() ) {
231  ++count;
232  //druga seria juz rozpatrzona lub wyraz w it1 istnieje i jest mlodszy niz w it2
233  if(it2 == ts2.end() || (it1 != ts1.end() && it1->getTime() < it2->getTime() ) ) {
234  sum_abs += std::abs(it1->getValue() );
235  ++it1;
236  } else if(it1 == ts1.end() || it2->getTime() < it1->getTime() ) { //tutaj it2 != end
237  sum_abs += std::abs(it2->getValue() );
238  ++it2;
239  } else { //tutaj it1 != end() oraz it2 != end() oraz it1->getTime() == it2->getTime()
240  sum_abs += std::abs(it1->getValue() - it2->getValue() );
241  ++it1;
242  ++it2;
243  }
244  }
245  return count > 0 ? sum_abs/(double)count : 0.0;
246  }
247 
248  /** the average of difference (relative) for two timeserieses */
249  inline double getAvgRelDiff(const TimeSeriesDigit& ts1, const TimeSeriesDigit& ts2) {
250 
251  typedef TimeSeriesDigit::const_iterator TSIter;
252 
253  TSIter it1 = ts1.begin();
254  TSIter it2 = ts2.begin();
255 
256  double sum_rel = 0.0;
257  int count = 0;
258  //przynajmniej jeden do rozpatrzenia
259  while( it1 != ts1.end() || it2 != ts2.end() ) {
260  //druga seria juz rozpatrzona lub wyraz w it1 istnieje i jest mlodszy niz w it2
261  if(it2 == ts2.end() || (it1 != ts1.end() && it1->getTime() < it2->getTime() ) ) {
262  ++count;
263  sum_rel += 1.0;
264  ++it1;
265  } else if(it1 == ts1.end() || it2->getTime() < it1->getTime() ) { //tutaj it2 != end
266  ++count;
267  sum_rel += 1.0;
268  ++it2;
269  } else { //tutaj it1 != end() oraz it2 != end() oraz it1->getTime() == it2->getTime()
270  double m = ( it1->getValue() + it2->getValue() ) / 2.0;
271  //pomija 'smieci', ale uwzglednia NaN
272  if( ( m > (2 * std::numeric_limits<double>::epsilon() ) )
273  || ( m != m ) ) {
274  ++count;
275  sum_rel += std::abs( (it1->getValue() - it2->getValue() )/ m );
276  }
277  ++it1;
278  ++it2;
279  }
280  }
281  return count > 0 ? sum_rel/(double)count : 0.0;
282  }
283 
284  /** for debugging */
285  inline std::ostream& operator<<(std::ostream& os, const TimeSeriesDigit& time_series) {
286  std::copy( time_series.begin(), time_series.end(), std::ostream_iterator<TimeValueDigit>(os," ") );
287  return os;
288  }
289 
290  // ---------------------------------------------------------------------------------------------------------
291  //
292  // TimeSeriesReal - implementation
293  //
294  // ---------------------------------------------------------------------------------------------------------
295 
296  namespace {
297 
298  struct FromTimestampValueGenerator {
299 
300  TimeValueReal operator()(const long& time, const double& value) {
301  return TimeValueReal( boost::posix_time::from_time_t(time), value );
302  }
303 
304  };
305 
306  //generator kolejnych timestamp-ow
307  struct FromValueGenerator {
308  FromValueGenerator(const RealTime& t, const RealDuration& d)
309  : curr_(t), delta_(d) {}
310 
311  //generuje kolejna probke czasu
312  TimeValueReal operator()(const double& value) {
313  RealTime out = curr_;
314  curr_ += delta_;
315  return TimeValueReal(out, value);
316  }
317  RealTime curr_;
318  RealDuration delta_;
319  };
320  } //namespace
321 
322 
323  /** tworzy szereg czasowy na podstawie tablicy timestamp (posix) oraz tablicy wartosci */
324  inline TimeSeriesReal::TimeSeriesReal(const long* begin, const long* end, const Value* input2) {
325  FromTimestampValueGenerator gen;
326  std::transform( begin, end, input2, back_inserter(*this), gen );
327  }
328 
329  /** tworzy szereg czasowy na podstawie tablicy wartosci, czas dla pierwszego pomiaru, delta */
330  inline TimeSeriesReal::TimeSeriesReal(const RealTime& start_time, const RealDuration& delta,
331  const Value* value_begin, const Value* value_end ) {
332 
333  FromValueGenerator gen(start_time, delta);
334  std::transform( value_begin, value_end, back_inserter(*this), gen );
335 
336  }
337 
338  namespace {
339  typedef std::vector<TimeValueReal>::const_iterator RealConstIter;
340 
341  //the trapezium square equation
342  double trapeziumSquare(double a, double b, double h) {
343  return (a+b)*h/2.0;
344  }
345 
346  //the it+1 must be valid
347  double trapeziumSquare(const std::vector<TimeValueReal>::const_iterator it) {
348  RealConstIter it_next(it);
349  ++it_next;
350  long len_sec = boost::posix_time::time_period(it_next->getTime(), it->getTime()).length().total_seconds();
351  return trapeziumSquare(it->getValue(), it_next->getValue(), std::abs(len_sec) );
352  }
353  }
354 
355  /** oblicza calke (pole pod krzywa) dla danego szeregu czasowego */
356  inline double TimeSeriesReal::getIntegral() const {
357  if( size() < 2)
358  return 0.0;
359 
360  std::vector<TimeValueReal>::const_iterator last = end();
361  --last; //mozna, bo size() >= 2
362  double sum = 0.0;
363  for(RealConstIter it = begin(); it != last; ++it ) {
364  sum += trapeziumSquare( it );
365  }
366  return sum;
367  }
368 
369 
370  // ---------------------------------------------------------------------------------------------------------
371  //
372  // TimeSeriesDigit - implementation
373  //
374  // ---------------------------------------------------------------------------------------------------------
375 
376 
377  namespace {
378  /** helping function, moves the time by the given offset */
379  inline TimeValueDigit moveFun(const TimeValueDigit& v, const DigitTime& offset) {
380  return TimeValueDigit(v.getTime() + offset, v.getValue(), v.getQuality() );
381  }
382 
383  /** helping functor, create TimeValueDigit for next points with given quality and for table of values */
384  struct NextStepFunctor {
385 
386  NextStepFunctor() : curr_(-1) { }
387  TimeValueDigit operator()(const Value& v) { return TimeValueDigit(++curr_, v); }
388  DigitTime curr_;
389  };
390 
391  typedef TimeSeriesDigit::const_iterator TSIter;
392  //helping functor - scalar multiplication two time stamp collections (with offset)
393  struct CalcCorrelationFunctor {
394  CalcCorrelationFunctor(const TimeSeriesDigit& ts1, const TimeSeriesDigit& ts2,
395  double avg1 = 0.0, double avg2 = 0.0, double sig1 = 1.0, double sig2 = 1.0)
396  : ts1_(ts1), ts2_(ts2), offset_(0), avg1_(avg1), avg2_(avg2), sig1_(sig1), sig2_(sig2)
397  { }
398 
399  //the scalar multiplication for given offset
400  TimeValueDigit operator()() {
401  Value val = 0.0;
402  TSIter it1 = ts1_.begin(); //znajduje pierwszy czas, ktory jest i tu i tu
403  TSIter it2 = ts2_.begin();
404  while( it1 != ts1_.end() && it2 != ts2_.end() && it1->getTime() != (it2->getTime() + offset_) ) {
405  if( it1->getTime() < (it2->getTime() + offset_) ) ++it1;
406  else ++it2;
407  }
408  int count = 0;
409  //teraz ustawione tak ze t0 = max( t1, t2 + offset )
410  for(; it1 != ts1_.end() && it2 != ts2_.end(); ++it1, ++it2 ) {
411  val += it1->getValue() * it2->getValue();
412  ++count;
413  }
414 
415  val -= count * avg1_* avg2_;
416  if(count > 1)
417  val /= (count - 1)* sig1_ * sig2_;
418  else
419  val /= sig1_ * sig2_;
420  TimeValueDigit out(offset_, val);
421  ++offset_;
422  return out;
423  }
424  const TimeSeriesDigit& ts1_;
425  const TimeSeriesDigit& ts2_;
426  DigitTime offset_;
427  double avg1_;
428  double avg2_;
429  double sig1_;
430  double sig2_;
431  };
432 
433  } //namespace
434 
435  /** \brief, c-tor from C style table of values. The timestamps are 0,1,..,n */
436  inline TimeSeriesDigit::TimeSeriesDigit(const Value* begin, const Value* end) {
437  NextStepFunctor functor;
438  std::transform( begin, end, std::back_inserter(*this), functor );
439 
440  }
441 
442  /** \brief c-tor from timeseries, the timestamps are modified, the offset is added */
443  inline TimeSeriesDigit::TimeSeriesDigit(const TimeSeriesDigit& ts, const DigitTime& offset) {
444  reserve(ts.size());
445  std::transform( ts.begin(), ts.end(), std::back_inserter(*this), boost::bind(&moveFun, _1, offset ) );
446  }
447 
448  /** the autocorelation of timeseries. The average is not substracted.
449  out(0) = sum(i=0, i<n) in(i)*in(i)
450  out(1) = sum(i=0, i<n-1) in(i)*in(i+1)
451  out(2) = sum(i=0, i<n-2) in(i)*in(i+2)
452  ...
453  */
455  TimeSeriesDigit out;
456  CalcCorrelationFunctor gen(*this, *this);
457  std::generate_n(back_inserter(out), scope, gen );
458  return out;
459  }
460 
461  /** the correlation of timeserieses in range 0..scope */
462  inline TimeSeriesDigit correlation(const TimeSeriesDigit& ts1, const TimeSeriesDigit& ts2, int scope) {
463  TimeSeriesDigit out;
464  CalcCorrelationFunctor gen(ts1, ts2, ts1.getAvg(), ts2.getAvg(), ts1.getSigma(), ts2.getSigma() );
465  generate_n(back_inserter(out), scope, gen );
466  return out;
467  }
468 
469 
470  } //namespace timeseries
471 } //namespace faif
472 
473 #endif //FAIF_TIME_SERIES
TimeSeriesDigit correlation(const TimeSeriesDigit &ts1, const TimeSeriesDigit &ts2, int scope)
Definition: TimeSeries.hpp:462
TimeSeriesReal(const_iterator begin, const_iterator end)
c-tor from a range
Definition: TimeSeries.hpp:91
double getAvgAbsDiff(const TimeSeriesDigit &ts1, const TimeSeriesDigit &ts2)
Definition: TimeSeries.hpp:220
timeseries - time hold as RealTime
Definition: TimeSeries.hpp:76
Definition: Chain.h:17
double getSumSquared() const
Definition: TimeSeries.hpp:195
double getIntegral() const
Definition: TimeSeries.hpp:356
double Quality
quality - real 0..1
Definition: TimeSeries.hpp:51
const Value & getValue() const
Definition: TimeSeries.hpp:140
int DigitTime
digit time type. DigitTime < 0 past, DigitTime >= 0 future, DigitTime == 0 now.
Definition: TimeSeries.hpp:47
STL namespace.
TimeSeriesReal(const TimeValueReal *begin, const TimeValueReal *end)
c-tor from a range
Definition: TimeSeries.hpp:88
double getSum() const
Definition: TimeSeries.hpp:113
TimeSeriesDigit()
c-tor, empty timeseries
Definition: TimeSeries.hpp:157
TimeValueDigit(DigitTime t=0, const Value &v=0.0, const Quality &q=0.0)
Definition: TimeSeries.hpp:134
TimeSeriesDigit(const_iterator begin, const_iterator end)
c-tor, from range
Definition: TimeSeries.hpp:162
double Value
value - real number
Definition: TimeSeries.hpp:49
TimeSeriesDigit & operator=(const TimeSeriesDigit &t)
Definition: TimeSeries.hpp:174
const RealTime & getTime() const
Definition: TimeSeries.hpp:61
TimeSeriesReal & operator=(const TimeSeriesReal &t)
Definition: TimeSeries.hpp:105
const DigitTime & getTime() const
Definition: TimeSeries.hpp:138
const Quality & getQuality() const
Definition: TimeSeries.hpp:65
TimeSeriesReal(const TimeSeriesReal &t)
copy c-tor
Definition: TimeSeries.hpp:85
double getSigmaSquared() const
Definition: TimeSeries.hpp:209
double getAvgRelDiff(const TimeSeriesDigit &ts1, const TimeSeriesDigit &ts2)
Definition: TimeSeries.hpp:249
TimeValueReal(RealTime t, const Value &v, const Quality &q=0.0)
Definition: TimeSeries.hpp:57
double getAvg() const
Definition: TimeSeries.hpp:118
TimeSeriesDigit autoCorrelationE(int scope) const
Definition: TimeSeries.hpp:454
double getAvg() const
Definition: TimeSeries.hpp:206
const Quality & getQuality() const
Definition: TimeSeries.hpp:142
double getSum() const
Definition: TimeSeries.hpp:189
timeseries value, single value in given real time. Plain old data
Definition: TimeSeries.hpp:54
Definition: TimeSeries.hpp:151
double getSigma() const
Definition: TimeSeries.hpp:212
Definition: TimeSeries.hpp:131
~TimeSeriesReal()
Definition: TimeSeries.hpp:110
TimeSeriesReal()
empty collection
Definition: TimeSeries.hpp:82
const Value & getValue() const
Definition: TimeSeries.hpp:63
TimeSeriesDigit(const TimeValueDigit *begin, const TimeValueDigit *end)
c-tor, from range
Definition: TimeSeries.hpp:159
long to_time_t(const RealTime &real_time)
convert from RealTime to posix_t (num of seconds from 1970)
Definition: TimeSeries.hpp:40
boost::posix_time::time_duration RealDuration
the real time duration
Definition: TimeSeries.hpp:37
boost::posix_time::ptime RealTime
the real time type
Definition: TimeSeries.hpp:35
TimeSeriesDigit(const TimeSeriesDigit &t)
copy c-tor
Definition: TimeSeries.hpp:168