![]() |
ノート/マイニング/図書データをRでhttp://pepper.is.sci.toho-u.ac.jp/pepper/index.php?%A5%CE%A1%BC%A5%C8%2F%A5%DE%A5%A4%A5%CB%A5%F3%A5%B0%2F%BF%DE%BD%F1%A5%C7%A1%BC%A5%BF%A4%F2%A3%D2%A4%C7 |
![]() |
ノート/マイニング/バスケット解析をRで
訪問者数 3523 最終更新 2008-05-21 (水) 11:45:52
"資料番号","利用者","年","身分","身分名称","所属","貸出種別","図書区分","和洋区分","和洋名称","貸出年月日","資料区分名称" "0000000003","020000006","","11","理学部学生","105S00000","001","1","1","和","20060401","図書" "0000000007","020000006","","11","理学部学生","105S00000","001","1","1","和","20060401","図書" "0000000011","010000005","","01","薬学部学生","104Y00000","001","1","1","和","20060401","図書" "1000000012","020000002","","11","理学部学生","105S00000","001","1","1","和","20060401","図書"
> x <- read.table("data06.txt", sep=",")読んだ結果を表示してみると
> head(x, n=5) V1 V2 V3 V4 V5 V6 V7 1 資料番号 利用者コード 学年 身分コード 身分名称 所属コード 貸出種別 2 0000000003 020000006 11 理学部学生 105S00000 001 3 0000000007 020000006 11 理学部学生 105S00000 001 4 0000000011 010000005 01 薬学部学生 104Y00000 001 5 1000000012 020000002 11 理学部学生 105S00000 001 V8 V9 V10 V11 V12 1 図書雑誌区分 和洋区分 和洋区分名称 貸出年月日 資料区分名称 2 1 1 和 20060401 図書 3 1 1 和 20060401 図書 4 1 1 和 20060401 図書 5 1 1 和 20060401 図書このように読み込んだ結果を、適宜切り出せばよいだろう。
同じ利用者(利用者コードで識別)が、同じ時期(たとえば同じ月、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であった。
もう少し丁寧に評価をして、改良を加える必要があるだろう。