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 +4 -4
- data/CHANGELOG.md +18 -18
- data/Gemfile.lock +11 -11
- data/README.md +39 -0
- data/lib/weighted_list_rank/strategies/exponential.rb +40 -38
- data/lib/weighted_list_rank/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3c2d9ea8239b54fa7d9423a36ae922c9de54a4bee54d8df0fdc17847b98eb60d
|
4
|
+
data.tar.gz: '03808fac0b2fd206b726734eeca8f3a62ad9b3aeab51f58237f22561775d4dbf'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
-
|
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
|
-
-
|
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-
|
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.
|
14
|
-
parallel (1.
|
15
|
-
parser (3.3.
|
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.
|
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.
|
22
|
+
rexml (3.3.5)
|
23
23
|
strscan
|
24
|
-
rubocop (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 (>=
|
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.
|
35
|
+
rubocop-ast (1.32.1)
|
36
36
|
parser (>= 3.3.1.0)
|
37
|
-
rubocop-minitest (0.35.
|
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.
|
46
|
+
standard (1.40.0)
|
47
47
|
language_server-protocol (~> 3.17.0.2)
|
48
48
|
lint_roller (~> 1.0)
|
49
|
-
rubocop (~> 1.
|
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
|
-
|
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
|
-
#
|
20
|
-
#
|
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
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
53
|
-
|
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
|
-
|
57
|
-
|
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
|
-
|
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
|
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.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-
|
11
|
+
date: 2024-08-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|