Skip to content

Commit 340ee55

Browse files
committedMar 17, 2014
Lower bound method, default confidence, and use continuity correction by default - closes #1
1 parent 00d1dcc commit 340ee55

File tree

3 files changed

+54
-22
lines changed

3 files changed

+54
-22
lines changed
 

‎README.md

+16-10
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,25 @@ Inspired by [How Not To Sort By Average Rating](http://www.evanmiller.org/how-no
99
3 positive ratings out of 5 with 95% confidence
1010

1111
```ruby
12-
WilsonScore.interval(3, 5, 0.95)
12+
WilsonScore.lower_bound(3, 5)
1313
```
1414

15-
[Continuity correction](http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval_with_continuity_correction) can improve the score, especially for a small number of samples (n < 30). Set the last paramter to true to use it.
15+
[Continuity correction](http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval_with_continuity_correction) can improve the score, especially for a small number of samples (n < 30). As of version 0.1.0, it is enabled by default. To disable continuity correction, use:
1616

1717
```ruby
18-
WilsonScore.interval(3, 5, 0.95, true)
18+
WilsonScore.lower_bound(3, 5, correction: false)
19+
```
20+
21+
The default confidence level is 95%. To change this, use:
22+
23+
```ruby
24+
WilsonScore.lower_bound(3, 5, confidence: 0.99)
25+
```
26+
27+
To get the full interval, use:
28+
29+
```ruby
30+
WilsonScore.interval(3, 5)
1931
```
2032

2133
## Star Ratings
@@ -28,16 +40,10 @@ A product has two ratings - one 4 star and one 5 star.
2840
average_rating = 4.5
2941
total_ratings = 2
3042
rating_range = 1..5 # 1 to 5 stars
31-
confidence = 0.95 # 95%
3243

33-
interval = WilsonScore.rating_interval(average_rating, total_ratings, rating_range, confidence)
34-
lower_bound = interval.first
44+
WilsonScore.rating_lower_bound(average_rating, total_ratings, rating_range)
3545
```
3646

37-
Use the lower bound of the interval to sort items.
38-
39-
Again, you can set the last parameter to `true` for continuity correction.
40-
4147
## Installation
4248

4349
Add this line to your application's Gemfile:

‎lib/wilson_score.rb

+20-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@
33
module WilsonScore
44

55
# http://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval
6-
def self.interval(k, n, confidence, correction = false)
6+
def self.interval(k, n, *args)
7+
args = args.dup
8+
options = args[-1].is_a?(Hash) ? args.pop : {}
9+
confidence = args[0] || options[:confidence] || 0.95
10+
correction = !args[1].nil? ? args[1] : (options.has_key?(:correction) ? options[:correction] : true)
11+
712
z = pnorm(1 - (1 - confidence) / 2.0)
813
phat = k / n.to_f
914
z2 = z**2
@@ -23,14 +28,27 @@ def self.interval(k, n, confidence, correction = false)
2328
end
2429
end
2530

26-
def self.rating_interval(avg, n, score_range, confidence, correction = false)
31+
def self.lower_bound(k, n, options = {})
32+
interval(k, n, options).first
33+
end
34+
35+
def self.rating_interval(avg, n, score_range, *args)
36+
args = args.dup
37+
options = args[-1].is_a?(Hash) ? args.pop : {}
38+
confidence = args[0] || options[:confidence] || 0.95
39+
correction = !args[1].nil? ? args[1] : (options.has_key?(:correction) ? options[:correction] : true)
40+
2741
min = score_range.first
2842
max = score_range.last
2943
range = max - min
3044
interval = interval(n * (avg - min) / range, n, confidence, correction)
3145
(min + range * interval.first)..(min + range * interval.last)
3246
end
3347

48+
def self.rating_lower_bound(avg, n, score_range, options = {})
49+
rating_interval(avg, n, score_range, options).first
50+
end
51+
3452
protected
3553

3654
# from the statistics2 gem

‎test/wilson_score_test.rb

+18-10
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,71 @@
33
class TestWilsonScore < Minitest::Test
44

55
def test_wilson_score
6-
interval = WilsonScore.interval(1, 2, 0.95)
6+
interval = WilsonScore.interval(1, 2, correction: false)
77
assert_in_delta 0.0945, interval.first
88
assert_in_delta 0.9055, interval.last
99
end
1010

11+
def test_lower_bound
12+
assert_in_delta 0.0267, WilsonScore.lower_bound(1, 2)
13+
end
14+
1115
def test_continuity_correction
12-
interval = WilsonScore.interval(1, 2, 0.95, true)
16+
interval = WilsonScore.interval(1, 2)
1317
assert_in_delta 0.0267, interval.first
1418
assert_in_delta 0.9733, interval.last
1519
end
1620

1721
def test_continuity_correction_zero_one
18-
interval = WilsonScore.interval(0, 1, 0.95, true)
22+
interval = WilsonScore.interval(0, 1)
1923
assert_in_delta 0, interval.first
2024
assert_in_delta 0.9454, interval.last
2125
end
2226

2327
def test_continuity_correction_zero_ten
24-
interval = WilsonScore.interval(0, 10, 0.95, true)
28+
interval = WilsonScore.interval(0, 10)
2529
assert_in_delta 0, interval.first
2630
assert_in_delta 0.3445, interval.last
2731
end
2832

2933
def test_continuity_correction_one_ten
30-
interval = WilsonScore.interval(1, 10, 0.95, true)
34+
interval = WilsonScore.interval(1, 10)
3135
assert_in_delta 0.0052, interval.first
3236
assert_in_delta 0.4588, interval.last
3337
end
3438

3539
def test_continuity_correction_one_fifty
36-
interval = WilsonScore.interval(1, 50, 0.95, true)
40+
interval = WilsonScore.interval(1, 50)
3741
assert_in_delta 0.0010, interval.first
3842
assert_in_delta 0.1201, interval.last
3943
end
4044

4145
def test_continuity_correction_one_one
42-
interval = WilsonScore.interval(1, 1, 0.95, true)
46+
interval = WilsonScore.interval(1, 1)
4347
assert_in_delta 0.0546, interval.first
4448
assert_in_delta 1, interval.last
4549
end
4650

4751
def test_continuity_correction_one_three
48-
interval = WilsonScore.interval(1, 3, 0.95, true)
52+
interval = WilsonScore.interval(1, 3)
4953
assert_in_delta 0.0176, interval.first
5054
assert_in_delta 0.8747, interval.last
5155
end
5256

5357
def test_rating
54-
interval = WilsonScore.rating_interval(5, 1, 1..5, 0.95)
58+
interval = WilsonScore.rating_interval(5, 1, 1..5, correction: false)
5559
assert_in_delta 1.8262, interval.first
5660
assert_in_delta 5, interval.last
5761
end
5862

5963
def test_rating_advanced
60-
interval = WilsonScore.rating_interval(3.7, 10, 1..5, 0.95)
64+
interval = WilsonScore.rating_interval(3.7, 10, 1..5, correction: false)
6165
assert_in_delta 2.4998, interval.first
6266
assert_in_delta 4.5117, interval.last
6367
end
6468

69+
def test_rating_lower_bound
70+
assert_in_delta 1.8262, WilsonScore.rating_lower_bound(5, 1, 1..5, correction: false)
71+
end
72+
6573
end

0 commit comments

Comments
 (0)
Please sign in to comment.