statsample-timeseries 0.0.3 → 0.3.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.
@@ -8,7 +8,7 @@ module Statsample
8
8
  include GSL::MultiMin if Statsample.has_gsl?
9
9
 
10
10
  #timeseries object
11
- attr_accessor :ts
11
+ attr_writer :ts
12
12
  #Autoregressive order
13
13
  attr_accessor :p
14
14
  #Integerated part order
@@ -22,14 +22,18 @@ module Statsample
22
22
  attr_reader :ma
23
23
 
24
24
  #Creates a new KalmanFilter object and computes the likelihood
25
- def initialize(ts=[].to_ts, p=0, i=0, q=0)
26
- @ts = ts
25
+ def initialize(ts=[], p=0, i=0, q=0)
26
+ @ts = ts.to_a
27
27
  @p = p
28
28
  @i = i
29
29
  @q = q
30
30
  ks #call the filter
31
31
  end
32
32
 
33
+ def ts
34
+ Daru::Vector.new(@ts)
35
+ end
36
+
33
37
  def to_s
34
38
  sprintf("ARIMA model(p = %d, i = %d, q = %d) on series(%d elements) - [%s]",
35
39
  @p, @i, @q, @ts.size, @ts.to_a.join(','))
@@ -54,7 +58,7 @@ module Statsample
54
58
  p,q = params[1], params[2]
55
59
  params = x
56
60
  #puts x
57
- -Arima::KF::LogLikelihood.new(x.to_a, timeseries, p, q).ll
61
+ -Arima::KF::LogLikelihood.new(x.to_a, timeseries, p, q).log_likelihood
58
62
  #KalmanFilter.ll(x.to_a, timeseries, p, q)
59
63
  }
60
64
  np = @p + @q
@@ -71,20 +75,13 @@ module Statsample
71
75
  while status == GSL::CONTINUE && iter < 100
72
76
  iter += 1
73
77
  begin
74
- status = minimizer.iterate()
78
+ status = minimizer.iterate
75
79
  status = minimizer.test_size(1e-2)
76
80
  x = minimizer.x
77
81
  rescue
78
82
  break
79
83
  end
80
- # printf("%5d ", iter)
81
- # for i in 0...np do
82
- # puts "#{x[i]}.to_f"
83
- # #printf("%10.3e ", x[i].to_f)
84
- # end
85
- # printf("f() = %7.3f size = %.3f\n", minimizer.fval, minimizer.size)
86
84
  end
87
- #
88
85
  @ar = (p > 0) ? x.to_a[0...p] : []
89
86
  @ma = (q > 0) ? x.to_a[p...(p+q)] : []
90
87
  x.to_a
@@ -112,36 +109,19 @@ module Statsample
112
109
  Arima::KF::LogLikelihood.new(params, timeseries, p, q)
113
110
  end
114
111
 
115
- #=T
116
- #The coefficient matrix for the state vector in state equation
117
- # It's dimensions is r+k x r+k
118
- #==Parameters
119
- #* *r*: integer, r is max(p, q+1), where p and q are orders of AR and MA respectively
120
- #* *k*: integer, number of exogeneous variables in ARMA model
121
- #* *q*: integer, The AR coefficient of ARMA model
122
112
 
123
- #==References Statsmodels tsa, Durbin and Koopman Section 4.7
124
- #def self.T(r, k, p)
125
- # arr = Matrix.zero(r)
126
- # params_padded = Statsample::Vector.new(Array.new(r, 0), :scale)
127
- #
128
- # params_padded[0...p] = params[k...(p+k)]
129
- # intermediate_matrix = (r-1).times.map { Array.new(r, 0) }
130
- # #appending an array filled with padded values in beginning
131
- # intermediate_matrix[0,0] = [params_padded]
132
- #
133
- # #now generating column matrix for that:
134
- # arr = Matrix.columns(intermediate_matrix)
135
- # arr_00 = arr[0,0]
136
- #
137
- # #identify matrix substituition in matrix except row[0] and column[0]
138
- # r.times do |i|
139
- # arr[r,r] = 1
140
- # end
141
- # arr[0,0] = arr_00
142
- # arr
143
- #end
113
+ def self.T(r, k, p)
114
+ #=T
115
+ #The coefficient matrix for the state vector in state equation
116
+ # It's dimensions is r+k x r+k
117
+ #==Parameters
118
+ #* *r*: integer, r is max(p, q+1), where p and q are orders of AR and MA respectively
119
+ #* *k*: integer, number of exogeneous variables in ARMA model
120
+ #* *q*: integer, The AR coefficient of ARMA model
144
121
 
