weighted_list_rank 0.5.3 → 0.6.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 +7 -0
- data/Gemfile.lock +38 -31
- data/README.md +25 -0
- data/lib/weighted_list_rank/context.rb +19 -6
- data/lib/weighted_list_rank/version.rb +1 -1
- metadata +3 -7
- data/weighted_list_rank.gemspec +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34f4b87da4046f63fd0771a985c63fab24e30dc51445ce9ceabcce4bde5b3f45
|
4
|
+
data.tar.gz: d857a6cb9a7e9114d2157a05a373876f08c52661130cf50126fa6258b4beaa5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea9c40e27791fafe638694e34a440c4df6e8e8c20770b2f9b1da6ca8e8d37d755ae5eaaeeb89fe2ca43b569805b9fde43c1b2616eb0a135bbdea37b02ae5bf4a
|
7
|
+
data.tar.gz: 7ad51b9288339b0728841a30d91ee9b0b5824f101801a090e7533a93f090a40b8ef0ea8143fea32c533fc333e0a96037ee3cba684efa9eaa8dfc9388edc20889
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## [0.6.0] - 2025-06-29
|
2
|
+
- Added `list_count_penalties` feature to `RankingContext` to allow penalizing items based on the number of lists they appear on. This is useful for de-emphasizing items that are not widely represented across your data.
|
3
|
+
- The feature accepts a hash mapping list counts to penalty percentages (e.g., `{1 => 0.50, 2 => 0.25}` to penalize items on 1 list by 50% and items on 2 lists by 25%).
|
4
|
+
- Penalties are applied to the total score after all list scores are aggregated but before sorting.
|
5
|
+
- This feature is fully backwards compatible and stacks with individual item penalties.
|
6
|
+
- Updated all dependencies to their latest versions.
|
7
|
+
|
1
8
|
## [0.5.3] - 2024-08-22
|
2
9
|
- Fixed an issue in the `Exponential` strategy where items with positions higher than the total number of items in a list could cause errors. Now, such items are treated as if they were in the last position, and a warning is logged.
|
3
10
|
- Added a new test case to verify the handling of items with positions exceeding the list size.
|
data/Gemfile.lock
CHANGED
@@ -6,57 +6,64 @@ PATH
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
ast (2.4.
|
10
|
-
json (2.
|
11
|
-
language_server-protocol (3.17.0.
|
9
|
+
ast (2.4.3)
|
10
|
+
json (2.12.2)
|
11
|
+
language_server-protocol (3.17.0.5)
|
12
12
|
lint_roller (1.1.0)
|
13
|
-
minitest (5.25.
|
14
|
-
parallel (1.
|
15
|
-
parser (3.3.
|
13
|
+
minitest (5.25.5)
|
14
|
+
parallel (1.27.0)
|
15
|
+
parser (3.3.8.0)
|
16
16
|
ast (~> 2.4.1)
|
17
17
|
racc
|
18
|
+
prism (1.4.0)
|
18
19
|
racc (1.8.1)
|
19
20
|
rainbow (3.1.1)
|
20
|
-
rake (13.
|
21
|
-
regexp_parser (2.
|
22
|
-
|
23
|
-
rubocop (1.65.1)
|
21
|
+
rake (13.3.0)
|
22
|
+
regexp_parser (2.10.0)
|
23
|
+
rubocop (1.75.8)
|
24
24
|
json (~> 2.3)
|
25
|
-
language_server-protocol (
|
25
|
+
language_server-protocol (~> 3.17.0.2)
|
26
|
+
lint_roller (~> 1.1.0)
|
26
27
|
parallel (~> 1.10)
|
27
28
|
parser (>= 3.3.0.2)
|
28
29
|
rainbow (>= 2.2.2, < 4.0)
|
29
|
-
regexp_parser (>= 2.
|
30
|
-
|
31
|
-
rubocop-ast (>= 1.31.1, < 2.0)
|
30
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
31
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
32
32
|
ruby-progressbar (~> 1.7)
|
33
|
-
unicode-display_width (>= 2.4.0, <
|
34
|
-
rubocop-ast (1.
|
35
|
-
parser (>= 3.3.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
rubocop (>= 1.
|
41
|
-
|
42
|
-
|
43
|
-
rubocop (
|
33
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
34
|
+
rubocop-ast (1.45.1)
|
35
|
+
parser (>= 3.3.7.2)
|
36
|
+
prism (~> 1.4)
|
37
|
+
rubocop-minitest (0.38.1)
|
38
|
+
lint_roller (~> 1.1)
|
39
|
+
rubocop (>= 1.75.0, < 2.0)
|
40
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
41
|
+
rubocop-performance (1.25.0)
|
42
|
+
lint_roller (~> 1.1)
|
43
|
+
rubocop (>= 1.75.0, < 2.0)
|
44
|
+
rubocop-ast (>= 1.38.0, < 2.0)
|
45
|
+
rubocop-rake (0.7.1)
|
46
|
+
lint_roller (~> 1.1)
|
47
|
+
rubocop (>= 1.72.1)
|
44
48
|
ruby-progressbar (1.13.0)
|
45
|
-
standard (1.
|
49
|
+
standard (1.50.0)
|
46
50
|
language_server-protocol (~> 3.17.0.2)
|
47
51
|
lint_roller (~> 1.0)
|
48
|
-
rubocop (~> 1.
|
52
|
+
rubocop (~> 1.75.5)
|
49
53
|
standard-custom (~> 1.0.0)
|
50
|
-
standard-performance (~> 1.
|
54
|
+
standard-performance (~> 1.8)
|
51
55
|
standard-custom (1.0.2)
|
52
56
|
lint_roller (~> 1.0)
|
53
57
|
rubocop (~> 1.50)
|
54
|
-
standard-performance (1.
|
58
|
+
standard-performance (1.8.0)
|
55
59
|
lint_roller (~> 1.1)
|
56
|
-
rubocop-performance (~> 1.
|
57
|
-
unicode-display_width (
|
60
|
+
rubocop-performance (~> 1.25.0)
|
61
|
+
unicode-display_width (3.1.4)
|
62
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
63
|
+
unicode-emoji (4.0.4)
|
58
64
|
|
59
65
|
PLATFORMS
|
66
|
+
arm64-darwin-24
|
60
67
|
x86_64-linux
|
61
68
|
|
62
69
|
DEPENDENCIES
|
data/README.md
CHANGED
@@ -204,6 +204,31 @@ ranked_items.each do |item|
|
|
204
204
|
end
|
205
205
|
```
|
206
206
|
|
207
|
+
### Penalizing Items by Number of Lists
|
208
|
+
You can optionally penalize items that appear on only a small number of lists. This is useful for de-emphasizing items that are not widely represented across your data. To use this feature, pass a `list_count_penalties` hash to the `RankingContext` constructor, where the keys are the number of lists and the values are the penalty percentages (as a float between 0 and 1).
|
209
|
+
|
210
|
+
For example, to penalize items that appear on only 1 list by 50%, and items on 2 lists by 25%:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
list_count_penalties = {1 => 0.50, 2 => 0.25}
|
214
|
+
ranking_context = WeightedListRank::RankingContext.new(
|
215
|
+
WeightedListRank::Strategies::Exponential.new,
|
216
|
+
list_count_penalties: list_count_penalties
|
217
|
+
)
|
218
|
+
|
219
|
+
ranked_items = ranking_context.rank([list1, list2, list3])
|
220
|
+
```
|
221
|
+
|
222
|
+
- Items that appear on only 1 list will have their total score multiplied by 0.5 (50% penalty).
|
223
|
+
- Items that appear on only 2 lists will have their total score multiplied by 0.75 (25% penalty).
|
224
|
+
- Items that appear on more lists will not be penalized unless specified in the hash.
|
225
|
+
|
226
|
+
#### How it works
|
227
|
+
- The penalty is applied to the total score of each item after all list scores are aggregated, but before sorting.
|
228
|
+
- If an item appears on a number of lists not present in the hash, no penalty is applied.
|
229
|
+
- This feature is fully backwards compatible: if you do not provide `list_count_penalties`, no penalties are applied.
|
230
|
+
- This penalty stacks with individual item penalties (e.g., `score_penalty` on an item).
|
231
|
+
|
207
232
|
## Development
|
208
233
|
|
209
234
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -3,12 +3,15 @@ module WeightedListRank
|
|
3
3
|
# It aggregates scores for each item across all lists, based on the provided strategy.
|
4
4
|
class RankingContext
|
5
5
|
# @strategy: The strategy used for calculating scores.
|
6
|
-
|
6
|
+
# @list_count_penalties: Hash mapping list counts to penalty percentages (e.g., {1 => 0.50, 2 => 0.25})
|
7
|
+
attr_reader :strategy, :list_count_penalties
|
7
8
|
|
8
|
-
# Initializes a new RankingContext with an optional ranking strategy.
|
9
|
+
# Initializes a new RankingContext with an optional ranking strategy and list count penalties.
|
9
10
|
# @param strategy [Strategy] the strategy to use for ranking items, defaults to Strategies::Exponential.
|
10
|
-
|
11
|
+
# @param list_count_penalties [Hash] hash mapping list counts to penalty percentages, defaults to empty hash.
|
12
|
+
def initialize(strategy = Strategies::Exponential.new, list_count_penalties: {})
|
11
13
|
@strategy = strategy
|
14
|
+
@list_count_penalties = list_count_penalties
|
12
15
|
end
|
13
16
|
|
14
17
|
# Ranks items across multiple lists according to the strategy's score calculation.
|
@@ -30,8 +33,8 @@ module WeightedListRank
|
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
# Convert hash to a
|
34
|
-
|
36
|
+
# Convert hash to a formatted array
|
37
|
+
formatted_items = items.map do |id, details|
|
35
38
|
{
|
36
39
|
id: id,
|
37
40
|
# Sort the score_details array by score in descending order before including it
|
@@ -40,8 +43,18 @@ module WeightedListRank
|
|
40
43
|
}
|
41
44
|
end
|
42
45
|
|
46
|
+
# Apply list count penalties if configured
|
47
|
+
if !list_count_penalties.empty?
|
48
|
+
formatted_items.each do |item|
|
49
|
+
list_count = item[:score_details].length
|
50
|
+
if (penalty = list_count_penalties[list_count])
|
51
|
+
item[:total_score] *= (1 - penalty)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
43
56
|
# Sort the array by total_score in descending order
|
44
|
-
|
57
|
+
formatted_items.sort_by { |item| -item[:total_score] }
|
45
58
|
end
|
46
59
|
end
|
47
60
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
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.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Sherman
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-06-29 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: rubocop
|
@@ -89,7 +88,6 @@ files:
|
|
89
88
|
- lib/weighted_list_rank/strategy.rb
|
90
89
|
- lib/weighted_list_rank/version.rb
|
91
90
|
- sig/weighted_list_rank.rbs
|
92
|
-
- weighted_list_rank.gemspec
|
93
91
|
homepage: https://github.com/ssherman/weighted_list_rank
|
94
92
|
licenses:
|
95
93
|
- MIT
|
@@ -97,7 +95,6 @@ metadata:
|
|
97
95
|
homepage_uri: https://github.com/ssherman/weighted_list_rank
|
98
96
|
source_code_uri: https://github.com/ssherman/weighted_list_rank
|
99
97
|
changelog_uri: https://github.com/ssherman/weighted_list_rank/CHANGELOG.md
|
100
|
-
post_install_message:
|
101
98
|
rdoc_options: []
|
102
99
|
require_paths:
|
103
100
|
- lib
|
@@ -112,8 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
109
|
- !ruby/object:Gem::Version
|
113
110
|
version: '0'
|
114
111
|
requirements: []
|
115
|
-
rubygems_version: 3.
|
116
|
-
signing_key:
|
112
|
+
rubygems_version: 3.6.2
|
117
113
|
specification_version: 4
|
118
114
|
summary: generate ranks of items from weighted lists
|
119
115
|
test_files: []
|
data/weighted_list_rank.gemspec
DELETED
@@ -1,42 +0,0 @@
|
|
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.35.1"
|
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
|