glicko2 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3264135c077789c8ce7d6f49a216c3ac50dafeb4
4
+ data.tar.gz: c621d811f861685e08a85c60d5252e4868231842
5
+ SHA512:
6
+ metadata.gz: 8d34d1bab836561f0fefb5f58807387af31d7d21adfd31344b547b8d804f75ba16c8ffd5a45d6982a620aca49c9e8374fb5ed93744b9050dea158536c4be0b61
7
+ data.tar.gz: 27fbeaf711881f18500a5277f2a9571d71bad500976949f73e873c0196cd29347723b382597c8d54e5961ff2751128f6591babb088a1efa60bcdb542da65468a
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .ruby-version
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
@@ -47,5 +47,6 @@ module Glicko2
47
47
  end
48
48
 
49
49
  require "glicko2/version"
50
+ require "glicko2/normal_distribution"
50
51
  require "glicko2/player"
51
52
  require "glicko2/rating_period"
@@ -0,0 +1,58 @@
1
+ module Glicko2
2
+ # Glicko ratings are represented with a rating and rating deviation. For this
3
+ # gem it is assumed that ratings are normally distributed where rating and
4
+ # rating deviation correspond to mean and standard deviation.
5
+ class NormalDistribution
6
+ attr_reader :mean, :standard_deviation
7
+ alias_method :sd, :standard_deviation
8
+
9
+ def initialize(mean, standard_deviation)
10
+ @mean = mean
11
+ @standard_deviation = standard_deviation
12
+ end
13
+
14
+ # Calculate the distribution variance
15
+ #
16
+ # @return [Numeric]
17
+ def variance
18
+ standard_deviation ** 2.0
19
+ end
20
+
21
+ # Calculate the sum
22
+ #
23
+ # @param [NormalDistribution] other
24
+ # @return [NormalDistribution]
25
+ def +(other)
26
+ self.class.new(mean + other.mean, Math.sqrt(variance + other.variance))
27
+ end
28
+
29
+ # Calculate the difference
30
+ #
31
+ # @param [NormalDistribution] other
32
+ # @return [NormalDistribution]
33
+ def -(other)
34
+ self.class.new(mean - other.mean, Math.sqrt(variance + other.variance))
35
+ end
36
+
37
+ # Calculate the probability density at `x`
38
+ #
39
+ # @param [Numeric] x
40
+ # @return [Numeric]
41
+ def pdf(x)
42
+ 1.0 / (sd * Math.sqrt(2.0 * Math::PI)) *
43
+ Math.exp(-(x - mean) ** 2.0 / 2.0 * variance)
44
+ end
45
+
46
+ # Calculate the cumulative distribution at `x`
47
+ #
48
+ # @param [Numeric] x
49
+ # @return [Numeric]
50
+ def cdf(x)
51
+ 0.5 * (1.0 + Math.erf((x - mean) / (sd * Math.sqrt(2.0))))
52
+ end
53
+
54
+ def to_s
55
+ "#<NormalDistribution mean=#{mean}, sd=#{sd}>"
56
+ end
57
+ end
58
+ end
@@ -22,11 +22,11 @@ module Glicko2
22
22
  #
23
23
  # puts player_seed
24
24
  #
25
- class Player
25
+ class Player < NormalDistribution
26
26
  TOLERANCE = 0.0000001
27
27
  MIN_SD = DEFAULT_GLICKO_RATING_DEVIATION / GLICKO_GRADIENT
28
28
 
29
- attr_reader :mean, :sd, :volatility, :obj
29
+ attr_reader :volatility, :obj
30
30
 
31
31
  # Create a {Player} from a seed object, converting from Glicko
32
32
  # ratings to Glicko2.
@@ -43,8 +43,7 @@ module Glicko2
43
43
  # @param [Numeric] volatility player volatility
44
44
  # @param [#rating,#rating_deviation,#volatility] obj seed values object
45
45
  def initialize(mean, sd, volatility, obj=nil, config=DEFAULT_CONFIG)
46
- @mean = mean
47
- @sd = sd
46
+ super(mean, sd)
48
47
  @volatility = volatility
49
48
  @obj = obj
50
49
  @config = config
@@ -55,7 +54,7 @@ module Glicko2
55
54
  #
56
55
  # @return [Numeric]
57
56
  def g
58
- @g ||= 1 / Math.sqrt(1 + 3 * sd ** 2 / Math::PI ** 2)
57
+ @g ||= 1 / Math.sqrt(1 + 3 * variance / Math::PI ** 2)
59
58
  end
60
59
 
61
60
  # Calculate `E(mu, mu_j, phi_j)` as defined in the Glicko2 paper
@@ -71,7 +70,7 @@ module Glicko2
71
70
  #
72
71
  # @param [Array<Player>] others other participating players.
73
72
  # @return [Numeric]
