lib/statsample/factor.rb
require 'statsample/factor/rotation'
require 'statsample/factor/pca'
require 'statsample/factor/principalaxis'
require 'statsample/factor/parallelanalysis'
require 'statsample/factor/map'
module Statsample
# Factor Analysis toolbox.
# * Classes for Extraction of factors:
# * Statsample::Factor::PCA
# * Statsample::Factor::PrincipalAxis
# * Classes for Rotation of factors:
# * Statsample::Factor::Varimax
# * Statsample::Factor::Equimax
# * Statsample::Factor::Quartimax
# * Classes for determining the number of components
# * Statsample::Factor::MAP
# * Statsample::Factor::ParallelAnalysis
#
# About number of components, O'Connor(2000) said:
# The two procedures [PA and MAP ] complement each other nicely,
# in that the MAP tends to err (when it does err) in the direction
# of underextraction, whereas parallel analysis tends to err
# (when it does err) in the direction of overextraction.
# Optimal decisions are thus likely to be made after considering
# the results of both analytic procedures. (p.10)
module Factor
# Anti-image covariance matrix.
# Useful for inspection of desireability of data for factor analysis.
# According to Dziuban & Shirkey (1974, p.359):
# "If this matrix does not exhibit many zero off-diagonal elements,
# the investigator has evidence that the correlation
# matrix is not appropriate for factor analysis."
#
def self.anti_image_covariance_matrix(matrix)
s2=Matrix.diagonal(*(matrix.inverse.diagonal)).inverse
aicm=(s2)*matrix.inverse*(s2)
aicm.extend(Statsample::CovariateMatrix)
aicm.fields=matrix.fields if matrix.respond_to? :fields
aicm
end
def self.anti_image_correlation_matrix(matrix)
matrix=matrix.to_matrix
s=Matrix.diagonal(*(matrix.inverse.diagonal)).sqrt.inverse
aicm=s*matrix.inverse*s
aicm.extend(Statsample::CovariateMatrix)
aicm.fields=matrix.fields if matrix.respond_to? :fields
aicm
end
# Kaiser-Meyer-Olkin measure of sampling adequacy for correlation matrix.
#
# Kaiser's (1974, cited on Dziuban & Shirkey, 1974) present calibration of the index is as follows :
# * .90s—marvelous
# * .80s— meritorious
# * .70s—middling
# * .60s—mediocre
# * .50s—miserable
# * .50 •—unacceptable
def self.kmo(matrix)
q=anti_image_correlation_matrix(matrix)
n=matrix.row_size
sum_r,sum_q=0,0
n.times do |j|
n.times do |k|
if j!=k
sum_r+=matrix[j,k]**2
sum_q+=q[j,k]**2
end
end
end
sum_r.quo(sum_r+sum_q)
end
# Kaiser-Meyer-Olkin measure of sampling adequacy for one variable.
#
def self.kmo_univariate(matrix, var)
if var.is_a? String
if matrix.respond_to? :fields
j=matrix.fields.index(var)
raise "Matrix doesn't have field #{var}" if j.nil?
else
raise "Matrix doesn't respond to fields"
end
else
j=var
end
q=anti_image_correlation_matrix(matrix)
n=matrix.row_size
sum_r,sum_q=0,0
n.times do |k|
if j!=k
sum_r+=matrix[j,k]**2
sum_q+=q[j,k]**2
end
end
sum_r.quo(sum_r+sum_q)
end
end
end