10 月 16 2008

[Ruby] 集合知プログラミング 02

Published by haga at 1:15 under ruby

オライリー「集合知プログラミング」のピアソン相関によるスコア計算のサンプルプログラムを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' )


pythonでリスト内包を使っての合計は、Rubyで書くとmapとinjectを組み合わせたものになると思う。 これはPythonに比べ長くなってしまい、読みにくかった上に繰り返し出現するのでmap_sumというショートカット用のメソッドを用意した。

# 長ったらしい
sum1 = si.map{|i| prefs[person1][i] }.inject(0.0){|res,item| res += item }


# python版
sum1 = sum([prefs[p1][it] for it in si])


Ruby版は式を評価する順番が、単純に左から右へ読めばいいのに対し、Python版では式の評価箇所が飛び飛びなのが、Pythonがわからない人間には辛いが、慣れればリスト内包の方が奇麗な気がしてくるから不思議だ。

両者の評価しているアイテムを収集する場合は、Hash#keys同士を&演算子で、集合の積を抽出するのがRubyらしいのかなと思う。

# 両者が互いに評価しているアイテムのリストを取得
si = prefs[person1].keys & prefs[person2].keys


Trackback URI | Comments RSS

Leave a Reply