Created
January 31, 2014 01:28
-
-
Save medhiwidjaja/8724990 to your computer and use it in GitHub Desktop.
AnalysisMethods::Magiq module.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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