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

集合知プログラミングの続き。R で図を書いてみた。

まずは、タブ区切り形式でデータファイルを作成した。存在しない値は空白にしている。この形式ではデータ(映画タイトル)が増えると横に巨大になって扱いがたい気がするが、とりあえずは扱いが簡単なので、この形式でいく。

Person	Night Listener	Dupree	Superman	Just My Luck	Snakes	Lady
Rose	3.0	2.5	3.5	3.0	3.5	2.5
Seymour	3.0	3.5	5.0	1.5	3.5	3.0
Phillips	4.0	 	3.5	 	3.0	2.5
Puig	4.5	2.5	4.0	3.0	3.5	 
LaSalle	3.0	2.0	3.0	2.0	4.0	3.0
Matthews	3.0	3.5	5.0	 	4.0	3.0
Toby	 	1.0	4.0	 	4.5	 

次に、R で読み込んで図にする。‥ここまでで見かけ以上にとても苦労した。だいたい本と同じような図が書けたつもり。ただし点が重なっているところは文字も重なってしまっている。(まだ良くわかっていないので、こういった処理に詳しい方にコメントを寄せて頂けると嬉しいです。)

処理としては、

  • fig01: データフレームを作って、特定の列だけプロット
  • fig02, fig03: データフレームを作って、特定の行を選択し、転置してからプロット
fig01 <- function() {
  data1 <- read.table("/Users/cranebird/Documents/rep.pci/critics3.tsv", header=T, sep="\t")
  # Dupree vs Snakes
  plot (data1[,3], data1[,6], type="n", xlim=c(1,5), ylim=c(1,5), xlab="Dupree", ylab="Snakes",
        main="Fig 2-1")
  persons <- data1[,1]
  text (data1[,3], data1[,6], persons)
}

fig02 <- function() {
  data1 <- read.table("/Users/cranebird/Documents/rep.pci/critics3.tsv", header=T, sep="\t")
  # LaSalle vs Seymour
  LaSalle <- t(data1[5, 2:7]) # todo; ugly.
  Seymour <- t(data1[2, 2:7])
  plot (LaSalle, Seymour, xlim=c(1,5), ylim=c(1,5), xlab="Mick LaSalle",
        ylab="Gene Seymour", main="Fig 2-2", type="n")
  titles <- colnames(data1)[2:7]
  text (LaSalle, Seymour, titles)
}

fig03 <- function() {
  data1 <- read.table("/Users/cranebird/Documents/rep.pci/critics3.tsv", header=T, sep="\t")
  # LaSalle vs Seymour
  Matthews <- t(data1[6, 2:7]) # todo; ugly.
  Rose <- t(data1[1, 2:7])
  plot (Matthews, Rose, xlim=c(1,5), ylim=c(1,5), xlab="Jack Matthews",
        ylab="Lisa Rose", main="Fig 2-3", type="n")
  titles <- colnames(data1)[2:7]
  text (Matthews, Rose, titles)
}


おっと、書いてみて気付いたけど、 p.12 の 図2-3 は Dupree の点が p.8 のデータと合っていないような。Jack Matthews の Dupree のスコアは 3.5, Lisa Rose の Dupree のスコアは 2.5。これは errata 行きだろうか?



そして R ならば実に素敵なことに、そもそも相関係数用関数なんて作る必要がない。最初っから関数 cor が用意されている。以下 R の対話環境(Emacs ESS)。

> data1 <- data1.load()
> data1
    Person Night.Listener Dupree Superman Just.My.Luck Snakes Lady
1     Rose            3.0    2.5      3.5          3.0    3.5  2.5
2  Seymour            3.0    3.5      5.0          1.5    3.5  3.0
3 Phillips            4.0     NA      3.5           NA    3.0  2.5
4     Puig            4.5    2.5      4.0          3.0    3.5   NA
5  LaSalle            3.0    2.0      3.0          2.0    4.0  3.0
6 Matthews            3.0    3.5      5.0           NA    4.0  3.0
7     Toby             NA    1.0      4.0           NA    4.5   NA
> rose <- scores.for(data1, "Rose")
> rose
               Rose
Night.Listener  3.0
Dupree          2.5
Superman        3.5
Just.My.Luck    3.0
Snakes          3.5
Lady            2.5
> seymour <- scores.for(data1, "Seymour")
> seymour
               Seymour
Night.Listener     3.0
Dupree             3.5
Superman           5.0
Just.My.Luck       1.5
Snakes             3.5
Lady               3.0
> cor(rose, seymour)
      Seymour
Rose 0.396059
> rose <- scores.for(data1, "Rose")
> matthews <- scores.for(data1, "Matthews")
> rose
               Rose
Night.Listener  3.0
Dupree          2.5
Superman        3.5
Just.My.Luck    3.0
Snakes          3.5
Lady            2.5
> matthews
               Matthews
Night.Listener      3.0
Dupree              3.5
Superman            5.0
Just.My.Luck         NA
Snakes              4.0
Lady                3.0
> cor(matthews, rose)
         Rose
Matthews   NA
> cor(matthews, rose, use="complete.obs")
              Rose
Matthews 0.7470179
> 

定義した関数は以下の通り。まだ全然洗練はされていないけど、動いたのでとても嬉しい。R も楽しいなぁ。

data1.load <- function() {
  read.table("/Users/cranebird/Documents/rep.pci/critics3.tsv", header=T, sep="\t")
}

scores.for <- function(data, person) {
  x <- t(subset(data, data$Person == person)[2:length(data)])
  colnames(x) <- person
  x
}

topMatches も実装。本当はもっとエレガントに書けるはず。そのうち http://www.okada.jp.org/RWiki/?R%A5%B3%A1%BC%A5%C9%BA%C7%C5%AC%B2%BD%A4%CE%A5%B3%A5%C4%A4%C8%BC%C2%CE%E3%BD%B8
Rコード最適化のコツと実例集 を参考に書き換えたい。特に for ループは R では不自然らしい。

top.matches <- function(prefs, person, n=5) {
  v <- c()
  others <- prefs[, "Person"]
  others <- others [ others != person]
  s1 <- scores.for(prefs, person)

  for (i in 1:length(others)) {
    other <- others[i]
    s2 <- scores.for(prefs, other)
    sim <- cor(s1, s2, use="complete.obs")
    v <- append(v, sim)
  }
  d <- data.frame(Score=v, Person=others)
  sorted <- order(d$Score, decreasing=TRUE)
  d[sorted,]
}

こんな感じに使う。R のデータフレームっていいなぁ。

>  top.matches(data1, "Toby") 
       Score   Person
1  0.9912407     Rose
5  0.9244735  LaSalle
4  0.8934051     Puig
6  0.6628490 Matthews
2  0.3812464  Seymour
3 -1.0000000 Phillips
>