元のデータの説明

TIC2000

tic.data.txtからの要約。

  • CoIL 2000 Challengeで用いられた保険会社の顧客に関するデータ。86個の変数は、契約状況(V44-V85)と社会人口統計学的な変数(V1-V43)を含んでいる。この調査は “Can you predict who would be interested in buying a caravan insurance policy and give an explanation why?” という問いに答えるように集められた。
  • このデータはオランダのデータマイニング会社Sentinent Machine Researchから提供され、現実のビジネスの問題に基づいている。学習用データ(ticdata2000.txt)は5000レコードでcaravan insurance policyの契約の有無(V86)を含んでおり、検証用データ(ticeval2000.txt)は4000レコードで契約の有無(V86)は含んでいない。検証用データの正解は、CoIL 2000 Challengeの開催時には公開されていなかったが、現在はテストデータ(tictest2000.txt)として公開されている。
  • V1-V43のうち、コード化が指定されていない変数はすべて、郵便番号の一桁目のエリアを指している。たとえばV30が9ならばその顧客は郵便番号が9で始まるエリアに家を借りていることを、V31が5ならば郵便番号が5のエリアに持ち家があることを意味する。職業、社会層などもすべて、該当するエリアの箇所が郵便番号の一桁目で埋まっている。
変数

dictionary.txtからの抜粋と要約、の日本語版。

変数分類メモ
V1顧客分類2L0でコード化されている、数字の大きさに意味なし
V2住居数大きいほど住む箇所が多い
V3世帯構成員数の平均人数
V4世帯構成員の平均年齢L1でコード化されている、年齢
V5顧客分類1L2でコード化されている、数字の大きさに意味なし
V6-V9宗教L3でコード化されている、V6+V7+V8+V9は9から12の間。それぞれの宗教を信じる割合?
V10-V13結婚場所を表す変数, 例えばV10が0ならば無し?
V14-V15世帯の大きさL3でコード化されている、なぜかV14+V15は10以下。割合?
V16-V18教育水準L3でコード化されている、なぜかV16+V17+V18はほぼ10、それぞれの年数?割合?
V19-V24職業L3でコード化されている、なぜかV19+V20+V21+V22+V23+V24は9から13の間
V25-V29社会層L3でコード化されている、なぜかV25+V26+V27+V28+V29は9から12の間
V30-V31住居L3でコード化されている、なぜかV30+V31は9か10
V32-V34自動車L3でコード化されている、なぜかV32+V33+V34は9から11の間
V35-V36健康保険L3でコード化されている、なぜかV35+V36は9か10
V37-V41収入L3でコード化されている、なぜかV37+V38+V39+V40+V41は9から13の間
V42平均収入L3でコード化されている
V43購買力L3でコード化されている、1から8の間。
V44-V64各種保険支払い額L4でコード化
V65-V85各種保険契約件数件数

メモの確認用のコード。

table((tic.learn$V16+tic.learn$V17+tic.learn$V18))
table((tic.learn$V19+tic.learn$V20+tic.learn$V21+tic.learn$V22+tic.learn$V23+tic.learn$V24))
table((tic.learn$V25+tic.learn$V26+tic.learn$V27+tic.learn$V28+tic.learn$V29))
table(tic.learn$V30+tic.learn$V31)
table(tic.learn$V32+tic.learn$V33+tic.learn$V34)
table(tic.learn$V35+tic.learn$V36)
table(tic.learn$V37+tic.learn$V38+tic.learn$V39+tic.learn$V40+tic.learn$V41)
各変数のコーディング

L0:分類を表す数字なので、大小関係に意味がなく、名義尺度である。そのままでは説明変数にならない。

ValueLabel
1High Income, expensive child
2Very Important Provincials
3High status seniors
4Affluent senior apartments
5Mixed seniors
6Career and childcare
7Dinki's (double income no kids)
8Middle class families
9Modern, complete families
10Stable family
11Family starters
12Affluent young families
13Young all american family
14Junior cosmopolitan
15Senior cosmopolitans
16Students in apartments
17Fresh masters in the city
18Single youth
19Suburban youth
20Etnically diverse
21Young urban have-nots
22Mixed apartment dwellers
23Young and rising
24Young, low educated
25Young seniors in the city
26Own home elderly
27Seniors in apartments
28Residential elderly
29Porchless seniors: no front yard
30Religious elderly singles
31Low income catholics
32Mixed seniors
33Lower class large families
34Large family, employed child
35Village families
36Couples with teens 'Married with children'
37Mixed small town dwellers
38Traditional families
39Large religous families
40Large family farms
41Mixed rurals

L1:大きさが年齢の順なので、そのまま説明変数に使える。

120-30 years
230-40 years
340-50 years
450-60 years
560-70 years
670-80 years

L2:数字は分類を表すだけなので、連続尺度でも順序尺度でもなく、名義尺度。そのままでは説明変数にならない。

1Successful hedonists
2Driven Growers
3Average Family
4Career Loners
5Living well
6Cruising Seniors
7Retired and Religeous
8Family with grown ups
9Conservative families
10Farmers

L3:順序尺度。このまま連続尺度の説明変数として用いる。

00%
11 - 10%
211 - 23%
324 - 36%
437 - 49%
550 - 62%
663 - 75%
776 - 88%
889 - 99%
9100%

L4: 順序尺度。今回はこのまま連続尺度の変数として用いる。

0f 0
1f 1 - 49
2f 50 - 99
3f 100 - 199
4f 200 - 499
5f 500 - 999
6f 1000 - 4999
7f 5000 - 9999
8f 10.000 - 19.999
9f 20.000 - ?
参考

kernlabパッケージに、加工済みのデータが入っていて、それを使うこともできる。

install.packages(c("kernlab"), dependencies=TRUE)
tic.learn <- ticdata[1:5822,]
tic.eval <- ticdata[5823:9822,]

今回の課題

概要
  • もっと大きな変数の組み合わせで、同じ事を試みよ。その際に生じる幾つかの困難を乗り越えよ。
  • CoIL 2000では、訪問する800人を選べ、という課題になっていたが、この課題では訪問する人数も各自で決めて良い。
準備
install.packages(c("mvpart", "gam", "kernlab"), dependencies=TRUE)

これでエラーが出る場合には、プロキシの設定を試みると良い。

library(mvpart)
library(gam)
library(MASS)
library(kernlab)

以上の4つのライブラリを、この課題では使う可能性がある。

データの読み込み
tic.leaan <- read.table("http://kdd.ics.uci.edu/databases/tic/ticdata2000.txt")
tic.eval <- read.table("http://kdd.ics.uci.edu/databases/tic/ticeval2000.txt")
tic.test <- read.table("http://kdd.ics.uci.edu/databases/tic/tictgts2000.txt")
tic.eval <- cbind(tic.eval, tic.test)
colnames(tic.eval)[86] <- "V86"
rm(tic.test)
少し加工する

以下の6行は、実行しない方がいい場合もある。

tic.learn$V1 <- as.factor(tic.learn$V1)
tic.learn$V5 <- as.factor(tic.learn$V5)
tic.learn$V86 <- as.factor(tic.learn$V86)
tic.eval$V1 <- as.factor(tic.eval$V1)
tic.eval$V5 <- as.factor(tic.eval$V5)
tic.eval$V86 <- as.factor(tic.eval$V86)

あとはそのまま。

考えたルールに基づく対象限定

各変数に閾値を設けてルールを生成したとする。 たとえば、「V47が5.5以上かつV44が1未満」または「V47が5.5以上かつV1が{1,3,6,8,12,20}のどれか」、というルールは 次のように記す。

(tic.eval$V47>5.5 & tic.eval$V44<1) | (tic.eval$V47>5.5 & (tic.eval$V1==1 |tic.eval$V1==3 | tic.eval$V1==6 | tic.eval$V1==8 | tic.eval$V1==12 | tic.eval$V1==20) ) 

「&」が「かつ(AND)」、「|」が「または(OR)」である。

このルールを検証用データに適用するには、

tic.eval.visit <- (tic.eval$V47>5.5 & tic.eval$V44<1) | (tic.eval$V47>5.5 & (tic.eval$V1==1 |tic.eval$V1==3 | tic.eval$V1==6 | tic.eval$V1==8 | tic.eval$V1==12 | tic.eval$V1==20) ) 

と、訪問するか否かを二値(TRUE, FALSE)で表すオブジェクトを生成する。 このモデルに予測に基づいた訪問の成果を検証するには、訪問対象のリストtic.visitと検証用データの正解V86のクロス集計を行えばよい。

table(tic.eval.visit)

FALSE  TRUE 
 3029   971 

table(tic.eval.visit, tic.eval$V86)

tic.eval.visit    0    1
         FALSE 2878  151
         TRUE   884   87

ここでは、訪問対象に884+87=971人を選定し、そのうちの87人が実際に契約してくれる人だったことになる。 契約率は87/971=8.96%。また誤判別率は

(884+151)/4000

で25.9%となる。

モデルに基づく対象限定

学習したモデルに基づいて、訪問対象を狭めるには、predict()という関数を用いて、訪問対象か否かというリストを作成する。 まず、設定まで調整したモデルを、学習用データ(tic.learn)から得る。

tic.rpart <- rpart(V86~., data=tic.learn, control=c(cp=0.005))

次に、このモデル(ここではtic.rpart)を検証用データ(tic.eval)に適用して、契約してくれるか否かの予測を行う。 この際、0.05という閾値も調整の必要がある。

tic.eval.visit <- predict(tic.rpart, newdata=tic.eval)[,2]>0.05

このモデルに予測に基づいた訪問の成果を検証するには、訪問対象のリストtic.visitと検証用データの正解V86のクロス集計を行えばよい。

table(tic.eval.visit)

tic.eval.visit 
FALSE  TRUE
 2389  1611

table(tic.eval.visit, tic.eval$V86)

tic.eval.visit    0    1
         FALSE 2310   79
         TRUE  1452  159

ここでは、訪問対象に1452+159=1611人を選定し、そのうちの159人が実際に契約してくれる人だったことになる。契約率は159/1452=11.0%。 また誤判別率は

(79+1452)/4000

で38.275%となる。

想定される困難

次の1行を実行すると、かなり時間がかかってエラーになる。

tic.glm.step <- step(glm(V86~., family="binomial", data=tic.learn)

次の4行、いずれもエラーになる。変数間の関係が悪すぎるよう。変数の意味を考えて、追加しないといけないかも。

tic.glm <- glm(V86~V1+V2+V3+V4+V5+V6+V7+V8+   V10+
V11+V12+    V14+    V16+V17+    V19+V20+
V21+V22+V23+    V25+V26+V27+V28+    V30+
        V33+V34+V35+    V37+V38+V39+V40+
    V42+V43+V44+V45+V46+V47+V48+V49+V50+
V51+V52+V53+V54+V55+V56+V57+V58+V59+V60+
V61+V62+V63+V64+V65+V66+V67+V68+V69+V70+
V71+V72+V73+V74+V75+V76+V77+V78+V79+V80+
V81+V82+V83+V84+V85, family="binomial", data=tic.learn)
table(predict(tic.glm, newdata=tic.eval)>0.5)
tic.glm <- glm(V86~     V2+V3+V4+    V6+V7+V8+   V10+
V11+V12+    V14+    V16+V17+    V19+V20+
V21+V22+V23+    V25+V26+V27+V28+    V30+
        V33+V34+V35+    V37+V38+V39+V40+
    V42+V43+V44+V45+V46+V47+V48+V49+V50+
V51+V52+V53+V54+V55+V56+V57+V58+V59+V60+
V61+V62+V63+V64+V65+V66+V67+V68+V69+V70+
V71+V72+V73+V74+V75+V76+V77+V78+V79+V80+
V81+V82+V83+V84+V85, family="binomial", data=tic.learn)
tic.glm <- glm(V86~V44+V45+V46+V47+V48+V49+V50+
V51+V52+V53+V54+V55+V56+V57+V58+V59+V60+
V61+V62+V63+V64+V65+V66+V67+V68+V69+V70+
V71+V72+V73+V74+V75+V76+V77+V78+V79+V80+
V81+V82+V83+V84+V85, family="binomial", data=tic.learn)
tic.glm <- glm(V86~V44+V45+V46+V47+V48+V49+V50+
V51+V52+V53+V54+V55+V56+V57+V58+V59+V60+
V61+V62+V63+V64, family="binomial", data=tic.learn)

次の1行を実行すると、不契約ばかり。

tic.rpart <- rpart(V86~., data=tic.learn)

次の1行は、動かない。

tic.gam <- gam(V86~V1+s(V2)+s(V3)+s(V4)+V5+s(V6)+s(V7)+s(V8)+s(V9)+s(V10)+
s(V11)+s(V12)+s(V13)+s(V14)+s(V15)+s(V16)+s(V17)+s(V18)+s(V19)+s(V20)+
s(V21)+s(V22)+s(V23)+s(V24)+s(V25)+s(V26)+s(V27)+s(V28)+s(V29)+s(V30)+
s(V31)+s(V32)+s(V33)+s(V34)+s(V35)+s(V36)+s(V37)+s(V38)+s(V39)+s(V40)+
s(V41)+s(V42)+s(V43)+s(V44)+s(V45)+s(V46)+s(V47)+s(V48)+s(V49)+s(V50)+
s(V51)+s(V52)+s(V53)+s(V54)+s(V55)+s(V56)+s(V57)+s(V58)+s(V59)+s(V60)+
s(V61)+s(V62)+s(V63)+s(V64)+s(V65)+s(V66)+s(V67)+s(V68)+s(V69)+s(V70)+
s(V71)+s(V72)+s(V73)+s(V74)+s(V75)+s(V76)+s(V77)+s(V78)+s(V79)+s(V80)+
s(V81)+s(V82)+s(V83)+s(V84)+s(V85), family="binomial", data=tic.learn)

s()が使えるのはデータが4種類以上ある時だけ、というメッセージが出るので、各変数の値の数を数えて、3個以下のものだけ表示させてみる。

for( j in c(1:86) ) {
  tmp <- table(tic.learn[,j])
  if(dim(tmp)<4) {
    print(j)
    print(tmp)
  }
}

これを反映させると、こうなる。

tic.gam <- gam(V86~V1+s(V2)+s(V3)+s(V4)+V5+s(V6)+s(V7)+s(V8)+s(V9)+s(V10)+
s(V11)+s(V12)+s(V13)+s(V14)+s(V15)+s(V16)+s(V17)+s(V18)+s(V19)+s(V20)+
s(V21)+s(V22)+s(V23)+s(V24)+s(V25)+s(V26)+s(V27)+s(V28)+s(V29)+s(V30)+
s(V31)+s(V32)+s(V33)+s(V34)+s(V35)+s(V36)+s(V37)+s(V38)+s(V39)+s(V40)+
s(V41)+s(V42)+s(V43)+s(V44)+s(V45)+s(V46)+s(V47)+s(V48)+s(V49)+s(V50)+
s(V51)+s(V52)+s(V53)+s(V54)+s(V55)+s(V56)+  V57 +s(V58)+s(V59)+  V60 +
s(V61)+  V62 +s(V63)+s(V64)+  V65 +  V66 +  V67 +s(V68)+s(V69)+s(V70)+
s(V71)+s(V72)+s(V73)+s(V74)+  V75 +s(V76)+  V77 +  V78 +  V79 +s(V80)+
  V81 +  V82 +s(V83)+  V84 +  V85 , family="binomial", data=tic.learn)

さらにここから次の1行を実行すると、エラーになる。

tic.gam.step <- step(gam(V86~V1+s(V2)+s(V3)+s(V4)+V5+s(V6)+s(V7)+s(V8)+s(V9)+s(V10)+
s(V11)+s(V12)+s(V13)+s(V14)+s(V15)+s(V16)+s(V17)+s(V18)+s(V19)+s(V20)+
s(V21)+s(V22)+s(V23)+s(V24)+s(V25)+s(V26)+s(V27)+s(V28)+s(V29)+s(V30)+
s(V31)+s(V32)+s(V33)+s(V34)+s(V35)+s(V36)+s(V37)+s(V38)+s(V39)+s(V40)+
s(V41)+s(V42)+s(V43)+s(V44)+s(V45)+s(V46)+s(V47)+s(V48)+s(V49)+s(V50)+
s(V51)+s(V52)+s(V53)+s(V54)+s(V55)+s(V56)+  V57 +s(V58)+s(V59)+  V60 +
s(V61)+  V62 +s(V63)+s(V64)+  V65 +  V66 +  V67 +s(V68)+s(V69)+s(V70)+
s(V71)+s(V72)+s(V73)+s(V74)+  V75 +s(V76)+  V77 +  V78 +  V79 +s(V80)+
  V81 +  V82 +s(V83)+  V84 +  V85 , family="binomial", data=tic.learn)

おまけ

AdaBoostを適用するには、adaパッケージをインストールする。

install.packages(c("ada"), dependencies=TRUE)

決定木を弱学習機械として用いるには例えば、

tic.ada <- ada(V86~., 
  data=tic.learn, 
  iter=100, 
  control=rpart.control(maxdepth=1, cp=-1, minsplit=0))

とする。rpart.controlの中身は、

  • maxdepth=1が1回だけ分岐せよ
  • cp=-1は不純度によらず必ず分岐せよ
  • minsplit=0は親ノードのレコード数が幾つでも必ず分岐せよ

と、必ず1段の決定木を学習で得ること、と指定されている。

これでiterとmaxdepthを変えると、もしかして良い訪問案が学習できるかもしれない? ただしこの二つのパラメータを大きくすると、計算時間が増大するので要注意。 iterに比例し、2のmaxdepth乗にも比例して、増えていく。