pcbr 0.1.3 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -7
  3. data/lib/pcbr.rb +53 -33
  4. data/pcbr.gemspec +11 -6
  5. data/spec/_spec.rb +88 -26
  6. metadata +28 -14
  7. data/.rspec +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b3ebecb25ead598113da23a081caff2edae89b2
4
- data.tar.gz: 017a54c57edab08278ea631356774a315a0ef976
3
+ metadata.gz: 9c14c95b38050d0a74a838c101e6321886aa7ac9
4
+ data.tar.gz: 4059ab70af92b3cad59bbc3199d29f948477d525
5
5
  SHA512:
6
- metadata.gz: 9935c310af1592e636e9e3c38bc239d7323053eb5c32e3c501ce974ecc17aab10d081892f988c0074b5e579fe8d19350d36d0bcaadceb10dc536a49e293dd58f
7
- data.tar.gz: 00ca5fcb6c35b5dec763a96638a25c3b39e548bad1e66f9c8b63905376d1470dc53ae5db0e2537c5a0229b88d30e44eff70bd75fe1624f9c62f32a1e7757439e
6
+ metadata.gz: '097ea8ee69316361d9124ca139757ddb8b0ef4e54ca4a5c91ca33f91cb80c9a06b8225d8cbaa608b0929c922c429c4b295af712d634983f4e9da997c47c05a8f'
7
+ data.tar.gz: d6be5246bf91af6e918583c98d25ffc96e48f7d7af5e6e647f66665c2c6f87db599ccdd77edcbd14204eadae9a96da1b387c3589c9074464f45f428be9c52d01
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # PCBR (Pairs Comparison Based Rating)
2
2
 
3
- Making ratings is fun. After applying my method several times I've decided to gemify it.
3
+ You often need to sort an array of vectors. This allows you to do it without knowing the optimal ranking function and with some pairs of vectors that are not even comparable.
4
4
 
5
5
  ### Examples
6
6
 
@@ -8,20 +8,20 @@ See [`describe "examples" do` in specs](spec/_spec.rb).
8
8
 
9
9
  ### How it works
10
10
 
11
- The first idea of rating items by one-to-one comparison was about QuakeLive players in 2013 or so and it didn't work well. At that time I was thinking about tree data structure. Later in May 2015 I've realised that it's really about dots in n-dimensional space and sectors. Applying it to Reddit RSS made my feed 50% more interesting.
12
-
13
- TODO: describe/illustrate algorithm?
14
-
15
- At the moment it's a "proof of concept" -- it needs huge optimisations for lookups, maybe using trees.
11
+ The first idea was in 2013 -- at that time I was imagining it as a tree data structure. Later in May 2015 I've realised that it's really about dots in n-dimensional space and sectors, and round-robin. Applying it to Reddit RSS feed made it 50% more interesting. It also applied well to boost tree search process in the [bad Facebook advertisment classifier](https://drive.google.com/file/d/0B3BLwu7Vb2U-SVhKYWVMR2JvOFk/view?usp=sharing) production project. Since then I mostly use it to oprimize tree searches, it's basically an automated replacement for euristics.
16
12
 
17
13
  ### Installation
18
14
 
19
15
  $ gem install pcbr
20
16
 
21
- ### Testing with RSpec before contributing
17
+ ### Testing
22
18
 
23
19
  rspec
24
20
 
25
21
  or
26
22
 
27
23
  rake spec
24
+
25
+ ### TODO
26
+
27
+ Illustrate this README, replace rspec with minitest.
@@ -1,43 +1,63 @@
1
- class PCBR
1
+ module PCBR
2
+ class Error < RuntimeError
3
+ # def initialize body
4
+ # super "#{Module.nesting[1]} error: #{body}"
5
+ # end
6
+ end
2
7
 
3
- VERSION = "0.1.3"
8
+ def self.new &block
9
+ Storage.new &block
10
+ end
4
11
 
5
- attr_reader :table
12
+ class Storage
13
+ attr_reader :table
14
+ attr_reader :set
15
+ @@default_lambda = lambda do |a_, b_|
16
+ raise Error.new "comparison vectors are of the different length" unless a_.size == b_.size
17
+ tt = [0, 0, 0]
18
+ [*a_].zip([*b_]) do |a, b|
19
+ t = a <=> b and tt[t] = t
20
+ end
21
+ tt[0] + tt[1] + tt[2]
22
+ end
6
23
 
7
- def initialize &block
8
- @table = []
9
- @callback = block || ->*_{[*_[0]]}
10
- end
24
+ def initialize &block
25
+ @table = []
26
+ @set = Set.new
27
+ @callback = block || @@default_lambda
28
+ end
11
29
 
12
- # def size
13
- # @table.size
14
- # end
15
-
16
- ARRAY_101 = [0, 0, 0]
17
- def store key, *vector
18
- vector = vector.empty? ? [key] : vector.first
19
- calculated = @callback[vector, key]
20
- score = 0
21
- @table.each do |item|
22
- array = ARRAY_101.dup
23
- calculated.zip(item[3]).each do |a, b|
24
- next unless t = a <=> b
25
- array[t] = t
30
+ def store key, vector = nil
31
+ raise Error.new "duplicating key" if @set.include? key
32
+ key = [NilClass, FalseClass, TrueClass, Numeric, Symbol, Method].any?{ |c| key.is_a? c } ? key : key.dup
33
+ vector = Array key if vector.nil?
34
+ score = 0
35
+ @table.each do |item|
36
+ point = @callback.call vector, item[1]
37
+ item[2] -= point
38
+ score += point
26
39
  end
27
- point = array.inject :+
28
- score += point
29
- item[2] -= point
40
+ @set.add key
41
+ @table.push [key, vector, score]
30
42
  end
31
- @table.push [key, vector, score, calculated]
32
- end
33
43
 
34
- def score key
35
- @table.assoc(key)[-2]
36
- end
44
+ def score key
45
+ @table.assoc(key)[2]
46
+ end
37
47
 
38
- def sorted
39
- # from the best to the worst
40
- @table.sort_by.with_index{ |item, i| [-item[2], i] }.map(&:first)
41
- end
48
+ def sorted
49
+ # from the best to the worst
50
+ @table.sort_by.with_index{ |item, i| [-item[2], i] }.map(&:first)
51
+ end
52
+
53
+ # def quality
54
+ # factorial = ->x{ (1..x).inject(:*) }
55
+ # (2...@table.size).each do |sublength|
56
+ # combinations = factorial[@table.size] / factorial[sublength] / factorial[@table.size - sublength]
57
+ # comparisons = sublength * (sublength - 1) / 2
58
+ # p [sublength, combinations, comparisons, combinations * comparisons]
59
+ # end
60
+ # end
42
61
 
62
+ end
43
63
  end
@@ -1,18 +1,23 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "pcbr"
3
- spec.version = (require_relative "lib/pcbr"; PCBR::VERSION)
3
+ spec.version = "0.4.2"
4
+ spec.summary = "Pair Comparison Based Rating"
5
+
4
6
  spec.author = "Victor Maslov"
5
7
  spec.email = "nakilon@gmail.com"
6
- spec.summary = "Pair Comparison Based Rating"
7
- spec.description = "Making ratings is fun. After applying my method several times I've decided to gemify it."
8
- spec.homepage = "https://github.com/Nakilon/pcbr"
9
8
  spec.license = "MIT"
9
+ spec.homepage = "https://github.com/Nakilon/pcbr"
10
+ spec.description = <<-EOF
11
+ Making ratings is fun. After applying my method several times I've decided to gemify it.
12
+ This is one of the first gems I made so it's far for being nicely done.
13
+ EOF
10
14
 
11
- spec.files = `git ls-files -z`.split("\x0")
12
15
  spec.test_files = ["spec/"]
16
+ spec.files = `git ls-files -z`.split(?\0) - spec.test_files
13
17
 
14
- spec.add_development_dependency "bundler", "~> 1.12.0"
18
+ spec.add_development_dependency "bundler"
15
19
  spec.add_development_dependency "rspec", "~> 3.3.0"
20
+ spec.add_development_dependency "ruby-prof"
16
21
 
17
22
  spec.required_ruby_version = ">= 2.0.0"
18
23
  end
@@ -13,13 +13,19 @@ describe "basic specs" do
13
13
  expect(rating.sorted).to eq([2, 1])
14
14
  end
15
15
 
16
- example "#size", skip: :deprecated do
16
+ example "raises if vectors are of the different length" do
17
17
  rating = PCBR.new
18
- rating.store 1
19
- rating.store 2
20
- expect(rating.size).to eq(2)
18
+ rating.store 1, [2]
19
+ expect{ rating.store 3, [4, 5] }.to raise_error PCBR::Error
21
20
  end
22
21
 
22
+ # example "#size" do
23
+ # rating = PCBR.new
24
+ # rating.store 1
25
+ # rating.store 2
26
+ # expect(rating.size).to eq(2)
27
+ # end
28
+
23
29
  example "Nil elements in vector are ignored" do
24
30
  rating = PCBR.new
25
31
  rating.store 1, [1, nil]
@@ -31,26 +37,41 @@ describe "basic specs" do
31
37
 
32
38
  example "&block" do
33
39
  n = 0
34
- rating = PCBR.new do |item|
40
+ rating = PCBR.new do |a, b|
35
41
  n += 1
36
- [item[:goodness], -item[:badness]]
42
+ a <=> b
43
+ end
44
+ rating.store 1
45
+ rating.store 2
46
+ rating.store 3
47
+ rating.store 4
48
+ expect(rating.sorted).to eq([4, 3, 2, 1])
49
+ expect(n).to eq(6)
50
+ end
51
+
52
+ example "the vector is not neccessary an Array" do
53
+ rating = PCBR.new do |a, b|
54
+ a <=> b
55
+ end
56
+ 0.class.class_eval do
57
+ def size
58
+ fail
59
+ end
37
60
  end
38
- rating.store 3, {goodness: 0, badness: 2}
39
- rating.store 2, {goodness: 1, badness: 2}
40
- rating.store 1, {goodness: 1, badness: 1}
41
- expect(rating.sorted).to eq([1, 2, 3])
42
- expect(n).to eq(3)
61
+ rating.store 1, 2
62
+ rating.store 2, 1
63
+ expect(rating.sorted).to eq([1, 2])
43
64
  end
44
65
 
45
66
  example "#sorted and #score[key]" do
46
67
  rating = PCBR.new
47
68
  table = [
48
- [1, [1, 1], -1, [1, 1]],
49
- [2, [2, 2], 5, [2, 2]],
50
- [3, [0, 0], -5, [0, 0]],
51
- [4, [1, 2], 3, [1, 2]],
52
- [6, [1, 1], -1, [1, 1]],
53
- [5, [0, 2], -1, [0, 2]],
69
+ [1, [1, 1], -1],
70
+ [2, [2, 2], 5],
71
+ [3, [0, 0], -5],
72
+ [4, [1, 2], 3],
73
+ [6, [1, 1], -1],
74
+ [5, [0, 2], -1],
54
75
  ].each do |key, vector, |
55
76
  rating.store key, vector
56
77
  end
@@ -62,6 +83,45 @@ describe "basic specs" do
62
83
  expect(rating.table).to eq(table)
63
84
  end
64
85
 
86
+ # example "quality estimation" do
87
+ # rating = PCBR.new
88
+ # table = [
89
+ # [1, [1, 1]],
90
+ # [2, [2, 2]],
91
+ # [3, [0, 0]],
92
+ # [4, [1, 2]],
93
+ # [6, [1, 1]],
94
+ # [5, [0, 2]],
95
+ # ].each do |key, vector, |
96
+ # rating.store key, vector
97
+ # end
98
+ # end
99
+
100
+ example "duplicating keys are forbidden" do
101
+ rating = PCBR.new
102
+ rating.store 0
103
+ expect{ rating.store 0 }.to raise_error PCBR::Error
104
+ end
105
+
106
+ example "keys are dupped" do
107
+ rating = PCBR.new
108
+ a = [[1]]
109
+ v = [0]
110
+ rating.store a, v
111
+ a[0][0] = 2
112
+ rating.store [[1]], [1]
113
+ expect(rating.sorted).to eq [[[1]], [[2]]]
114
+ end
115
+
116
+ example "keys are dupped but not deeply" do
117
+ rating = PCBR.new
118
+ a = [[1]]
119
+ v = [0]
120
+ rating.store a, v
121
+ a[0][0] = 2
122
+ expect{ rating.store [[2]], [1] }.to raise_error PCBR::Error
123
+ end
124
+
65
125
  end
66
126
 
67
127
 
@@ -96,26 +156,28 @@ describe "examples" do
96
156
  "DOT: capistrano/capistrano" => {issue: 38, pr: 6, watch: 339, star: 8392, fork: 1365},
97
157
  }