122
+ #==References Statsmodels tsa, Durbin and Koopman Section 4.7
123
+ raise NotImplementedError
124
+ end
145
125
  end
146
126
  end
147
127
  end
@@ -16,7 +16,7 @@ module Statsample
16
16
 
17
17
  def initialize(params, timeseries, p, q)
18
18
  @params = params
19
- @timeseries = timeseries
19
+ @timeseries = timeseries.to_a
20
20
  @p = p
21
21
  @q = q
22
22
  ll
@@ -50,7 +50,7 @@ module Statsample
50
50
  t = Matrix.zero(m)
51
51
  #set_column is available in utility.rb
52
52
  t = t.set_column(0, phi)
53
- if(m > 1)
53
+ if m > 1
54
54
  t[0...(m-1), 1...m] = Matrix.I(m-1)
55
55
  #chances of extra constant 0 values as unbalanced column, so:
56
56
  t = Matrix.columns(t.column_vectors)
@@ -66,10 +66,9 @@ module Statsample
66
66
 
67
67
  n.times do |i|
68
68
  v_t[i] = (z * a_t).map { |x| timeseries[i] - x }[0,0]
69
-
70
69
  f_t[i] = (z * p_t * (z.transpose)).map { |x| x + 1 }[0,0]
71
70
 
72
- k_t = ((t * p_t * z.transpose) + h).map { |x| x / f_t[i] }
71
+ k_t = ((t * p_t * z.transpose) + h).map { |x| x.quo f_t[i] }
73
72
 
74
73
  a_t = (t * a_t) + (k_t * v_t[i])
75
74
  l_t = t - k_t * z
@@ -0,0 +1,78 @@
1
+ require 'statsample-timeseries/timeseries/pacf'
2
+ module Statsample::TimeSeriesShorthands
3
+ # Creates a new Statsample::TimeSeries object
4
+ # Argument should be equal to TimeSeries.new
5
+ def to_time_series(*args)
6
+ Daru::Vector.new(self, *args)
7
+ end
8
+
9
+ alias :to_ts :to_time_series
10
+ end
11
+
12
+ class Array
13
+ include Statsample::TimeSeriesShorthands
14
+ end
15
+
16
+ module Daru
17
+ class Vector
18
+ include Statsample::TimeSeries::Pacf
19
+
20
+ # = Partial Autocorrelation
21
+ # Generates partial autocorrelation series for a timeseries
22
+ #
23
+ # == Arguments
24
+ #
25
+ #* *max_lags*: integer, optional - provide number of lags
26
+ #* *method*: string. Default: 'yw'.
27
+ # * *yw*: For yule-walker algorithm unbiased approach
28
+ # * *mle*: For Maximum likelihood algorithm approach
29
+ # * *ld*: Forr Levinson-Durbin recursive approach
30
+ #
31
+ # == Returns
32
+ #
33
+ # array of pacf
34
+ def pacf(max_lags = nil, method = :yw)
35
+ helper = Statsample::TimeSeries::Pacf
36
+ method = method.downcase.to_sym
37
+ max_lags ||= (10 * Math.log10(size)).to_i
38
+ if method == :yw or method == :mle
39
+ helper.pacf_yw(self, max_lags, method.to_s)
40
+ elsif method == :ld
41
+ series = self.acvf
42
+ helper.levinson_durbin(series, max_lags, true)[2]
43
+ else
44
+ raise "Method presents for pacf are 'yw', 'mle' or 'ld'"
45
+ end
46
+ end
47
+
48
+ # == Autoregressive estimation
49
+ # Generates AR(k) series for the calling timeseries by yule walker.
50
+ #
51
+ # == Parameters
52
+ #
53
+ #* *n*: integer, (default = 1500) number of observations for AR.
54
+ #* *k*: integer, (default = 1) order of AR process.
55
+ #
56
+ # == Returns
57
+ #
58
+ # Array constituting estimated AR series.
59
+ def ar(n = 1500, k = 1)
60
+ series = Statsample::TimeSeries.arima
61
+ #series = Statsample::TimeSeries::ARIMA.new
62
+ series.yule_walker(self, n, k)
63
+ end
64
+ end
65
+ end
66
+
67
+ module Statsample
68
+ module TimeSeries
69
+
70
+ # Deprecated. Use Daru::Vector.
71
+ class Series < Daru::Vector
72
+ def initialize *args, &block
73
+ $stderr.puts "This class has been deprecated. Use Daru::Vector directly."
74
+ super(*args, &block)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,14 +1,14 @@
1
1
  module Statsample
