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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5e3b387809912d1000f8f8ee13704cec1b04d691b783d6bd19a9f9b6e6cf0d6d
4
- data.tar.gz: caadd577bf061e63c1357943da439e06d6c1c5e441bb84ddbff250fb49a45020
3
+ metadata.gz: 34f4b87da4046f63fd0771a985c63fab24e30dc51445ce9ceabcce4bde5b3f45
4
+ data.tar.gz: d857a6cb9a7e9114d2157a05a373876f08c52661130cf50126fa6258b4beaa5b
5
5
  SHA512:
6
- metadata.gz: eb8c04911951b50c39b7f45583b1932274377106357fae605185bad10f8e37fb4e5c86047698e5e1752778311a5ea3403ce82fe494d44035ac889625b0703056
7
- data.tar.gz: 955ad9589e4418ca509065a7dc92b23af4c3c962ee8523ab89585bda58f04a9cedbedc11124523c69b385ba3f146d7db6856be6f86dc66143c251055b69bc545
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.2)
10
- json (2.7.2)
11
- language_server-protocol (3.17.0.3)
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.1)
14
- parallel (1.26.3)
15
- parser (3.3.5.0)
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.2.1)
21
- regexp_parser (2.9.2)
22
- rexml (3.3.8)
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 (>= 3.17.0)
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.4, < 3.0)
30
- rexml (>= 3.2.5, < 4.0)
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, < 3.0)
34
- rubocop-ast (1.32.3)
35
- parser (>= 3.3.1.0)
36
- rubocop-minitest (0.36.0)
37
- rubocop (>= 1.61, < 2.0)
38
- rubocop-ast (>= 1.31.1, < 2.0)
39
- rubocop-performance (1.21.1)
40
- rubocop (>= 1.48.1, < 2.0)
41
- rubocop-ast (>= 1.31.1, < 2.0)
42
- rubocop-rake (0.6.0)
43
- rubocop (~> 1.0)
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.40.1)
49
+ standard (1.50.0)
46
50
  language_server-protocol (~> 3.17.0.2)
47
51
  lint_roller (~> 1.0)
48
- rubocop (~> 1.65.0)
52
+ rubocop (~> 1.75.5)
49
53
  standard-custom (~> 1.0.0)
50
- standard-performance (~> 1.4)
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.4.0)
58
+ standard-performance (1.8.0)
55
59
  lint_roller (~> 1.1)
56
- rubocop-performance (~> 1.21.0)
57
- unicode-display_width (2.6.0)
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
- attr_reader :strategy
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
- def initialize(strategy = Strategies::Exponential.new)
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 sorted array
34
- sorted_items = items.map do |id, details|
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
- sorted_items.sort_by { |item| -item[:total_score] }
57
+ formatted_items.sort_by { |item| -item[:total_score] }
45
58
  end
46
59
  end
47
60
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WeightedListRank
4
- VERSION = "0.5.3"
4
+ VERSION = "0.6.0"
5
5
  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.5.3
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: 2024-10-06 00:00:00.000000000 Z
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.5.11
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: []
@@ -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