[[ノート/マイニング/バスケット解析をRで]]~
訪問者数 &counter();      最終更新 &lastmod();

**図書データをRで [#l605f530]

***図書データの読み込み [#w3d80c7b]

-図書データは次のような形をしていた
 "資料番号","利用者","年","身分","身分名称","所属","貸出種別","図書区分","和洋区分","和洋名称","貸出年月日","資料区分名称"
 "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","図書"
-Rで読み込むには
 > 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         図書
このように読み込んだ結果を、適宜切り出せばよいだろう。

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

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

***まずバスケットを作る [#x0969ff1]
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の要素はz[[i]]で指定されるが、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))

***処理結果 [#h8f7a66d]
年間を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