2
2
  module TimeSeries
3
3
  module Pacf
4
- class Pacf
5
-
6
- def self.pacf_yw(timeseries, max_lags, method = 'yw')
4
+ class << self
5
+ def pacf_yw(timeseries, max_lags, method = 'yw')
7
6
  #partial autocorrelation by yule walker equations.
8
7
  #Inspiration: StatsModels
9
8
  pacf = [1.0]
9
+ arr = timeseries.to_a
10
10
  (1..max_lags).map do |i|
11
- pacf << yule_walker(timeseries, i, method)[0][-1]
11
+ pacf << yule_walker(arr, i, method)[0][-1]
12
12
  end
13
13
  pacf
14
14
  end
@@ -25,8 +25,7 @@ module Statsample
25
25
  #* *arcoefs*: AR coefficients
26
26
  #* *pacf*: pacf function
27
27
  #* *sigma*: some function
28
- def self.levinson_durbin(series, nlags = 10, is_acovf = false)
29
-
28
+ def levinson_durbin(series, nlags = 10, is_acovf = false)
30
29
  if is_acovf
31
30
  series = series.map(&:to_f)
32
31
  else
@@ -60,29 +59,34 @@ module Statsample
60
59
  return [sigma_v, arcoefs, pacf, sig, phi]
61
60
  end
62
61
 
63
- #Returns diagonal elements of matrices
64
- # Will later abstract it to utilities
65
- def self.diag(mat)
62
+ # Returns diagonal elements of matrices
63
+ def diag(mat)
66
64
  return mat.each_with_index(:diagonal).map { |x, r, c| x }
67
65
  end
68
66
 
69
67
 
70
68
  #=Yule Walker Algorithm
71
- #From the series, estimates AR(p)(autoregressive) parameter using Yule-Waler equation. See -
72
- #http://en.wikipedia.org/wiki/Autoregressive_moving_average_model
73
69
  #
74
- #==Parameters
70
+ # From the series, estimates AR(p)(autoregressive) parameter using
71
+ # Yule-Waler equation. See -
72
+ # http://en.wikipedia.org/wiki/Autoregressive_moving_average_model
73
+ #
74
+ # == Parameters
75
+ #
75
76
  #* *ts*: timeseries
76
77
  #* *k*: order, default = 1
77
78
  #* *method*: can be 'yw' or 'mle'. If 'yw' then it is unbiased, denominator is (n - k)
78
79
  #
79
- #==Returns
80
+ # == Returns
81
+ #
80
82
  #* *rho*: autoregressive coefficients
81
83
  #* *sigma*: sigma parameter
82
- def self.yule_walker(ts, k = 1, method='yw')
83
- ts = ts - ts.mean
84
+ def yule_walker(ts, k = 1, method='yw')
84
85
  n = ts.size
85
- if method.downcase.eql? 'yw'
86
+ mean = (ts.inject(:+) / n)
87
+ ts = ts.map { |t| t - mean }
88
+
89
+ if method == 'yw'
86
90
  #unbiased => denominator = (n - k)
87
91
  denom =->(k) { n - k }
88
92
  else
@@ -94,36 +98,41 @@ module Statsample
94
98
  r[0] = ts.map { |x| x**2 }.inject(:+).to_f / denom.call(0).to_f
95
99
 
96
100
  1.upto(k) do |l|
97
- r[l] = (ts[0...-l].zip(ts[l...ts.size])).map do |x|
101
+ r[l] = (ts[0...-l].zip(ts[l...n])).map do |x|
98
102
  x.inject(:*)
99
103
  end.inject(:+).to_f / denom.call(l).to_f
100
104
  end
101
105
 
102
106
  r_R = toeplitz(r[0...-1])
103
107
 
