ノート/マイニング/バスケット解析をRで
訪問者数 3523      最終更新 2008-05-21 (水) 11:45:52

図書データをRで

図書データの読み込み

どんな処理をしたいか

同じ利用者(利用者コードで識別)が、同じ時期(たとえば同じ月、5月なら5月)に借り出した本の集合を、バスケット(トランザクション)と考えてみる。

こうして得られたトランザクションについて、バスケット分析をしてみよう。

まずバスケットを作る

2つのバスケット単位、月別に1つずつ、と、1年間で1つ、を作ってみよう。 (週単位だと日付に週番号がないので、別途計算しなければならない)

月を取り出すには、x[,11]のうち、substring関数で5〜6文字目を取り出せばよい(substring(x[,11], 5, 6))だろう。図書番号(資料番号)はx[,1]そのままでよい。

関数(というよりfunctorだが)tapplyを使うと、集計操作などが簡単に出来る。tapplyは「フィールドXが同じ値をもつレコードのフィールドYを集めて指定した関数を適用」といった感じの関数である。たとえば、同じフィールドX値を持つレコードのフィールドYの平均値(mean)を計算することが出来る。

 tapply(field_x, factor(field_y), mean)

これを使ってバスケットを作成する、つまり同じユーザidのレコードを集めてリストにするには、

 z <- tapply(bookid, factor(userid), c)

でよい。cはリストを作るconcatenate関数。

この時、同じユーザが同じ本を借りていると、出力には2つ以上入ってしまう。これは後の処理で具合が悪いので、ユニークになるように、ここでは

 for (i in 1:length(z)) {
    z[[i]] <- levels(factor(z[[i]]))
 }

とする。zの要素はzi?で指定されるが、vectorになっている。

あとは、transactionに変換すればよいが、そのためにzをlist (list of vectors) に変換する。

 z.list <- as(z, "list")
 z.trans <- as(z.list, "transactions")

この後、apriori処理をすればよい。--> ノート/マイニング/バスケット解析をRで

全体は、

# 年単位でバスケット
# 実行するためには source("test2.R")
# あらかじめ、library("arules")をしておくこと。
#
y <- read.table("data06.txt", sep=",")
# 資料idは第1カラム
bookid <- as.vector(y[,1])
# 第1行目はタイトル行なので取り除く
bookid <- bookid[2: length(bookid)]
# factor側は、××<userid-month>の文字列にする××
# factor側は、useridだけにする。ファイル全体が年単位。
user <- as.vector(y[,2])
# 第1行目はタイトル行なので取り除く
user <- user[2: length(user)]
#
# これで、tapplyを使って関数cを実行。tapply(data, factor, function)は
#   factor内の異なる要素ごとに関数functionを適用。
#   個人ごとに県名(factor側)と収入(data側)が準備してあるとき、関数がmean
#   なら県ごとに属する個人を集めた上でその平均meanを計算してくれる。
z <- tapply(bookid, factor(user), c)

# 気をつけなければならないのは、同じ月に同じユーザが同じ本を借りていると、
# cした結果のリストに同じ資料idが2回以上入ってしまう。この問題はfactorで扱う。

# zの各要素(z[[*]])をfactorに変換する。*は数字。factor(z)はエラーになる。
# これってuniqueでもいいのかも?
for (i in 1:length(z)) {
  z[[i]] <- levels(factor(z[[i]]))
}

# zをlist型(list of vectors)にし、それをtransactionsに変換する
z.list <- as(z, "list")
z.trans <- as(z.list, "transactions")

# transactions型になったものを、apriori解析する。
z.ap <- apriori(z.trans, parameter=list(supp

また、月単位のバスケットを作るには、ユーザidと月番号をくっつけて新しいユーザidとすればよい。たとえば 0000020305 に 06 (6月、年月日の5〜6桁目)を結合して 000002030506 を作り(これがバスケットのidになる)、これで処理すればよい。

# 実行するためには source("test1.R")
# あらかじめ、library("arules")をしておくこと。
#
y <- read.table("data06.txt", sep=",")
# 資料idは第1カラム
bookid <- as.vector(y[,1])
# 第1行目はタイトル行なので取り除く
bookid <- bookid[2: length(bookid)]
# factor側は、<userid-month>の文字列にする
# 貸出の月は第11カラム(日付「20060401」)の5〜6文字目
# 文字列をくっつけるのはpaste(文字列, 文字列, sep="")
user_month <- as.vector(paste(y[,2],substr(y[,11], 5, 6), sep=""))
# 第1行目はタイトル行なので取り除く
user_month <- user_month[2: length(user_month)]
#
# これで、tapplyを使って関数cを実行。tapply(data, factor, function)は
#   factor内の異なる要素ごとに関数functionを適用。
#   個人ごとに県名(factor側)と収入(data側)が準備してあるとき、関数がmean
#   なら県ごとに属する個人を集めた上でその平均meanを計算してくれる。
z <- tapply(bookid, factor(user_month), c)

# 気をつけなければならないのは、同じ月に同じユーザが同じ本を借りていると、
# cした結果のリストに同じ資料idが2回以上入ってしまう。この問題はfactorで扱う。

# zの各要素(z[[*]])をfactorに変換する。*は数字。factor(z)はエラーになる。
# これってuniqueでもいいのかも?
for (i in 1:length(z)) {
  z[[i]] <- levels(factor(z[[i]]))
}

# zをlist型(list of vectors)にし、それをtransactionsに変換する
z.list <- as(z, "list")
z.trans <- as(z.list, "transactions")

# transactions型になったものを、apriori解析する。
z.ap <- apriori(z.trans, parameter=list(support=0.0005, confidence=0.01))
#
z.inspect <- inspect(head(SORT(z.ap, by="confidence"), n=20))

処理結果

年間を1つのバスケットにした場合の結果:10804ルール(support=0.0005, confidence=0.01)となり、confidenceが高いルール10個は、

   lhs            rhs          support       confidence lift
1  {0001103456} => {0000462168} 0.0009062075          1 1103.5000
2  {0000462168} => {0001103456} 0.0009062075          1 1103.5000
3  {0000683615} => {0001156546} 0.0009062075          1  735.6667
4  {1000366888} => {1000445443} 0.0009062075          1  735.6667
5  {0000633032} => {0000665398} 0.0009062075          1 1103.5000
6  {0000665398} => {0000633032} 0.0009062075          1 1103.5000
7  {0001502434} => {1000012474} 0.0009062075          1  735.6667
8  {1000468890} => {1000392389} 0.0009062075          1  441.4000
9  {1000135705} => {1000294593} 0.0009062075          1  551.7500
10 {1000388841} => {1000476265} 0.0009062075          1  441.4000

読みにくいので、資料番号を図書名(簡略化)にすると

lhs                       rhs                support        confidence  lift
1  {固定化酵素}       => {固定化酵素}           0.0009062075          1 1103.5000
2  {固定化酵素}       => {固定化酵素}           0.0009062075          1 1103.5000
3  {言語工学}         => {自然言語解析の基礎}   0.0009062075          1  735.6667
4  {地球の化学像と    => {「新」地球温暖化      0.0009062075          1  735.6667
      環境問題}            とその影響}
5  {生化学 (上)}      => {生化学 (下)}          0.0009062075          1 1103.5000
6  {生化学 (下)}      => {生化学 (上)}          0.0009062075          1 1103.5000
7  {ハーブ大全}     => {ホームハーブ : 美容と}  0.0009062075          1  735.6667
                        病気予防に役立つ薬用ハーブ
8  {葉っぱの不思議な力} => {新アクセス独和辞典} 0.0009062075          1  441.4000
9  {マスタリングTCP/IP} => {マスタリングTCP/IP} 0.0009062075          1  551.7500
     (第2版)              (第3版)
10 {.就職活動エントリー} => {面接の達人}        0.0009062075          1  441.4000
       シートの書き方教室

となっている。

これらのルールの根拠を元データで細かく見ると、

"言語工学          ","Cさん","","11","和","20060712","図書"
"自然言語解析の基礎","Cさん","","11","和","20060712","図書"

"言語工学          ","Dさん","","11","和","20070109","図書"
"自然言語解析の基礎","Dさん","","11","和","20070109","図書"

"自然言語解析の基礎","Eさん","","11","和","20060616","図書"

であり、これは2人の学生が同じ2冊の本を借りている(しかも2冊を同一日に)ので、 バスケット解析上は意味がありそうである。

他方、「固定化酵素」の場合は、同じ内容の本が図書館に2冊蔵書されており、別々の 資料ID番号が振られている。それぞれを固定化酵素(1)、(2)と呼んで区別すると、

"固定化酵素(1)","Aさん","","11","和","20060414","図書" 
"固定化酵素(2)","Aさん","","11","和","20061025","図書"
"固定化酵素(2)","Aさん","","11","和","20070115","図書"
"固定化酵素(2)","Aさん","","11","和","20070205","図書"

"固定化酵素(2)","Bさん","","11","和","20060422","図書"
"固定化酵素(1)","Bさん","","11","和","20061017","図書"
"固定化酵素(1)","Bさん","","11","和","20070207","図書"

のように貸し出されている。本来この2冊は区別するべきでないのだが、区別されているために強い関連があると指摘されたわけである。これに対しては、資料IDではなくて、書籍名にあたる番号を利用するのがよいのだろう。

同じ観点で、前表の9番目に現れる「マスタリングTCP/IPの第2版と第3版も、区別することに余り意味がないように思う。

第5番目・6番目に現れる「生化学(上)」「(下)」は、2冊を組にして借り出すのも当然であり、これは1つの資料としてカウントすべきものだろう。

では次に、ユーザごと&月ごとにバスケットとして分析した場合の結果を見てみる。

   lhs                       rhs                support      confidence  lift
1  {図説日本のゲンゴロウ} => {日本産水生昆虫}   0.0006178179  1.0000000 899.2222
2  {バイオ実験超基本Q&A} => {バイオ実験トラブル 0.0006178179  0.8333333 843.0208
                               解決超基本Q&A}
3  {バイオ実験トラブル => {バイオ実験超基本Q&A} 0.0006178179  0.6250000 843.0208
      解決超基本Q&A}
4  {日本産水生昆虫}    => {図説日本のゲンゴロウ}0.0006178179  0.5555556 899.2222
5  {臨床化学}         => {微生物学/臨床微生物学}0.0006178179  0.5555556 408.7374
6  {有機化学(上)}     => {有機化学(中)}     0.0006178179  0.5000000 289.0357
7  {微生物学/臨床微生物学}  => {臨床化学}       0.0006178179  0.4545455 408.7374
8  {有機化学(中)}     => {有機化学(上)}     0.0006178179  0.3571429 289.0357
9  {有機化学(上)}     => {有機化学(中)}     0.0006178179  0.2777778 118.3187
10 {有機化学(中)}     => {有機化学(上)}     0.0006178179  0.2631579 118.3187

第1番目のルールは、元データで見ると同じ利用者が2冊を組にして繰返し借り出している、特異な例である。

また、この結果では第1番目と第4番目や、第2番目と第3番目、第5番目と第7番目が、同じ本のペアを逆向きに現している。これは、ほとんどのバスケットにおいて2冊が必ず組にして借り出されているためで、もし他の本も借りていれば必ずしも逆向きのルールが組みになって出てくることは無い。バスケット期間を1ヶ月に限定するとこのようなことがおこりやすいのだろうか。

第8・9・10番目のルールはややこしくなっている。9と10は上記と同じようなペアになっている。他方、8は10と同じルールだが、年単位解析の固定化酵素の例と同じように、同一の本が複数冊所蔵され異なる資料IDが振られているため、別のルールとして挙げられているのである。

全般に、月ごとの集計は、バスケット内のアイテムが少ないため、妙な癖が見えるような気がする。また、ルール9や10のconfidenceがずいぶん小さく、confidence>0.01の条件では上記10ルールしか残らなかったことは、全体のconfidenceがかなり小さい。つまり、バスケットが小さすぎてルールになるような対が少ないのである。

他方で、年ごとの集計は、confidence>0.01では10804ルールも出来てしまうし(少し 多すぎるような気もする)、上位10ルールはみなconfidence=1であった。

もう少し丁寧に評価をして、改良を加える必要があるだろう。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2008-05-21 (水) 11:45:52 (5425d)