bio-statsample-timeseries 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
- data/VERSION +1 -1
- data/lib/bio-statsample-timeseries.rb +2 -0
- data/lib/bio-statsample-timeseries/arima.rb +25 -75
- data/lib/bio-statsample-timeseries/arima/kalman.rb +137 -0
- data/lib/bio-statsample-timeseries/arima/likelihood.rb +104 -0
- data/lib/bio-statsample-timeseries/timeseries.rb +17 -10
- data/lib/bio-statsample-timeseries/utility.rb +35 -2
- data/test/helper.rb +2 -2
- data/test/test_arima_ks.rb +106 -0
- metadata +22 -3
data/Gemfile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -11,6 +11,8 @@
|
|
11
11
|
require 'statsample'
|
12
12
|
require_relative 'bio-statsample-timeseries/timeseries.rb'
|
13
13
|
require_relative 'bio-statsample-timeseries/arima.rb'
|
14
|
+
require_relative 'bio-statsample-timeseries/arima/kalman'
|
15
|
+
require_relative 'bio-statsample-timeseries/arima/likelihood'
|
14
16
|
require_relative 'bio-statsample-timeseries/utility.rb'
|
15
17
|
|
16
18
|
|
@@ -1,4 +1,6 @@
|
|
1
1
|
#require 'debugger'
|
2
|
+
require 'bio-statsample-timeseries/arima/kalman'
|
3
|
+
require 'bio-statsample-timeseries/arima/likelihood'
|
2
4
|
module Statsample
|
3
5
|
module TimeSeries
|
4
6
|
|
@@ -10,21 +12,31 @@ module Statsample
|
|
10
12
|
|
11
13
|
class ARIMA < Statsample::Vector
|
12
14
|
include Statsample::TimeSeries
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
|
16
|
+
#Kalman filter on ARIMA model
|
17
|
+
#*Params*:
|
18
|
+
#-_ts_::timeseries object
|
19
|
+
#-_p_::AR order
|
20
|
+
#-_i_::Integerated part
|
21
|
+
#-_q_::MA order
|
22
|
+
#
|
23
|
+
#*Usage*:
|
24
|
+
# ts = (1..100).map { rand }.to_ts
|
25
|
+
# k_obj = ARIMA.ks(ts, 2, 1, 1)
|
26
|
+
# k_obj.ar
|
27
|
+
# #=> AR's phi coefficients
|
28
|
+
# k_obj.ma
|
29
|
+
# #=> MA's theta coefficients
|
30
|
+
#
|
31
|
+
#*Returns*:
|
32
|
+
#Kalman filter object
|
33
|
+
def self.ks(ts, p, i, q)
|
19
34
|
#prototype
|
20
|
-
|
21
|
-
|
22
|
-
if q.zero?
|
23
|
-
self.ar(p)
|
24
|
-
elsif p.zero?
|
25
|
-
self.ma(p)
|
26
|
-
# ISSUE-> ELSE -> simultaneuos estimation of MA and AR parameters
|
35
|
+
if i > 0
|
36
|
+
ts = ts.diff(i).reject { |x| x.nil? }.to_ts
|
27
37
|
end
|
38
|
+
filter = Arima::KalmanFilter.new(ts, p, i, q)
|
39
|
+
filter
|
28
40
|
end
|
29
41
|
|
30
42
|
def ar(p)
|
@@ -211,67 +223,5 @@ module Statsample
|
|
211
223
|
end
|
212
224
|
end
|
213
225
|
|
214
|
-
module Arima
|
215
|
-
class KalmanFilter < Statsample::Vector
|
216
|
-
include Statsample::TimeSeries
|
217
|
-
|
218
|
-
#=T
|
219
|
-
#The coefficient matrix for the state vector in state equation
|
220
|
-
# It's dimensions is r+k x r+k
|
221
|
-
#*Parameters*
|
222
|
-
#-_r_::integer, r is max(p, q+1), where p and q are orders of AR and MA respectively
|
223
|
-
#-_k_::integer, number of exogeneous variables in ARMA model
|
224
|
-
#-_q_::integer, The AR coefficient of ARMA model
|
225
|
-
|
226
|
-
#*References*: Statsmodels tsa, Durbin and Koopman Section 4.7
|
227
|
-
def self.T(r, k, p)
|
228
|
-
arr = Matrix.zero(r)
|
229
|
-
params_padded = Statsample::Vector.new(Array.new(r, 0), :scale)
|
230
|
-
|
231
|
-
params_padded[0...p] = params[k...(p+k)]
|
232
|
-
intermediate_matrix = (r-1).times.map { Array.new(r, 0) }
|
233
|
-
#appending an array filled with padded values in beginning
|
234
|
-
intermediate_matrix[0,0] = [params_padded]
|
235
|
-
|
236
|
-
#now generating column matrix for that:
|
237
|
-
arr = Matrix.columns(intermediate_matrix)
|
238
|
-
arr_00 = arr[0,0]
|
239
|
-
|
240
|
-
#identify matrix substituition in matrix except row[0] and column[0]
|
241
|
-
r.times do |i|
|
242
|
-
arr[r,r] = 1
|
243
|
-
end
|
244
|
-
arr[0,0] = arr_00
|
245
|
-
arr
|
246
|
-
end
|
247
|
-
|
248
|
-
|
249
|
-
#=R
|
250
|
-
#The coefficient matrix for the state vector in the observation matrix.
|
251
|
-
#It's dimension is r+k x 1
|
252
|
-
#*Parameters*
|
253
|
-
#-_r_::integer, r is max(p, q+1) where p and q are order of AR and MA respectively
|
254
|
-
#-_k_::integer, number of exogeneous variables in ARMA model
|
255
|
-
#-_q_::integer, The MA order in ARMA model
|
256
|
-
#-_p_::integer, The AR order in ARMA model
|
257
|
-
#*References*: Statsmodels tsa, Durbin and Koopman
|
258
|
-
def self.R(r, k, q, p)
|
259
|
-
arr = Matrix.column_vector(Array.new(r,0.0))
|
260
|
-
|
261
|
-
#pending - in kind of difficult end here;
|
262
|
-
end
|
263
|
-
|
264
|
-
#=Z
|
265
|
-
#The Z selector matrix
|
266
|
-
#*Parameters*
|
267
|
-
#-_r_::integer, max(p, q+1)
|
268
|
-
#Returns: vector
|
269
|
-
def self.Z(r)
|
270
|
-
arr = Statsample::Vector.new(Array.new(r, 0.0), :scale)
|
271
|
-
arr[0] = 1.0
|
272
|
-
return arr
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end
|
276
226
|
end
|
277
227
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'bio-statsample-timeseries/arima/likelihood'
|
2
|
+
module Statsample
|
3
|
+
module TimeSeries
|
4
|
+
module Arima
|
5
|
+
|
6
|
+
class KalmanFilter
|
7
|
+
include Statsample::TimeSeries
|
8
|
+
include GSL::MultiMin
|
9
|
+
attr_accessor :ts, :p, :i, :q
|
10
|
+
attr_reader :ar, :ma
|
11
|
+
def initialize(ts=[].to_ts, p=0, i=0, q=0)
|
12
|
+
@ts = ts
|
13
|
+
@p = p
|
14
|
+
@i = i
|
15
|
+
@q = q
|
16
|
+
ks #call the filter
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
sprintf("ARIMA model(p = %d, i = %d, q = %d) on series(%d elements) - [%s]",
|
21
|
+
@p, @i, @q, @ts.size, @ts.to_a.join(','))
|
22
|
+
end
|
23
|
+
|
24
|
+
#=Kalman Filter
|
25
|
+
#Function which minimizes KalmanFilter.ll iteratively for initial parameters
|
26
|
+
#*Parameters*:
|
27
|
+
#-_timeseries_: timeseries object, against which the ARMA params has to be estimated
|
28
|
+
#-_p_: order of AR
|
29
|
+
#-_q_: order of MA
|
30
|
+
#*Usage*:
|
31
|
+
#- ts = (1..100).to_a.to_ts
|
32
|
+
#- KalmanFilter.ks(ts, 3, 1)
|
33
|
+
#NOTE: Suceptible to syntactical change later. Can be called directly on timeseries.
|
34
|
+
#NOTE: Return parameters
|
35
|
+
def ks
|
36
|
+
initial = Array.new((@p+@q), 0.0)
|
37
|
+
|
38
|
+
my_f = Proc.new{ |x, params|
|
39
|
+
#In rb-gsl, params remain idle, x is varied upon
|
40
|
+
#In R code, initial parameters varied in each iteration
|
41
|
+
#my_func.set_params([(1..100).to_a.to_ts, p_value, q_value])
|
42
|
+
timeseries = params[0]
|
43
|
+
p,q = params[1], params[2]
|
44
|
+
params = x
|
45
|
+
#puts x
|
46
|
+
-Arima::KF::LogLikelihood.new(x.to_a, timeseries, p, q).ll
|
47
|
+
#KalmanFilter.ll(x.to_a, timeseries, p, q)
|
48
|
+
}
|
49
|
+
np = @p + @q
|
50
|
+
my_func = Function.alloc(my_f, np)
|
51
|
+
my_func.set_params([@ts, @p, @q])
|
52
|
+
x = GSL::Vector.alloc(initial)
|
53
|
+
ss = GSL::Vector.alloc(np)
|
54
|
+
ss.set_all(0.1)
|
55
|
+
|
56
|
+
minimizer = FMinimizer.alloc("nmsimplex", np)
|
57
|
+
minimizer.set(my_func, x, ss)
|
58
|
+
status = GSL::CONTINUE
|
59
|
+
iter = 0
|
60
|
+
while status == GSL::CONTINUE && iter < 100
|
61
|
+
iter += 1
|
62
|
+
begin
|
63
|
+
status = minimizer.iterate()
|
64
|
+
status = minimizer.test_size(1e-2)
|
65
|
+
x = minimizer.x
|
66
|
+
rescue
|
67
|
+
break
|
68
|
+
end
|
69
|
+
# printf("%5d ", iter)
|
70
|
+
# for i in 0...np do
|
71
|
+
# puts "#{x[i]}.to_f"
|
72
|
+
# #printf("%10.3e ", x[i].to_f)
|
73
|
+
# end
|
74
|
+
# printf("f() = %7.3f size = %.3f\n", minimizer.fval, minimizer.size)
|
75
|
+
end
|
76
|
+
#
|
77
|
+
@ar = (p > 0) ? x.to_a[0...p] : []
|
78
|
+
@ma = (q > 0) ? x.to_a[p...(p+q)] : []
|
79
|
+
x.to_a
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
#=log_likelihood
|
84
|
+
#Computes Log likelihood on given parameters, ARMA order and timeseries
|
85
|
+
#*params*:
|
86
|
+
#-_params_::array of floats, contains phi/theta parameters
|
87
|
+
#-_timeseries_::timeseries object
|
88
|
+
#-_p_::integer, AR(p) order
|
89
|
+
#-_q_::integer, MA(q) order
|
90
|
+
#*Returns*:
|
91
|
+
#LogLikelihood object
|
92
|
+
#*Usage*:
|
93
|
+
# s = (1..100).map { rand }.to_ts
|
94
|
+
# p, q = 1, 0
|
95
|
+
# ll = KalmanFilter.log_likelihood([0.2], s, p, q)
|
96
|
+
# ll.log_likelihood
|
97
|
+
# #=> -22.66
|
98
|
+
# ll.sigma
|
99
|
+
# #=> 0.232
|
100
|
+
def self.log_likelihood(params, timeseries, p, q)
|
101
|
+
Arima::KF::LogLikelihood.new(params, timeseries, p, q)
|
102
|
+
end
|
103
|
+
|
104
|
+
#=T
|
105
|
+
#The coefficient matrix for the state vector in state equation
|
106
|
+
# It's dimensions is r+k x r+k
|
107
|
+
#*Parameters*
|
108
|
+
#-_r_::integer, r is max(p, q+1), where p and q are orders of AR and MA respectively
|
109
|
+
#-_k_::integer, number of exogeneous variables in ARMA model
|
110
|
+
#-_q_::integer, The AR coefficient of ARMA model
|
111
|
+
|
112
|
+
#*References*: Statsmodels tsa, Durbin and Koopman Section 4.7
|
113
|
+
#def self.T(r, k, p)
|
114
|
+
# arr = Matrix.zero(r)
|
115
|
+
# params_padded = Statsample::Vector.new(Array.new(r, 0), :scale)
|
116
|
+
#
|
117
|
+
# params_padded[0...p] = params[k...(p+k)]
|
118
|
+
# intermediate_matrix = (r-1).times.map { Array.new(r, 0) }
|
119
|
+
# #appending an array filled with padded values in beginning
|
120
|
+
# intermediate_matrix[0,0] = [params_padded]
|
121
|
+
#
|
122
|
+
# #now generating column matrix for that:
|
123
|
+
# arr = Matrix.columns(intermediate_matrix)
|
124
|
+
# arr_00 = arr[0,0]
|
125
|
+
#
|
126
|
+
# #identify matrix substituition in matrix except row[0] and column[0]
|
127
|
+
# r.times do |i|
|
128
|
+
# arr[r,r] = 1
|
129
|
+
# end
|
130
|
+
# arr[0,0] = arr_00
|
131
|
+
# arr
|
132
|
+
#end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module Statsample
|
2
|
+
module TimeSeries
|
3
|
+
module Arima
|
4
|
+
module KF
|
5
|
+
class LogLikelihood
|
6
|
+
|
7
|
+
#log_likelihood
|
8
|
+
#Gives log likelihood value of an ARMA(p, q) process on given parameters
|
9
|
+
attr_reader :log_likelihood
|
10
|
+
|
11
|
+
#sigma
|
12
|
+
#Gives sigma value of an ARMA(p,q) process on given parameters
|
13
|
+
attr_reader :sigma
|
14
|
+
|
15
|
+
#aic
|
16
|
+
#Gives AIC(Akaike Information Criterion)
|
17
|
+
#https://www.scss.tcd.ie/Rozenn.Dahyot/ST7005/13AICBIC.pdf
|
18
|
+
attr_reader :aic
|
19
|
+
|
20
|
+
def initialize(params, timeseries, p, q)
|
21
|
+
@params = params
|
22
|
+
@timeseries = timeseries
|
23
|
+
@p = p
|
24
|
+
@q = q
|
25
|
+
ll
|
26
|
+
end
|
27
|
+
|
28
|
+
#Log likelihood function.
|
29
|
+
#iteratively minimized by simplex algorithm via KalmanFilter.ks
|
30
|
+
#Not meant to be used directly. Will make it private later.
|
31
|
+
def ll
|
32
|
+
params, timeseries = @params, @timeseries
|
33
|
+
p, q = @p, @q
|
34
|
+
|
35
|
+
phi = []
|
36
|
+
theta = []
|
37
|
+
phi = params[0...p] if p > 0
|
38
|
+
theta = params[(p)...(p + q)] if q > 0
|
39
|
+
|
40
|
+
[phi, theta].each do |v|
|
41
|
+
if v.size>0 and v.map(&:abs).inject(:+) > 1
|
42
|
+
return
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
m = [p, q].max
|
47
|
+
h = Matrix.column_vector(Array.new(m,0))
|
48
|
+
m.times do |i|
|
49
|
+
h[i,0] = phi[i] if i< p
|
50
|
+
h[i,0] = h[i,0] + theta[i] if i < q
|
51
|
+
end
|
52
|
+
|
53
|
+
t = Matrix.zero(m)
|
54
|
+
#set_column is available in utility.rb
|
55
|
+
t = t.set_column(0, phi)
|
56
|
+
if(m > 1)
|
57
|
+
t[0...(m-1), 1...m] = Matrix.I(m-1)
|
58
|
+
#chances of extra constant 0 values as unbalanced column, so:
|
59
|
+
t = Matrix.columns(t.column_vectors)
|
60
|
+
end
|
61
|
+
|
62
|
+
g = Matrix[[1]]
|
63
|
+
a_t = Matrix.column_vector(Array.new(m,0))
|
64
|
+
n = timeseries.size
|
65
|
+
z = Matrix.row_vector(Array.new(m,0))
|
66
|
+
z[0,0] = 1
|
67
|
+
p_t = Matrix.I(m)
|
68
|
+
v_t, f_t = Array.new(n,0), Array.new(n, 0)
|
69
|
+
|
70
|
+
n.times do |i|
|
71
|
+
v_t[i] = (z * a_t).map { |x| timeseries[i] - x }[0,0]
|
72
|
+
|
73
|
+
f_t[i] = (z * p_t * (z.transpose)).map { |x| x + 1 }[0,0]
|
74
|
+
|
75
|
+
k_t = ((t * p_t * z.transpose) + h).map { |x| x / f_t[i] }
|
76
|
+
|
77
|
+
a_t = (t * a_t) + (k_t * v_t[i])
|
78
|
+
l_t = t - k_t * z
|
79
|
+
j_t = h - k_t
|
80
|
+
|
81
|
+
p_t = (t * p_t * (l_t.transpose)) + (h * (j_t.transpose))
|
82
|
+
end
|
83
|
+
|
84
|
+
pot = v_t.map(&:square).zip(f_t).map { |x,y| x / y}.inject(:+)
|
85
|
+
sigma_2 = pot.to_f / n.to_f
|
86
|
+
|
87
|
+
f_t_log_sum = f_t.map { |x| Math.log(x) }.inject(:+)
|
88
|
+
@log_likelihood = -0.5 * (n*Math.log(2*Math::PI) + n*Math.log(sigma_2) + f_t_log_sum + n)
|
89
|
+
|
90
|
+
@sigma = sigma_2
|
91
|
+
@aic = -(2 * @log_likelihood - 2*(p+q+1))
|
92
|
+
#puts ("ll = #{-ll}")
|
93
|
+
return @log_likelihood
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
sprintf("LogLikelihood(p = %d, q = %d) on params: [%s]",
|
98
|
+
@p, @q, @params.join(', '))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -107,7 +107,7 @@ module Statsample
|
|
107
107
|
else
|
108
108
|
demeaned_series = self
|
109
109
|
end
|
110
|
-
n =
|
110
|
+
n = (10 * Math.log10(size)).to_i + 1
|
111
111
|
m = self.mean
|
112
112
|
if unbiased
|
113
113
|
d = Array.new(self.size, self.size)
|
@@ -168,21 +168,28 @@ module Statsample
|
|
168
168
|
end
|
169
169
|
|
170
170
|
#=Diff
|
171
|
-
# Performs
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
|
171
|
+
# Performs the difference of the series.
|
172
|
+
# Note: The first difference of series is X(t) - X(t-1)
|
173
|
+
# But, second difference of series is NOT X(t) - X(t-2)
|
174
|
+
# It is the first difference of the first difference
|
175
|
+
# => (X(t) - X(t-1)) - (X(t-1) - X(t-2))
|
176
|
+
#*Params*:
|
177
|
+
#-_max_lags_::integer, (default: 1), number of differences reqd.
|
177
178
|
#*Usage*:
|
178
179
|
#
|
179
180
|
# ts = (1..10).map { rand }.to_ts
|
180
181
|
# # => [0.69, 0.23, 0.44, 0.71, ...]
|
181
182
|
#
|
182
183
|
# ts.diff # => [nil, -0.46, 0.21, 0.27, ...]
|
183
|
-
|
184
|
-
def diff
|
185
|
-
|
184
|
+
#*Returns*: Timeseries object
|
185
|
+
def diff(max_lags = 1)
|
186
|
+
ts = self
|
187
|
+
difference = []
|
188
|
+
max_lags.times do
|
189
|
+
difference = ts - ts.lag
|
190
|
+
ts = difference
|
191
|
+
end
|
192
|
+
difference
|
186
193
|
end
|
187
194
|
|
188
195
|
#=Moving Average
|
@@ -78,8 +78,8 @@ module Statsample
|
|
78
78
|
#Class method
|
79
79
|
#Returns the chain product of two matrices
|
80
80
|
#==Usage:
|
81
|
-
#Let `a` be 4 * 3 matrix,
|
82
|
-
#Let `b` be 3 * 3 matrix,
|
81
|
+
#Let `a` be 4 * 3 matrix,
|
82
|
+
#Let `b` be 3 * 3 matrix,
|
83
83
|
#Let `c` be 3 * 1 matrix,
|
84
84
|
#then `Matrix.chain_dot(a, b, c)`
|
85
85
|
#===*NOTE*: Send the matrices in multiplicative order with proper dimensions
|
@@ -114,5 +114,38 @@ module Statsample
|
|
114
114
|
end
|
115
115
|
return Matrix.rows(vectors)
|
116
116
|
end
|
117
|
+
|
118
|
+
def set_column(i, arr)
|
119
|
+
columns = self.column_vectors
|
120
|
+
column = columns[i].to_a
|
121
|
+
column[0...arr.size] = arr
|
122
|
+
columns[i] = column
|
123
|
+
return Matrix.columns(columns)
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_row(i, arr)
|
127
|
+
#similar implementation as set_column
|
128
|
+
#writing and commenting metaprogrammed version
|
129
|
+
#Please to give opinion :)
|
130
|
+
rows = self.row_vectors
|
131
|
+
row = rows[i].to_a
|
132
|
+
row[0...arr.size] = arr
|
133
|
+
rows[i] = row
|
134
|
+
return Matrix.rows(rows)
|
135
|
+
end
|
136
|
+
|
137
|
+
#Metaprogrammed version of set_column, set_row
|
138
|
+
# self.class_eval do
|
139
|
+
# ["row", "column"].each do |dimension|
|
140
|
+
# define_method("set_#{dimension}s") do |i, arr|
|
141
|
+
# dims = send("#{dimension}_vectors")
|
142
|
+
# dim = dims[i].to_a
|
143
|
+
# dim[0...arr.size] = arr
|
144
|
+
# dims[i] = dim
|
145
|
+
# return Matrix.send("#{dimension}s", dims)
|
146
|
+
# end
|
147
|
+
# end
|
148
|
+
# end
|
117
149
|
end
|
150
|
+
|
118
151
|
end
|
data/test/helper.rb
CHANGED
@@ -10,9 +10,9 @@ end
|
|
10
10
|
require 'minitest/unit'
|
11
11
|
require 'shoulda'
|
12
12
|
require 'shoulda-context'
|
13
|
-
require 'mocha'
|
13
|
+
require 'mocha/setup'
|
14
14
|
|
15
|
-
require 'bio-statsample-timeseries'
|
15
|
+
#require 'bio-statsample-timeseries'
|
16
16
|
|
17
17
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
18
18
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
|
2
|
+
|
3
|
+
class StatsampleArimaKSTestCase < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
context("AR(0.5) simulation") do
|
6
|
+
#include Statsample::TimeSeries
|
7
|
+
setup do
|
8
|
+
@s = [-1.16025577,0.64758021,0.77158601,0.14989543,2.31358162,3.49213868,1.14826956,0.58169457,-0.30813868,-0.34741084,-1.41175595,0.06040081, -0.78230232,0.86734837,0.95015787,-0.49781397,0.53247330,1.56495187,0.30936619,0.09750217,1.09698829,-0.81315490,-0.79425607,-0.64568547,-1.06460320,1.24647894,0.66695937,1.50284551,1.17631218,1.64082872,1.61462736,0.06443761,-0.17583741,0.83918339,0.46610988,-0.54915270,-0.56417108,-1.27696654,0.89460084,1.49970338,0.24520493,0.26249138,-1.33744834,-0.57725961,1.55819543,1.62143157,0.44421891,-0.74000084 ,0.57866347,3.51189333,2.39135077,1.73046244,1.81783890,0.21454040,0.43520890,-1.42443856,-2.72124685,-2.51313877,-1.20243091,-1.44268002 ,-0.16777305,0.05780661,2.03533992,0.39187242,0.54987983,0.57865693,-0.96592469,-0.93278473,-0.75962671,-0.63216906,1.06776183, 0.17476059 ,0.06635860,0.94906227,2.44498583,-1.04990407,-0.88440073,-1.99838258,-1.12955558,-0.62654882,-1.36589161,-2.67456821,-0.97187696, -0.84431782 ,-0.10051809,0.54239549,1.34622861,1.25598105,0.19707759,3.29286114,3.52423499,1.69146333,-0.10150024,0.45222903,-0.01730516, -0.49828727, -1.18484684,-1.09531773,-1.17190808,0.30207662].to_ts
|
9
|
+
end
|
10
|
+
context "passed through the Kalman Filter" do
|
11
|
+
setup do
|
12
|
+
@kf=Statsample::TimeSeries::ARIMA.ks(@s,1,0,0)
|
13
|
+
end
|
14
|
+
should "return correct object" do
|
15
|
+
assert_instance_of Statsample::TimeSeries::Arima::KalmanFilter, @kf
|
16
|
+
end
|
17
|
+
should "return correct parameters" do
|
18
|
+
assert_equal @kf.p,1
|
19
|
+
assert_equal @kf.q,0
|
20
|
+
assert_equal @kf.i,0
|
21
|
+
end
|
22
|
+
should "return correct ar estimators" do
|
23
|
+
assert_equal @kf.ar.length,1
|
24
|
+
assert_in_delta @kf.ar[0], 0.700 #0.564
|
25
|
+
end
|
26
|
+
should "return correct ma estimators" do
|
27
|
+
assert_equal @kf.ma.length,0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context "passed through the Kalman Filter with AR(0.564)" do
|
31
|
+
setup do
|
32
|
+
@kf_likehood=Statsample::TimeSeries::Arima::KalmanFilter.log_likelihood([0.564],@s,1,0)
|
33
|
+
end
|
34
|
+
should "return correct object for log_likehood" do
|
35
|
+
assert_instance_of Statsample::TimeSeries::Arima::KF::LogLikelihood, @kf_likehood
|
36
|
+
end
|
37
|
+
should "return correct log_likehood" do
|
38
|
+
assert_in_delta -148.7003, @kf_likehood.log_likelihood
|
39
|
+
end
|
40
|
+
should "return correct sigma" do
|
41
|
+
assert_in_delta 1.137915, @kf_likehood.sigma
|
42
|
+
end
|
43
|
+
should "return correct AIC value" do
|
44
|
+
assert_in_delta 301.44, @kf_likehood.aic, 0.1
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
context "passed through the Kalman Filter with AR(0.2)" do
|
49
|
+
setup do
|
50
|
+
@kf_likehood=Statsample::TimeSeries::Arima::KalmanFilter.log_likelihood([0.2],@s,1,0)
|
51
|
+
end
|
52
|
+
should "return correct object for log_likehood" do
|
53
|
+
assert_instance_of Statsample::TimeSeries::Arima::KF::LogLikelihood, @kf_likehood
|
54
|
+
end
|
55
|
+
should "return correct log_likehood" do
|
56
|
+
assert_in_delta -66.40337-0.5*@s.size*(Math.log(2*Math::PI)), @kf_likehood.log_likelihood, 0.01
|
57
|
+
end
|
58
|
+
should "return correct sigma" do
|
59
|
+
assert_in_delta 1.378693, @kf_likehood.sigma, 0.01
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context("ARMA(1, 1) process") do
|
66
|
+
setup do
|
67
|
+
@s = [-1.16025577,0.64758021,0.77158601,0.14989543,2.31358162,3.49213868,1.14826956,0.58169457,-0.30813868,-0.34741084,-1.41175595,0.06040081, -0.78230232,0.86734837,0.95015787,-0.49781397,0.53247330,1.56495187,0.30936619,0.09750217,1.09698829,-0.81315490,-0.79425607,-0.64568547,-1.06460320,1.24647894,0.66695937,1.50284551,1.17631218,1.64082872,1.61462736,0.06443761,-0.17583741,0.83918339,0.46610988,-0.54915270,-0.56417108,-1.27696654,0.89460084,1.49970338,0.24520493,0.26249138,-1.33744834,-0.57725961,1.55819543,1.62143157,0.44421891,-0.74000084 ,0.57866347,3.51189333,2.39135077,1.73046244,1.81783890,0.21454040,0.43520890,-1.42443856,-2.72124685,-2.51313877,-1.20243091,-1.44268002 ,-0.16777305,0.05780661,2.03533992,0.39187242,0.54987983,0.57865693,-0.96592469,-0.93278473,-0.75962671,-0.63216906,1.06776183, 0.17476059 ,0.06635860,0.94906227,2.44498583,-1.04990407,-0.88440073,-1.99838258,-1.12955558,-0.62654882,-1.36589161,-2.67456821,-0.97187696, -0.84431782 ,-0.10051809,0.54239549,1.34622861,1.25598105,0.19707759,3.29286114,3.52423499,1.69146333,-0.10150024,0.45222903,-0.01730516, -0.49828727, -1.18484684,-1.09531773,-1.17190808,0.30207662].to_ts
|
68
|
+
end
|
69
|
+
context "passed through the Kalman Filter" do
|
70
|
+
setup do
|
71
|
+
@kf = Statsample::TimeSeries::ARIMA.ks(@s, 2, 0, 1)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "return correct parameters" do
|
75
|
+
assert_equal @kf.p, 2
|
76
|
+
assert_equal @kf.q, 1
|
77
|
+
assert_equal @kf.i, 0
|
78
|
+
end
|
79
|
+
should "return correct AR estimators" do
|
80
|
+
assert_equal @kf.ar.length, 2
|
81
|
+
assert_in_delta @kf.ar[0], 0.45, 0.01
|
82
|
+
assert_in_delta @kf.ar[1], 0.016, 0.01
|
83
|
+
end
|
84
|
+
should "return correct ma estimators" do
|
85
|
+
assert_equal @kf.ma.length, 1
|
86
|
+
assert_in_delta @kf.ma[0], 0.18, 0.01
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "passed through the LogLikelihood with ARMA([0.45, 0.16, 0.18])" do
|
91
|
+
setup do
|
92
|
+
@ll = Statsample::TimeSeries::Arima::KF::LogLikelihood.new([0.45, 0.16, 0.18], @s, 2, 1)
|
93
|
+
end
|
94
|
+
should "return correct log likelihood" do
|
95
|
+
assert_in_delta -149.55, @ll.log_likelihood, 0.01
|
96
|
+
end
|
97
|
+
should "return correct sigma" do
|
98
|
+
assert_in_delta 1.14, @ll.sigma, 0.1
|
99
|
+
end
|
100
|
+
should "return correct AIC value" do
|
101
|
+
assert_in_delta 307.11, @ll.aic, 0.01
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bio-statsample-timeseries
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-09-
|
13
|
+
date: 2013-09-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: statsample
|
@@ -188,6 +188,22 @@ dependencies:
|
|
188
188
|
- - ~>
|
189
189
|
- !ruby/object:Gem::Version
|
190
190
|
version: 0.14.0
|
191
|
+
- !ruby/object:Gem::Dependency
|
192
|
+
name: gsl
|
193
|
+
requirement: !ruby/object:Gem::Requirement
|
194
|
+
none: false
|
195
|
+
requirements:
|
196
|
+
- - ! '>='
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
|
+
type: :development
|
200
|
+
prerelease: false
|
201
|
+
version_requirements: !ruby/object:Gem::Requirement
|
202
|
+
none: false
|
203
|
+
requirements:
|
204
|
+
- - ! '>='
|
205
|
+
- !ruby/object:Gem::Version
|
206
|
+
version: '0'
|
191
207
|
description: Statsample-timeseries is an extension to Statsample. It incorporates
|
192
208
|
helpful timeseries functions and modules like ARMA, ARIMA, acf, pacf, lags etc.
|
193
209
|
email: ankurgel@gmail.com
|
@@ -216,11 +232,14 @@ files:
|
|
216
232
|
- features/support/env.rb
|
217
233
|
- lib/bio-statsample-timeseries.rb
|
218
234
|
- lib/bio-statsample-timeseries/arima.rb
|
235
|
+
- lib/bio-statsample-timeseries/arima/kalman.rb
|
236
|
+
- lib/bio-statsample-timeseries/arima/likelihood.rb
|
219
237
|
- lib/bio-statsample-timeseries/timeseries.rb
|
220
238
|
- lib/bio-statsample-timeseries/timeseries/pacf.rb
|
221
239
|
- lib/bio-statsample-timeseries/utility.rb
|
222
240
|
- test/fixtures/stock_data.csv
|
223
241
|
- test/helper.rb
|
242
|
+
- test/test_arima_ks.rb
|
224
243
|
- test/test_arima_simulators.rb
|
225
244
|
- test/test_matrix.rb
|
226
245
|
- test/test_pacf.rb
|
@@ -241,7 +260,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
241
260
|
version: '0'
|
242
261
|
segments:
|
243
262
|
- 0
|
244
|
-
hash:
|
263
|
+
hash: 68777245
|
245
264
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
246
265
|
none: false
|
247
266
|
requirements:
|