104
- mat = Matrix.columns(r_R).inverse()
108
+ mat = Matrix.columns(r_R).inverse
105
109
  phi = solve_matrix(mat, r[1..r.size])
106
- phi_vector = Statsample::Vector.new(phi, :scale)
107
- r_vector = Statsample::Vector.new(r[1..r.size], :scale)
108
- sigma = r[0] - (r_vector * phi_vector).sum
110
+ phi_vector = phi
111
+ r_vector = r[1..-1]
112
+ sigma = r[0] - (r_vector.map.with_index {|e,i| e*phi_vector[i] }).inject(:+)
109
113
  return [phi, sigma]
110
114
  end
111
115
 
112
116
  #=ToEplitz
113
- # Generates teoeplitz matrix from an array
114
- #http://en.wikipedia.org/wiki/Toeplitz_matrix
115
- #Toeplitz matrix are equal when they are stored in row & column major
116
- #==Parameters
117
+ #
118
+ # Generates teoeplitz matrix from an array
119
+ # http://en.wikipedia.org/wiki/Toeplitz_matrix.
120
+ # Toeplitz matrix are equal when they are stored in row & column major
121
+ #
122
+ # == Parameters
123
+ #
117
124
  #* *arr*: array of integers;
118
- #==Usage
119
- # arr = [0,1,2,3]
120
- # Pacf.toeplitz(arr)
121
- #==Returns
122
- # [[0, 1, 2, 3],
123
- # [1, 0, 1, 2],
124
- # [2, 1, 0, 1],
125
- # [3, 2, 1, 0]]
126
- def self.toeplitz(arr)
125
+ #
126
+ # == Usage
127
+ #
128
+ # arr = [0,1,2,3]
129
+ # Pacf.toeplitz(arr)
130
+ #
131
+ # #=> [[0, 1, 2, 3],
132
+ # #=> [1, 0, 1, 2],
133
+ # #=> [2, 1, 0, 1],
134
+ # #=> [3, 2, 1, 0]]
135
+ def toeplitz(arr)
127
136
  eplitz_matrix = Array.new(arr.size) { Array.new(arr.size) }
128
137
 
129
138
  0.upto(arr.size - 1) do |i|
@@ -143,9 +152,10 @@ module Statsample
143
152
  eplitz_matrix
144
153
  end
145
154
 
146
- #===Solves matrix equations
147
- #Solves for X in AX = B
148
- def self.solve_matrix(matrix, out_vector)
155
+ #=Solves matrix equations
156
+ #
157
+ # Solves for X in AX = B
158
+ def solve_matrix(matrix, out_vector)
149
159
  solution_vector = Array.new(out_vector.size, 0)
150
160
  matrix = matrix.to_a
151
161
  k = 0
@@ -157,7 +167,6 @@ module Statsample
157
167
  end
158
168
  solution_vector
159
169
  end
160
-
161
170
  end
162
171
  end
163
172
  end
@@ -1,154 +1,126 @@
1
- module Statsample
2
- class Vector
3
- include Enumerable
4
- include Writable
5
- include Summarizable
6
-
7
- #=Squares of sum
8
- #==Parameter
9
- #* *demean*: boolean - optional. __default__: false
10
- #==Returns
11
- #Sums the timeseries and then returns the square
12
- def squares_of_sum(demean = false)
13
- if demean
14
- m = self.mean
15
- self.map { |x| (x-m) }.sum**2
16
- else
17
- return self.sum.to_f**2
18
- end
1
+ class ::Matrix
2
+ # == Squares of sum
3
+ #
4
+ # Does squares of sum in column order.
5
+ # Necessary for computations in various processes
6
+ def squares_of_sum
7
+ (0...column_size).map do |j|
8
+ self.column(j).sum**2
19
9
  end
20
10
  end
21
11
 
12
+ # == Symmetric?
13
+ # `symmetric?` is present in Ruby Matrix 1.9.3+, but not in 1.8.*
14
+ #
15
+ # == Returns
16
+ #
17
+ # bool
18
+ def symmetric?
19
+ return false unless square?
22
20
 
23
- class ::Matrix
24
- #==Squares of sum
25
- #Does squares of sum in column order.
26
- #Necessary for computations in various processes
27
- def squares_of_sum
28
- (0...column_size).map do |j|
29
- self.column(j).sum**2
21
+ (0...row_size).each do |i|
22
+ 0.upto(i).each do |j|
23
+ return false if self[i, j] != self[j, i]
30
24
  end
