Class: Statsample::TimeSeries::Series

Inherits:
Vector
  • Object
show all
Includes:
Pacf
Defined in:
lib/bio-statsample-timeseries/timeseries.rb

Overview

Collection of data indexed by time. The order goes from earliest to latest.

Instance Method Summary collapse

Methods inherited from Vector

#squares_of_sum

Instance Method Details

#+(series) ⇒ Object

Borrow the operations from Vector, but convert to time series



286
287
288
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 286

def + series
  super.to_a.to_ts
end

#-(series) ⇒ Object



290
291
292
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 290

def - series
  super.to_a.to_ts
end

#acf(max_lags = nil) ⇒ Object

Calculates the autocorrelation coefficients of the series.

The first element is always 1, since that is the correlation of the series with itself.

Usage:

ts = (1..100).map { rand }.to_time_series

ts.acf   # => array with first 21 autocorrelations
ts.acf 3 # => array with first 3 autocorrelations


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 34

def acf(max_lags = nil)
  max_lags ||= (10 * Math.log10(size)).to_i

  (0..max_lags).map do |i|
    if i == 0
      1.0
    else
      m = self.mean

      # can't use Pearson coefficient since the mean for the lagged series should
      # be the same as the regular series
      ((self - m) * (self.lag(i) - m)).sum / self.variance_sample / (self.size - 1)
    end
  end
end

#acvf(demean = true, unbiased = true) ⇒ Object

AutoCovariance

Provides autocovariance of timeseries. -Parameters: demean = true; optional. Supply false if series is not to be demeaned unbiased = true; optional. true/false for unbiased/biased form of autocovariance -Returns-: Autocovariance value



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 100

def acvf(demean = true, unbiased = true)
  #TODO: change parameters list in opts.merge as suggested by John
  #functionality: computes autocovariance of timeseries data
  #returns: array of autocovariances

  if demean
    demeaned_series = self - self.mean
  else
    demeaned_series = self
  end
  n = (10 * Math.log10(size)).to_i + 1
  m = self.mean
  if unbiased
    d = Array.new(self.size, self.size)
  else
    d = ((1..self.size).to_a.reverse)[0..n]
  end


  0.upto(n - 1).map do |i|
    (demeaned_series * (self.lag(i) - m)).sum / d[i]
  end
end

#ar(n = 1500, k = 1) ⇒ Object

Autoregressive estimation

Generates AR(k) series for the calling timeseries by yule walker. Parameters: -n::integer, (default = 1500) number of observations for AR. -k::integer, (default = 1) order of AR process. Returns: Array constituting estimated AR series.



87
88
89
90
91
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 87

def ar(n = 1500, k = 1)
  series = Statsample::TimeSeries.arima
  #series = Statsample::TimeSeries::ARIMA.new
  series.yule_walker(self, n, k)
end

#correlate(a, v, mode = 'full') ⇒ Object

Correlation

Gives correlation of timeseries.



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 127

def correlate(a, v, mode = 'full')
  #peforms cross-correlation of two series
  #multiarray.correlate2(a, v, 'full')
  if a.size < v.size
    raise("Should have same size!")
  end
  ps = a.size + v.size - 1
  a_padded = Array.new(ps, 0)
  a_padded[0...a.size] = a

  out = (mode.downcase.eql? 'full') ? Array.new(ps) : Array.new(a.size)
  #ongoing
end

#diff(max_lags = 1) ⇒ Object

Diff

Performs the difference of the series. Note: The first difference of series is X(t) - X(t-1) But, second difference of series is NOT X(t) - X(t-2) It is the first difference of the first difference

> (X(t) - X(t-1)) - (X(t-1) - X(t-2))

Params: -max_lags::integer, (default: 1), number of differences reqd. Usage:

ts = (1..10).map { rand }.to_ts
          # => [0.69, 0.23, 0.44, 0.71, ...]

ts.diff   # => [nil, -0.46, 0.21, 0.27, ...]

Returns: Timeseries object



185
186
187
188
189
190
191
192
193
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 185

def diff(max_lags = 1)
  ts = self
  difference = []
  max_lags.times do
    difference = ts - ts.lag
    ts = difference
  end
  difference
end

#ema(n = 10, wilder = false) ⇒ Object

Exponential Moving Average

Calculates an exponential moving average of the series using a specified parameter. If wilder is false (the default) then the EMA uses a smoothing value of 2 / (n + 1), if it is true then it uses the Welles Wilder smoother of 1 / n.

Warning for EMA usage: EMAs are unstable for small series, as they use a lot more than n observations to calculate. The series is stable if the size of the series is >= 3.45 * (n + 1)

Parameters: -n::integer, (default = 10) -wilder::boolean, (default = false), if true, 1/n value is used for smoothing; if false, uses 2/(n+1) value

Usage:

ts = (1..100).map { rand }.to_ts
         # => [0.69, 0.23, 0.44, 0.71, ...]

# first 9 observations are nil
ts.ema   # => [ ... nil, 0.509... , 0.433..., ... ]

Returns: EMA timeseries



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 244

def ema(n = 10, wilder = false)
  smoother = wilder ? 1.0 / n : 2.0 / (n + 1)

  # need to start everything from the first non-nil observation
  start = self.data.index { |i| i != nil }

  # first n - 1 observations are nil
  base = [nil] * (start + n - 1)

  # nth observation is just a moving average
  base << self[start...(start + n)].inject(0.0) { |s, a| a.nil? ? s : s + a } / n

  (start + n).upto size - 1 do |i|
    base << self[i] * smoother + (1 - smoother) * base.last
  end

  base.to_time_series
end

#lag(k = 1) ⇒ Object

Lags the series by k periods.

The convention is to set the oldest observations (the first ones in the series) to nil so that the size of the lagged series is the same as the original.

Usage:

ts = (1..10).map { rand }.to_time_series
         # => [0.69, 0.23, 0.44, 0.71, ...]

ts.lag   # => [nil, 0.69, 0.23, 0.44, ...]
ts.lag 2 # => [nil, nil, 0.69, 0.23, ...]


155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 155

def lag(k = 1)
  return self if k == 0

  dup.tap do |lagged|
    (lagged.size - 1).downto k do |i|
      lagged[i] = lagged[i - k]
    end

    (0...k).each do |i|
      lagged[i] = nil
    end
    lagged.set_valid_data
  end
end

#ma(n = 10) ⇒ Object

Moving Average

Calculates the moving average of the series using the provided lookback argument. The lookback defaults to 10 periods. Parameters: -n::integer, (default = 10) - loopback argument

Usage:

ts = (1..100).map { rand }.to_ts
         # => [0.69, 0.23, 0.44, 0.71, ...]

# first 9 observations are nil
ts.ma    # => [ ... nil, 0.484... , 0.445... , 0.513 ... , ... ]

Returns: Resulting moving average timeseries object



211
212
213
214
215
216
217
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 211

def ma(n = 10)
  return mean if n >= size

  ([nil] * (n - 1) + (0..(size - n)).map do |i|
    self[i...(i + n)].inject(&:+) / n
  end).to_time_series
end

#macd(fast = 12, slow = 26, signal = 9) ⇒ Object

Moving Average Convergence-Divergence

Calculates the MACD (moving average convergence-divergence) of the time series - this is a comparison of a fast EMA with a slow EMA.

Parameters: -fast::integer, (default = 12) - fast component of MACD -slow::integer, (default = 26) - slow component of MACD -signal::integer, (default = 9) - signal component of MACD

Usage: ts = (1..100).map { rand }.to_ts

# => [0.69, 0.23, 0.44, 0.71, ...]

ts.macd(13)

Returns: Array of two timeseries - comparison of fast EMA with slow and EMA with signal value



280
281
282
283
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 280

def macd(fast = 12, slow = 26, signal = 9)
  series = ema(fast) - ema(slow)
  [series, series.ema(signal)]
end

#pacf(max_lags = nil, method = :yw) ⇒ Object

Partial Autocorrelation

Generates partial autocorrelation series for a timeseries Parameters: -max_lags::integer, optional - provide number of lags -method::string. Default: ‘yw’.

* _yw_:: For yule-walker algorithm unbiased approach
* _mle_:: For Maximum likelihood algorithm approach
* _ld_:: Forr Levinson-Durbin recursive approach

Returns - array of pacf



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 60

def pacf(max_lags = nil, method = :yw)
  #parameters:
  #max_lags => maximum number of lags for pcf
  #method => for autocovariance in yule_walker:
    #'yw' for 'yule-walker unbaised', 'mle' for biased maximum likelihood
    #'ld' for Levinson-Durbin recursion

  method = method.downcase.to_sym
  max_lags ||= (10 * Math.log10(size)).to_i
  if method.eql? :yw or method.eql? :mle
    Pacf::Pacf.pacf_yw(self, max_lags, method.to_s)
  elsif method == :ld
    series = self.acvf
    Pacf::Pacf.levinson_durbin(series, max_lags, true)[2]
  else
    raise "Method presents for pacf are 'yw', 'mle' or 'ld'"
  end
end

#to_sObject



294
295
296
297
# File 'lib/bio-statsample-timeseries/timeseries.rb', line 294

def to_s
  sprintf("Time Series(type:%s, n:%d)[%s]", @type.to_s, @data.size,
          @data.collect{|d| d.nil? ? "nil":d}.join(","))
end