Class: WeightedListRank::Strategies::Exponential

Inherits:
WeightedListRank::Strategy show all
Defined in:
lib/weighted_list_rank/strategies/exponential.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(exponent: 1.5, bonus_pool_percentage: 1.0, average_list_length: nil, include_unranked_items: false) ⇒ Exponential

Initializes the Exponential strategy with optional parameters for exponent, bonus pool percentage, average list length, and whether to include unranked items in the bonus pool.

defaults to 1.0 (100%). defaults to nil. defaults to false for backward compatibility.

Parameters:

  • exponent (Float) (defaults to: 1.5)

    the exponent to use in the score calculation formula, defaults to 1.5.

  • bonus_pool_percentage (Float) (defaults to: 1.0)

    the percentage of the list’s weight to be used as the bonus pool,

  • average_list_length (Float, NilClass) (defaults to: nil)

    the average length of lists in the system, either as a mean or median,

  • include_unranked_items (Boolean) (defaults to: false)

    whether to include unranked items in the bonus pool calculation,



16
17
18
19
20
21
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 16

def initialize(exponent: 1.5, bonus_pool_percentage: 1.0, average_list_length: nil, include_unranked_items: false)
  @exponent = exponent
  @bonus_pool_percentage = bonus_pool_percentage
  @average_list_length = average_list_length
  @include_unranked_items = include_unranked_items
end

Instance Attribute Details

#average_list_lengthObject (readonly)

Returns the value of attribute average_list_length.



4
5
6
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 4

def average_list_length
  @average_list_length
end

#bonus_pool_percentageObject (readonly)

Returns the value of attribute bonus_pool_percentage.



4
5
6
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 4

def bonus_pool_percentage
  @bonus_pool_percentage
end

#exponentObject (readonly)

Returns the value of attribute exponent.



4
5
6
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 4

def exponent
  @exponent
end

#include_unranked_itemsObject (readonly)

Returns the value of attribute include_unranked_items.



4
5
6
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 4

def include_unranked_items
  @include_unranked_items
end

Instance Method Details

#calculate_score(list, item) ⇒ Float

Calculates the score of an item within a list based on its rank position, the total number of items, and the list’s weight, using an exponential formula. The bonus pool for score adjustments is determined by the specified bonus pool percentage of the list’s total weight, adjusted by the average list length.

If include_unranked_items is true, unranked items will also receive a portion of the bonus pool. Ranked items will receive an exponential bonus, while unranked items will split the remaining bonus pool evenly.

and the bonus pool percentage.

Parameters:

Returns:

  • (Float)

    the calculated score for the item, adjusted by the list’s weight, the specified exponent,



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/weighted_list_rank/strategies/exponential.rb', line 35

def calculate_score(list, item)
  # Default score to the list's weight
  score = list.weight

  # Total number of items in the list
  total_items = list.items.count

  # Separate ranked and unranked items
  ranked_items = list.items.select { |i| i.position }
  num_ranked_items = ranked_items.count

  if num_ranked_items > 0 && item.position.nil?
    # If there are ranked items, unranked items get no bonus, only the list's weight
    score = list.weight
  else
    # Calculate the total bonus pool
    total_bonus_pool = list.weight * bonus_pool_percentage

    # Adjust the bonus pool based on the average list length
    adjusted_bonus_pool = if average_list_length && average_list_length > 0
      total_bonus_pool * (total_items / average_list_length.to_f)
    else
      total_bonus_pool
    end

    if item.position.nil?
      # Unranked items get no bonus if there are ranked items
      if include_unranked_items && num_ranked_items == 0
        unranked_bonus = adjusted_bonus_pool / total_items
        score += unranked_bonus
      end
    else
      # Check if the item's position is higher than the total number of items
      if item.position > total_items
        puts "Warning: Item position (#{item.position}) is higher than the total number of items (#{total_items}) in the list. Using total items as position."
        item_position = total_items
      else
        item_position = item.position
      end

      # Ranked items receive a bonus calculated using the exponential formula
      exponential_factor = (total_items + 1 - item_position)**exponent
      total_exponential_factor = (1..total_items).sum { |pos| (total_items + 1 - pos)**exponent }

      # Allocate a portion of the adjusted bonus pool based on the item's exponential factor
      item_bonus = (exponential_factor / total_exponential_factor) * adjusted_bonus_pool
      score += item_bonus
    end
  end

  # Apply score penalty if it exists, for both ranked and unranked items
  score = apply_penalty(score, item.score_penalty)

  # Ensure the score is not less than 1
  [score, 1].max
end