31
25
  end
26
+ true
27
+ end
32
28
 
33
- #==Symmetric?
34
- #`symmetric?` is present in Ruby Matrix 1.9.3+, but not in 1.8.*
35
- #===Returns
36
- # bool
37
- def symmetric?
38
- return false unless square?
39
-
40
- (0...row_size).each do |i|
41
- 0.upto(i).each do |j|
42
- return false if self[i, j] != self[j, i]
43
- end
44
- end
45
- true
46
- end
47
-
48
- #==Cholesky decomposition
49
- #Reference: http://en.wikipedia.org/wiki/Cholesky_decomposition
50
- #===Description
51
- #Cholesky decomposition is reprsented by `M = L X L*`, where
52
- #M is the symmetric matrix and `L` is the lower half of cholesky matrix,
53
- #and `L*` is the conjugate form of `L`.
54
- #===Returns
55
- # Cholesky decomposition for a given matrix(if symmetric)
56
- #===Utility
57
- # Essential matrix function, requisite in kalman filter, least squares
58
- def cholesky
59
- raise ArgumentError, "Given matrix should be symmetric" unless symmetric?
60
- c = Matrix.zero(row_size)
61
- 0.upto(row_size - 1).each do |k|
62
- 0.upto(row_size - 1).each do |i|
63
- if i == k
64
- sum = (0..(k-1)).inject(0.0){ |sum, j| sum + c[k, j] ** 2 }
65
- value = Math.sqrt(self[k,k] - sum)
66
- c[k, k] = value
67
- elsif i > k
68
- sum = (0..(k-1)).inject(0.0){ |sum, j| sum + c[i, j] * c[k, j] }
69
- value = (self[k,i] - sum) / c[k, k]
70
- c[i, k] = value
71
- end
29
+ # == Cholesky decomposition
30
+ #
31
+ # Reference: http://en.wikipedia.org/wiki/Cholesky_decomposition
32
+ # == Description
33
+ #
34
+ # Cholesky decomposition is reprsented by `M = L X L*`, where
35
+ # M is the symmetric matrix and `L` is the lower half of cholesky matrix,
36
+ # and `L*` is the conjugate form of `L`.
37
+ #
38
+ # == Returns
39
+ #
40
+ # Cholesky decomposition for a given matrix(if symmetric)
41
+ #
42
+ # == Utility
43
+ #
44
+ # Essential matrix function, requisite in kalman filter, least squares
45
+ def cholesky
46
+ raise ArgumentError, "Given matrix should be symmetric" unless symmetric?
47
+ c = Matrix.zero(row_size)
48
+ 0.upto(row_size - 1).each do |k|
49
+ 0.upto(row_size - 1).each do |i|
50
+ if i == k
51
+ sum = (0..(k-1)).inject(0.0){ |sum, j| sum + c[k, j] ** 2 }
52
+ value = Math.sqrt(self[k,k] - sum)
53
+ c[k, k] = value
54
+ elsif i > k
55
+ sum = (0..(k-1)).inject(0.0){ |sum, j| sum + c[i, j] * c[k, j] }
56
+ value = (self[k,i] - sum) / c[k, k]
57
+ c[i, k] = value
72
58
  end
73
59
  end
74
- c
75
60
  end
61
+ c
62
+ end
76
63
 
77
- #==Chain Product
78
- #Class method
79
- #Returns the chain product of two matrices
80
- #===Usage:
81
- #Let `a` be 4 * 3 matrix,
82
- #Let `b` be 3 * 3 matrix,
83
- #Let `c` be 3 * 1 matrix,
84
- #then `Matrix.chain_dot(a, b, c)`
85
- #===NOTE:
86
- # Send the matrices in multiplicative order with proper dimensions
87
- def self.chain_dot(*args)
88
- #inspired by Statsmodels
89
- begin
90
- args.reduce { |x, y| x * y } #perform matrix multiplication in order
91
- rescue ExceptionForMatrix::ErrDimensionMismatch
92
- puts "ExceptionForMatrix: Please provide matrices with proper multiplicative dimensions"
93
- end
64
+ #==Chain Product
65
+ #Class method
66
+ #Returns the chain product of two matrices
67
+ #===Usage:
68
+ #Let `a` be 4 * 3 matrix,
69
+ #Let `b` be 3 * 3 matrix,
70
+ #Let `c` be 3 * 1 matrix,
71
+ #then `Matrix.chain_dot(a, b, c)`
72
+ #===NOTE:
73
+ # Send the matrices in multiplicative order with proper dimensions
74
+ def self.chain_dot(*args)
75
+ #inspired by Statsmodels
76
+ begin
77
+ args.reduce { |x, y| x * y } #perform matrix multiplication in order
78
+ rescue ExceptionForMatrix::ErrDimensionMismatch
79
+ puts "ExceptionForMatrix: Please provide matrices with proper multiplicative dimensions"
94
80
  end
