『集合知プログラミング』をR言語で書く
- 作者: Toby Segaran,當山仁健,鴨澤眞夫
- 出版社/メーカー: オライリージャパン
- 発売日: 2008/07/25
- メディア: 大型本
- 購入: 91人 クリック: 2,220回
- この商品を含むブログ (277件) を見る
「2.6 del.icio.usのリンクを推薦するシステムを作る」は思い切って省略。
PythonでRあたりをよく読めばPython用のAPIを使って何とかなるかもしれないけど。
2.7.1 アイテム間の類似度のデータセットを作る
この項では先に作ったtopMatches関数を使って作品ごとに類似性スコア高いほかの作品のリストを作っている。
だけど、R言語では作品ごとに処理するよりも行列として処理したほうが簡単なので、topMatches関数は使わずに類似性スコアの行列を直接作ってみる。
それと例によって類似性の指標をオプションで選べるようにしてある。
# 作品同士の類似性スコア行列を算出する関数。 # prefs 行が作品、列が評者のマトリクス # person 対象者 # similarity 類似性の指標。既定値はユークリッド平方距離。 # "euclidean" ユークリッド平方距離 # "euclidean.center" 評者ごとに中心化したユークリッド平方距離 # "manhattan" マンハッタン距離 # "manhattan.center" 評者ごとに中心化したマンハッタン距離 # "pearson" ピアソンの積率相関係数 # "spearman" スピアマンの順位相関係数 # "kendall" ケンドールの順位相関係数 calculateSimilarItems <- function(prefs, similarity="euclidean"){ switch(similarity, "euclidean" = sim.index <- as.matrix(1/(1+dist(prefs)^2)), "euclidean.center" = sim.index <- as.matrix(1/(1+dist(scale(prefs, scale=FALSE))^2)), "manhattan" = sim.index <- as.matrix(1/(1+dist(prefs, method="manhattan"))), "manhattan.center" = sim.index <- as.matrix(1/(1+dist(scale(prefs, scale=FALSE), method="manhattan"))), "pearson" = sim.index <- cor(t(prefs), use="pairwise.complete.obs"), "spearman" = sim.index <- cor(t(prefs), use="pairwise.complete.obs", method="spearman"), "kendall" = sim.index <- cor(t(prefs), use="pairwise.complete.obs", method="kendall") ) sim.index[is.na(sim.index)] <- 0 diag(sim.index) <- NA sim.index }
使い方。
(itemsim <- calculateSimilarItems(critics)) # 出力 # Lady.in.the.Water Snakes.on.a.Plane Just.My.Luck Superman.Returns You.Me.and.Dupree The.Night.Listener # Lady.in.the.Water 0.00000000 0.16949153 0.10909091 0.06666667 0.27586207 0.22222222 # Snakes.on.a.Plane 0.16949153 0.00000000 0.06299213 0.16666667 0.04428044 0.16000000 # Just.My.Luck 0.10909091 0.06299213 0.00000000 0.03791469 0.11267606 0.09411765 # Superman.Returns 0.06666667 0.16666667 0.03791469 0.00000000 0.04606526 0.08921933 # You.Me.and.Dupree 0.27586207 0.04428044 0.11267606 0.04606526 0.00000000 0.11049724 # The.Night.Listener 0.22222222 0.16000000 0.09411765 0.08921933 0.11049724 0.00000000
う〜ん、p25に載っているサンプルコードの結果と合わないぞ。
どうもdist関数におけるNAの扱いが要因みたい。
RjpWikiの初級Q&A アーカイブより、NAを含む行列を関数distで計算。
相関係数のほうは一致する。
逐語訳ではなくて意訳、超訳と宣言しましたので、ここはRの流儀のままで行きたいと思います。
2.7.2 推薦を行う
事前に用意した映画の類似度行列に基づいて、任意のユーザーに次に見るべき映画を推薦する。
ここで評価と類似度を乗算したものを類似度の合計で割るという計算をしているのだけど、これも納得がいかないので算術平均を使うことにする。
前述したように評者ごとの個人差を補正するなら相関係数を使うか、評者ごとに中心化した距離行列を使えばいいと思う。
# 任意のユーザーの評価と事前に用意した映画の類似度行列に基づいて、任意のユーザーに次に見るべき映画を推薦する関数。 # itemMatch calculateSimilarItemsで作った映画の類似度行列 # user 任意のユーザーの評点ベクトル getRecommendedItems <- function(itemMatch, user){ x1 <- apply(as.matrix(itemMatch*user), 2, mean, na.rm=TRUE) x2 <- x1[is.na(user)] sort(x1[is.na(user)], decreasing=TRUE) }
使い方
# 先に類似度行列を用意しておく。 # 今回は評者ごとに中心化したユークリッド平方距離 itemsim <- calculateSimilarItems(critics, "euclidean.center") getRecommendedItems(itemsim, Toby) # 出力 # Lady.in.the.Water The.Night.Listener Just.My.Luck # 0.4350802 0.3957915 0.1825998
2.8 MovieLensのデータセットを使う
MovieLens Data Sets | GroupLens Researchから「100,000 Data Set (.zip)」をダウンロード。
解凍したら、Rのワーキング・ディレクトリに「u.data」「u.item」を置く。
とりあえずファイル全体を読み込んでから必要な列だけを取り出すことにする。
u.item.list <- c("movie.id", "movie.title", "release.date", "video.release.date", "IMDb.URL", "unknown", "Action", "Adventure", "Animation", "Children's", "Comedy", "Crime", "Documentary", "Drama", "Fantasy", "Film-Noir", "Horror", "Musical", "Mystery", "Romance", "Sci-Fi", "Thriller", "War", "Western") u.item <- read.table("u.item2", sep="|", col.names=u.item.list) # 出力 # 以下にエラー scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, : # '15' 行目には,24 個の要素がありません
む、エラーが出た。
どうも区切り記号をうまく読めていないみたい。
試行錯誤してみましたが、u.data は読み込めるものの u.item が読み込めない。
ひとまず、ここはスキップして3章へ進むことにします。
3.3 階層的クラスタリング
「3.3.2 フィード中の単語を数える」は省略。
tmパッケージあたりを使えば良さそうですが。
Rを使ったテキストマイニングについていえば、近々本も出版されるみたいです。
- 作者: 石田基広
- 出版社/メーカー: 森北出版
- 発売日: 2008/12/16
- メディア: 単行本(ソフトカバー)
- 購入: 26人 クリック: 342回
- この商品を含むブログ (34件) を見る
それと個人的には「階層*型*クラスタリング」の方がなじみのある訳語なので、ここではそう書きます。
まずはブログのデータを抽出したデータセットをダウンロードしてきます。
http://kiwitobes.com/clusters/blogdata.txt
このblogdata.txtをRのワーキングディレクトリに入れます。
このデータセットはタブ区切りなのでread.delim関数でそのまま読めます。
blogdata <- read.delim("blogdata.txt") ncol(blogdata) # [1] 707 nrow(blogdata) # [1] 99
このデータセットのサイズは99行(ブログの数)×707列(単語の数)ということです。
続けて、ピアソンの積率相関係数に基づいてブログ間の距離(非類似度)行列を算出します。
1列目にはブログ名が入っているのでこれは除きます。
# データセットを転置する blogdata2 <- (t(blogdata[,-1])) colnames(blogdata2) <- blogdata[,1] # 距離行列を算出する blog.dist <- as.dist(1-cor(blogdata2))
そして、クラスタリングします。本文を読む限り、群平均法を採用しているようです。
blog.hc <- hclust(blog.dist, "average")
3.4 デンドログラムを描く
クラスタリングが終わっていれば、樹形図を描くのは至極簡単。
plot(blog.hc, main="ブログのクラスタのデンドログラム", cex=0.75, hang=-1)
サンプルサイズが99もあるのでちょっと見にくいですね。
普通、階層型クラスタリングを使うのはサンプルサイズが50個ぐらいまでの場合です。
サンプルではjpegで樹形図を出力してますが、グラフですからPNGで出力した方がいいように思います。
# なんとなく壁紙サイズで出力 png("cluster.png", width=1280, height=1024) plot(blog.hc, main="ブログのクラスタのデンドログラム", cex=0.75, hang=-1) dev.off()
↓FotoLifeに壁紙サイズで置いてあります。
「3.5 列のクラスタリング」は省略。データセットを転置するだけなのと、サンプルサイズが大きくなり過ぎて階層型クラスタリングは適切じゃないから。