weighted_list_rank 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5d85e4ccc5456ad1bce7763d39d34d4dc14f831806b9c9bcee94f0c4d926898
4
- data.tar.gz: dcf0013d694ce996c21335ed6d56d29f7dad96c03376e9a49ede00ae634c980b
3
+ metadata.gz: 3c2d9ea8239b54fa7d9423a36ae922c9de54a4bee54d8df0fdc17847b98eb60d
4
+ data.tar.gz: '03808fac0b2fd206b726734eeca8f3a62ad9b3aeab51f58237f22561775d4dbf'
5
5
  SHA512:
6
- metadata.gz: 6983978ad5b4b876e6e47100ec1134aa937606d5ba0a7bafcfd235c7a737b1f84063f7ee3b8bb19f1b56521cd7e6d53196fd15ef64c2f040b90ac8facf50354e
7
- data.tar.gz: bc4fe3145c9f563c69fc13a718c65ba0696473a134b23fa46b960ffd0952c9a1ad775502e402f798661f758d9b63a745a3fa3dd953402e34e7ae854782da6d64
6
+ metadata.gz: 590693e2ea27c84cf861ba36dacfc50f97374f5b12c47b1b6f72693f6652a92d875c5d9a56090809ffeefc2b91e17e76e2288586d3bbd01baa408c3cbbcb91a4
7
+ data.tar.gz: 462ed842d02c3a5aa70103b2e9d7dfa40e496eb7f3bc2cd9946a518319e3b72610bfed43007d4b80a71c4927d5d39867e638e4325dace2c6e9bf5f6d680bb2d9
data/CHANGELOG.md CHANGED
@@ -1,39 +1,39 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2024-07-23
4
+ - Added `average_list_length` feature to the `Exponential` strategy to adjust the bonus pool based on the average number of items across all lists. This helps prevent smaller lists from receiving disproportionately large bonuses.
5
+ - Introduced a new configuration option `include_unranked_items` in the `Exponential` strategy. When enabled, unranked items will receive an equal share of the bonus pool, while ranked items will receive an exponential bonus.
6
+ - Updated RDoc documentation to explain the new `average_list_length` and `include_unranked_items` options.
7
+ - Fixed test cases to correctly calculate expected values when using `average_list_length`.
8
+ - Updated all dependencies to their latest versions.
9
+
3
10
  ## [0.4.2] - 2024-07-01
4
- - fixed bug where a score could be set to 0. the lowest a score can be now is 1
11
+ - Fixed bug where a score could be set to 0. The lowest a score can be now is 1.
5
12
 
6
13
  ## [0.4.1] - 2024-06-30
7
- - add score_penalty to the list details hash
14
+ - Added `score_penalty` to the list details hash.
8
15
 
9
16
  ## [0.4.0] - 2024-06-26
10
- - Added score_penalty feature to allow percentage-based penalties on item scores.
17
+ - Added `score_penalty` feature to allow percentage-based penalties on item scores.
11
18
  - Updated all dependencies to their latest versions.
12
19
 
13
- ## [0.3.1] - 2024-05-026
14
-
15
- - Fixed issue with lists with only a single item getting bonus points
20
+ ## [0.3.1] - 2024-05-26
21
+ - Fixed issue with lists with only a single item getting bonus points.
16
22
 
17
23
  ## [0.3.0] - 2024-02-03
18
-
19
- - added bonus_pool_percentage feature to exponential
24
+ - Added `bonus_pool_percentage` feature to `Exponential` strategy.
20
25
 
21
26
  ## [0.2.0] - 2024-02-03
22
-
23
- - refactored the exponential algorithm to only add bonus points out of a pool of 50% of a list's weight
27
+ - Refactored the `Exponential` algorithm to only add bonus points out of a pool of 50% of a list's weight.
24
28
 
25
29
  ## [0.1.3] - 2024-02-03
26
-
27
- - sort the score_details by the highest score from each list first
30
+ - Sorted the score details by the highest score from each list first.
28
31
 
29
32
  ## [0.1.2] - 2024-02-03
33
+ - Added list weight to rank result.
30
34
 
31
- - Added list weight to rank result
32
- -
33
35
  ## [0.1.1] - 2024-02-03
34
-
35
- - Fixed Exponential strategy to just return the list weight if the item has a nil position
36
+ - Fixed `Exponential` strategy to just return the list weight if the item has a nil position.
36
37
 
37
38
  ## [0.1.0] - 2024-02-01
38
-
39
- - Initial release
39
+ - Initial release.
data/Gemfile.lock CHANGED
@@ -10,31 +10,31 @@ 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.24.0)
14
- parallel (1.25.1)
15
- parser (3.3.3.0)
13
+ minitest (5.25.1)
14
+ parallel (1.26.3)
15
+ parser (3.3.4.2)
16
16
  ast (~> 2.4.1)
17
17
  racc
18
- racc (1.8.0)
18
+ racc (1.8.1)
19
19
  rainbow (3.1.1)
