Skip to content

Instantly share code, notes, and snippets.

@medhiwidjaja
Created January 31, 2014 01:28
Show Gist options
  • Save medhiwidjaja/8724990 to your computer and use it in GitHub Desktop.
Save medhiwidjaja/8724990 to your computer and use it in GitHub Desktop.
AnalysisMethods::Magiq module.
# Copyright (c) 2012 Medhi Widjaja
# Magiq Module contains the classes for modeling and calculating rank scores using MAGIQ method
#
# For background theory of Multi-Attribute Global Inference of Quality (MAGIQ), see the article by
# Dr. James McCaffrey
# http://msdn.microsoft.com/en-us/magazine/cc300812.aspx
#
# MAGIQ method uses Rank Order Centroid to determine rank scores, this module implements 3 more
# methods to calculate to rank scores: Rank Sum, Rank Reciprocal, and Rank Exponential
# require 'analysis_methods/ordinal_comparison.rb'
module AnalysisMethods
module Magiq
extend ActiveSupport::Concern
include RankComparable
included do
embeds_many :rank_comparisons, class_name: 'AnalysisMethods::Magiq::RankComparable::OrdinalComparison', as: :ordinal_comparable
accepts_nested_attributes_for :rank_comparisons, allow_destroy: false
field :rank_method, type: Integer
field :rank_notes, type: String
end
module ClassMethods
# Rank Order Centroid
# k = total number of criteria
# i = rank of the i-th criterion
def rank_order_centroid(k, i)
(i..k).map { |j| 1.0/j }.sum / k
end
def rank_order_centroid_table(k)
(1..k).map { |j| rank_order_centroid(k, j) }
end
# Rank Sum
# k = total number of criteria
# i = rank of the i-th criterion
def rank_sum(k, i)
(k - i + 1.0) / (1..k).map { |j| k - j + 1.0 }.sum
end
def rank_sum_table(k)
(1..k).map { |j| rank_sum(k, j) }
end
# Rank Reciprocal
# k = total number of criteria
# i = rank of the i-th criterion
def rank_reciprocal(k, i)
(1.0/i) / (1..k).map { |j| 1.0/j }.sum
end
def rank_reciprocal_table(k)
(1..k).map { |j| rank_reciprocal(k, j) }
end
# Rank Exponential
# k = total number of criteria
# i = rank of the i-th criterion
# d = dispersion (0.0 .. 1.0)
def rank_exponential(k, i, d=0.2)
(k-i+1)**d / (1..k).map { |j| (k - j + 1.0)**d }.sum
end
def rank_exponential_table(k, d=0.2)
(1..k).map { |j| rank_exponential(k, j, d) }
end
end
# Returns an array of hash tables
# table = a_judgment.comparison_weights_by_magiq
# table.each {|row| puts "#{row[:rank]}. #{Criterion.find(row[:criterion_id]).title}\t #{row[:weight]}" }
def comparison_weights_by_magiq
k = rank_comparisons.size
self.rank_comparisons.asc(:rank).collect {|c| Hash[ rank:c.rank, criterion_id:c.id1, weight:self.rank_order_centroid(k,c.rank)] }
end
# Returns score computed with the specified method;
# Default method is rank order centroid. Other accepted methods are
# :rank_sum, :rank_reciprocal, :rank_exponential, :simple_rank
def update_rank_comparisons(method=:rank_order_centroid)
k = self.rank_comparisons.size
score_table = self.class.send(method.to_s+'_table', k)
j = 1 # to keep track of the number of items
r = 1 # to keep track of the number of rank slots
begin
n = self.rank_comparisons.where(rank:r).count
unless n==0
score = score_table.slice(j-1, n).sum / n
self.rank_comparisons.where(rank:r).each do |rc|
rc.update_attribute :score, score
end
j += n
else
j += 1
end
r += 1
end while j <= k
end
def update_rank_scores
scorables = self.scorables
self.rank_comparisons.each do |rank_comparison|
weight = rank_comparison.score
weight_n = weight
rank = rank_comparison.rank
scorable = scorables.find(rank_comparison.id1)
self.save_score weight: weight,
weight_n: weight_n,
rank_order: rank,
scorable: scorable,
with_respect_to: evaluable,
eval_method: AnalysisMethods::Base::EVALUATION_METHODS['MAGIQ']
end
end
def ranks_are_valid?
ranks = self.rank_nums
ranks == (1..ranks.size).to_a
end
def rank_nums
rank_comparisons.collect(&:rank).uniq.sort
end
def rank_hash
rank_comparisons.sort_by(&:rank).group_by(&:rank)
end
def rank_pairs
rank_hash.to_a.combination(2).to_a
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment