Class: Statsample::TimeSeries::Series

Inherits:
Vector
  • Object
show all
Includes:
Pacf
Defined in:
lib/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


277
278
279
# File 'lib/statsample-timeseries/timeseries.rb', line 277

def + series
  super.to_a.to_ts
end

#-(series) ⇒ Object


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

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/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


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/statsample-timeseries/timeseries.rb', line 94

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.


81
82
83
84
85
# File 'lib/statsample-timeseries/timeseries.rb', line 81

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.


120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/statsample-timeseries/timeseries.rb', line 120

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


179
180
181
182
183
184
185
186
187
# File 'lib/statsample-timeseries/timeseries.rb', line 179

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


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/statsample-timeseries/timeseries.rb', line 236

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, ...]

148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/statsample-timeseries/timeseries.rb', line 148

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


205
206
207
208
209
210
211
# File 'lib/statsample-timeseries/timeseries.rb', line 205

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


271
272
273
274
# File 'lib/statsample-timeseries/timeseries.rb', line 271

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
# File 'lib/statsample-timeseries/timeseries.rb', line 60

def pacf(max_lags = nil, method = :yw)

  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


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

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