集合知プログラミングの勉強(5)

getRecommendation を R でも実装しようとしてしばらく苦戦。ハッシュテーブルなんてものは R には無い?

ふっと、表2-2 筆者のための推薦の作成 の表そのものを、データフレームとして作ればいいことに気付く。R はモダーンな言語で、関数を引数にするなんてことは余裕でできるのだった。

f1 <- function(person) {
  prefs <- data1.load()
  prefs["sim"] <- sapply(prefs$Person, function(other) { sim.pearson(prefs, person, other) })
  # prefs["S.xNight"] <- prefs$sim * m$Night.Listener
  prefs
}

対話的環境だとこのようになる。Rose に対する推薦の表、Toby に対する推薦の表。

> f1("Rose")
    Person Night.Listener Dupree Superman Just.My.Luck Snakes Lady       sim
1     Rose            3.0    2.5      3.5          3.0    3.5  2.5 1.0000000
2  Seymour            3.0    3.5      5.0          1.5    3.5  3.0 0.3960590
3 Phillips            4.0     NA      3.5           NA    3.0  2.5 0.4045199
4     Puig            4.5    2.5      4.0          3.0    3.5   NA 0.5669467
5  LaSalle            3.0    2.0      3.0          2.0    4.0  3.0 0.5940885
6 Matthews            3.0    3.5      5.0           NA    4.0  3.0 0.7470179
7     Toby             NA    1.0      4.0           NA    4.5   NA 0.9912407
> f1("Toby")
    Person Night.Listener Dupree Superman Just.My.Luck Snakes Lady        sim
1     Rose            3.0    2.5      3.5          3.0    3.5  2.5  0.9912407
2  Seymour            3.0    3.5      5.0          1.5    3.5  3.0  0.3812464
3 Phillips            4.0     NA      3.5           NA    3.0  2.5 -1.0000000
4     Puig            4.5    2.5      4.0          3.0    3.5   NA  0.8934051
5  LaSalle            3.0    2.0      3.0          2.0    4.0  3.0  0.9244735
6 Matthews            3.0    3.5      5.0           NA    4.0  3.0  0.6628490
7     Toby             NA    1.0      4.0           NA    4.5   NA  1.0000000
> 

あとはそれぞれのタイトル毎に、sim をかけ算してやればいい。本文では、python コードを「非常に理解しやすい」と書いているが、ひょっとすると、分かりやすさという点で R はそれ以上かもしれない。

なんとか getrecommendation を実装。もっとエレガントに書けると思えるがとりあえず。まだ R の基本も分かっていないことが、プログラムを書いたら良くわかった。

> getrecommendations("Toby")
   Night     Lady     Luck 
3.347790 2.832550 2.530981 
# データフレーム prefs に対し、
# person 以外について、 類似性と S.x タイトル の値を計算したデータフレームを返す
data1.sim <- function(prefs, person) {
    titles <- colnames(prefs)[2:length(prefs)]
    sxtitles <- sapply(titles,
                       function(title) {
                         paste("S.x", title, sep="")
                       })
    ## sim を計算
    prefs["sim"] <- sapply(prefs$Person,
                           function(other) {
                             sim.pearson(prefs, person, other)
                           })
    for (i in 1:length(titles)) {
      title <- titles[i]
      sxtitle <- sxtitles[i]
      prefs[sxtitle] <- prefs["sim"] * prefs[title]
    }
    # 自分以外
    subset(prefs, prefs$Person != person)
}

getrecommendations <- function(person) {
  prefs <- data1.load()
  sims <- data1.sim(prefs, person) # sim, S.x の列を追加したデータを生成
  # 見ていないタイトル一覧を作成
  whos <- scores.for(prefs, person)
  titles <- rownames(subset(whos, is.na(whos[,1])))
  # 見ていないタイトル全てに対して S.x列の合計を計算
  totals <- sapply(titles,
                   function(title) {
                     sxtitle <- paste("S.x", title, sep="")
                     scores <- sims[sxtitle]
                     scores <- scores[scores > 0] # 必要。
                     sum(scores, na.rm=TRUE)
                   })
  ## 見ていないタイトル全てに対して、その映画を見た評者の類似度の合計を計算
  sim.sum <- sapply(titles,
                    function(title) {
                      sxtitle <- paste("S.x", title, sep="")
                      sims.ex <- sims[ ! is.na(sims[title]),]
                      sim <- sims.ex["sim"]
                      sim <- sim[sim > 0]
                      sim.sum <- sum (sim, na.rm=TRUE)
                      sim.sum
                    })
  ##
  result <- totals/sim.sum
  ## ソート
  sorted <- order(result, decreasing=TRUE)
  result[sorted]
}