bio-statsample-timeseries 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|