81
+ end
95
82
 
96
83
 
97
- #==Adds a column of constants.
98
- #Appends a column of ones to the matrix/array if first argument is false
99
- #If an n-array, first checks if one column of ones is already present
100
- #if present, then original(self) is returned, else, prepends with a vector of ones
101
- def add_constant(prepend = true)
102
- #for Matrix
103
- (0...column_size).each do |i|
104
- if self.column(i).map(&:to_f) == Object::Vector.elements(Array.new(row_size, 1.0))
105
- return self
106
- end
107
- end
108
- #append/prepend a column of one's
109
- vectors = (0...row_size).map do |r|
110
- if prepend
111
- [1.0].concat(self.row(r).to_a)
112
- else
113
- self.row(r).to_a.push(1.0)
114
- end
84
+ #==Adds a column of constants.
85
+ #Appends a column of ones to the matrix/array if first argument is false
86
+ #If an n-array, first checks if one column of ones is already present
87
+ #if present, then original(self) is returned, else, prepends with a vector of ones
88
+ def add_constant(prepend = true)
89
+ #for Matrix
90
+ (0...column_size).each do |i|
91
+ if self.column(i).map(&:to_f) == Object::Vector.elements(Array.new(row_size, 1.0))
92
+ return self
115
93
  end
116
- return Matrix.rows(vectors)
117
- end
118
-
119
- #populates column i of given matrix with arr
120
- def set_column(i, arr)
121
- columns = self.column_vectors
122
- column = columns[i].to_a
123
- column[0...arr.size] = arr
124
- columns[i] = column
125
- return Matrix.columns(columns)
126
94
  end
127
-
128
- #populates row i of given matrix with arr
129
- def set_row(i, arr)
130
- #similar implementation as set_column
131
- #writing and commenting metaprogrammed version
132
- #Please to give opinion :)
133
- rows = self.row_vectors
134
- row = rows[i].to_a
135
- row[0...arr.size] = arr
136
- rows[i] = row
137
- return Matrix.rows(rows)
95
+ #append/prepend a column of one's
96
+ vectors = (0...row_size).map do |r|
97
+ if prepend
98
+ [1.0].concat(self.row(r).to_a)
99
+ else
100
+ self.row(r).to_a.push(1.0)
101
+ end
138
102
  end
103
+ return Matrix.rows(vectors)
104
+ end
139
105
 
140
- #Metaprogrammed version of set_column, set_row
141
- # self.class_eval do
142
- # ["row", "column"].each do |dimension|
143
- # define_method("set_#{dimension}s") do |i, arr|
144
- # dims = send("#{dimension}_vectors")
145
- # dim = dims[i].to_a
146
- # dim[0...arr.size] = arr
147
- # dims[i] = dim
148
- # return Matrix.send("#{dimension}s", dims)
149
- # end
150
- # end
151
- # end
106
+ #populates column i of given matrix with arr
107
+ def set_column(i, arr)
108
+ columns = self.column_vectors
109
+ column = columns[i].to_a
110
+ column[0...arr.size] = arr
111
+ columns[i] = column
112
+ return Matrix.columns(columns)
152
113
  end
153
114
 
115
+ #populates row i of given matrix with arr
116
+ def set_row(i, arr)
117
+ #similar implementation as set_column
118
+ #writing and commenting metaprogrammed version
119
+ #Please to give opinion :)
120
+ rows = self.row_vectors
121
+ row = rows[i].to_a
122
+ row[0...arr.size] = arr
123
+ rows[i] = row
124
+ return Matrix.rows(rows)
125
+ end
154
126
  end