オライリー「集合知プログラミング」のピアソン相関によるスコア計算のサンプルプログラムをRubyに置き換えたもの
def sim_pearson( prefs, person1, person2 )
# 両者が互いに評価しているアイテムのリストを取得
si = prefs[person1].keys & prefs[person2].keys
# 要素の数を調べる
n = si.size
# 共に評価しているアイテムがなければ0を返す
return 0 if n == 0
# 配列をブロックで収集し、合計するメソッド
def map_sum( list, &block )
list.map{|i| yield(i) }.inject(0.0){|res,item| res += item }
end
# すべての嗜好を合計する
sum1 = map_sum( si ){|i| prefs[person1][i] }
sum2 = map_sum( si ){|i| prefs[person2][i] }
# 平方を合計する
sum1_sq = map_sum( si ){|i| prefs[person1][i] ** 2 }
sum2_sq = map_sum( si ){|i| prefs[person2][i] ** 2 }
# 積を合計する
p_sum = map_sum(si){|i| prefs[person1][i] * prefs[person2][i] }
# ピアソンによるスコアを計算する
num = p_sum - ( sum1 * sum2 / n)
den = Math.sqrt(( sum1_sq - sum1 ** 2 / n ) * ( sum2_sq - sum2 ** 2 / n ))
return 0 if den == 0
return num / den
end
puts sim_pearson( critics, 'Lisa Rose', 'Gene Seymour' )
# 両者が互いに評価しているアイテムのリストを取得
si = prefs[person1].keys & prefs[person2].keys
# 要素の数を調べる
n = si.size
# 共に評価しているアイテムがなければ0を返す
return 0 if n == 0
# 配列をブロックで収集し、合計するメソッド
def map_sum( list, &block )
list.map{|i| yield(i) }.inject(0.0){|res,item| res += item }
end
# すべての嗜好を合計する
sum1 = map_sum( si ){|i| prefs[person1][i] }
sum2 = map_sum( si ){|i| prefs[person2][i] }
# 平方を合計する
sum1_sq = map_sum( si ){|i| prefs[person1][i] ** 2 }
sum2_sq = map_sum( si ){|i| prefs[person2][i] ** 2 }
# 積を合計する
p_sum = map_sum(si){|i| prefs[person1][i] * prefs[person2][i] }
# ピアソンによるスコアを計算する
num = p_sum - ( sum1 * sum2 / n)
den = Math.sqrt(( sum1_sq - sum1 ** 2 / n ) * ( sum2_sq - sum2 ** 2 / n ))
return 0 if den == 0
return num / den
end
puts sim_pearson( critics, 'Lisa Rose', 'Gene Seymour' )
pythonでリスト内包を使っての合計は、Rubyで書くとmapとinjectを組み合わせたものになると思う。 これはPythonに比べ長くなってしまい、読みにくかった上に繰り返し出現するのでmap_sumというショートカット用のメソッドを用意した。
# 長ったらしい
sum1 = si.map{|i| prefs[person1][i] }.inject(0.0){|res,item| res += item }
sum1 = si.map{|i| prefs[person1][i] }.inject(0.0){|res,item| res += item }
# python版
sum1 = sum([prefs[p1][it] for it in si])
sum1 = sum([prefs[p1][it] for it in si])
Ruby版は式を評価する順番が、単純に左から右へ読めばいいのに対し、Python版では式の評価箇所が飛び飛びなのが、Pythonがわからない人間には辛いが、慣れればリスト内包の方が奇麗な気がしてくるから不思議だ。
両者の評価しているアイテムを収集する場合は、Hash#keys同士を&演算子で、集合の積を抽出するのがRubyらしいのかなと思う。
# 両者が互いに評価しているアイテムのリストを取得
si = prefs[person1].keys & prefs[person2].keys
si = prefs[person1].keys & prefs[person2].keys
関連記事