weighted_list_rank 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -1
- data/Gemfile.lock +10 -10
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/lib/weighted_list_rank/item.rb +6 -0
- data/lib/weighted_list_rank/strategies/exponential.rb +28 -11
- data/lib/weighted_list_rank/version.rb +1 -1
- data/weighted_list_rank.gemspec +42 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9afa895dc286e9cc5dd2370376b965eda825669debad06e21c232b996e2cab6
|
4
|
+
data.tar.gz: 6116b3e97dc978a0ca8021f683ff7d5b31b1ebd1e0e3f07fc10c7a266b34659e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 71656bad8660b6ae1455afc06a406dddf69f688feb22b690b6dcef57b39aa2ba60df1fe14be91aa22af957b71960b7fe856a980b1769090ea039149fcb005abe
|
7
|
+
data.tar.gz: cb30e3d19ed531e74ff7d665c92029a105c83a6d18e3ce98671c3471218c1c5fa6fc39672394161444b38cc2a97d0dfc2aeab60cfaedefde2664f246cd9ac5c6
|
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.4.0] - 2024-06-26
|
4
|
+
- Added score_penalty feature to allow percentage-based penalties on item scores.
|
5
|
+
- Updated all dependencies to their latest versions.
|
6
|
+
|
3
7
|
## [0.3.1] - 2024-05-026
|
4
8
|
|
5
9
|
- Fixed issue with lists with only a single item getting bonus points
|
6
10
|
|
7
11
|
## [0.3.0] - 2024-02-03
|
8
12
|
|
9
|
-
- added bonus_pool_percentage feature to
|
13
|
+
- added bonus_pool_percentage feature to exponential
|
10
14
|
|
11
15
|
## [0.2.0] - 2024-02-03
|
12
16
|
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
weighted_list_rank (0.
|
4
|
+
weighted_list_rank (0.4.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -10,18 +10,18 @@ GEM
|
|
10
10
|
json (2.7.2)
|
11
11
|
language_server-protocol (3.17.0.3)
|
12
12
|
lint_roller (1.1.0)
|
13
|
-
minitest (5.
|
14
|
-
parallel (1.
|
15
|
-
parser (3.3.
|
13
|
+
minitest (5.24.0)
|
14
|
+
parallel (1.25.1)
|
15
|
+
parser (3.3.3.0)
|
16
16
|
ast (~> 2.4.1)
|
17
17
|
racc
|
18
18
|
racc (1.8.0)
|
19
19
|
rainbow (3.1.1)
|
20
20
|
rake (13.2.1)
|
21
21
|
regexp_parser (2.9.2)
|
22
|
-
rexml (3.
|
23
|
-
strscan
|
24
|
-
rubocop (1.
|
22
|
+
rexml (3.3.1)
|
23
|
+
strscan
|
24
|
+
rubocop (1.64.1)
|
25
25
|
json (~> 2.3)
|
26
26
|
language_server-protocol (>= 3.17.0)
|
27
27
|
parallel (~> 1.10)
|
@@ -37,16 +37,16 @@ GEM
|
|
37
37
|
rubocop-minitest (0.35.0)
|
38
38
|
rubocop (>= 1.61, < 2.0)
|
39
39
|
rubocop-ast (>= 1.31.1, < 2.0)
|
40
|
-
rubocop-performance (1.21.
|
40
|
+
rubocop-performance (1.21.1)
|
41
41
|
rubocop (>= 1.48.1, < 2.0)
|
42
42
|
rubocop-ast (>= 1.31.1, < 2.0)
|
43
43
|
rubocop-rake (0.6.0)
|
44
44
|
rubocop (~> 1.0)
|
45
45
|
ruby-progressbar (1.13.0)
|
46
|
-
standard (1.
|
46
|
+
standard (1.39.0)
|
47
47
|
language_server-protocol (~> 3.17.0.2)
|
48
48
|
lint_roller (~> 1.0)
|
49
|
-
rubocop (~> 1.
|
49
|
+
rubocop (~> 1.64.0)
|
50
50
|
standard-custom (~> 1.0.0)
|
51
51
|
standard-performance (~> 1.4)
|
52
52
|
standard-custom (1.0.2)
|
data/README.md
CHANGED
@@ -92,6 +92,78 @@ custom_ranked_items.each do |item|
|
|
92
92
|
end
|
93
93
|
```
|
94
94
|
|
95
|
+
### Using Item Penalties
|
96
|
+
The WeightedListRank system also supports applying penalties to individual items. A penalty is defined as a percentage reduction in the item's score. This feature can be used to de-emphasize certain items based on specific criteria.
|
97
|
+
|
98
|
+
To use the penalty feature, include the score_penalty attribute when defining your items:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
class MyItem
|
102
|
+
include WeightedListRank::Item
|
103
|
+
attr_reader :id, :position, :score_penalty
|
104
|
+
|
105
|
+
def initialize(id, position, score_penalty = nil)
|
106
|
+
@id = id
|
107
|
+
@position = position
|
108
|
+
@score_penalty = score_penalty
|
109
|
+
end
|
110
|
+
end
|
111
|
+
```
|
112
|
+
|
113
|
+
You can then apply the penalties when calculating the scores:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
require 'weighted_list_rank'
|
117
|
+
|
118
|
+
# Initialize items and list with text identifiers and penalties
|
119
|
+
items = [
|
120
|
+
MyItem.new("Item 1", 1, 0.20), # 20% penalty
|
121
|
+
MyItem.new("Item 2", 2, 0.10), # 10% penalty
|
122
|
+
MyItem.new("Item 3", 3, nil) # No penalty
|
123
|
+
]
|
124
|
+
list = MyList.new("List 1", 10, items)
|
125
|
+
|
126
|
+
# Initialize the Exponential strategy with an optional exponent
|
127
|
+
exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 1.5)
|
128
|
+
|
129
|
+
# Create a RankingContext using the Exponential strategy
|
130
|
+
ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)
|
131
|
+
|
132
|
+
# Rank the items
|
133
|
+
ranked_items = ranking_context.rank([list])
|
134
|
+
|
135
|
+
# Display the ranked items
|
136
|
+
ranked_items.each do |item|
|
137
|
+
puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
### Customizing Penalties Example
|
142
|
+
You can customize the penalty values to see how they affect the final scores of the items.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
# Customizing penalties for different items
|
146
|
+
items = [
|
147
|
+
MyItem.new("Item 1", 1, 0.30), # 30% penalty
|
148
|
+
MyItem.new("Item 2", 2, 0.15), # 15% penalty
|
149
|
+
MyItem.new("Item 3", 3, nil) # No penalty
|
150
|
+
]
|
151
|
+
list = MyList.new("List 1", 10, items)
|
152
|
+
|
153
|
+
# Initialize the Exponential strategy with an optional exponent
|
154
|
+
exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 2.0)
|
155
|
+
|
156
|
+
# Create a RankingContext using the Exponential strategy
|
157
|
+
ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)
|
158
|
+
|
159
|
+
# Rank the items
|
160
|
+
ranked_items = ranking_context.rank([list])
|
161
|
+
|
162
|
+
# Output the results
|
163
|
+
ranked_items.each do |item|
|
164
|
+
puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
|
165
|
+
end
|
166
|
+
```
|
95
167
|
|
96
168
|
## Development
|
97
169
|
|
data/Rakefile
CHANGED
@@ -11,5 +11,11 @@ module WeightedListRank
|
|
11
11
|
def id
|
12
12
|
raise NotImplementedError, "Implement this method to return the item's unique identifier"
|
13
13
|
end
|
14
|
+
|
15
|
+
# Returns the score penalty of the item. Can be overridden by the including class.
|
16
|
+
# @return [Float, NilClass] the score penalty of the item as a percentage (e.g., 0.20 for 20%) or nil if no penalty
|
17
|
+
def score_penalty
|
18
|
+
nil
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
@@ -42,21 +42,38 @@ module WeightedListRank
|
|
42
42
|
# @return [Float] the calculated score for the item, adjusted by the list's weight, the specified exponent,
|
43
43
|
# and the bonus pool percentage.
|
44
44
|
def calculate_score(list, item)
|
45
|
-
#
|
46
|
-
|
45
|
+
# Default score to the list's weight
|
46
|
+
score = list.weight
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
unless item.position.nil? || list.items.count == 1
|
49
|
+
num_items = list.items.count
|
50
|
+
total_bonus_pool = list.weight * bonus_pool_percentage
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
52
|
+
# Calculate the exponential factor for the item's rank position
|
53
|
+
exponential_factor = (num_items + 1 - item.position)**exponent
|
54
|
+
total_exponential_factor = (1..num_items).sum { |pos| (num_items + 1 - pos)**exponent }
|
54
55
|
|
55
|
-
|
56
|
-
|
56
|
+
# Allocate a portion of the total bonus pool based on the item's exponential factor
|
57
|
+
item_bonus = (exponential_factor / total_exponential_factor) * total_bonus_pool
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
# Add the item's allocated bonus to the default score
|
60
|
+
score += item_bonus
|
61
|
+
end
|
62
|
+
|
63
|
+
# Apply score penalty if it exists
|
64
|
+
apply_penalty(score, item.score_penalty)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Applies the score penalty if it exists
|
70
|
+
#
|
71
|
+
# @param score [Float] the original score of the item
|
72
|
+
# @param penalty [Float, NilClass] the score penalty of the item as a percentage (e.g., 0.20 for 20%) or nil if no penalty
|
73
|
+
#
|
74
|
+
# @return [Float] the score after applying the penalty
|
75
|
+
def apply_penalty(score, penalty)
|
76
|
+
penalty ? score * (1 - penalty) : score
|
60
77
|
end
|
61
78
|
end
|
62
79
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/weighted_list_rank/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "weighted_list_rank"
|
7
|
+
spec.version = WeightedListRank::VERSION
|
8
|
+
spec.authors = ["Shane Sherman"]
|
9
|
+
spec.email = ["shane.sherman@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "generate ranks of items from weighted lists"
|
12
|
+
spec.description = "generate ranks of items from weighted lists"
|
13
|
+
spec.homepage = "https://github.com/ssherman/weighted_list_rank"
|
14
|
+
spec.license = "MIT"
|
15
|
+
spec.required_ruby_version = ">= 2.6.0"
|
16
|
+
|
17
|
+
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = "https://github.com/ssherman/weighted_list_rank"
|
21
|
+
spec.metadata["changelog_uri"] = "https://github.com/ssherman/weighted_list_rank/CHANGELOG.md"
|
22
|
+
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
spec.bindir = "exe"
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
32
|
+
spec.require_paths = ["lib"]
|
33
|
+
|
34
|
+
# Uncomment to register a new dependency of your gem
|
35
|
+
# spec.add_dependency "example-gem", "~> 1.0"
|
36
|
+
spec.add_development_dependency "rubocop", "~> 1.0"
|
37
|
+
spec.add_development_dependency "standard", "~> 1.0"
|
38
|
+
spec.add_development_dependency "rubocop-minitest", "~> 0.3"
|
39
|
+
spec.add_development_dependency "rubocop-rake", "~> 0.6"
|
40
|
+
# For more information and examples about making a new gem, check out our
|
41
|
+
# guide at: https://bundler.io/guides/creating_gem.html
|
42
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: weighted_list_rank
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Sherman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -89,6 +89,7 @@ files:
|
|
89
89
|
- lib/weighted_list_rank/strategy.rb
|
90
90
|
- lib/weighted_list_rank/version.rb
|
91
91
|
- sig/weighted_list_rank.rbs
|
92
|
+
- weighted_list_rank.gemspec
|
92
93
|
homepage: https://github.com/ssherman/weighted_list_rank
|
93
94
|
licenses:
|
94
95
|
- MIT
|
@@ -111,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
112
|
- !ruby/object:Gem::Version
|
112
113
|
version: '0'
|
113
114
|
requirements: []
|
114
|
-
rubygems_version: 3.
|
115
|
+
rubygems_version: 3.5.11
|
115
116
|
signing_key:
|
116
117
|
specification_version: 4
|
117
118
|
summary: generate ranks of items from weighted lists
|