Rubyで書くユークリッド距離

oreillyから「集合知プログラミング」という書籍が発売されました.
この本文中で使われている例題をRubyで書いてみようと思います.
どこまで行けるか分かりませんが,やるだけやってみます.
では「ユークリッド距離によるスコア」から.

criticsの表現

本文ではPythonを使っているため,ディクショナリで書かれています.
今回はHashを使って書いてみます.
こんな感じ.

critics = {
  'Lisa Rose' => {
    'Lady in the Water' => 2.5,
    'Snakes on a Plane' => 3.5,
    'Just My Luck' => 3.0,
    ...
  }
  ...
}

ユークリッド距離の計算

メソッド名とかは本文のものから変えていますが,やってることは同じです.
今回のデータでは問題ないのですが,正規化の除算をする時に値が整数だと丸まってしまうので,Numeric#quoを使いました.
quoを使うと実数の商を返してくれます.

module My
  class Recommender
    #
    # 任意の2人の類似性を算出する
    #
    def get_euclidean_similarity(critics, person1, person2)
      # 2人が共に批評している映画を抽出
      movies = critics[person1].keys & critics[person2].keys
      return 0 if movies.empty?

      # 映画ごとの批評差を求める
      distances = movies.map { |movie| get_distance(critics, movie, person1, person2) }
      # 批評差の総和を正規化して返す
      sum_distances = distances.inject(0) { |sum, distance| sum + distance }
      normalize(sum_distances)
    end

    private

    #
    # 批評差(差分の2乗)を算出する
    #
    def get_distance(critics, movie, person1, person2)
      (critics[person1][movie] - critics[person2][movie]) ** 2
    end

    #
    # 正規化(完全一致なら1,何も一致しなければ0)
    #
    def normalize(distance)
      1.quo(1 + distance)
    end
  end
end

実行結果

以下のように呼び出す部分を足して,実行してみます.

recommender = My:Recommender.new
puts recommender.get_euclidean_similarity(critics, 'Lisa Rose', 'Gene Seymour')

いざ,実行!

% ./recommender.rb
0.148148148148148

本文のp.11と同じ結果が得られました.