20
20
  rake (13.2.1)
21
21
  regexp_parser (2.9.2)
22
- rexml (3.3.1)
22
+ rexml (3.3.5)
23
23
  strscan
24
- rubocop (1.64.1)
24
+ rubocop (1.65.1)
25
25
  json (~> 2.3)
26
26
  language_server-protocol (>= 3.17.0)
27
27
  parallel (~> 1.10)
28
28
  parser (>= 3.3.0.2)
29
29
  rainbow (>= 2.2.2, < 4.0)
30
- regexp_parser (>= 1.8, < 3.0)
30
+ regexp_parser (>= 2.4, < 3.0)
31
31
  rexml (>= 3.2.5, < 4.0)
32
32
  rubocop-ast (>= 1.31.1, < 2.0)
33
33
  ruby-progressbar (~> 1.7)
34
34
  unicode-display_width (>= 2.4.0, < 3.0)
35
- rubocop-ast (1.31.3)
35
+ rubocop-ast (1.32.1)
36
36
  parser (>= 3.3.1.0)
37
- rubocop-minitest (0.35.0)
37
+ rubocop-minitest (0.35.1)
38
38
  rubocop (>= 1.61, < 2.0)
39
39
  rubocop-ast (>= 1.31.1, < 2.0)
40
40
  rubocop-performance (1.21.1)
@@ -43,10 +43,10 @@ GEM
43
43
  rubocop-rake (0.6.0)
44
44
  rubocop (~> 1.0)
45
45
  ruby-progressbar (1.13.0)
46
- standard (1.39.0)
46
+ standard (1.40.0)
47
47
  language_server-protocol (~> 3.17.0.2)
48
48
  lint_roller (~> 1.0)
