module Statsample module Anova # = Generic Anova one-way. # You could enter the sum of squares or the mean squares. You # should enter the degrees of freedom for numerator and denominator. # == Usage # anova=Statsample::Anova::OneWay(:ss_num=>10,:ss_den=>20, :df_num=>2, :df_den=>10, @name=>"ANOVA for....") class OneWay include Summarizable attr_reader :df_num, :df_den, :ss_num, :ss_den, :ms_num, :ms_den, :ms_total, :df_total, :ss_total # Name of ANOVA Analisys attr_accessor :name attr_accessor :name_denominator attr_accessor :name_numerator def initialize(opts=Hash.new) @name=@name_numerator=@name_denominator=nil # First see if sum of squares or mean squares are entered raise ArgumentError, "You should set d.f." unless (opts.has_key? :df_num and opts.has_key? :df_den) @df_num=opts.delete :df_num @df_den=opts.delete :df_den @df_total=@df_num+@df_den if(opts.has_key? :ss_num and opts.has_key? :ss_den) @ss_num = opts.delete :ss_num @ss_den =opts.delete :ss_den @ms_num =@ss_num.quo(@df_num) @ms_den =@ss_den.quo(@df_den) elsif (opts.has_key? :ms_num and opts.has_key? :ms_den) @ms_num =opts.delete :ms_num @ms_den =opts.delete :ms_den @ss_num =@ms_num * @df_num @ss_den =@ss_den * @df_den end @ss_total=@ss_num+@ss_den @ms_total=@ms_num+@ms_den opts_default={:name=>"ANOVA", :name_denominator=>_("Explained variance"), :name_numerator=>_("Unexplained variance")} @opts=opts_default.merge(opts) opts.keys.each {|k| send("#{k}=", @opts[k]) if self.respond_to? "#{k}=" } @f_object=Statsample::Test::F.new(@ms_num, @ms_den, @df_num,@df_den) end # F value def f @f_object.f end # P-value of F test def probability @f_object.probability end def report_building(builder) #:nodoc: builder.section(:name=>@name) do |b| report_building_table(b) end end def report_building_table(builder) #:nodoc: builder.table(:name=>_("%s Table") % @name, :header=>%w{source ss df ms f p}.map {|v| _(v)}) do |t| t.row([@name_numerator, sprintf("%0.3f",@ss_num), @df_num, sprintf("%0.3f",@ms_num), sprintf("%0.3f",f), sprintf("%0.3f", probability)]) t.row([@name_denominator, sprintf("%0.3f",@ss_den), @df_den, sprintf("%0.3f",@ms_den), "", ""]) t.row([_("Total"), sprintf("%0.3f",@ss_total), @df_total, sprintf("%0.3f",@ms_total),"",""]) end end end # One Way Anova with vectors # Example: # v1 = Daru::Vector.new([2,3,4,5,6]) # v2 = Daru::Vector.new([3,3,4,5,6]) # v3 = Daru::Vector.new([5,3,1,5,6]) # anova=Statsample::Anova::OneWayWithVectors.new([v1,v2,v3]) # anova.f # => 0.0243902439024391 # anova.probability # => 0.975953044203438 # anova.sst # => 32.9333333333333 # class OneWayWithVectors < OneWay # Show on summary Levene test attr_accessor :summary_levene # Show on summary descriptives for vectors attr_accessor :summary_descriptives # Show on summary of contrasts attr_accessor :summary_contrasts # Array with stored contrasts attr_reader :contrasts def initialize(*args) if args[0].is_a? Array @vectors = args.shift else @vectors = args.find_all {|v| v.is_a? Daru::Vector} opts = args.find {|v| v.is_a? Hash} end opts||=Hash.new opts_default={:name=>_("Anova One-Way"), :name_numerator=>_("Between Groups"), :name_denominator=>_("Within Groups"), :summary_descriptives=>false, :summary_levene=>true, :summary_contrasts=>true } @opts=opts_default.merge(opts).merge(:ss_num=>ssbg, :ss_den=>sswg, :df_num=>df_bg, :df_den=>df_wg) @contrasts=[] super(@opts) end alias :sst :ss_total alias :msb :ms_num alias :msw :ms_den # Generates and store a contrast. # Options should be provided as a hash # [:c]=>contrast vector # [:c1 - :c2]=>index for automatic construction of contrast # [:name]=>contrast name def contrast(opts=Hash.new) name=opts[:name] || _("Contrast for %s") % @name opts=opts.merge({:vectors=>@vectors, :name=>name}) c=Statsample::Anova::Contrast.new(opts) @contrasts.push(c) c end def levene Statsample::Test.levene(@vectors, :name=>_("Test of Homogeneity of variances (Levene)")) end # Total mean def total_mean sum=@vectors.inject(0){|a,v| a+v.sum} sum.quo(n) end # Sum of squares within groups def sswg @sswg||=@vectors.inject(0) {|total,vector| total+vector.ss } end # Sum of squares between groups def ssbg m=total_mean @vectors.inject(0) do |total,vector| total + (vector.mean-m).square * vector.size end end # Degrees of freedom within groups def df_wg @dk_wg||=n-k end def k @k||=@vectors.size end # Degrees of freedom between groups def df_bg k-1 end # Total number of cases def n @vectors.inject(0){|a,v| a+v.size} end def report_building(builder) # :nodoc: builder.section(:name=>@name) do |s| if summary_descriptives s.table(:name=>_("Descriptives"),:header=>%w{Name N Mean SD Min Max}.map {|v| _(v)}) do |t| @vectors.each do |v| t.row [v.name, v.reject_values(*Daru::MISSING_VALUES).size, "%0.4f" % v.mean, "%0.4f" % v.sd, "%0.4f" % v.min, "%0.4f" % v.max] end end end if summary_levene s.parse_element(levene) end report_building_table(s) if summary_contrasts and @contrasts.size>0 @contrasts.each do |c| s.parse_element(c) end end end end end end end