weighted_list_rank 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|