# from the book 'Programming Collective Intelligence' Toby Segran # module Recommendations Critics = { 'Lisa Rose' => { 'Lady in the Water' => 2.5, 'Snakes on a Plane' => 3.5, 'Just My Luck' => 3.0, 'Superman Returns' => 3.5, 'You, Me and Dupree' => 2.5, 'The Night Listener' => 3.0}, 'Gene Seymour' => { 'Lady in the Water' => 3.0, 'Snakes on a Plane' => 3.5, 'Just My Luck' => 1.5, 'Superman Returns' => 5.0, 'You, Me and Dupree' => 3.5, 'The Night Listener' => 3.0}, 'Michael Phillips' => { 'Lady in the Water' => 2.5, 'Snakes on a Plane' => 3.0, 'Superman Returns' => 3.5, 'The Night Listener' => 4.0}, 'Claudia Puig' => { 'Snakes on a Plane' => 3.5, 'Just My Luck' => 3.0, 'Superman Returns' => 4.0, 'You, Me and Dupree' => 2.5, 'The Night Listener' => 4.5}, 'Mick LaSalle' => { 'Lady in the Water' => 3.0, 'Snakes on a Plane' => 4.0, 'Just My Luck' => 2.0, 'Superman Returns' => 3.0, 'You, Me and Dupree' => 2.0, 'The Night Listener' => 3.0}, 'Jack Mattews' => { 'Lady in the Water' => 3.0, 'Snakes on a Plane' => 4.0, 'Superman Returns' => 5.0, 'You, Me and Dupree' => 3.5, 'The Night Listener' => 3.0}, 'Toby' => { 'Snakes on a Plane' => 4.5, 'Superman Returns' => 4.0, 'You, Me and Dupree' => 1.0} } def self.sim_distance(prefs, person1, person2) si = prefs[person1].keys.inject([]) do |r, item| if prefs[person2].key?(item) r << item else r end end return 0 if si.empty? sum_of_squares = si.inject(0) do |r, item| r + (prefs[person1][item] - prefs[person2][item])**2 end 1 / (1 + sum_of_squares) end def self.sim_pearson(prefs, p1, p2) si = prefs[p1].keys.inject([]) do |r, item| if prefs[p2].key?(item) r << item else r end end return 0 if si.empty? sum1 = prefs[p1].values_at(*si).inject(0) {|r, v| r + v } sum2 = prefs[p2].values_at(*si).inject(0) {|r, v| r + v } sum1sq = prefs[p1].values_at(*si).inject(0) {|r, v| r + v**2 } sum2sq = prefs[p2].values_at(*si).inject(0) {|r, v| r + v**2 } psum = prefs[p1].values_at(*si).zip(prefs[p2].values_at(*si)).inject(0) do |r, v| r + v[0] * v[1] end num = psum - (sum1 * sum2 / si.size) den = Math.sqrt((sum1sq - sum1**2 / si.size) * (sum2sq - sum2**2 / si.size)) return 0 if den == 0 num / den end def self.top_matches(prefs, person, n = 5, similarity = method(:sim_pearson)) scores = prefs.keys.inject([]) do |r, other| if person == other r else r << [similarity.call(prefs, person, other), other] end end scores.sort! scores.reverse! scores[0...n] end def self.get_recommendations(prefs, person, similarity = method(:sim_pearson)) totals = {} totals.default = 0.0 simsums = {} simsums.default = 0.0 prefs.keys.each do |other| next if other == person sim = similarity.call(prefs, person, other) next if sim <= 0 prefs[other].each_key do |item| if !prefs[person].has_key?(item) || prefs[person][item] == 0 totals[item] += prefs[other][item] * sim simsums[item] += sim end end end rankings = totals.map do |item, total| [total / simsums[item], item] end rankings.sort! rankings.reverse end def self.transform_prefs(prefs) result = Hash.new {|h, k| h[k] = {}} prefs.each_key do |person| prefs[person].each_key do |item| result[item][person] = prefs[person][item] end end result end end