File: t_test.rb

package info (click to toggle)
ruby-statistics 2.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, sid, trixie
  • size: 224 kB
  • sloc: ruby: 989; sh: 4; makefile: 4
file content (92 lines) | stat: -rw-r--r-- 3,543 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
module Statistics
  module StatisticalTest
    class TTest
      # Errors for Zero std
      class ZeroStdError < StandardError
        STD_ERROR_MSG = 'Standard deviation for the difference or group is zero. Please, reconsider sample contents'.freeze
      end

      # Perform a T-Test for one or two samples.
      # For the tails param, we need a symbol: :one_tail or :two_tail
      def self.perform(alpha, tails, *args)
        return if args.size < 2

        degrees_of_freedom = 0

        # If the comparison mean has been specified
        t_score = if args[0].is_a? Numeric
                    data_mean = args[1].mean
                    data_std = args[1].standard_deviation

                    raise ZeroStdError, ZeroStdError::STD_ERROR_MSG if data_std == 0

                    comparison_mean = args[0]
                    degrees_of_freedom = args[1].size

                    (data_mean - comparison_mean)/(data_std / Math.sqrt(args[1].size).to_f).to_f
                  else
                    sample_left_mean = args[0].mean
                    sample_left_variance = args[0].variance
                    sample_right_variance = args[1].variance
                    sample_right_mean = args[1].mean
                    degrees_of_freedom = args.flatten.size - 2

                    left_root = sample_left_variance/args[0].size.to_f
                    right_root = sample_right_variance/args[1].size.to_f

                    standard_error = Math.sqrt(left_root + right_root)

                    (sample_left_mean - sample_right_mean).abs/standard_error.to_f
                  end

        t_distribution = Distribution::TStudent.new(degrees_of_freedom)
        probability = t_distribution.cumulative_function(t_score)

        # Steps grabbed from https://support.minitab.com/en-us/minitab/18/help-and-how-to/statistics/basic-statistics/supporting-topics/basics/manually-calculate-a-p-value/
        # See https://github.com/estebanz01/ruby-statistics/issues/23
        p_value = if tails == :two_tail
                  2 * (1 - t_distribution.cumulative_function(t_score.abs))
                  else
                    1 - probability
                  end

        { t_score: t_score,
          probability: probability,
          p_value: p_value,
          alpha: alpha,
          null: alpha < p_value,
          alternative: p_value <= alpha,
          confidence_level: 1 - alpha }
      end

      def self.paired_test(alpha, tails, left_group, right_group)
        raise StandardError, 'both samples are the same' if left_group == right_group

        # Handy snippet grabbed from https://stackoverflow.com/questions/2682411/ruby-sum-corresponding-members-of-two-or-more-arrays
        differences = [left_group, right_group].transpose.map { |value| value.reduce(:-) }

        degrees_of_freedom = differences.size - 1
        difference_std = differences.standard_deviation

        raise ZeroStdError, ZeroStdError::STD_ERROR_MSG if difference_std == 0

        down = difference_std/Math.sqrt(differences.size)

        t_score = (differences.mean - 0)/down.to_f

        probability = Distribution::TStudent.new(degrees_of_freedom).cumulative_function(t_score)

        p_value = 1 - probability
        p_value *= 2 if tails == :two_tail

        { t_score: t_score,
          probability: probability,
          p_value: p_value,
          alpha: alpha,
          null: alpha < p_value,
          alternative: p_value <= alpha,
          confidence_level: 1 - alpha }
      end
    end
  end
end