98
158
 
99
- push_repos = lambda do |rating|
100
- repos.each do |repo_name, repo_stats|
101
- rating.store repo_name, repo_stats
159
+ create_rating = lambda do |&callback|
160
+ PCBR.new.tap do |rating|
161
+ repos.each do |repo_name, repo_stats|
162
+ rating.store repo_name, callback[repo_stats, repo_name]
163
+ end
102
164
  end
103
165
  end
104
166
 
105
- contribution_intensivity_rating = PCBR.new do |repo_stats| [
167
+ contribution_intensivity_rating = create_rating.call do |repo_stats| [
106
168
  repo_stats[:pr],
107
169
  -repo_stats[:fork],
108
- ] end.tap &push_repos
170
+ ] end
109
171
 
110
- quality_rating = PCBR.new do |repo_stats| [
172
+ quality_rating = create_rating.call do |repo_stats| [
111
173
  repo_stats[:star],
112
174
  -repo_stats[:issue],
113
- ] end.tap &push_repos
175
+ ] end
114
176
 
115
- resulting_rating = PCBR.new do |_, repo_name| [
177
+ resulting_rating = create_rating.call do |repo_stats, repo_name| [
116
178
  contribution_intensivity_rating.score(repo_name),
117
179
  quality_rating.score(repo_name),
118
- ] end.tap &push_repos
180
+ ] end
119
181
 
