lib/statsample/matrix.rb
class ::Vector
def to_matrix
::Matrix.columns([self.to_a])
end
def to_vector
self
end
end
class ::Matrix
def to_matrix
self
end
def to_dataframe
f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map {|i| "VAR_#{i+1}".to_sym }
f = [f] unless f.is_a?(Array)
ds = Daru::DataFrame.new({}, order: f)
f.each do |ff|
ds[ff].rename ff
end
row_size.times {|i|
ds.add_row(self.row(i).to_a)
}
ds.rename(self.name) if self.respond_to? :name
ds
end
alias :to_dataset :to_dataframe
if defined? :eigenpairs
alias_method :eigenpairs_ruby, :eigenpairs
end
if Statsample.has_gsl?
# Optimize eigenpairs of extendmatrix module using gsl
def eigenpairs
to_gsl.eigenpairs
end
end
def eigenvalues
eigenpairs.collect {|v| v[0]}
end
def eigenvectors
eigenpairs.collect {|v| v[1]}
end
def eigenvectors_matrix
Matrix.columns(eigenvectors)
end
def to_gsl
out=[]
self.row_size.times{|i|
out[i]=self.row(i).to_a
}
GSL::Matrix[*out]
end
def []=(i, j, x)
@rows[i][j] = x
end
end
module GSL
class Vector
class Col
def to_matrix
::Matrix.columns([self.size.times.map {|i| self[i]}])
end
def to_ary
to_a
end
def to_gsl
self
end
end
end
class Matrix
def to_gsl
self
end
def to_dataframe
f = (self.respond_to? :fields_y) ? fields_y : column_size.times.map { |i| "VAR_#{i+1}".to_sym }
ds=Daru::DataFrame.new({}, order: f)
f.each do |ff|
ds[ff].rename ff
end
row_size.times {|i|
ds.add_row(self.row(i).to_a)
}
ds.rename(self.name) if self.respond_to? :name
ds
end
alias :to_dataset :to_dataframe
def row_size
size1
end
def column_size
size2
end
def determinant
det
end
def inverse
GSL::Linalg::LU.invert(self)
end
def eigenvalues
eigenpairs.collect {|v| v[0]}
end
def eigenvectors
eigenpairs.collect {|v| v[1]}
end
# Matrix sum of squares
def mssq
sum=0
to_v.each {|i| sum+=i**2}
sum
end
def eigenvectors_matrix
eigval, eigvec= GSL::Eigen.symmv(self)
GSL::Eigen::symmv_sort(eigval, eigvec, GSL::Eigen::SORT_VAL_DESC)
eigvec
end
def eigenpairs
eigval, eigvec= GSL::Eigen.symmv(self)
GSL::Eigen::symmv_sort(eigval, eigvec, GSL::Eigen::SORT_VAL_DESC)
@eigenpairs=eigval.size.times.map {|i|
[eigval[i],eigvec.get_col(i)]
}
end
#def eigenpairs_ruby
# self.to_matrix.eigenpairs_ruby
#end
def square?
size1==size2
end
def to_matrix
rows=self.size1
cols=self.size2
out=(0...rows).collect{|i| (0...cols).collect {|j| self[i,j]} }
::Matrix.rows(out)
end
def total_sum
sum=0
size1.times {|i|
size2.times {|j|
sum+=self[i,j]
}
}
sum
end
end
end
module Statsample
# Module to add names to X and Y fields
module NamedMatrix
include Summarizable
def fields
raise "Should be square" if !square?
fields_x
end
def fields=(v)
raise "Matrix should be square" if !square?
@fields_x=v
@fields_y=v
end
def fields_x=(v)
raise "Size of fields != row_size" if v.size!=row_size
@fields_x=v
end
def fields_y=(v)
raise "Size of fields != column_size" if v.size!=column_size
@fields_y=v
end
def fields_x
@fields_x||=row_size.times.collect {|i| _("X%d") % i}
end
def fields_y
@fields_y||=column_size.times.collect {|i| _("Y%d") % i}
end
def name
@name||=get_new_name
end
def name=(v)
@name=v
end
def get_new_name
@@named_matrix||=0
@@named_matrix+=1
_("Matrix %d") % @@named_matrix
end
end
# Module to add method for variance/covariance and correlation matrices
# == Usage
# matrix=Matrix[[1,2],[2,3]]
# matrix.extend CovariateMatrix
#
module CovariateMatrix
include NamedMatrix
@@covariatematrix=0
# Get type of covariate matrix. Could be :covariance or :correlation
def _type
if row_size==column_size
if row_size.times.find {|i| self[i,i]!=1.0}
:covariance
else
:correlation
end
else
@type
end
end
def _type=(t)
@type=t
end
def correlation
if(_type==:covariance)
matrix=Matrix.rows(row_size.times.collect { |i|
column_size.times.collect { |j|
if i==j
1.0
else
self[i,j].quo(Math::sqrt(self[i,i])*Math::sqrt(self[j,j]))
end
}
})
matrix.extend CovariateMatrix
matrix.fields_x=fields_x
matrix.fields_y=fields_y
matrix._type=:correlation
matrix
else
self
end
end
# Get variance for field k
#
def variance(k)
submatrix([k])[0,0]
end
def get_new_name
@@covariatematrix+=1
_("Covariate matrix %d") % @@covariatematrix
end
# Select a submatrix of factors. If you have a correlation matrix
# with a, b and c, you could obtain a submatrix of correlations of
# a and b, b and c or a and b
#
# You could use labels or index to select the factors.
# If you don't specify columns, its will be equal to rows.
#
# Example:
# a=Matrix[[1.0, 0.3, 0.2],
# [0.3, 1.0, 0.5],
# [0.2, 0.5, 1.0]]
# a.extend CovariateMatrix
# a.fields=%w{a b c}
# a.submatrix(%w{c a}, %w{b})
# => Matrix[[0.5],[0.3]]
# a.submatrix(%w{c a})
# => Matrix[[1.0, 0.2] , [0.2, 1.0]]
def submatrix(rows,columns = nil)
raise ArgumentError, "rows shouldn't be empty" if rows.respond_to? :size and rows.size == 0
columns ||= rows
# Convert all fields on index
row_index = rows.collect do |v|
r = v.is_a?(Numeric) ? v : fields_x.index(v)
raise "Index #{v} doesn't exists on matrix" if r.nil?
r
end
column_index = columns.collect do |v|
r = v.is_a?(Numeric) ? v : fields_y.index(v)
raise "Index #{v} doesn't exists on matrix" if r.nil?
r
end
fx=row_index.collect {|v| fields_x[v]}
fy=column_index.collect {|v| fields_y[v]}
matrix = Matrix.rows(row_index.collect { |i| column_index.collect { |j| self[i, j] }})
matrix.extend CovariateMatrix
matrix.fields_x = fx
matrix.fields_y = fy
matrix._type = _type
matrix
end
def report_building(generator)
@name||= (_type==:correlation ? _("Correlation"):_("Covariance"))+_(" Matrix")
generator.table(:name=>@name, :header=>[""]+fields_y) do |t|
row_size.times {|i|
t.row([fields_x[i]]+row(i).to_a.collect {|i1|
i1.nil? ? "--" : sprintf("%0.3f",i1).gsub("0.",".")
})
}
end
end
end
end