Class: VoteFu::Algorithms::WilsonScore

Inherits:
Object
  • Object
show all
Defined in:
lib/vote_fu/algorithms/wilson_score.rb

Overview

Wilson Score Confidence Interval for Bernoulli Parameter

This algorithm provides the lower bound of a Wilson score confidence interval. It's excellent for ranking items by quality when you have binary ratings (up/down votes). Unlike simple averages, it accounts for statistical uncertainty when there are few votes.

Examples:

post.wilson_score # => 0.85 (high confidence it's good)

See Also:

Constant Summary collapse

Z_SCORES =

Z-scores for common confidence levels

{
  0.80 => 1.28,
  0.85 => 1.44,
  0.90 => 1.64,
  0.95 => 1.96,
  0.99 => 2.58
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(voteable, confidence:, scope:) ⇒ WilsonScore

Returns a new instance of WilsonScore.



37
38
39
40
41
# File 'lib/vote_fu/algorithms/wilson_score.rb', line 37

def initialize(voteable, confidence:, scope:)
  @voteable = voteable
  @z = Z_SCORES.fetch(confidence) { Z_SCORES[0.95] }
  @scope = scope
end

Class Method Details

.call(voteable, confidence: 0.95, scope: nil) ⇒ Float

Calculate the Wilson Score Lower Bound

Parameters:

  • voteable (ActiveRecord::Base)

    The voteable object

  • confidence (Float) (defaults to: 0.95)

    Confidence level (0.80 to 0.99)

  • scope (Symbol, nil) (defaults to: nil)

    Optional voting scope

Returns:

  • (Float)

    Score from 0.0 to 1.0



33
34
35
# File 'lib/vote_fu/algorithms/wilson_score.rb', line 33

def self.call(voteable, confidence: 0.95, scope: nil)
  new(voteable, confidence: confidence, scope: scope).calculate
end

Instance Method Details

#calculateObject



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/vote_fu/algorithms/wilson_score.rb', line 43

def calculate
  n = total_votes
  return 0.0 if n.zero?

  pos = positive_votes
  phat = pos / n

  # Wilson Score Interval lower bound formula
  numerator = phat + (@z**2 / (2 * n)) -
              @z * Math.sqrt((phat * (1 - phat) + @z**2 / (4 * n)) / n)
  denominator = 1 + @z**2 / n

  (numerator / denominator).clamp(0.0, 1.0).round(6)
end