74
- def variance(others)
73
+ def estimated_variance(others)
75
74
  return 0.0 if others.length < 1
76
75
  others.reduce(0) do |v, other|
77
76
  e_other = e(other)
@@ -89,14 +88,14 @@ module Glicko2
89
88
  def delta(others, scores)
90
89
  others.zip(scores).reduce(0) do |d, (other, score)|
91
90
  d + other.g * (score - e(other))
92
- end * variance(others)
91
+ end * estimated_variance(others)
93
92
  end
94
93
 
95
94
  # Calculate `f(x)` as defined in the Glicko2 paper
96
95
  #
97
96
  # @param [Numeric] x
98
97
  # @param [Numeric] d the result of calculating {#delta}
99
- # @param [Numeric] v the result of calculating {#variance}
98
+ # @param [Numeric] v the result of calculating {#estimated_variance}
100
99
  # @return [Numeric]
101
100
  def f(x, d, v)
102
101
  f_part1(x, d, v) - f_part2(x)
@@ -105,12 +104,12 @@ module Glicko2
105
104
  # Calculate the new value of the volatility
106
105
  #
107
106
  # @param [Numeric] d the result of calculating {#delta}
108
- # @param [Numeric] v the result of calculating {#variance}
107
+ # @param [Numeric] v the result of calculating {#estimated_variance}
109
108
  # @return [Numeric]
110
109
  def volatility1(d, v)
111
110
  a = Math::log(volatility ** 2)
112
- if d > sd ** 2 + v
113
- b = Math.log(d - sd ** 2 - v)
111
+ if d > variance + v
112
+ b = Math.log(d - variance - v)
114
113
  else
115
114
  k = 1
116
115
  k += 1 while f(a - k * @config[:volatility_change], d, v) < 0
@@ -162,15 +161,15 @@ module Glicko2
162
161
  private
163
162
 
164
163
  def generate_next_without_games
165
- sd_pre = [Math.sqrt(sd ** 2 + volatility ** 2), MIN_SD].min
164
+ sd_pre = [Math.sqrt(variance + volatility ** 2), MIN_SD].min
166
165
  self.class.new(mean, sd_pre, volatility, obj)
167
166
  end
168
167
 
169
168
  def generate_next_with_games(others, scores)
170
- _v = variance(others)
169
+ _v = estimated_variance(others)
171
170
  _d = delta(others, scores)
172
171
  _volatility = volatility1(_d, _v)
173
- sd_pre = Math.sqrt(sd ** 2 + _volatility ** 2)
172
+ sd_pre = [Math.sqrt(variance + volatility ** 2), MIN_SD].min
174
173
  _sd = 1 / Math.sqrt(1 / sd_pre ** 2 + 1 / _v)
