lib/statsample/factor/map.rb
module Statsample
module Factor
# = Velicer's Minimum Average Partial
#
# "Velicer’s (1976) MAP test involves a complete princi-
# pal components analysis followed by the examination of
# a series of matrices of partial correlations. Specifically,
# on the first step, the first principal component is par-
# tialed out of the correlations between the variables of in-
# terest, and the average squared coefficient in the off-
# diagonals of the resulting partial correlation matrix is
# computed. On the second step, the first two principal
# components are partialed out of the original correlation
# matrix and the average squared partial correlation is
# again computed. These computations are conducted for k
# (the number of variables) minus one steps. The average
# squared partial correlations from these steps are then
# lined up, and the number of components is determined by
# the step number in the analyses that resulted in the lowest
# average squared partial correlation. The average squared
# coefficient in the original correlation matrix is also com-
# puted, and if this coefficient happens to be lower than
# the lowest average squared partial correlation, then no
# components should be extracted from the correlation ma-
# trix. Statistically, components are retained as long as the
# variance in the correlation matrix represents systematic
# variance. Components are no longer retained when there
# is proportionately more unsystematic variance than sys-
# tematic variance." (O'Connor, 2000, p.397).
#
# Current algorithm is loosely based on SPSS O'Connor algorithm
#
# == Reference
# * O'Connor, B. (2000). SPSS and SAS programs for determining the number of components using parallel analysis and Velicer's MAP test. Behavior Research Methods, Instruments, & Computers, 32(3), 396-402.
#
class MAP
include Summarizable
include DirtyMemoize
# Name of analysis
attr_accessor :name
attr_reader :eigenvalues
# Number of factors to retain
attr_reader :number_of_factors
# Average squared correlations
attr_reader :fm
# Smallest average squared correlation
attr_reader :minfm
attr_accessor :use_gsl
def self.with_dataset(ds,opts=Hash.new)
new(ds.correlation_matrix,opts)
end
def initialize(matrix, opts=Hash.new)
@matrix=matrix
opts_default={
:use_gsl=>true,
:name=>_("Velicer's MAP")
}
@opts=opts_default.merge(opts)
opts_default.keys.each {|k| send("#{k}=", @opts[k]) }
end
def compute
gsl_m=(use_gsl and Statsample.has_gsl?) ? @matrix.to_gsl : @matrix
klass_m=gsl_m.class
eigvect,@eigenvalues=gsl_m.eigenvectors_matrix, gsl_m.eigenvalues
eigenvalues_sqrt=@eigenvalues.collect {|v| Math.sqrt(v)}
loadings=eigvect*(klass_m.diagonal(*eigenvalues_sqrt))
fm=Array.new(@matrix.row_size)
ncol=@matrix.column_size
fm[0]=(gsl_m.mssq - ncol).quo(ncol*(ncol-1))
(ncol-1).times do |m|
puts "MAP:Eigenvalue #{m+1}" if $DEBUG
a=use_gsl ? loadings[0..(loadings.row_size-1),0..m] :
loadings.minor(0..(loadings.row_size-1),0..m)
partcov= gsl_m - (a*a.transpose)
d=klass_m.diagonal(*(partcov.diagonal.collect {|v| Math::sqrt(1/v)}))
pr=d*partcov*d
fm[m+1]=(pr.mssq-ncol).quo(ncol*(ncol-1))
end
minfm=fm[0]
nfactors=0
@errors=[]
fm.each_with_index do |v,s|
if defined?(Complex) and v.is_a? ::Complex
@errors.push(s)
else
if v < minfm
minfm=v
nfactors=s
end
end
end
@number_of_factors=nfactors
@fm=fm
@minfm=minfm
end
def report_building(g) #:nodoc:
g.section(:name=>@name) do |s|
s.table(:name=>_("Eigenvalues"),:header=>[_("Value")]) do |t|
eigenvalues.each_with_index do |e,i|
t.row([@errors.include?(i) ? "*" : "%0.6f" % e])
end
end
s.table(:name=>_("Velicer's Average Squared Correlations"), :header=>[_("number of components"),_("average square correlation")]) do |t|
fm.each_with_index do |v,i|
t.row(["%d" % i, @errors.include?(i) ? "*" : "%0.6f" % v])
end
end
s.text(_("The smallest average squared correlation is : %0.6f" % minfm))
s.text(_("The number of components is : %d" % number_of_factors))
end
end
dirty_memoize :number_of_factors, :fm, :minfm, :eigenvalues
end
end
end