120
182
  aggregate_failures do
121
183
  expect(
metadata CHANGED
@@ -1,51 +1,65 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pcbr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Maslov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-07 00:00:00.000000000 Z
11
+ date: 2020-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 1.12.0
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 1.12.0
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 3.3.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 3.3.0
41
- description: Making ratings is fun. After applying my method several times I've decided
42
- to gemify it.
41
+ - !ruby/object:Gem::Dependency
42
+ name: ruby-prof
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: |2
56
+ Making ratings is fun. After applying my method several times I've decided to gemify it.
57
+ This is one of the first gems I made so it's far for being nicely done.
43
58
  email: nakilon@gmail.com
44
59
  executables: []
45
60
  extensions: []
46
61
  extra_rdoc_files: []
47
62
  files:
48
- - .rspec
49
63
  - Gemfile
50
64
  - README.md
51
65
  - Rakefile
@@ -62,17 +76,17 @@ require_paths:
62
76
  - lib
63
77
  required_ruby_version: !ruby/object:Gem::Requirement
64
78
  requirements:
65
- - - '>='
79
+ - - ">="
66
80
  - !ruby/object:Gem::Version
67
81
  version: 2.0.0
68
82
  required_rubygems_version: !ruby/object:Gem::Requirement
69
83
  requirements:
70
- - - '>='
84
+ - - ">="
71
85
  - !ruby/object:Gem::Version
72
86
  version: '0'
73
87
  requirements: []
74
88
  rubyforge_project:
75
- rubygems_version: 2.0.14.1
89
+ rubygems_version: 2.5.2.3
76
90
  signing_key:
77
91
  specification_version: 4
78
92
  summary: Pair Comparison Based Rating
data/.rspec DELETED
@@ -1 +0,0 @@
1
- --color