module Hiccup module Inferable class Scorer def initialize(options={}) @verbose = options.fetch(:verbose, false) end def pick_best_guess(guesses, dates) scored_guesses = guesses \ .map { |guess| [guess, score_guess(guess, dates)] } \ .sort_by { |(guess, score)| -score.to_f } if @verbose puts "\nGUESSES FOR #{dates}:" scored_guesses.each do |(guess, score)| puts " (%.3f p/%.3f b/%.3f c/%.3f) #{guess.humanize}" % [ score.to_f, score.prediction_rate, score.brick_penalty, score.complexity_penalty] end puts "" end scored_guesses.first end def score_guess(guess, input_dates) predicted_dates = guess.occurrences_between(guess.start_date, guess.end_date) # prediction_rate is the percent of input dates predicted predictions = (predicted_dates & input_dates).length prediction_rate = Float(predictions) / Float(input_dates.length) # bricks are dates predicted by this guess but not in the input bricks = (predicted_dates - input_dates).length # brick_rate is the percent of bricks to predictions # A brick_rate >= 1 means that this guess bricks more than it predicts brick_rate = Float(bricks) / Float(input_dates.length) # complexity measures how many rules are necesary # to describe the pattern complexity = complexity_of(guess) # complexity_rate is the number of rules per inputs complexity_rate = Float(complexity) / Float(input_dates.length) Score.new(prediction_rate, brick_rate, complexity_rate) end def complexity_of(schedule) return schedule.weekly_pattern.length if schedule.weekly? return schedule.monthly_pattern.length if schedule.monthly? 1 end end end end