Skip to content

Instantly share code, notes, and snippets.

@onodes
Created August 22, 2009 15:36
Show Gist options
  • Save onodes/172819 to your computer and use it in GitHub Desktop.
Save onodes/172819 to your computer and use it in GitHub Desktop.
###########################################################################
# #Programming Collective Intelligence#
# PythonCode to RubyCode
###########################################################################
#人間の嗜好データ Python to Ruby
#irbなどからloadされることを前提としています.
#その際に$criticsとしてグローバル変数にしています.
$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, 'The Night Listener'=> 3.0,
'You, Me and Dupree'=> 3.5},
'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,
'The Night Listener'=> 4.5, 'Superman Returns'=> 4.0,
'You, Me and Dupree'=> 2.5},
'Mick LaSalle'=> {'Lady in the Water'=> 3.0, 'Snakes on a Plane'=> 4.0,
'Just My Luck'=> 2.0, 'Superman Returns'=> 3.0, 'The Night Listener'=> 3.0,
'You, Me and Dupree'=> 2.0},
'Jack Matthews'=> {'Lady in the Water'=> 3.0, 'Snakes on a Plane'=> 4.0,
'The Night Listener'=> 3.0, 'Superman Returns'=> 5.0, 'You, Me and Dupree'=> 3.5},
'Toby'=> {'Snakes on a Plane'=>4.5,'You, Me and Dupree'=>1.0,'Superman Returns'=>4.0}}
include Math
def sim_distance2(prefs,person1,person2)
si = prefs[person1].keys & prefs[person2].keys
return 0.0 if si.empty?
sum_of_squares = si.inject(0.0){|result,item|
result + (prefs[person1][item] - prefs[person2][item])**2}
return 1/(1+sum_of_squares)
end
def sim_distance(prefs,person1,person2)
sum = 0.0
si = {}
prefs[person1].keys.each{|item|
if prefs[person2].key?(item)
si[item] = 1
end
}
if si.length == 0
return 0.0
end
sum_of_squares = 0.0
prefs[person1].keys.each{|item|
if prefs[person2].key?(item) then
sum_of_squares += (prefs[person1][item] - prefs[person2][item]) ** 2
end
}
return 1/(1+sum_of_squares)
end
def sim_pearson(prefs,p1,p2)
si = prefs[p1].keys & prefs[p2].keys
return 0.0 if si.empty?
n = si.size
sum1 = si.inject(0.0){|result, item|
result + prefs[p1][item]
}
sum2 = si.inject(0.0){|result, item|
result + prefs[p2][item]
}
sum1Sq = si.inject(0.0){|result, item|
result + prefs[p1][item]**2
}
sum2Sq = si.inject(0.0){|result, item|
result + prefs[p2][item]**2
}
pSum = si.inject(0.0){|result, item|
result + prefs[p1][item] * prefs[p2][item]
}
num = pSum-(sum1*sum2/n)
p ((sum1Sq - (sum1**2)/n) * (sum2Sq - (sum2**2)/n))
den = sqrt((sum1Sq - (sum1**2)/n) * (sum2Sq - (sum2**2)/n))
return 0.0 if den == 0
r = num/den
return r
end
def topMatches(prefs,person,n=5)
scores = (prefs.keys - [person]).map{|other|
[sim_distance(prefs,person,other),other]
}
scores.sort!
scores.reverse!
return scores[0,n]
end
def getRecommendations(prefs,person)
totals = Hash.new(0.0)
simSums = Hash.new(0.0)
# for other,item in prefs
prefs.each{|other,item|
#自分とは比較しない
next if other == person
#ここで返ってくるのは数値
sim = sim_pearson(prefs,person,other)
#0以下のスコアは無視
next if sim <= 0.0
#for item,value in prefs[other]
prefs[other].each{|item,value|
if prefs[person].has_key?(item) == false || prefs[person][item] == 0.0
totals[item] += prefs[other][item]*sim
simSums[item] += sim
end
}
#p simSums
}
rankings = totals.keys.map{|item|
[totals[item]/simSums[item].to_f,item]
}
rankings.sort!
rankings.reverse!
return rankings
end
def transformPrefs(prefs)
result = {}
prefs.each{|person,item|
prefs[person].each{|item,value|
result[item] ||= {} #左辺が初期化されていないとき右辺が動く
#result[item][person] = prefs[person][item]
result[item][person] = value # prefs[person][item]とvalueは同値
}
}
return result
end
def calculateSimilarItems(prefs,n=10)
#アイテムをキーとして持ち、それぞれのアイテムに似ている
#アイテムのリストを値として持つディクショナリを作る
result = {}
#嗜好の行列をアイテム中心な形に反転させる
itemPrefs = transformPrefs(prefs)
c = 0
itemPrefs.each{|item,person|
#巨大なデータセット用にステータスを表示
c += 1
if c%100 == 0
print c.to_s + "/" + itemPrefs.length.to_s
end
#このアイテムにもっとも似ているアイテムたちを探す
scores = topMatches(itemPrefs,item,n=n)
result[item] = scores
}
return result
end
def getRecommendedItems(prefs,itemMatch,user)
userRatings=prefs[user]
scores = Hash.new(0.0)
totalSim = Hash.new(0.0)
#このユーザに評価されたアイテムをループする
userRatings.each{|item,rating|
#このアイテムに似ているアイテムたちをループする
itemMatch[item].each{|similarity,item2|
#このアイテムに対してユーザがすでに評価を行っていれば無視する
next if userRatings.key?(item2)
#評点と類似度を掛け合わせたものの合計で重み付けする
scores[item2] += similarity * rating
#すべての類似度の合計
totalSim[item2] += similarity
}
}
#正規化のため、それぞれの重みづけしたスコアを類似度の合計で割る
rankings = scores.map{|item,score|
[score/totalSim[item].to_f,item]
}
rankings.sort!
rankings.reverse!
return rankings
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment