statsample-timeseries 0.0.2

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.
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'minitest/unit'
11
+ require 'shoulda'
12
+ require 'shoulda-context'
13
+ require 'mocha/setup'
14
+
15
+ #require 'statsample-timeseries'
16
+
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
+ require 'statsample-timeseries'
20
+ module MiniTest
21
+ class Unit
22
+ class TestCase
23
+ include Shoulda::Context::Assertions
24
+ include Shoulda::Context::InstanceMethods
25
+ extend Shoulda::Context::ClassMethods
26
+ def self.should_with_gsl(name,&block)
27
+ should(name) do
28
+ if Statsample.has_gsl?
29
+ instance_eval(&block)
30
+ else
31
+ skip("Requires GSL")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module Assertions
39
+ def assert_similar_vector(exp, obs, delta=1e-10,msg=nil)
40
+ msg||="Different vectors #{exp} - #{obs}"
41
+ assert_equal(exp.size, obs.size)
42
+ exp.data_with_nils.each_with_index {|v,i|
43
+ assert_in_delta(v,obs[i],delta)
44
+ }
45
+ end
46
+ def assert_equal_vector(exp,obs,delta=1e-10,msg=nil)
47
+ assert_equal(exp.size, obs.size, "Different size.#{msg}")
48
+ exp.size.times {|i|
49
+ assert_in_delta(exp[i],obs[i],delta, "Different element #{i}. \nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}")
50
+ }
51
+ end
52
+ def assert_equal_matrix(exp,obs,delta=1e-10,msg=nil)
53
+ assert_equal(exp.row_size, obs.row_size, "Different row size.#{msg}")
54
+ assert_equal(exp.column_size, obs.column_size, "Different column size.#{msg}")
55
+ exp.row_size.times {|i|
56
+ exp.column_size.times {|j|
57
+ assert_in_delta(exp[i,j],obs[i,j], delta, "Different element #{i},#{j}\nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}")
58
+ }
59
+ }
60
+ end
61
+ alias :assert_raise :assert_raises unless method_defined? :assert_raise
62
+ alias :assert_not_equal :refute_equal unless method_defined? :assert_not_equal
63
+ alias :assert_not_same :refute_same unless method_defined? :assert_not_same
64
+ unless method_defined? :assert_nothing_raised
65
+ def assert_nothing_raised(msg=nil)
66
+ msg||="Nothing should be raised, but raised %s"
67
+ begin
68
+ yield
69
+ not_raised=true
70
+ rescue Exception => e
71
+ not_raised=false
72
+ msg=sprintf(msg,e)
73
+ end
74
+ assert(not_raised,msg)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ MiniTest::Unit.autorun
81
+
@@ -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
@@ -0,0 +1,186 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+
3
+ class StatsampleArimaSimulatorsTest < MiniTest::Unit::TestCase
4
+ def generate_acf(simulation)
5
+ ts = simulation.to_ts
6
+ ts.acf
7
+ end
8
+
9
+ def generate_pacf(simulation)
10
+ ts = simulation.to_ts
11
+ ts.pacf
12
+ end
13
+ context("AR(1) simulations") do
14
+ include Statsample
15
+
16
+ setup do
17
+ @series = TimeSeries.arima
18
+ @ar_1_positive = @series.ar_sim(1500, [0.9], 2)
19
+ @ar_1_negative = @series.ar_sim(1500, [-0.9], 2)
20
+
21
+ #generating acf
22
+ @positive_acf = generate_acf(@ar_1_positive)
23
+ @negative_acf = generate_acf(@ar_1_negative)
24
+
25
+ #generating pacf
26
+ @positive_pacf = generate_pacf(@ar_1_positive)
27
+ @negative_pacf = generate_pacf(@ar_1_negative)
28
+ end
29
+
30
+
31
+ should "have exponential decay of acf on positive side with phi > 0" do
32
+ @acf = @positive_acf
33
+ assert_equal @acf[0], 1.0
34
+ assert_operator @acf[1], :>=, 0.7
35
+ assert_operator @acf[@acf.size - 1], :<=, 0.2
36
+ #visualization:
37
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_positive_phi_acf.png
38
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_positive_phi_acf_line.png
39
+ end
40
+
41
+ should "have series with alternating sign on acf starting on negative side with phi < 0" do
42
+ @acf = @negative_acf
43
+ assert_equal @acf[0], 1.0
44
+ #testing for alternating series
45
+ assert_operator @acf[1], :<, 0
46
+ assert_operator @acf[2], :>, 0
47
+ assert_operator @acf[3], :<, 0
48
+ assert_operator @acf[4], :>, 0
49
+ #visualization:
50
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_acf.png
51
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_acf_line.png
52
+ end
53
+
54
+ should "have positive spike on pacf at lag 1 for phi > 0" do
55
+ @pacf = @positive_pacf
56
+ assert_operator @pacf[1], :>=, 0.7
57
+ assert_operator @pacf[2], :<=, 0.2
58
+ assert_operator @pacf[3], :<=, 0.14
59
+ #visualization:
60
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_postive_phi_pacf.png
61
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_postive_phi_pacf_line.png
62
+ end
63
+
64
+ should "have negative spike on pacf at lag 1 for phi < 0" do
65
+ @pacf = @negative_pacf
66
+ assert_operator @pacf[1], :<=, 0
67
+ assert_operator @pacf[1], :<=, -0.5
68
+ assert_operator @pacf[2], :>=, -0.5
69
+ #visualizaton:
70
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_pacf.png
71
+ #[hided @pacf[0] = 1 to convey accurate picture]
72
+ end
73
+ end
74
+
75
+ context("AR(p) simulations") do
76
+ include Statsample
77
+
78
+ setup do
79
+ @series = TimeSeries.arima
80
+ @ar_p_positive = @series.ar_sim(1500, [0.3, 0.5], 2)
81
+ @ar_p_negative = @series.ar_sim(1500, [-0.3, -0.5], 2)
82
+ end
83
+
84
+
85
+ should "have damped sine wave starting on positive side on acf" do
86
+ @ar = @ar_p_positive
87
+ @acf = generate_acf(@ar)
88
+ assert_operator @acf[0], :>=, @acf[1]
89
+ assert_operator @acf[1], :>=, 0.0
90
+ #caution: sine curve can split on cartesian plane,
91
+ #visualization:
92
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR(p)_positive_phi_sine_wave.png
93
+ end
94
+
95
+ should "have damped sine wave starting on negative side on acf" do
96
+ @ar = @ar_p_negative
97
+ @acf = generate_acf(@ar)
98
+ assert_operator @acf[0], :>=, @acf[1]
99
+ assert_operator @acf[1], :<=, 0.0
100
+ assert_operator @acf[1], :>=, @acf[2]
101
+ #caution: sine curve can split on cartesian plane,
102
+ #visualization:
103
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%28p%29_negative_phi_acf_sine_wave.png
104
+ end
105
+
106
+ should "have spikes from 1 to p for pacf" do
107
+ #here p = 2
108
+ @ar = @ar_p_positive
109
+ @pacf = generate_pacf(@ar)
110
+ assert_equal @pacf[0], 1.0
111
+ assert_operator @pacf[1], :>, @pacf[3]
112
+ assert_operator @pacf[1], :>, @pacf[4]
113
+ assert_operator @pacf[1], :>, @pacf[5]
114
+ assert_operator @pacf[2], :>, @pacf[3]
115
+ assert_operator @pacf[2], :>, @pacf[4]
116
+ #visualization:
117
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR(p)_positive_phi_pacf_spikes.png
118
+ end
119
+ end
120
+
121
+
122
+ context("MA(1) simulations") do
123
+ include Statsample
124
+ setup do
125
+ @series = TimeSeries.arima
126
+ @ma_positive = @series.ar_sim(1500, [0.5], 2)
127
+ @ma_negative = @series.ar_sim(1500, [-0.5], 2)
128
+ end
129
+
130
+ should "have one positive spike at lag 1 on acf at positive theta" do
131
+ @acf = generate_acf(@ma_positive)
132
+ assert_equal @acf[0], 1.0
133
+ assert_operator @acf[1], :>=, 0 #test if positive
134
+ #test if spike
135
+ assert_operator @acf[2], :>=, 0.1
136
+ assert_operator @acf[3], :<=, 0.2
137
+ assert_operator @acf[4], :<=, 0.2
138
+ #visualization:
139
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_postive_acf.png
140
+ end
141
+
142
+ should "have one negative spike at lag 1 on acf at negative theta" do
143
+ @acf = generate_acf(@ma_negative)
144
+ assert_operator @acf[1], :<, 0
145
+ assert_operator @acf[2], :>=, @acf[1]
146
+ assert_operator @acf[3], :>=, @acf[1]
147
+ #visualization:
148
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_negative_acf.png
149
+ #positive_vs_negative:
150
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_acf_positive_vs_negative.png
151
+ end
152
+
153
+ end
154
+
155
+ context("MA(q) simulations") do
156
+ include Statsample
157
+ setup do
158
+ @series = TimeSeries.arima
159
+ @ma_positive = @series.ar_sim(1500, [0.5, 0.3, 0.2], 2)
160
+ @ma_negative = @series.ar_sim(1500, [-0.5], 2)
161
+ end
162
+
163
+ should "have q positive spikes at lag 1 to q on acf at positive thetas" do
164
+ @acf = generate_acf(@ma_positive)
165
+ assert_operator @acf[1], :>=, @acf[2]
166
+ assert_operator @acf[2], :>=, @acf[3]
167
+ assert_operator @acf[3], :>=, @acf[4]
168
+ #Visualization: http://jsfiddle.net/YeK2c/
169
+ end
170
+
171
+ should "have damped sine wave on pacf at positive thetas" do
172
+ #visualization: http://jsfiddle.net/7keHK/2/
173
+ end
174
+ end
175
+
176
+ context("Yule walker estimations") do
177
+ include Statsample
178
+
179
+ setup do
180
+ @timeseries = 100.times.map { rand }.to_ts
181
+ @arma_simulation =->(n) { @timeseries.ar(n, k)}
182
+ end
183
+ #to write test
184
+ end
185
+ end
186
+
@@ -0,0 +1,92 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+ class StatsampleMatrixTestCase < MiniTest::Unit::TestCase
3
+
4
+ def setup_square_matrix(arr, n)
5
+ #returns n * n matrix by slicing arr
6
+ return Matrix.rows(arr.each_slice(n).to_a)
7
+ end
8
+ def setup
9
+ @arr_square = (1..16)
10
+ @mat_non_symmetric = setup_square_matrix(@arr_square, 4)
11
+
12
+ @arr_non_square = (1..12).to_a
13
+ #this is a 4 X 3 matrix
14
+ @mat_non_square = Matrix.rows(@arr_non_square.each_slice(3).to_a)
15
+ end
16
+
17
+ #TESTS for matrix symmetricity - Matrix#symmetric?
18
+ context("symmetric?") do
19
+
20
+ should "return false for non-symmetric matrix" do
21
+ assert_equal @mat_non_symmetric.symmetric?, false
22
+ end
23
+
24
+ should "return false for non-square matrix" do
25
+ assert_equal @mat_non_square.symmetric?, false
26
+ end
27
+
28
+ should "return true for symmetrix matrix" do
29
+ arr = %w[4 12 -16 12 37 -43 -16 -43 93].map(&:to_i)
30
+ mat = setup_square_matrix(arr, 3)
31
+ assert_equal mat.symmetric?, true
32
+ end
33
+ end
34
+
35
+ #TESTS for cholesky decomposition - Matrix#cholesky
36
+ context("Cholesky Decomposition") do
37
+
38
+ should "raise error for non symmetric matrix" do
39
+ assert_raises(ArgumentError) { @mat_non_symmetric.cholesky }
40
+ end
41
+
42
+ should "raise raise error if non-square matix" do
43
+ arr = (1..12).to_a
44
+ mat = Matrix.rows(arr.each_slice(3).to_a)
45
+ assert_raises(ArgumentError) { @mat_non_square.cholesky }
46
+ end
47
+
48
+ should "give hermitian cholesky decomposed matrix for symmetrix matrix" do
49
+ arr = %w[4 12 -16 12 37 -43 -16 -43 93].map(&:to_i)
50
+ mat = setup_square_matrix(arr, 3)
51
+ assert_equal Matrix[[2.0, 0, 0], [6.0, 1.0, 0], [-8.0, 5.0, 2.0]], mat.cholesky
52
+ end
53
+ end
54
+
55
+ #TESTS for matrix squares of sum - Matrix#squares_of_sum
56
+ context("Squares of sum") do
57
+
58
+ should "return array of size 4 for matrix - #{@mat_non_symmetric}" do
59
+ #equal to column size
60
+ assert_equal @mat_non_symmetric.squares_of_sum.size, 4
61
+ end
62
+
63
+ should "return [784, 1024, 1296, 1600] for matrix - #{@mat_non_symmetric}" do
64
+ assert_equal @mat_non_symmetric.squares_of_sum, [784, 1024, 1296, 1600]
65
+ end
66
+ end
67
+
68
+ #TESTS for adding constants to matrix
69
+ context("Add constant") do
70
+
71
+ should "prepend all rows with ones" do
72
+ mat = @mat_non_symmetric.add_constant
73
+ assert_equal @mat_non_symmetric.column_size, 4
74
+ assert_equal mat.column_size, 5
75
+ assert_equal mat.column(0).to_a, [1.0, 1.0,1.0,1.0]
76
+ end
77
+
78
+ should "append all rows with ones if prepend = false" do
79
+ mat = @mat_non_symmetric.add_constant(false)
80
+ assert_equal @mat_non_symmetric.column_size, 4
81
+ assert_equal mat.column_size, 5
82
+ assert_equal mat.column(mat.column_size - 1).to_a, [1.0, 1.0,1.0,1.0]
83
+ end
84
+
85
+ should "not append/prepend if a column of ones already exists in matrix" do
86
+ matrix = Matrix[[1, 2, 1, 4], [5, 6, 1, 8], [9, 10, 1, 12]]
87
+ const_mat = matrix.add_constant
88
+ assert_equal matrix.column_size, const_mat.column_size
89
+ assert_equal matrix.row_size, const_mat.row_size
90
+ end
91
+ end
92
+ end