WeightedListRank

The WeightedListRank gem is a robust, flexible system designed for ranking items within weighted lists. It leverages scoring algorithms to evaluate and prioritize items based on defined criteria, making it an essential tool for data analysis, recommendation systems, and more. The only algorithm currently is the Exponential strategy, an algorithm that forms the backbone of The Greatest Books (thegreatestbooks.org).

WeightedListRank is built with versatility in mind, accommodating different ranking needs and scenarios. By implementing a strategy pattern, it offers the flexibility to apply various algorithms for item scoring and ranking within lists, each considering the list's weight and item positions. The default Exponential strategy underscores the significance of an item's rank in the list, applying an exponential formula to determine its score, thus ensuring that higher-ranked items receive proportionally greater emphasis.

The system is designed for easy extension, allowing developers to introduce new strategies that cater to specific requirements or experimental approaches.

Installation

Install the gem and add to the application's Gemfile by executing:

$ bundle add weighted_list_rank

If bundler is not being used to manage dependencies, install the gem by executing:

$ gem install weighted_list_rank

Usage

The WeightedListRank system allows for flexible ranking of items within weighted lists using various strategies. Below is how to employ the Exponential strategy for ranking, using text identifiers for better readability.

Defining Items and Lists You need to define your items and lists to include the required methods from WeightedListRank::Item and WeightedListRank::List interfaces:

class MyItem
  include WeightedListRank::Item
  attr_reader :id, :position

  def initialize(id, position)
    @id = id
    @position = position
  end
end

class MyList
  include WeightedListRank::List
  attr_reader :id, :weight, :items

  def initialize(id, weight, items)
    @id = id
    @weight = weight
    @items = items
  end
end

Using the Exponential Strategy

After setting up your items and list, you can apply the Exponential strategy to rank the items. The strategy allows for customization of the exponential rate at which scores decrease according to rank.

require 'weighted_list_rank'

# Initialize items and list with text identifiers
items = [
  MyItem.new("Item 1", 1),
  MyItem.new("Item 2", 2),
  MyItem.new("Item 3", 3)
]
list = MyList.new("List 1", 10, items)

# Initialize the Exponential strategy with an optional exponent
exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 1.5)

# Create a RankingContext using the Exponential strategy
ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)

# Rank the items
ranked_items = ranking_context.rank([list])

# Display the ranked items
ranked_items.each do |item|
  puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
end

Customization Example

Adjust the exponent parameter to change how significantly item rank affects the score. A higher exponent increases the disparity between high and low-ranked items.

# Customizing the exponent for greater emphasis on higher ranks
custom_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 2.0)

# Ranking with the customized strategy
custom_ranking_context = WeightedListRank::RankingContext.new(custom_strategy)
custom_ranked_items = custom_ranking_context.rank([list])

# Output the results
custom_ranked_items.each do |item|
  puts "Item: #{item[:id]}, Custom Total Score: #{item[:total_score]}"
end

New Features: average_list_length and include_unranked_items

average_list_length

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.

# Customizing with average_list_length
strategy_with_avg_length = WeightedListRank::Strategies::Exponential.new(
  exponent: 1.5,
  average_list_length: 50  # Use mean or median list length
)

ranking_context_with_avg = WeightedListRank::RankingContext.new(strategy_with_avg_length)
ranked_items = ranking_context_with_avg.rank([list])

# Output the results
ranked_items.each do |item|
  puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
end

include_unranked_items

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.

# Including unranked items in the bonus pool
strategy_with_unranked = WeightedListRank::Strategies::Exponential.new(
  exponent: 1.5,
  include_unranked_items: true
)

ranking_context_with_unranked = WeightedListRank::RankingContext.new(strategy_with_unranked)
ranked_items = ranking_context_with_unranked.rank([list])

# Output the results
ranked_items.each do |item|
  puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
end

Using Item Penalties

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.

To use the penalty feature, include the score_penalty attribute when defining your items:

class MyItem
  include WeightedListRank::Item
  attr_reader :id, :position, :score_penalty

  def initialize(id, position, score_penalty = nil)
    @id = id
    @position = position
    @score_penalty = score_penalty
  end
end

You can then apply the penalties when calculating the scores:

require 'weighted_list_rank'

# Initialize items and list with text identifiers and penalties
items = [
  MyItem.new("Item 1", 1, 0.20), # 20% penalty
  MyItem.new("Item 2", 2, 0.10), # 10% penalty
  MyItem.new("Item 3", 3, nil)   # No penalty
]
list = MyList.new("List 1", 10, items)

# Initialize the Exponential strategy with an optional exponent
exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 1.5)

# Create a RankingContext using the Exponential strategy
ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)

# Rank the items
ranked_items = ranking_context.rank([list])

# Display the ranked items
ranked_items.each do |item|
  puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
end

Customizing Penalties Example

You can customize the penalty values to see how they affect the final scores of the items.

# Customizing penalties for different items
items = [
  MyItem.new("Item 1", 1, 0.30), # 30% penalty
  MyItem.new("Item 2", 2, 0.15), # 15% penalty
  MyItem.new("Item 3", 3, nil)   # No penalty
]
list = MyList.new("List 1", 10, items)

# Initialize the Exponential strategy with an optional exponent
exponential_strategy = WeightedListRank::Strategies::Exponential.new(exponent: 2.0)

# Create a RankingContext using the Exponential strategy
ranking_context = WeightedListRank::RankingContext.new(exponential_strategy)

# Rank the items
ranked_items = ranking_context.rank([list])

# Output the results
ranked_items.each do |item|
  puts "Item: #{item[:id]}, Total Score: #{item[:total_score]}"
end

Development

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.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and the created tag, and push the .gem file to rubygems.org.

Contributing

We're on the lookout for more algorithm strategies and implementations! If you're passionate about data analysis, algorithm design, or simply have ideas for improving item ranking methodologies, WeightedListRank offers a welcoming platform for your contributions. Whether it's enhancing existing strategies, proposing new ones, or optimizing the framework for broader applications, your input can significantly impact the community and the projects relying on this system.

Bug reports and pull requests are welcome on GitHub at https://github.com/ssherman/weighted_list_rank. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the code of conduct.

License

The gem is available as open source under the terms of the MIT License.

Code of Conduct

Everyone interacting in the WeightedListRank project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.