module Statsample
module Reliability
class << self
# Calculate Chonbach's alpha for a given dataset.
# only uses tuples without missing data
def cronbach_alpha(ods)
ds = ods.reject_values(*Daru::MISSING_VALUES)
n_items = ds.ncols
return nil if n_items <= 1
s2_items = ds.to_h.values.inject(0) { |ac,v|
ac + v.variance }
total = ds.vector_sum
(n_items.quo(n_items - 1)) * (1 - (s2_items.quo(total.variance)))
end
# Calculate Chonbach's alpha for a given dataset
# using standarized values for every vector.
# Only uses tuples without missing data
# Return nil if one or more vectors has 0 variance
def cronbach_alpha_standarized(ods)
ds = ods.reject_values(*Daru::MISSING_VALUES)
return nil if ds.any? { |v| v.variance==0}
ds = Daru::DataFrame.new(
ds.vectors.to_a.inject({}) { |a,i|
a[i] = ods[i].standardize
a
}
)
cronbach_alpha(ds)
end
# Predicted reliability of a test by replicating
# +n+ times the number of items
def spearman_brown_prophecy(r,n)
(n*r).quo(1+(n-1)*r)
end
alias :sbp :spearman_brown_prophecy
# Returns the number of items
# to obtain +r_d+ desired reliability
# from +r+ current reliability, achieved with
# +n+ items
def n_for_desired_reliability(r,r_d,n=1)
return nil if r.nil?
(r_d*(1-r)).quo(r*(1-r_d))*n
end
# Get Cronbach alpha from n cases,
# s2 mean variance and cov
# mean covariance
def cronbach_alpha_from_n_s2_cov(n,s2,cov)
(n.quo(n-1)) * (1-(s2.quo(s2+(n-1)*cov)))
end
# Get Cronbach's alpha from a covariance matrix
def cronbach_alpha_from_covariance_matrix(cov)
n = cov.row_size
raise "covariance matrix should have at least 2 variables" if n < 2
s2 = n.times.inject(0) { |ac,i| ac + cov[i,i] }
(n.quo(n - 1)) * (1 - (s2.quo(cov.total_sum)))
end
# Returns n necessary to obtain specific alpha
# given variance and covariance mean of items
def n_for_desired_alpha(alpha,s2,cov)
# Start with a regular test : 50 items
min=2
max=1000
n=50
prev_n=0
epsilon=0.0001
dif=1000
c_a=cronbach_alpha_from_n_s2_cov(n,s2,cov)
dif=c_a - alpha
while(dif.abs>epsilon and n!=prev_n)
prev_n=n
if dif<0
min=n
n=(n+(max-min).quo(2)).to_i
else
max=n
n=(n-(max-min).quo(2)).to_i
end
c_a=cronbach_alpha_from_n_s2_cov(n,s2,cov)
dif=c_a - alpha
end
n
end
# First derivative for alfa
# Parameters
# n: Number of items
# sx: mean of variances
# sxy: mean of covariances
def alpha_first_derivative(n,sx,sxy)
(sxy*(sx-sxy)).quo(((sxy*(n-1))+sx)**2)
end
# Second derivative for alfa
# Parameters
# n: Number of items
# sx: mean of variances
# sxy: mean of covariances
def alfa_second_derivative(n,sx,sxy)
(2*(sxy**2)*(sxy-sx)).quo(((sxy*(n-1))+sx)**3)
end
end
class ItemCharacteristicCurve
attr_reader :totals, :counts, :vector_total
def initialize (ds, vector_total=nil)
vector_total||=ds.vector_sum
raise ArgumentError, "Total size != Dataset size" if vector_total.size != ds.nrows
@vector_total=vector_total
@ds=ds
@totals={}
@counts=@ds.vectors.to_a.inject({}) {|a,v| a[v]={};a}
process
end
def process
i=0
@ds.each_row do |row|
tot=@vector_total[i]
@totals[tot]||=0
@totals[tot]+=1
@ds.vectors.each do |f|
item=row[f].to_s
@counts[f][tot]||={}
@counts[f][tot][item]||=0
@counts[f][tot][item] += 1
end
i+=1
end
end
# Return a hash with p for each different value on a vector
def curve_field(field, item)
out={}
item=item.to_s
@totals.each do |value,n|
count_value= @counts[field][value][item].nil? ? 0 : @counts[field][value][item]
out[value]=count_value.quo(n)
end
out
end # def
end # self
end # Reliability
end # Statsample
require 'statsample/reliability/icc.rb'
require 'statsample/reliability/scaleanalysis.rb'
require 'statsample/reliability/skillscaleanalysis.rb'
require 'statsample/reliability/multiscaleanalysis.rb'