statsample-timeseries 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - jruby-19mode # JRuby in 1.9 mode
7
+ - rbx-19mode
8
+ # - 1.8.7
9
+ # - jruby-18mode # JRuby in 1.8 mode
10
+ # - rbx-18mode
11
+
12
+ # uncomment this line if your project needs to run something other than `rake`:
13
+ # script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'statsample', '=1.2.0'
4
+
5
+ # Add dependencies required to use your gem here.
6
+ # Example:
7
+ gem "activesupport", "= 3.2.10"
8
+
9
+ # Add dependencies to develop your gem here.
10
+ # Include everything needed to run rake, tests, features, etc.
11
+ group :development do
12
+ gem "shoulda", ">= 0"
13
+ gem "rdoc", "~> 3.12"
14
+ gem "minitest", "~> 4.7.5"
15
+ gem "cucumber", ">= 0"
16
+ gem "bundler", "~> 1.3.5"
17
+ gem "jeweler", "~> 1.8.4"
18
+ gem "bio", ">= 1.4.2"
19
+ gem "rdoc", "~> 3.12"
20
+ gem 'mocha', '~> 0.14.0'
21
+ gem 'gsl'
22
+ end
@@ -0,0 +1,22 @@
1
+ This version of Statsample-Timeseries is licensed under the BSD 2-clause license.
2
+
3
+ * http://sciruby.com
4
+ * http://github.com/sciruby/sciruby/wiki/License
5
+
6
+ You *must* read the Contributor Agreement before contributing code to the SciRuby Project. This is available online:
7
+
8
+ * http://github.com/sciruby/sciruby/wiki/Contributor-Agreement
9
+
10
+ -----
11
+
12
+ Copyright (c) 2010 - 2013, Ruby Science Foundation
13
+ All rights reserved.
14
+
15
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
16
+
17
+ * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
18
+
19
+ * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
20
+
21
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
22
+
@@ -0,0 +1,72 @@
1
+ = statsample-timeseries
2
+
3
+ {<img
4
+ src="https://secure.travis-ci.org/AnkurGel/statsample-timeseries.png"
5
+ />}[http://travis-ci.org/#!/AnkurGel/statsample-timeseries]
6
+
7
+ Statsample-Timeseries is an extension to [Statsample](https://github.com/clbustos/statsample), a suite of advance statistics in Ruby.
8
+
9
+ * {sciruby.com}[http://sciruby.com]
10
+ * {Google+}[https://plus.google.com/109304769076178160953/posts]
11
+ * {Ankur Goel}[http://ankurgoel.com]
12
+ * {Statsample}[https://github.com/SciRuby/statsample]
13
+
14
+
15
+ == Description
16
+
17
+ Statsample-Timeseries is extension of Statsample, and incorporates helpful timeseries functions, estimations and modules such as:
18
+
19
+ * ACF
20
+ * PACF
21
+ * ARIMA
22
+ * KalmanFilter
23
+ * LogLikelihood
24
+ * Autocovariances
25
+ * Moving Averages
26
+
27
+ Statsample-Timeseries was created by Ankur Goel as part of Google's Summer of Code 2013. It is the part of {SciRuby}[http://sciruby.com]
28
+
29
+
30
+ == Dependency
31
+
32
+ Please install [`rb-gsl`](http://rb-gsl.rubyforge.org/) which is a Ruby wrapper over GNU Scientific Library. It enables us to use various minimization techniques during estimations.
33
+
34
+
35
+ == Installation
36
+
37
+ gem install statsample-timeseries
38
+
39
+ == Usage
40
+
41
+ To use the library
42
+
43
+ require 'statsample-timeseries'
44
+
45
+ You can also go through the blog-posts on {my blog}[http://ankurgoel.com] for descriptive explanation and examples.
46
+
47
+
48
+ == Documentation
49
+
50
+ The API doc is {online}[http://rubygems.org/gems/statsample-timeseries]. For more code examples see also the test files in the source tree.
51
+
52
+
53
+ == Contributing
54
+
55
+ * Fork the project.
56
+ * Create your feature branch
57
+ * Add/Modify code.
58
+ * Write equivalent documentation and **tests**.
59
+ * Run `rake test` to verify that all test case passes.
60
+ * Push your branch.
61
+ * Pull request. :)
62
+
63
+ == Project home page
64
+
65
+ Information on the source tree, documentation, issues and how to contribute, see
66
+
67
+ {http://github.com/AnkurGel/statsample-timeseries}[git@github.com:AnkurGel/statsample-timeseries.git]
68
+
69
+ == Copyright
70
+
71
+ Copyright (c) 2013 Ankur Goel. See LICENSE.txt for further details.
72
+
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "statsample-timeseries"
18
+ gem.homepage = "http://github.com/AnkurGel/statsample-timeseries"
19
+ gem.summary = %Q{TimeSeries modules for Statsample}
20
+ gem.description = %Q{Statsample-timeseries is an extension to Statsample. It incorporates helpful timeseries functions and modules like ARMA, ARIMA, acf, pacf, lags etc.}
21
+ gem.email = "ankurgel@gmail.com"
22
+ gem.authors = ["Ankur Goel", "Claudio Bustos"]
23
+ # dependencies defined in Gemfile
24
+ end
25
+ Jeweler::RubygemsDotOrgTasks.new
26
+
27
+ require 'rake/testtask'
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ require 'cucumber/rake/task'
35
+ Cucumber::Rake::Task.new(:features)
36
+
37
+ task :default => :test
38
+
39
+ require 'rdoc/task'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "statsample-timeseries #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # BioRuby bio-statsample-timeseries Plugin BioStatsampleTimeseries
4
+ # Author:: Ankur Goel
5
+ # Copyright:: 2013
6
+
7
+ USAGE = "Describe bio-statsample-timeseries"
8
+
9
+ if ARGV.size == 0
10
+ print USAGE
11
+ end
12
+
13
+ require 'bio-statsample-timeseries'
14
+ require 'optparse'
15
+
16
+ # Uncomment when using the bio-logger
17
+ # require 'bio-logger'
18
+ # Bio::Log::CLI.logger('stderr')
19
+ # Bio::Log::CLI.trace('info')
20
+
21
+ options = {:example_switch=>false,:show_help=>false}
22
+ opts = OptionParser.new do |o|
23
+ o.banner = "Usage: #{File.basename($0)} [options] reponame\ne.g. #{File.basename($0)} the-perfect-gem"
24
+
25
+ o.on('--example_parameter [EXAMPLE_PARAMETER]', 'TODO: put a description for the PARAMETER') do |example_parameter|
26
+ # TODO: your logic here, below an example
27
+ options[:example_parameter] = 'this is a parameter'
28
+ end
29
+
30
+ o.separator ""
31
+ o.on("--switch-example", 'TODO: put a description for the SWITCH') do
32
+ # TODO: your logic here, below an example
33
+ self[:example_switch] = true
34
+ end
35
+
36
+ # Uncomment the following when using the bio-logger
37
+ # o.separator ""
38
+ # o.on("--logger filename",String,"Log to file (default stderr)") do | name |
39
+ # Bio::Log::CLI.logger(name)
40
+ # end
41
+ #
42
+ # o.on("--trace options",String,"Set log level (default INFO, see bio-logger)") do | s |
43
+ # Bio::Log::CLI.trace(s)
44
+ # end
45
+ #
46
+ # o.on("-q", "--quiet", "Run quietly") do |q|
47
+ # Bio::Log::CLI.trace('error')
48
+ # end
49
+ #
50
+ # o.on("-v", "--verbose", "Run verbosely") do |v|
51
+ # Bio::Log::CLI.trace('info')
52
+ # end
53
+ #
54
+ # o.on("--debug", "Show debug messages") do |v|
55
+ # Bio::Log::CLI.trace('debug')
56
+ # end
57
+
58
+ o.separator ""
59
+ o.on_tail('-h', '--help', 'display this help and exit') do
60
+ options[:show_help] = true
61
+ end
62
+ end
63
+
64
+ begin
65
+ opts.parse!(ARGV)
66
+
67
+ # Uncomment the following when using the bio-logger
68
+ # Bio::Log::CLI.configure('bio-statsample-timeseries')
69
+
70
+ # TODO: your code here
71
+ # use options for your logic
72
+ rescue OptionParser::InvalidOption => e
73
+ options[:invalid_argument] = e.message
74
+ end
@@ -0,0 +1,31 @@
1
+ Feature: ACF
2
+
3
+ As a statistician
4
+ So that I can evaluate autocorrelation of a series
5
+ I want to evaluate acf
6
+
7
+ Background: a timeseries
8
+
9
+ Given the following values in a timeseries:
10
+ | timeseries |
11
+ | 10 20 30 40 50 60 70 80 90 100 |
12
+ | 110 120 130 140 150 160 170 180 190 200 |
13
+
14
+ Scenario: cross-check acf for 10 lags
15
+ When I provide 10 lags for acf
16
+ And I calculate acf
17
+ Then I should get 11 values in resultant acf
18
+ And I should see "1.0, 0.85, 0.7015037593984963, 0.556015037593985, 0.4150375939849624, 0.2800751879699248, 0.15263157894736842, 0.034210526315789476, -0.07368421052631578, -0.16954887218045114, -0.2518796992481203" as complete series
19
+
20
+ Scenario: cross-check acf for 5 lags
21
+ When I provide 5 lags for acf
22
+ And I calculate acf
23
+ Then I should get 6 values in resultant acf
24
+ And I should see "1.0, 0.85, 0.7015037593984963, 0.556015037593985, 0.4150375939849624, 0.2800751879699248" as complete series
25
+
26
+ Scenario: first value should be 1.0
27
+ When I provide 2 lags for acf
28
+ And I calculate acf
29
+ Then I should get 3 values in resultant acf
30
+ And I should see 1.0 as first value
31
+
@@ -0,0 +1,42 @@
1
+ Feature: PACF
2
+
3
+ As a statistician
4
+ So that I can quickly evaluate partial autocorrelation of a series
5
+ I want to evaluate pacf
6
+
7
+ Background: a timeseries
8
+
9
+ Given the following values in a timeseries:
10
+ | timeseries |
11
+ | 10 20 30 40 50 60 70 80 90 100 |
12
+ | 110 120 130 140 150 160 170 180 190 200 |
13
+
14
+ Scenario: check pacf for 10 lags with unbiased
15
+ When I provide 10 lags for pacf
16
+ When I provide yw yule walker as method
17
+ Then I should get Array as resultant output
18
+ Then I should get 11 values in resultant pacf
19
+
20
+ Scenario: check pacf for 5 lags with mle
21
+ When I provide 5 lags for pacf
22
+ When I provide mle yule walker as method
23
+ Then I should get Array as resultant output
24
+ Then I should get 6 values in resultant pacf
25
+
26
+ Scenario: check first value of pacf
27
+ When I provide 5 lags for pacf
28
+ When I provide yw yule walker as method
29
+ Then I should get Array as resultant output
30
+ And I should see 1.0 as first value
31
+
32
+ Scenario: check all values in pacf for 5 lags with mle
33
+ When I provide 5 lags for pacf
34
+ When I provide mle yule walker as method
35
+ Then I should get Array as resultant output
36
+ And I should see "1.0, 0.85, -0.07566212829370711, -0.07635069706072706, -0.07698628638512295, -0.07747034005560738" as complete series
37
+
38
+ Scenario: check all values in pacf for 5 lags with unbiased
39
+ When I provide 5 lags for pacf
40
+ When I provide yw yule walker as method
41
+ Then I should get Array as resultant output
42
+ And I should see "1.0, 0.8947368421052632, -0.10582010582010604, -0.11350188273265083, -0.12357534824820737, -0.13686534216335522" as complete series
@@ -0,0 +1,37 @@
1
+ require 'statsample-timeseries'
2
+ include Statsample::TimeSeries
3
+
4
+ Given /^the following values in a timeseries:$/ do |series|
5
+ arr = []
6
+ series.hashes.each do |sequence|
7
+ arr += sequence['timeseries'].split(' ').map(&:to_i).to_ts
8
+ end
9
+ @timeseries = arr.to_ts
10
+ end
11
+
12
+ When /^I provide (\d+) lags for p?acf$/ do |lags|
13
+ @lags = lags.to_i
14
+ end
15
+
16
+ When /^I provide (\w+) yule walker as method$/ do |method|
17
+ @method = method
18
+ end
19
+
20
+ Then /^I should get (\w+) as resultant output$/ do |klass|
21
+ @result = @timeseries.pacf(@lags, @method)
22
+ assert_equal @result.class.to_s, klass
23
+ end
24
+
25
+ Then /^I should get (\w+) values in resultant p?acf$/ do |values_count|
26
+ assert_equal @result.size, values_count.to_i
27
+ end
28
+
29
+ And /^I should see (\d+\.\d) as first value$/ do |first_value|
30
+ assert_equal @result.first, first_value.to_f
31
+ end
32
+
33
+ And /^I should see \"(.+)\" as complete series$/ do |series|
34
+ series = series.split(',').map(&:to_f)
35
+ assert_equal @result, series
36
+ end
37
+
@@ -0,0 +1,8 @@
1
+ require 'statsample-timeseries'
2
+ include Statsample::TimeSeries
3
+
4
+ #all instance variable and cucumber DSL s DRYed up in step_definitions.rb
5
+ And /^I calculate acf$/ do
6
+ @result = @timeseries.acf(@lags)
7
+ end
8
+
@@ -0,0 +1,15 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+
10
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
11
+ require 'statsample-timeseries'
12
+
13
+ require 'test/unit/assertions'
14
+
15
+ World(Test::Unit::Assertions)
@@ -0,0 +1,18 @@
1
+ # Please require your code below, respecting the naming conventions in the
2
+ # bioruby directory tree.
3
+ #
4
+ # For example, say you have a plugin named bio-plugin, the only uncommented
5
+ # line in this file would be
6
+ #
7
+ # require 'bio/bio-plugin/plugin'
8
+ #
9
+ # In this file only require other files. Avoid other source code.
10
+
11
+ require 'statsample'
12
+ require_relative 'statsample-timeseries/timeseries.rb'
13
+ require_relative 'statsample-timeseries/arima.rb'
14
+ require_relative 'statsample-timeseries/arima/kalman'
15
+ require_relative 'statsample-timeseries/arima/likelihood'
16
+ require_relative 'statsample-timeseries/utility.rb'
17
+
18
+
@@ -0,0 +1,246 @@
1
+ #require 'debugger'
2
+ require 'statsample-timeseries/arima/kalman'
3
+ require 'statsample-timeseries/arima/likelihood'
4
+ module Statsample
5
+ module TimeSeries
6
+
7
+ def self.arima
8
+ #not passing (ds,p,i,q) elements for now
9
+ #will do that once #arima is ready for all modelling
10
+ Statsample::TimeSeries::ARIMA.new
11
+ end
12
+
13
+ class ARIMA < Statsample::Vector
14
+ include Statsample::TimeSeries
15
+
16
+ #= Kalman filter on ARIMA model
17
+ #== Params
18
+ #
19
+ #* *ts*: timeseries object
20
+ #* *p*: AR order
21
+ #* *i*: Integerated part order
22
+ #* *q*: MA order
23
+ #
24
+ #== Usage
25
+ # ts = (1..100).map { rand }.to_ts
26
+ # k_obj = Statsample::TimeSeries::ARIMA.ks(ts, 2, 1, 1)
27
+ # k_obj.ar
28
+ # #=> AR's phi coefficients
29
+ # k_obj.ma
30
+ # #=> MA's theta coefficients
31
+ #
32
+ #== Returns
33
+ #Kalman filter object
34
+ def self.ks(ts, p, i, q)
35
+ #prototype
36
+ if i > 0
37
+ ts = ts.diff(i).reject { |x| x.nil? }.to_ts
38
+ end
39
+ filter = Arima::KalmanFilter.new(ts, p, i, q)
40
+ filter
41
+ end
42
+
43
+ def ar(p)
44
+ #AutoRegressive part of model
45
+ #http://en.wikipedia.org/wiki/Autoregressive_model#Definition
46
+ #For finding parameters(to fit), we will use either Yule-walker
47
+ #or Burg's algorithm(more efficient)
48
+ end
49
+
50
+ #Converts a linear array into a Statsample vector
51
+ #== Parameters
52
+ #
53
+ #* *arr*: Array which has to be converted in Statsample vector
54
+ def create_vector(arr)
55
+ Statsample::Vector.new(arr, :scale)
56
+ end
57
+
58
+ #=Yule Walker
59
+ #Performs yule walker estimation on given timeseries, observations and order
60
+ #==Parameters
61
+ #
62
+ #* *ts*: timeseries object
63
+ #* *n* : number of observations
64
+ #* *k* : order
65
+ #
66
+ #==Returns
67
+ #phi and sigma vectors
68
+ def yule_walker(ts, n, k)
69
+ phi, sigma = Pacf::Pacf.yule_walker(ts, k)
70
+ return phi, sigma
71
+ #return ar_sim(n, phi, sigma)
72
+ end
73
+
74
+ #=Levinson Durbin estimation
75
+ #Performs levinson durbin estimation on given timeseries, observations and order
76
+ #==Parameters
77
+ #
78
+ #* *ts*: timeseries object
79
+ #* *n* : number of observations
80
+ #* *k* : autoregressive order
81
+ #
82
+ #==Returns
83
+ #phi and sigma vectors
84
+ def levinson_durbin(ts, n, k)
85
+ intermediate = Pacf::Pacf.levinson_durbin(ts, k)
86
+ phi, sigma = intermediate[1], intermediate[0]
87
+ return phi, sigma
88
+ #return ar_sim(n, phi, sigma)
89
+ end
90
+
91
+ #=Autoregressive Simulator
92
+ #Simulates an autoregressive AR(p) model with specified number of
93
+ #observations(n), with phi number of values for order p and sigma.
94
+ #
95
+ #==Analysis:
96
+ # [http://ankurgoel.com/blog/2013/07/20/ar-ma-arma-acf-pacf-visualizations/](http://ankurgoel.com/blog/2013/07/20/ar-ma-arma-acf-pacf-visualizations/)
97
+ #
98
+ #==Parameters:
99
+ #* *n*: integer, number of observations
100
+ #* *phi* :array of phi values, e.g: [0.35, 0.213] for p = 2
101
+ #* *sigma*: float, sigma value for error generalization
102
+ #
103
+ #==Usage
104
+ # ar = ARIMA.new
105
+ # ar.ar_sim(1500, [0.3, 0.9], 0.12)
106
+ # # => AR(2) autoregressive series of 1500 values
107
+ #
108
+ #==Returns
109
+ #Array of generated autoregressive series against attributes
110
+ def ar_sim(n, phi, sigma)
111
+ #using random number generator for inclusion of white noise
112
+ err_nor = Distribution::Normal.rng(0, sigma)
113
+ #creating buffer with 10 random values
114
+ buffer = Array.new(10, err_nor.call())
115
+
116
+ x = buffer + Array.new(n, 0)
117
+
118
+ #For now "phi" are the known model parameters
119
+ #later we will obtain it by Yule-walker/Burg
120
+
121
+ #instead of starting from 0, start from 11
122
+ #and later take away buffer values for failsafe
123
+ 11.upto(n+11) do |i|
124
+ if i <= phi.size
125
+ #dependent on previous accumulation of x
126
+ backshifts = create_vector(x[0...i].reverse)
127
+ else
128
+ #dependent on number of phi size/order
129
+ backshifts = create_vector(x[(i - phi.size)...i].reverse)
130
+ end
131
+ parameters = create_vector(phi[0...backshifts.size])
132
+
133
+ summation = (backshifts * parameters).inject(:+)
134
+ x[i] = summation + err_nor.call()
135
+ end
136
+ x - buffer
137
+ end
138
+
139
+ #=Moving Average Simulator
140
+ #Simulates a moving average model with specified number of
141
+ #observations(n), with theta values for order k and sigma
142
+ #
143
+ #==Parameters
144
+ #* *n*: integer, number of observations
145
+ #* *theta*: array of floats, e.g: [0.23, 0.732], must be < 1
146
+ #* *sigma*: float, sigma value for whitenoise error
147
+ #
148
+ #==Usage
149
+ # ar = ARIMA.new
150
+ # ar.ma_sim(1500, [0.23, 0.732], 0.27)
151
+ #
152
+ #==Returns
153
+ #Array of generated MA(q) model
154
+ def ma_sim(n, theta, sigma)
155
+ #n is number of observations (eg: 1000)
156
+ #theta are the model parameters containting q values
157
+ #q is the order of MA
158
+ mean = theta.to_ts.mean()
159
+ whitenoise_gen = Distribution::Normal.rng(0, sigma)
160
+ x = Array.new(n, 0)
161
+ q = theta.size
162
+ noise_arr = (n+1).times.map { whitenoise_gen.call() }
163
+
164
+ 1.upto(n) do |i|
165
+ #take care that noise vector doesn't try to index -ve value:
166
+ if i <= q
167
+ noises = create_vector(noise_arr[0..i].reverse)
168
+ else
169
+ noises = create_vector(noise_arr[(i-q)..i].reverse)
170
+ end
171
+ weights = create_vector([1] + theta[0...noises.size - 1])
172
+
173
+ summation = (weights * noises).inject(:+)
174
+ x[i] = mean + summation
175
+ end
176
+ x
177
+ end
178
+
179
+ #=ARMA(Autoregressive and Moving Average) Simulator
180
+ #ARMA is represented by:
181
+ #http://upload.wikimedia.org/math/2/e/d/2ed0485927b4370ae288f1bc1fe2fc8b.png
182
+ #This simulates the ARMA model against p, q and sigma.
183
+ #If p = 0, then model is pure MA(q),
184
+ #If q = 0, then model is pure AR(p),
185
+ #otherwise, model is ARMA(p, q) represented by above.
186
+ #
187
+ #==Detailed analysis:
188
+ # [http://ankurgoel.com/blog/2013/07/20/ar-ma-arma-acf-pacf-visualizations/](http://ankurgoel.com/blog/2013/07/20/ar-ma-arma-acf-pacf-visualizations/)
189
+ #
190
+ #==Parameters
191
+ #* *n*: integer, number of observations
192
+ #* *p*: array, contains p number of phi values for AR(p) process
193
+ #* *q*: array, contains q number of theta values for MA(q) process
194
+ #* *sigma*: float, sigma value for whitenoise error generation
195
+ #
196
+ #==Usage
197
+ # ar = ARIMA.new
198
+ # ar.arma_sim(1500, [0.3, 0.272], [0.8, 0.317], 0.92)
199
+ #
200
+ #==Returns
201
+ #array of generated ARMA model values
202
+ def arma_sim(n, p, q, sigma)
203
+ #represented by :
204
+ #http://upload.wikimedia.org/math/2/e/d/2ed0485927b4370ae288f1bc1fe2fc8b.png
205
+
206
+
207
+ whitenoise_gen = Distribution::Normal.rng(0, sigma)
208
+ noise_arr = (n+11).times.map { whitenoise_gen.call() }
209
+
210
+ buffer = Array.new(10, whitenoise_gen.call())
211
+ x = buffer + Array.new(n, 0)
212
+
213
+ 11.upto(n+11) do |i|
214
+ if i <= p.size
215
+ backshifts = create_vector(x[0...i].reverse)
216
+ else
217
+ backshifts = create_vector(x[(i - p.size)...i].reverse)
218
+ end
219
+ parameters = create_vector(p[0...backshifts.size])
220
+
221
+ ar_summation = (backshifts * parameters).inject(:+)
222
+
223
+ if i <= q.size
224
+ noises = create_vector(noise_arr[0..i].reverse)
225
+ else
226
+ noises = create_vector(noise_arr[(i-q.size)..i].reverse)
227
+ end
228
+ weights = create_vector([1] + q[0...noises.size - 1])
229
+
230
+ ma_summation = (weights * noises).inject(:+)
231
+
232
+ x[i] = ar_summation + ma_summation
233
+ end
234
+ x - buffer
235
+ end
236
+
237
+ #=Hannan-Rissanen for ARMA fit
238
+ def self.hannan(ts, p, q, k)
239
+ start_params = create_vector(Array.new(p+q+k, 0))
240
+ ts_dup = ts.dup
241
+
242
+ end
243
+ end
244
+
245
+ end
246
+ end