175
174
  _mean = mean + _sd ** 2 * others.zip(scores).reduce(0) {
176
175
  |x, (other, score)| x + other.g * (score - e(other))
@@ -180,7 +179,7 @@ module Glicko2
180
179
 
181
180
  def f_part1(x, d, v)
182
181
  exp_x = Math.exp(x)
183
- sd_sq = sd ** 2
182
+ sd_sq = variance
184
183
  (exp_x * (d ** 2 - sd_sq - v - exp_x)) / (2 * (sd_sq + v + exp_x) ** 2)
185
184
  end
186
185
 
@@ -1,3 +1,3 @@
1
1
  module Glicko2
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -0,0 +1,69 @@
1
+ require 'minitest_helper'
2
+
3
+ describe Glicko2::NormalDistribution do
4
+ describe "#variance" do
5
+ it "must return the square of the standard deviation" do
6
+ Glicko2::NormalDistribution.new(1.0, 1.0).variance.must_equal 1.0 ** 2.0
7
+ Glicko2::NormalDistribution.new(1.0, 2.0).variance.must_equal 2.0 ** 2.0
8
+ Glicko2::NormalDistribution.new(1.0, 10.0).variance.must_equal 10.0 ** 2.0
9
+ end
10
+ end
11
+
12
+ describe "#+" do
13
+ let(:dist1) { Glicko2::NormalDistribution.new(10.0, 0.5) }
14
+ let(:dist2) { Glicko2::NormalDistribution.new(5.0, 1.0) }
15
+
16
+ it "must sum the means" do
17
+ (dist1 + dist2).mean.must_equal 15.0
18
+ end
19
+
20
+ it "must sqrt the sum of the variances" do
21
+ (dist1 + dist2).sd.must_equal Math.sqrt(0.5 ** 2.0 + 1.0 ** 2.0)
22
+ end
23
+ end
24
+
25
+ describe "#-" do
26
+ let(:dist1) { Glicko2::NormalDistribution.new(10.0, 0.5) }
27
+ let(:dist2) { Glicko2::NormalDistribution.new(5.0, 1.0) }
28
+
29
+ it "must subtract the means" do
30
+ (dist1 - dist2).mean.must_equal 5.0
31
+ end
32
+
33
+ it "must sqrt the sum of the variances" do
34
+ (dist1 - dist2).sd.must_equal Math.sqrt(0.5 ** 2.0 + 1.0 ** 2.0)
35
+ end
36
+ end
37
+
38
+ describe "#pdf" do
39
+ describe "standard normal" do
40
+ let(:dist) { Glicko2::NormalDistribution.new(0.0, 1.0) }
41
+
42
+ it "must calculate PDF at x" do
43
+ dist.pdf(-5.0).must_be_close_to 0.00000149, 0.00000001
44
+ dist.pdf(-2.5).must_be_close_to 0.01752830, 0.00000001
45
+ dist.pdf(-1.0).must_be_close_to 0.24197072, 0.00000001
46
+ dist.pdf(0.0).must_be_close_to 0.39894228, 0.00000001
47
+ dist.pdf(1.0).must_be_close_to 0.24197072, 0.00000001
48
+ dist.pdf(2.5).must_be_close_to 0.01752830, 0.00000001
49
+ dist.pdf(5.0).must_be_close_to 0.00000149, 0.00000001
50
+ end
51
+ end
52
+ end
53
+
54
+ describe "#cdf" do
55
+ describe "standard normal" do
56
+ let(:dist) { Glicko2::NormalDistribution.new(0.0, 1.0) }
57
+
58
+ it "must calculate CDF at x" do
59
+ dist.cdf(-5.0).must_be_close_to 0.00000029, 0.00000001
60
+ dist.cdf(-2.5).must_be_close_to 0.00620967, 0.00000001
61
+ dist.cdf(-1.0).must_be_close_to 0.15865525, 0.00000001
62
+ dist.cdf(0.0).must_be_close_to 0.50000000, 0.00000001
63
+ dist.cdf(1.0).must_be_close_to 0.84134475, 0.00000001
64
+ dist.cdf(2.5).must_be_close_to 0.99379033, 0.00000001
65
+ dist.cdf(5.0).must_be_close_to 0.99999971, 0.00000001
66
+ end
67
+ end
68
+ end
69
+ end
@@ -66,9 +66,9 @@ describe Glicko2::Player do
66
66
  end
67
67
  end
68
68
 
69
- describe "#variance" do
69
+ describe "#estimated_variance" do
70
70
  it "must be close to example" do
71
- @player.variance(@others).must_be_close_to 1.7785
71
+ @player.estimated_variance(@others).must_be_close_to 1.7785
72
72
  end
73
73
  end
74
74
 
metadata CHANGED
@@ -1,30 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glicko2
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
5
- prerelease:
4
+ version: 0.1.2
6
5
  platform: ruby
7
6
  authors:
8
7
  - James Fargher
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-18 00:00:00.000000000 Z
11
+ date: 2013-03-04 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: minitest
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  description: Implementation of Glicko2 ratings
@@ -41,39 +38,42 @@ files:
41
38
  - Rakefile
42
39
  - glicko2.gemspec
43
40
  - lib/glicko2.rb
41
+ - lib/glicko2/normal_distribution.rb
44
42
  - lib/glicko2/player.rb
45
43
  - lib/glicko2/rating_period.rb
46
44
  - lib/glicko2/version.rb
47
45
  - spec/minitest_helper.rb
46
+ - spec/normal_distribution_spec.rb
48
47
  - spec/player_spec.rb
49
48
  - spec/rating_period_spec.rb
50
49
  - spec/util_spec.rb
51
50
  homepage: https://github.com/proglottis/glicko2
52
51
  licenses: []
52
+ metadata: {}
53
53
  post_install_message:
54
54
  rdoc_options: []
55
55
  require_paths:
56
56
  - lib
57
57
  required_ruby_version: !ruby/object:Gem::Requirement
58
- none: false
59
58
  requirements:
60
- - - ! '>='
59
+ - - '>='
61
60
  - !ruby/object:Gem::Version
62
61
  version: '0'
63
62
  required_rubygems_version: !ruby/object:Gem::Requirement
64
- none: false
65
63
  requirements:
66
- - - ! '>='
64
+ - - '>='
67
65
  - !ruby/object:Gem::Version
68
66
  version: '0'
69
67
  requirements: []
70
68
  rubyforge_project:
71
- rubygems_version: 1.8.23
69
+ rubygems_version: 2.0.0
72
70
  signing_key:
73
- specification_version: 3
71
+ specification_version: 4
74
72
  summary: Implementation of Glicko2 ratings
75
73
  test_files:
76
74
  - spec/minitest_helper.rb
75
+ - spec/normal_distribution_spec.rb
77
76
  - spec/player_spec.rb
78
77
  - spec/rating_period_spec.rb
79
78
  - spec/util_spec.rb
79
+ has_rdoc: