Module: RubricLLM::Statistics
- Defined in:
- lib/rubric_llm/statistics.rb
Overview
Pure statistical helpers. No LLM calls, no state.
Class Method Summary collapse
- .beta_continued_fraction(a, b, x) ⇒ Object
-
.holm_adjust(p_values) ⇒ Object
Holm-Bonferroni step-down adjustment.
-
.paired_t_test(scores_a, scores_b) ⇒ Object
Two-tailed paired t-test.
-
.regularized_beta(x, a, b) ⇒ Object
Regularized incomplete beta function via continued fraction (Lentz's method).
-
.two_tailed_p(t, df) ⇒ Object
Two-tailed p-value for Student's t-distribution.
Class Method Details
.beta_continued_fraction(a, b, x) ⇒ Object
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/rubric_llm/statistics.rb', line 69 def beta_continued_fraction(a, b, x) tiny = 1e-30 qab = a + b qap = a + 1.0 qam = a - 1.0 c = 1.0 d = 1.0 - ((qab * x) / qap) d = tiny if d.abs < tiny d = 1.0 / d fraction = d (1..200).each do |m| m2 = 2 * m numerator = (m * (b - m) * x) / ((qam + m2) * (a + m2)) d = 1.0 + (numerator * d) d = tiny if d.abs < tiny c = 1.0 + (numerator / c) c = tiny if c.abs < tiny d = 1.0 / d fraction *= c * d numerator = -((a + m) * (qab + m) * x) / ((a + m2) * (qap + m2)) d = 1.0 + (numerator * d) d = tiny if d.abs < tiny c = 1.0 + (numerator / c) c = tiny if c.abs < tiny d = 1.0 / d delta = c * d fraction *= delta break if (delta - 1.0).abs < 1e-12 end fraction end |
.holm_adjust(p_values) ⇒ Object
Holm-Bonferroni step-down adjustment. Takes p-values in any order, returns adjusted values in the same order.
37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/rubric_llm/statistics.rb', line 37 def holm_adjust(p_values) count = p_values.size return p_values.dup if count.zero? adjusted = Array.new(count) running_max = 0.0 p_values.each_with_index.sort_by(&:first).each_with_index do |(p_value, position), rank| running_max = ((count - rank) * p_value).clamp(running_max, 1.0) adjusted[position] = running_max end adjusted end |
.paired_t_test(scores_a, scores_b) ⇒ Object
Two-tailed paired t-test. Both arrays must already be paired and equal length. Returns 1.0 when there is nothing to test.
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/rubric_llm/statistics.rb', line 10 def paired_t_test(scores_a, scores_b) n = scores_a.size return 1.0 if n < 2 diffs = scores_a.zip(scores_b).map { |x, y| y - x } mean_d = diffs.sum / n.to_f var_d = diffs.sum { |d| (d - mean_d)**2 } / (n - 1).to_f se = Math.sqrt(var_d / n) # A constant shift has no spread to test. Compare against the scale of the # data rather than exact zero, or float error turns it into a huge t value. return 1.0 if se <= Float::EPSILON * [mean_d.abs, 1.0].max two_tailed_p((mean_d / se).abs, n - 1) end |
.regularized_beta(x, a, b) ⇒ Object
Regularized incomplete beta function via continued fraction (Lentz's method).
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/rubric_llm/statistics.rb', line 53 def regularized_beta(x, a, b) return 0.0 if x <= 0.0 return 1.0 if x >= 1.0 ln_beta = Math.lgamma(a + b)[0] - Math.lgamma(a)[0] - Math.lgamma(b)[0] front = Math.exp(ln_beta + (a * Math.log(x)) + (b * Math.log(1.0 - x))) result = if x < ((a + 1.0) / (a + b + 2.0)) front * beta_continued_fraction(a, b, x) / a else 1.0 - ((front * beta_continued_fraction(b, a, 1.0 - x)) / b) end result.clamp(0.0, 1.0) end |
.two_tailed_p(t, df) ⇒ Object
Two-tailed p-value for Student's t-distribution. p = I_x(df/2, 1/2) where x = df/(df + t²)
28 29 30 31 32 33 |
# File 'lib/rubric_llm/statistics.rb', line 28 def two_tailed_p(t, df) x = df / (df + (t**2)) regularized_beta(x, df / 2.0, 0.5) rescue Math::DomainError, ZeroDivisionError, FloatDomainError 1.0 end |