49
- rubocop (~> 1.64.0)
49
+ rubocop (~> 1.65.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,45 @@ custom_ranked_items.each do |item|
92
92
  end
93
93
  ```
94
94
 
95
+ ### New Features: average_list_length and include_unranked_items
96
+ #### average_list_length
97
+ The average_list_length setting adjusts the bonus pool based on the typical size of lists across your system. This value can be calculated as the mean or median number of items across all lists. It helps prevent smaller lists from receiving disproportionately large bonuses.
98
+
99
+ ```ruby
100
+ # Customizing with average_list_length
101
+ strategy_with_avg_length = WeightedListRank::Strategies::Exponential.new(
102
+ exponent: 1.5,
103
+ average_list_length: 50 # Use mean or median list length
104
+ )
105
+
106
+ ranking_context_with_avg = WeightedListRank::RankingContext.new(strategy_with_avg_length)
107
+ ranked_items = ranking_context_with_avg.rank([list])
108
+
109
+ # Output the results
110
+ ranked_items.each do |item|
111
+ puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
112
+ end
113
+ ```
114
+
115
+ #### include_unranked_items
116
+ The include_unranked_items setting allows you to distribute a portion of the bonus pool to unranked items. Ranked items receive an exponential bonus, while unranked items split the remaining bonus pool evenly. This feature ensures that unranked lists are still valuable and fairly scored.
117
+
118
+ ```ruby
119
+ # Including unranked items in the bonus pool
120
+ strategy_with_unranked = WeightedListRank::Strategies::Exponential.new(
121
+ exponent: 1.5,
122
+ include_unranked_items: true
123
+ )
124
+
125
+ ranking_context_with_unranked = WeightedListRank::RankingContext.new(strategy_with_unranked)
126
+ ranked_items = ranking_context_with_unranked.rank([list])
127
+
128
+ # Output the results
129
+ ranked_items.each do |item|
130
+ puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
131
+ end
132
+ ```
133
+
95
134
  ### Using Item Penalties
96
135
  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
136
 
@@ -1,40 +1,31 @@
1
1
  module WeightedListRank
2
2
  module Strategies
3
- # The Exponential strategy calculates the score of an item within a list using an exponential formula.
4
- # This strategy emphasizes the significance of an item's rank within the list, where items with higher
5
- # ranks (closer to 1) are exponentially more valuable than those with lower ranks. The magnitude of the bonus
6
- # applied to each item's score is determined by the bonus pool percentage of the list's total weight.
7
- #
8
- # The exponential nature of the calculation is controlled by the +exponent+ attribute, allowing for
9
- # flexible adjustment of how steeply the score decreases as rank increases. The +bonus_pool_percentage+
10
- # attribute determines the size of the bonus pool as a percentage of the list's total weight, allowing
11
- # customization of the bonus impact on the final scores.
12
3
  class Exponential < WeightedListRank::Strategy
13
- # The exponent used in the score calculation formula. Higher values increase the rate at which
14
- # scores decrease as item rank increases.
15
- #
16
- # @return [Float] the exponent value
17
- attr_reader :exponent
4
+ attr_reader :exponent, :bonus_pool_percentage, :average_list_length, :include_unranked_items
18
5
 
19
- # The percentage of the list's total weight that constitutes the bonus pool. This value determines
20
- # how the total bonus pool is calculated as a percentage of the list's weight.
6
+ # Initializes the Exponential strategy with optional parameters for exponent,
7
+ # bonus pool percentage, average list length, and whether to include unranked items in the bonus pool.
21
8
  #
22
- # @return [Float] the bonus pool percentage, defaulting to 1.0 (100% of the list's weight).
23
- attr_reader :bonus_pool_percentage
24
-
25
- # Initializes a new instance of the Exponential strategy with optional parameters for exponent and
26
- # bonus pool percentage.
27
9
  # @param exponent [Float] the exponent to use in the score calculation formula, defaults to 1.5.
28
10
  # @param bonus_pool_percentage [Float] the percentage of the list's weight to be used as the bonus pool,
29
11
  # defaults to 1.0 (100%).
30
- def initialize(exponent: 1.5, bonus_pool_percentage: 1.0)
12
+ # @param average_list_length [Float, NilClass] the average length of lists in the system, either as a mean or median,
13
+ # defaults to nil.
14
+ # @param include_unranked_items [Boolean] whether to include unranked items in the bonus pool calculation,
15
+ # defaults to false for backward compatibility.
16
+ def initialize(exponent: 1.5, bonus_pool_percentage: 1.0, average_list_length: nil, include_unranked_items: false)
31
17
  @exponent = exponent
32
18
  @bonus_pool_percentage = bonus_pool_percentage
19
+ @average_list_length = average_list_length
20
+ @include_unranked_items = include_unranked_items
33
21
  end
34
22
 
35
23
  # Calculates the score of an item within a list based on its rank position, the total number of items,
36
24
  # and the list's weight, using an exponential formula. The bonus pool for score adjustments is determined
37
- # by the specified bonus pool percentage of the list's total weight.
25
+ # by the specified bonus pool percentage of the list's total weight, adjusted by the average list length.
26
+ #
27
+ # If +include_unranked_items+ is true, unranked items will also receive a portion of the bonus pool.
28
+ # Ranked items will receive an exponential bonus, while unranked items will split the remaining bonus pool evenly.
38
29
  #
39
30
  # @param list [WeightedListRank::List] the list containing the item being scored.
40
31
  # @param item [WeightedListRank::Item] the item for which to calculate the score.
@@ -45,18 +36,35 @@ module WeightedListRank
45
36
  # Default score to the list's weight
46
37
  score = list.weight
47
38
 
48
- unless item.position.nil? || list.items.count == 1
49
- num_items = list.items.count
50
- total_bonus_pool = list.weight * bonus_pool_percentage
39
+ # Determine the number of ranked and unranked items
40
+ ranked_items = list.items.select { |i| i.position }
41
+ unranked_items = list.items.reject { |i| i.position }
42
+ num_ranked_items = ranked_items.count
43
+ num_unranked_items = unranked_items.count
51
44
 
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 }
45
+ # Calculate the total bonus pool
46
+ total_bonus_pool = list.weight * bonus_pool_percentage
55
47
 
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
48
+ # Adjust the bonus pool based on the average list length
49
+ adjusted_bonus_pool = if average_list_length && average_list_length > 0
50
+ total_bonus_pool * (list.items.count / average_list_length.to_f)
51
+ else
52
+ total_bonus_pool
53
+ end
58
54
 
59
- # Add the item's allocated bonus to the default score
55
+ if item.position.nil?
56
+ # If the item is unranked, give it an equal share of the remaining bonus pool
57
+ if include_unranked_items && num_unranked_items > 0
58
+ unranked_bonus = adjusted_bonus_pool / list.items.count
59
+ score += unranked_bonus
60
+ end
61
+ else
62
+ # If the item is ranked, calculate its bonus using the exponential formula
63
+ exponential_factor = (num_ranked_items + 1 - item.position)**exponent
64
+ total_exponential_factor = (1..num_ranked_items).sum { |pos| (num_ranked_items + 1 - pos)**exponent }
65
+
66
+ # Allocate a portion of the adjusted bonus pool based on the item's exponential factor
67
+ item_bonus = (exponential_factor / total_exponential_factor) * adjusted_bonus_pool
60
68
  score += item_bonus
61
69
  end
62
70
 
@@ -69,12 +77,6 @@ module WeightedListRank
69
77
 
70
78
  private
71
79
 
72
- # Applies the score penalty if it exists
73
- #
74
- # @param score [Float] the original score of the item
75
- # @param penalty [Float, NilClass] the score penalty of the item as a percentage (e.g., 0.20 for 20%) or nil if no penalty
76
- #
77
- # @return [Float] the score after applying the penalty
78
80
  def apply_penalty(score, penalty)
79
81
  penalty ? score * (1 - penalty) : score
80
82
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module WeightedListRank
4
- VERSION = "0.4.2"
4
+ VERSION = "0.5.0"
5
5
  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.2
4
+ version: 0.5.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-07-02 00:00:00.000000000 Z
11
+ date: 2024-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop