Sys.setenv("http_proxy"="http://130.153.8.66:8080/") install.packages(c("mvpart", "kernlab", "), dependencies=TRUE)
これでエラーが出る場合には、プロキシの設定を試みると良い。
library(mvpart) library(MASS) library(kernlab)
以上の3つのライブラリを、この課題では使う可能性がある。
mvpartのインストールについて。
で対応して欲しい。同様のことを、kernlab_0.9-14.zipについても行う。
データ読み込みはできても、CRANにも接続できないなんて。
tic.learn <- 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)
回帰分析とロジスティック回帰分析は、上の6行を実行しないtic.learnとtic.evalを用いるのがよい。 決定木では上の6行を実行した後のtic.learnとtic.evalを用いるのがよい。 また決定木に関しては、実行した場合と実行しない場合とで結果を比較すると良いかもしれない。
タイタニック号の乗客の生死のデータがある。Rで
Titanic
と実行すると、表示される。
これを個別のレコードに展開し、更に救出の優先順位を高く設定された女性もしくは子供と、低めに設定された成人男性という変数も加える。
data("Titanic", package = "datasets") titanic <- as.data.frame(Titanic) titanic <- titanic[rep(1:nrow(titanic), titanic$Freq), 1:4] names(titanic)[2] <- "Gender" titanic <- transform(titanic, Treatment = factor( Gender == "Female" | Age == "Child", levels = c(FALSE, TRUE), labels = c("Normal\n(Male&Adult)", "Preferential\n(Female|Child)") ))
さらに、数値に変換したデータも用意する。これは、線形学習機械とロジスティック学習機械のために用いる。
titanic.2 <- data.frame(Gender=(titanic$Gender=="Female")*1, Age=(titanic$Age=="Child")*1, Survived=(titanic$Survived=="Yes")*1, Class=(titanic$Class=="Crew")*1+(titanic$Class=="3rd")*2+(titanic$Class=="2nd")*3+(titanic$Class=="1st")*4)
変数 | 数値化情報 |
性別(Gender) | 女性(Female)=1, 男性(Male)=0 |
年齢(Age) | 子供(Child)=1, 大人(Adult)=0 |
客室等級(Class) | 1等(1st)=4,2等(2nd)=3,3等(3rd)=2,乗組員(Crew)=1 |
生存(Survived) | 生存(Yes)=1,死亡(No)=0 |
先週の課題は、タイタニック号で、生存率が高くなる条件を求めよ、という問題と同等。 まず titanic というデータに含まれる変数の一覧を
names(titanic)
で取り出す。すると
[1] "Class" "Gender" "Age" "Survived" "Treatment"
のように5個の変数があることが分かる。
これを用いて、生存(Survived)についての集計を、客室等級(Class)、性別(Gender)、年齢層(Age)の組み合わせで行うには、次の1行を実行する。 「$」の前がデータ名、「$」の後ろに変数名(フィールド名)を付ける。
ftable(titanic$Gender,titanic$Age,titanic$Class, titanic$Survived)
すると
No Yes Male Child 1st 0 5 2nd 0 11 3rd 35 13 Crew 0 0 Adult 1st 118 57 2nd 154 14 3rd 387 75 Crew 670 192 Female Child 1st 0 1 2nd 0 13 3rd 17 14 Crew 0 0 Adult 1st 4 140 2nd 13 80 3rd 89 76 Crew 3 20
と出力されるのを、各自確認せよ。
同じことは、数値化したデータを用いても得られる。
ftable(titanic.2$Gender,titanic.2$Age,titanic.2$Class, titanic.2$Survived)
上の出力と下の出力を見比べて、各変数の値の数値化を確認せよ。
0 1 0 0 1 670 192 2 387 75 3 154 14 4 118 57 1 1 0 0 2 35 13 3 0 11 4 0 5 1 0 1 3 20 2 89 76 3 13 80 4 4 140 1 1 0 0 2 17 14 3 0 13 4 0 1
上のデータ(titanic.2)に対して
lm(Survived~., data=titanic.2)
を実行すると、全変数を用いた線形学習機械が最小二乗法により、学習される。学習結果のみなら、この一行で
Call: lm(formula = Survived ~ ., data = titanic.2) Coefficients: (Intercept) Gender Age Class 0.11725 0.46493 0.09655 0.05029
と出力される。
この結果に、統計的推測の結果を付与するなら
summary(lm(Survived~., data=titanic.2))
を実行すればよい。summary()の出力は次のように得られる。
Call: lm(formula = Survived ~ ., data = titanic.2) Residuals (残差の分布): Min 1Q Median 3Q Max -0.7833 -0.2178 -0.1675 0.2207 0.8325 Coefficients (回帰係数): Estimate Std. Error t value Pr(>|t|) 推定値 標準誤差 t値 P値 (Intercept) 0.117252 0.019113 6.135 1.01e-09 *** 切片 Gender 0.464930 0.023325 19.933 < 2e-16 *** Age 0.096553 0.040856 2.363 0.0182 * Class 0.050289 0.008989 5.594 2.49e-08 *** --- Signif. codes (有意水準の表示法): 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 Residual standard error (残差の標準偏差): 0.4131 on 2197 degrees of freedom Multiple R-squared (決定係数): 0.2209, Adjusted R-squared (自由度調整済み決定係数): 0.2198 F-statistic (F統計量、分散分析の結果): 207.7 on 3 and 2197 DF, p-value: < 2.2e-16
AICを用いた説明変数の選択を行うのであれば、
library(MASS)
とMASSパッケージを読み込んでから使える関数stepAIC()を用いて、
stepAIC(lm(Survived~., data=titanic.2))
を実行する。
Start: AIC=-3887.24 Survived ~ Gender + Age + Class Df Sum of Sq RSS AIC <none> 374.99 -3887.2 - Age 1 0.953 375.95 -3883.7 - Class 1 5.342 380.33 -3858.1 - Gender 1 67.815 442.81 -3523.4 Call: lm(formula = Survived ~ Gender + Age + Class, data = titanic.2) Coefficients: (Intercept) Gender Age Class 0.11725 0.46493 0.09655 0.05029
これはAICを変数増減法で最適化する関数であり、最終的に採用するモデルを出力してくれる便利な関数である。
titanic.lm <- lm(Survived~., data=titanic.2) par(mfrow=c(2,2)) plot(titanic.lm) par(mfrow=c(1,1))
上の回帰分析から
Survived = 0.11725 + 0.46493 * Gender + 0.09655 * Age + 0.05029 * Class
という学習結果が得られた。Survivedを大きくする(=生存する)ために、Genderが0よりは1の方が良いことは、回帰係数 0.46493 を見れば分かる。 同様に Age も0よりは1の方が良く、Classも1よりは4の方が良い。 そしてどの変数も有意であることから、生存するためには、
のが好条件となることが分かる。
生存確率をpとして
log(p/1-p)
を与える学習機械が、ロジスティック回帰モデルである。Rではglm()という関数を用いて
glm(Survived~., data=titanic.2, family="binomial")
で、ロジスティック回帰モデルを最尤推定で学習させることができる。 結果は
Call: glm(formula = Survived ~ ., family = "binomial", data = titanic.2) Coefficients: (Intercept) Gender Age Class -1.8622 2.0580 0.5115 0.2783 Degrees of Freedom: 2200 Total (i.e. Null); 2197 Residual Null Deviance: 2769 Residual Deviance: 2299 AIC: 2307
と、lm()と似た出力を得る。 これもsummary()を加えて
summary(glm(Survived~., data=titanic.2, family="binomial"))
を実行すると、
Call: glm(formula = Survived ~ ., family = "binomial", data = titanic.2) Deviance Residuals: Min 1Q Median 3Q Max -1.7597 -0.6926 -0.6109 0.7055 1.8818 Coefficients: Estimate Std. Error z value Pr(>|z|) (Intercept) -1.86224 0.11615 -16.033 < 2e-16 *** Gender 2.05802 0.12604 16.328 < 2e-16 *** Age 0.51147 0.22292 2.294 0.0218 * Class 0.27834 0.05047 5.515 3.48e-08 *** --- Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 (Dispersion parameter for binomial family taken to be 1) Null deviance: 2769.5 on 2200 degrees of freedom Residual deviance: 2299.2 on 2197 degrees of freedom AIC: 2307.2 Number of Fisher Scoring iterations: 4
と、種々の検定統計量が一緒に出力される。
AICを用いた説明変数の選択を行うのであれば、
library(MASS)
とMASSパッケージを読み込んでから使える関数stepAIC()を用いて、
stepAIC(glm(Survived~., data=titanic.2, family="binomial"))
を実行する。
Start: AIC=2307.21 Survived ~ Gender + Age + Class Df Deviance AIC <none> 2299.2 2307.2 - Age 1 2304.4 2310.4 - Class 1 2329.1 2335.1 - Gender 1 2595.5 2601.5 Call: glm(formula = Survived ~ Gender + Age + Class, family = "binomial", data = titanic.2) Coefficients: (Intercept) Gender Age Class -1.8622 2.0580 0.5115 0.2783 Degrees of Freedom: 2200 Total (i.e. Null); 2197 Residual Null Deviance: 2769 Residual Deviance: 2299 AIC: 2307
これはAICを変数増減法で最適化する関数であり、最終的に採用するモデルを出力してくれる便利な関数である。
titanic.glm <- glm(Survived~., data=titanic.2, family="binomial") par(mfrow=c(2,2)) plot(titanic.glm) par(mfrow=c(1,1))
上の回帰分析から
log(Pr[Survived]/(1-Pr[Survived])) = -1.86224 + 2.050802 * Gender + 0.51147 * Age + 0.27834 * Class
という学習結果が得られた。Pr[Survived](生存する確率)を大きくするために、Genderが0よりは1の方が良いことは、回帰係数 2.050802 を見れば分かる。 同様に Age も0よりは1の方が良く、Classも1よりは4の方が良い。 そしてどの変数も有意であることから、生存するためには、
のが好条件となることが分かる。
決定木は生存確率pの高低を際立たせるような、データの分割を表現するモデルである。Rではrpartパッケージの中のrpart()、もしくはmvpartパッケージの中のmvpart()という関数を用いて
library(mvpart) rpart(Survived~.,data=titanic)
で、決定木を学習させることができる。 結果は
n= 2201 node), split, n, loss, yval, (yprob) * denotes terminal node 1) root 2201 711 No (0.6769650 0.3230350) 2) Gender=Male 1731 367 No (0.7879838 0.2120162) 4) Age=Adult 1667 338 No (0.7972406 0.2027594) * 5) Age=Child 64 29 No (0.5468750 0.4531250) 10) Class=3rd 48 13 No (0.7291667 0.2708333) * 11) Class=1st,2nd 16 0 Yes (0.0000000 1.0000000) * 3) Gender=Female 470 126 Yes (0.2680851 0.7319149) 6) Class=3rd 196 90 No (0.5408163 0.4591837) * 7) Class=1st,2nd,Crew 274 20 Yes (0.0729927 0.9270073) *
と、これまでとは違ったものになる。各行はノードと呼ばれる分割単位を表し、 たとえば
1) root 2201 711 No (0.6769650 0.3230350)
は、
を意味する。これを性別で分割すると
2) Gender=Male 1731 367 No (0.7879838 0.2120162) 3) Gender=Female 470 126 Yes (0.2680851 0.7319149)
を得た、とある。これは
と読み取ることができる。 生存確率の高いノードを探すと
7) Class=1st,2nd,Crew 274 20 Yes (0.0729927 0.9270073) *
が目に付く。これは、
と条件がふたつついたノードであり、「女性かつ1等客室の乗客、2等客室の乗客、または乗組員」となる。 この属性を持つ人々は、生存確率が0.9270073と最も高い。 逆に最も生存確率が低いのは
10) Class=3rd 48 13 No (0.7291667 0.2708333) *
であり、このノードまでを
と辿れることから、「男性で成人で3等客室の乗客」は生存確率が0.2708333と最も低かったことが分かる。
これもsummary()を加えて
summary(rpart(Survived~.,data=titanic))
を実行すると、
Call: rpart(formula = Survived ~ ., data = titanic) n= 2201 CP nsplit rel error xerror xstd 1 0.30661041 0 1.0000000 1.0000000 0.03085662 2 0.02250352 1 0.6933896 0.7102672 0.02774463 3 0.01125176 2 0.6708861 0.6947961 0.02752965 4 0.01000000 4 0.6483826 0.6877637 0.02743006 Node number 1: 2201 observations, complexity param=0.3066104 predicted class=No expected loss=0.323035 class counts: 1490 711 probabilities: 0.677 0.323 left son=2 (1731 obs) right son=3 (470 obs) Primary splits: Gender splits as LR, improve=199.821600, (0 missing) Treatment splits as LR, improve=198.792000, (0 missing) Class splits as RRLL, improve= 69.684100, (0 missing) Age splits as RL, improve= 9.165241, (0 missing) Surrogate splits: Treatment splits as LR, agree=0.971, adj=0.864, (0 split) Node number 2: 1731 observations, complexity param=0.01125176 predicted class=No expected loss=0.2120162 class counts: 1364 367 probabilities: 0.788 0.212 left son=4 (1667 obs) right son=5 (64 obs) Primary splits: Age splits as RL, improve=7.726764, (0 missing) Treatment splits as LR, improve=7.726764, (0 missing) Class splits as RLLL, improve=7.046106, (0 missing) Surrogate splits: Treatment splits as LR, agree=1, adj=1, (0 split) Node number 3: 470 observations, complexity param=0.02250352 predicted class=Yes expected loss=0.2680851 class counts: 126 344 probabilities: 0.268 0.732 left son=6 (196 obs) right son=7 (274 obs) Primary splits: Class splits as RRLR, improve=50.015320, (0 missing) Age splits as LR, improve= 1.197586, (0 missing) Surrogate splits: Age splits as LR, agree=0.619, adj=0.087, (0 split) Node number 4: 1667 observations predicted class=No expected loss=0.2027594 class counts: 1329 338 probabilities: 0.797 0.203 Node number 5: 64 observations, complexity param=0.01125176 predicted class=No expected loss=0.453125 class counts: 35 29 probabilities: 0.547 0.453 left son=10 (48 obs) right son=11 (16 obs) Primary splits: Class splits as RRL-, improve=12.76042, (0 missing) Node number 6: 196 observations predicted class=No expected loss=0.4591837 class counts: 106 90 probabilities: 0.541 0.459 Node number 7: 274 observations predicted class=Yes expected loss=0.0729927 class counts: 20 254 probabilities: 0.073 0.927 Node number 10: 48 observations predicted class=No expected loss=0.2708333 class counts: 35 13 probabilities: 0.729 0.271 Node number 11: 16 observations predicted class=Yes expected loss=0 class counts: 0 16 probabilities: 0.000 1.000
と、分割の経緯が一緒に出力される。
上のsummary()の出力の中に「complexity param」という項目が見られる。 rpart()では、この値の下限を指定することで、生成する決定木の深さを選択できる。
次の3行による学習結果の違いを観察してみよ。
print(rpart(Survived~.,data=titanic, control=c(cp=0.5))) print(rpart(Survived~.,data=titanic, control=c(cp=0.05))) print(rpart(Survived~.,data=titanic, control=c(cp=0.005)))
titanic.rpart <- rpart(Survived~., data=titanic) plot(titanic.rpart) text(titanic.rpart)
上の決定木の結果から、生存確率が高い条件は
であり、生存確率が特に低いのは
であったことが分かる。
tic.data.txtからの要約。
各変数に閾値を設けてルールを生成したとする。 たとえば、「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)
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の中身は、
と、必ず1段の決定木を学習で得ること、と指定されている。
これでiterとmaxdepthを変えると、もしかして良い訪問案が学習できるかもしれない? ただしこの二つのパラメータを大きくすると、計算時間が増大するので要注意。 iterに比例し、2のmaxdepth乗にも比例して、増えていく。