##This is analysis of email vs spam data set
library(tree)
## Warning: package 'tree' was built under R version 4.5.3
library(kernlab)
data(spam)
class(spam)
## [1] "data.frame"
str(spam)
## 'data.frame': 4601 obs. of 58 variables:
## $ make : num 0 0.21 0.06 0 0 0 0 0 0.15 0.06 ...
## $ address : num 0.64 0.28 0 0 0 0 0 0 0 0.12 ...
## $ all : num 0.64 0.5 0.71 0 0 0 0 0 0.46 0.77 ...
## $ num3d : num 0 0 0 0 0 0 0 0 0 0 ...
## $ our : num 0.32 0.14 1.23 0.63 0.63 1.85 1.92 1.88 0.61 0.19 ...
## $ over : num 0 0.28 0.19 0 0 0 0 0 0 0.32 ...
## $ remove : num 0 0.21 0.19 0.31 0.31 0 0 0 0.3 0.38 ...
## $ internet : num 0 0.07 0.12 0.63 0.63 1.85 0 1.88 0 0 ...
## $ order : num 0 0 0.64 0.31 0.31 0 0 0 0.92 0.06 ...
## $ mail : num 0 0.94 0.25 0.63 0.63 0 0.64 0 0.76 0 ...
## $ receive : num 0 0.21 0.38 0.31 0.31 0 0.96 0 0.76 0 ...
## $ will : num 0.64 0.79 0.45 0.31 0.31 0 1.28 0 0.92 0.64 ...
## $ people : num 0 0.65 0.12 0.31 0.31 0 0 0 0 0.25 ...
## $ report : num 0 0.21 0 0 0 0 0 0 0 0 ...
## $ addresses : num 0 0.14 1.75 0 0 0 0 0 0 0.12 ...
## $ free : num 0.32 0.14 0.06 0.31 0.31 0 0.96 0 0 0 ...
## $ business : num 0 0.07 0.06 0 0 0 0 0 0 0 ...
## $ email : num 1.29 0.28 1.03 0 0 0 0.32 0 0.15 0.12 ...
## $ you : num 1.93 3.47 1.36 3.18 3.18 0 3.85 0 1.23 1.67 ...
## $ credit : num 0 0 0.32 0 0 0 0 0 3.53 0.06 ...
## $ your : num 0.96 1.59 0.51 0.31 0.31 0 0.64 0 2 0.71 ...
## $ font : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num000 : num 0 0.43 1.16 0 0 0 0 0 0 0.19 ...
## $ money : num 0 0.43 0.06 0 0 0 0 0 0.15 0 ...
## $ hp : num 0 0 0 0 0 0 0 0 0 0 ...
## $ hpl : num 0 0 0 0 0 0 0 0 0 0 ...
## $ george : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num650 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ lab : num 0 0 0 0 0 0 0 0 0 0 ...
## $ labs : num 0 0 0 0 0 0 0 0 0 0 ...
## $ telnet : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num857 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ data : num 0 0 0 0 0 0 0 0 0.15 0 ...
## $ num415 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num85 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ technology : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num1999 : num 0 0.07 0 0 0 0 0 0 0 0 ...
## $ parts : num 0 0 0 0 0 0 0 0 0 0 ...
## $ pm : num 0 0 0 0 0 0 0 0 0 0 ...
## $ direct : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ cs : num 0 0 0 0 0 0 0 0 0 0 ...
## $ meeting : num 0 0 0 0 0 0 0 0 0 0 ...
## $ original : num 0 0 0.12 0 0 0 0 0 0.3 0 ...
## $ project : num 0 0 0 0 0 0 0 0 0 0.06 ...
## $ re : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ edu : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ table : num 0 0 0 0 0 0 0 0 0 0 ...
## $ conference : num 0 0 0 0 0 0 0 0 0 0 ...
## $ charSemicolon : num 0 0 0.01 0 0 0 0 0 0 0.04 ...
## $ charRoundbracket : num 0 0.132 0.143 0.137 0.135 0.223 0.054 0.206 0.271 0.03 ...
## $ charSquarebracket: num 0 0 0 0 0 0 0 0 0 0 ...
## $ charExclamation : num 0.778 0.372 0.276 0.137 0.135 0 0.164 0 0.181 0.244 ...
## $ charDollar : num 0 0.18 0.184 0 0 0 0.054 0 0.203 0.081 ...
## $ charHash : num 0 0.048 0.01 0 0 0 0 0 0.022 0 ...
## $ capitalAve : num 3.76 5.11 9.82 3.54 3.54 ...
## $ capitalLong : num 61 101 485 40 40 15 4 11 445 43 ...
## $ capitalTotal : num 278 1028 2259 191 191 ...
## $ type : Factor w/ 2 levels "nonspam","spam": 2 2 2 2 2 2 2 2 2 2 ...
#The variable "type" is the outcome variable
# I'll produce my own training and test sets of the size described in the book
set.seed(3731)
train.ind<-sort(sample(1:4601, 3065, replace=FALSE))
test.ind<- seq(1,4601)[-train.ind]
spam.train<-spam[train.ind,]
spam.test<-spam[test.ind,]
#Fit a classification tree to the training data
spam.tree<-tree(type~.,data=spam.train)
plot(spam.tree)
text(spam.tree)

summary(spam.tree)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 15
## Residual mean deviance: 0.4404 = 1343 / 3050
## Misclassification error rate: 0.07732 = 237 / 3065
conf.mat.train<-table(spam.train$type,predict(spam.tree,type='class'))
conf.mat.train
##
## nonspam spam
## nonspam 1775 78
## spam 159 1053
#How well does this tree perform on the holdout set
pred.tree<-predict(spam.tree, newdata=spam.test,type="class")
conf.mat<-table(spam.test$type,pred.tree)
conf.mat
## pred.tree
## nonspam spam
## nonspam 888 47
## spam 93 508
#misclassification rate
1-sum(diag(conf.mat))/sum(conf.mat)
## [1] 0.09114583
1536-sum(diag(conf.mat)) #Number of incorrect calls
## [1] 140
#Cost complexity pruning the default tree shows misclassification best for full tree
spam.cv <- cv.tree(spam.tree, FUN = prune.misclass)
par(mfrow = c(1, 2))
plot(spam.cv$size, spam.cv$dev, type = "b")
plot(spam.cv$k, spam.cv$dev, type = "b")

spam.cv
## $size
## [1] 15 14 9 8 7 6 5 3 2 1
##
## $dev
## [1] 299 299 309 331 333 337 368 435 644 1212
##
## $k
## [1] -Inf 0.0 7.6 12.0 13.0 14.0 34.0 48.0 177.0 591.0
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
min(spam.cv$dev)
## [1] 299
which(spam.cv$dev==min(spam.cv$dev))
## [1] 1 2
spam.cv$size[2] #size of best tree that is smallest in that class
## [1] 14
#prune down and see how the size 14 tree works
#DOUBLE CHECK best above
spam.prune <- prune.misclass(spam.tree, best = 14)
par(mfrow=c(1,2))
plot(spam.tree) #original tree
plot(spam.prune) #pruned tree

#check in-sample error rate of pruned tree
summary(spam.prune)
##
## Classification tree:
## snip.tree(tree = spam.tree, nodes = 12L)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 14
## Residual mean deviance: 0.4563 = 1392 / 3051
## Misclassification error rate: 0.07732 = 237 / 3065
#check out-of-sample error rate of pruned tree
pred.prune<-predict(spam.prune, newdata=spam.test,type="class")
conf.mat.prune<-table(spam.test$type,pred.prune)
conf.mat.prune
## pred.prune
## nonspam spam
## nonspam 888 47
## spam 93 508
#misclassification rate
1-sum(diag(conf.mat.prune))/sum(conf.mat.prune)
## [1] 0.09114583
1536-(sum(diag(conf.mat.prune)))
## [1] 140
#Build a larger tree for cost complexity pruning
spam.tree.ctrl<-tree(type~.,data=spam.train,control=tree.control(nobs = 3065, minsize = 2, mindev=0))
par(mfrow=c(1,1))
plot(spam.tree.ctrl)
text(spam.tree.ctrl)

summary(spam.tree.ctrl)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train, control = tree.control(nobs = 3065,
## minsize = 2, mindev = 0))
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "free" "internet"
## [9] "capitalAve" "you" "edu" "re"
## [13] "capitalTotal" "our" "mail" "all"
## [17] "technology" "will" "your" "charSemicolon"
## [21] "meeting" "address" "num3d" "num650"
## [25] "data" "charRoundbracket" "make" "hpl"
## [29] "over" "email" "charHash" "report"
## [33] "num1999" "business" "receive" "people"
## Number of terminal nodes: 176
## Residual mean deviance: 0.0009597 = 2.773 / 2889
## Misclassification error rate: 0.0003263 = 1 / 3065
#Out of sample misclassification of the large tree
#How well does this tree perform on the holdout set
pred.tree.ctrl<-predict(spam.tree.ctrl, newdata=spam.test,type="class")
conf.mat.ctrl<-table(spam.test$type,pred.tree.ctrl)
conf.mat.ctrl
## pred.tree.ctrl
## nonspam spam
## nonspam 870 65
## spam 71 530
#misclassification rate
1-sum(diag(conf.mat.ctrl))/sum(conf.mat.ctrl)
## [1] 0.08854167
1536-sum(diag(conf.mat.ctrl))
## [1] 136
#Well growing the biggest possible tree overfit relative to the training error rate
#but was superior to the smaller tree in the out-of-sample misclassification rate.
spam.ctrl.cv<- cv.tree(spam.tree.ctrl, FUN = prune.misclass)
par(mfrow = c(1, 2))
plot(spam.ctrl.cv$size, spam.ctrl.cv$dev, type = "b")
plot(spam.ctrl.cv$k, spam.ctrl.cv$dev, type = "b")

spam.ctrl.cv
## $size
## [1] 176 175 170 136 133 125 116 111 104 76 66 64 59 51 39 36 34 30 28
## [20] 24 19 18 16 11 10 9 8 7 6 5 3 2 1
##
## $dev
## [1] 293 293 293 293 293 293 293 293 293 293 293 293 293 293 293
## [16] 293 293 293 293 293 293 296 297 300 296 321 324 328 328 368
## [31] 443 638 1212
##
## $k
## [1] -Inf 0.0000000 0.2000000 0.5000000 0.6666667 0.7500000
## [7] 0.7777778 0.8000000 0.8571429 1.0000000 1.2000000 1.5000000
## [13] 1.6000000 1.7500000 2.0000000 2.3333333 2.5000000 2.7500000
## [19] 3.0000000 3.7500000 4.0000000 5.0000000 6.0000000 7.6000000
## [25] 8.0000000 12.0000000 13.0000000 14.0000000 15.0000000 34.0000000
## [31] 48.0000000 177.0000000 591.0000000
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
min(spam.ctrl.cv$dev)
## [1] 293
which(spam.ctrl.cv$dev==min(spam.ctrl.cv$dev))
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#be sure to change for current analysis
spam.ctrl.cv$size[1] #Recommends a size __ tree
## [1] 176
#prune down and see how the suize 14 tree works
spam.prune.ctrl <- prune.misclass(spam.tree.ctrl, best = 176)
par(mfrow=c(1,2))
plot(spam.tree) #original tree
plot(spam.prune.ctrl) #pruned tree

#check in-sample error rate of pruned tree
summary(spam.prune.ctrl)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train, control = tree.control(nobs = 3065,
## minsize = 2, mindev = 0))
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "free" "internet"
## [9] "capitalAve" "you" "edu" "re"
## [13] "capitalTotal" "our" "mail" "all"
## [17] "technology" "will" "your" "charSemicolon"
## [21] "meeting" "address" "num3d" "num650"
## [25] "data" "charRoundbracket" "make" "hpl"
## [29] "over" "email" "charHash" "report"
## [33] "num1999" "business" "receive" "people"
## Number of terminal nodes: 176
## Residual mean deviance: 0.0009597 = 2.773 / 2889
## Misclassification error rate: 0.0003263 = 1 / 3065
#check out-of-sample error rate of pruned tree
pred.prune.ctrl<-predict(spam.prune.ctrl, newdata=spam.test,type="class")
conf.mat.prune.ctrl<-table(spam.test$type,pred.prune.ctrl)
conf.mat.prune.ctrl
## pred.prune.ctrl
## nonspam spam
## nonspam 871 64
## spam 71 530
#misclassification rate
1-sum(diag(conf.mat.prune.ctrl))/sum(conf.mat.prune.ctrl)
## [1] 0.08789062
1536-sum(diag(conf.mat.prune.ctrl))
## [1] 135
###Revisit the Gini index version
#spam.tree.Gini<-tree(type~.,data=spam.train,split="gini")
spam.tree.Gini<-tree(type~.,
data=spam.train,
split="gini",
control=tree.control(nobs=3065,
mincut=100,
minsize=200,
mindev=.01))
summary(spam.tree.Gini)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train, control = tree.control(nobs = 3065,
## mincut = 100, minsize = 200, mindev = 0.01), split = "gini")
## Variables actually used in tree construction:
## [1] "conference" "project" "num415"
## [4] "original" "pm" "technology"
## [7] "num85" "data" "charSquarebracket"
## [10] "report" "charSemicolon" "num1999"
## [13] "hp" "credit" "receive"
## [16] "people" "make" "address"
## [19] "all" "will" "re"
## [22] "charExclamation" "capitalTotal"
## Number of terminal nodes: 24
## Residual mean deviance: 1.051 = 3197 / 3041
## Misclassification error rate: 0.262 = 803 / 3065
summary(spam.tree)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 15
## Residual mean deviance: 0.4404 = 1343 / 3050
## Misclassification error rate: 0.07732 = 237 / 3065
par(mfrow=c(1,1))
plot(spam.tree)
text(spam.tree)

plot(spam.tree.Gini)
text(spam.tree.Gini)

#Compare the various in- and out-of-sample misclassification rates - expectations met?
################################################################################
######################## Bagging ################################################
################################################################################
library(randomForest)
## Warning: package 'randomForest' was built under R version 4.5.3
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
#There are 57 candidate predictors
#For bagging, allow all predictors to be considered at every split
set.seed(23784)
spam.bag<-randomForest(type~.,
data=spam.train,
mtry=57,
importance=TRUE,
ntree=500)
#training predictions
pred.bag.train<-predict(spam.bag,
newdata=spam.train,
type="class")
conf.mat.bag.train<-table(spam.train$type,
pred.bag.train)
conf.mat.bag.train
## pred.bag.train
## nonspam spam
## nonspam 1852 1
## spam 0 1212
#training misclassification rate
MCR.bag.train<-
1-sum(diag(conf.mat.bag.train))/sum(conf.mat.bag.train)
MCR.bag.train
## [1] 0.0003262643
#holdout predictions
pred.bag<-predict(spam.bag,
newdata=spam.test,
type="class")
conf.mat.bag<-table(spam.test$type,
pred.bag)
conf.mat.bag
## pred.bag
## nonspam spam
## nonspam 899 36
## spam 50 551
#holdout misclassification rate
MCR.bag<-
1-sum(diag(conf.mat.bag))/sum(conf.mat.bag)
MCR.bag
## [1] 0.05598958
#Number incorrectly classified
1536-sum(diag(conf.mat.bag))
## [1] 86
#variable importance
importance(spam.bag)
## nonspam spam MeanDecreaseAccuracy MeanDecreaseGini
## make 3.4375107 7.1340748 7.482192 4.5451238
## address 8.3856032 7.0315143 11.220811 3.4421350
## all 1.7399876 6.5128243 6.859883 5.8280717
## num3d 11.2614372 -0.6113121 9.583824 2.8370080
## our 25.6120408 24.1997680 33.596296 23.5780096
## over 16.1088210 5.2778569 16.357111 4.9756630
## remove 71.8486869 43.8974403 79.650696 180.6049774
## internet 15.1688083 6.2211822 15.190525 8.0687118
## order 11.4536096 1.9803425 12.250713 3.5688217
## mail 5.7479403 5.5055160 8.060951 7.3231617
## receive 17.2489085 2.0130431 17.464684 4.4653853
## will 2.8378200 11.4937870 11.110846 9.2491864
## people -0.9692594 3.4124512 2.245570 3.9128499
## report 10.3119508 8.1193903 12.179344 3.8215417
## addresses 5.3310266 3.0249328 6.265170 1.1292987
## free 36.5417447 30.3690826 44.990199 55.4029369
## business 19.7444486 9.2738823 20.978700 11.3550531
## email 13.0181662 9.0610999 14.245406 9.4070060
## you 14.4237939 15.8286180 22.036126 28.0117604
## credit 12.8634464 -0.4241135 12.880596 2.5884365
## your 15.2606515 15.2803819 20.378356 27.4350838
## font 15.6207333 12.5478591 17.354343 3.4502325
## num000 21.1644025 3.6890746 21.741198 9.3520811
## money 18.3806807 13.6155787 20.588810 16.2026072
## hp 31.4991029 67.7601565 64.548040 65.5995410
## hpl 0.5631187 28.2139028 28.070692 7.2740788
## george 16.9778364 35.7693596 38.520775 28.9517581
## num650 11.2438092 10.7478919 14.596898 7.3190271
## lab -8.9360976 19.5199376 19.372771 1.9241327
## labs 4.2785020 8.2201427 8.687946 2.1191606
## telnet 2.2491732 8.8969068 9.346173 0.9649704
## num857 0.8190727 7.9052724 8.042649 0.5664608
## data -2.5536741 8.9450087 5.547927 4.4758298
## num415 2.6899538 3.6647482 4.089579 0.2738140
## num85 4.6964260 6.2857471 7.123029 1.2681270
## technology 18.0540808 12.3067954 20.195553 7.4682756
## num1999 10.8959235 26.8137012 27.820515 9.9769696
## parts -0.6516205 8.3552941 6.561458 1.2131948
## pm 5.6746546 13.5804438 14.458598 5.5073597
## direct 4.4844411 2.2614370 4.740264 0.6561704
## cs 0.8271131 2.8452786 3.008445 0.5193167
## meeting 5.2054936 33.3063918 33.510794 9.0726166
## original -1.1300318 3.4338742 2.764312 0.4990267
## project 3.5687084 3.7956868 5.058334 2.0685303
## re 10.1173362 15.4814868 18.254379 10.6315605
## edu 26.0358121 80.1141936 79.113022 33.7404122
## table 1.3425112 0.8639931 1.492275 0.1664714
## conference 7.9498170 22.4471542 22.735973 4.0165862
## charSemicolon 8.5157965 8.1002914 11.720263 4.9244527
## charRoundbracket 8.5072320 17.9463037 19.491600 14.0817382
## charSquarebracket 2.3574388 4.5555113 3.972918 1.9406875
## charExclamation 51.0938315 41.6571227 68.471513 304.2808033
## charDollar 43.0037494 35.3829713 53.606872 326.4922243
## charHash 6.1926457 3.1911337 7.443912 2.7632185
## capitalAve 36.7954884 21.9632386 39.753905 68.3362847
## capitalLong 29.2341462 27.5832542 41.734219 46.1432726
## capitalTotal 24.2365427 14.2889067 29.397600 58.2561668
varImpPlot(spam.bag)

################################################################################
######################## Random Forest #########################################
################################################################################
#For classification random forest,
#sqrt(p) is a common number of variables considered at each split
#sqrt(57) is approximately 7.55
set.seed(23784)
spam.rf<-randomForest(type~.,
data=spam.train,
mtry=7,
importance=TRUE,
ntree=500)
#training predictions
pred.rf.train<-predict(spam.rf,
newdata=spam.train,
type="class")
conf.mat.rf.train<-table(spam.train$type,
pred.rf.train)
conf.mat.rf.train
## pred.rf.train
## nonspam spam
## nonspam 1852 1
## spam 11 1201
MCR.rf.train<-
1-sum(diag(conf.mat.rf.train))/sum(conf.mat.rf.train)
MCR.rf.train
## [1] 0.003915171
#holdout predictions
pred.rf<-predict(spam.rf,
newdata=spam.test,
type="class")
conf.mat.rf<-table(spam.test$type,
pred.rf)
conf.mat.rf
## pred.rf
## nonspam spam
## nonspam 910 25
## spam 47 554
MCR.rf<-
1-sum(diag(conf.mat.rf))/sum(conf.mat.rf)
MCR.rf
## [1] 0.046875
#Number incorrectly classified
1536-sum(diag(conf.mat.rf))
## [1] 72
#variable importance
importance(spam.rf)
## nonspam spam MeanDecreaseAccuracy MeanDecreaseGini
## make 4.172762312 7.730498 8.606268 6.4290947
## address 8.775436501 6.989352 10.185876 7.3984319
## all 3.429542992 13.497652 12.122856 12.6517634
## num3d 5.874022376 2.118182 5.905361 1.6734415
## our 17.370010409 21.739281 23.454021 44.9488924
## over 10.425509143 8.677616 12.824353 9.2365711
## remove 34.396460241 28.102982 36.837358 105.2911379
## internet 14.057782133 8.018537 14.480463 15.1322670
## order 8.370562647 6.129955 9.430022 6.6958514
## mail 6.399696975 10.586146 10.791954 11.9998776
## receive 14.498088715 6.968835 15.193493 13.5891028
## will 6.503649346 17.924424 18.668541 14.9806514
## people 3.669521165 7.454563 7.987547 5.8165125
## report 7.887721532 5.645060 9.183213 3.4231855
## addresses 7.237411910 5.752441 8.111511 2.6741580
## free 30.319691024 25.874944 34.191058 96.2002136
## business 15.982405274 11.737043 18.060900 16.9623703
## email 8.688529493 8.775227 11.534552 11.2719564
## you 13.565899965 17.534811 21.589365 36.7619148
## credit 10.476075838 4.319404 10.525087 6.4001370
## your 19.150353774 24.863154 28.951164 85.5409998
## font 10.629564609 8.547643 12.021160 3.7157930
## num000 19.144023742 11.275828 20.222333 35.2129670
## money 16.524854210 13.866646 18.428611 53.4728971
## hp 22.343878699 31.983010 33.900568 55.7070011
## hpl 13.414458885 21.709436 23.222424 26.9939141
## george 17.478682867 26.338498 28.727691 30.0739091
## num650 8.825311841 13.216057 15.344915 8.3044543
## lab -0.760073886 9.238654 9.257735 2.3728877
## labs 5.143684389 9.888046 10.723883 5.8399688
## telnet 3.916863476 8.778323 9.315714 2.7687222
## num857 2.861237911 6.959428 7.326002 1.4336598
## data 3.421868438 8.645324 8.946168 4.2508983
## num415 2.348389027 5.572533 6.056352 1.0793383
## num85 7.396792964 10.900817 12.619649 4.6745368
## technology 12.022905752 10.862526 14.842010 6.0165158
## num1999 17.245118055 23.364987 26.103243 23.4691994
## parts 0.003538345 4.681149 3.588038 0.7465097
## pm 5.695144790 12.052192 12.714622 5.4017764
## direct 5.197500170 2.415312 5.919370 1.7760713
## cs 2.991477790 6.497331 7.019599 1.3724744
## meeting 10.371722194 17.210066 18.679016 7.2812600
## original 1.125585677 6.803357 6.904095 1.6717831
## project 3.953626403 9.158794 9.627615 3.2448685
## re 11.262027858 21.137658 23.386036 14.3692627
## edu 22.139511918 25.827662 29.758170 25.2252029
## table 0.253394991 2.515123 2.396912 0.3314469
## conference 4.741402409 8.485712 9.223826 2.5321774
## charSemicolon 7.934700481 7.790897 11.072341 6.8531019
## charRoundbracket 8.657169191 17.703867 18.257575 17.4500285
## charSquarebracket 8.420872837 8.398282 10.661389 4.5936004
## charExclamation 34.697670170 38.968735 45.600515 182.8879880
## charDollar 32.826689506 28.901154 37.931025 146.1263182
## charHash 7.255782135 6.012007 8.693029 5.0509040
## capitalAve 29.241755112 28.234180 38.750399 89.2816847
## capitalLong 25.383483041 24.178059 33.084177 76.4753412
## capitalTotal 23.536835109 19.603281 30.082176 63.1651356
varImpPlot(spam.rf)

################################################################################
######################## Boosting ##############################################
################################################################################
library(gbm)
## Warning: package 'gbm' was built under R version 4.5.3
## Loaded gbm 2.3.1
## This version of gbm is no longer under development. Consider transitioning to gbm3, https://github.com/gbm-developers/gbm3
#gbm with Bernoulli loss needs a 0/1 outcome
spam.train.boost<-spam.train
spam.test.boost<-spam.test
spam.train.boost$spam01<-
ifelse(spam.train.boost$type=="spam",1,0)
spam.test.boost$spam01<-
ifelse(spam.test.boost$type=="spam",1,0)
#remove original factor outcome so it is not used as a predictor
spam.train.boost$type<-NULL
spam.test.boost$type<-NULL
set.seed(23784)
spam.boost<-gbm(spam01~.,
data=spam.train.boost,
distribution="bernoulli",
n.trees=5000,
interaction.depth=4,
verbose=FALSE)
summary(spam.boost)

## var rel.inf
## charDollar charDollar 1.929183e+01
## charExclamation charExclamation 1.822695e+01
## remove remove 1.018396e+01
## free free 6.207823e+00
## hp hp 5.271819e+00
## capitalAve capitalAve 4.766057e+00
## your your 4.633751e+00
## capitalTotal capitalTotal 4.235524e+00
## capitalLong capitalLong 3.708443e+00
## edu edu 3.013665e+00
## people people 2.795790e+00
## george george 2.761526e+00
## you you 1.911746e+00
## our our 1.770247e+00
## money money 1.582761e+00
## num000 num000 8.428473e-01
## hpl hpl 8.199242e-01
## will will 7.621925e-01
## num1999 num1999 7.398950e-01
## charRoundbracket charRoundbracket 5.606778e-01
## re re 5.510159e-01
## internet internet 5.459915e-01
## num650 num650 5.170344e-01
## meeting meeting 4.873904e-01
## charSemicolon charSemicolon 4.727749e-01
## receive receive 4.080249e-01
## all all 3.513144e-01
## technology technology 3.092416e-01
## business business 2.902337e-01
## email email 2.597703e-01
## mail mail 2.322399e-01
## make make 2.231470e-01
## report report 2.129757e-01
## over over 1.342410e-01
## project project 1.010395e-01
## order order 9.878013e-02
## num3d num3d 9.808688e-02
## font font 9.155955e-02
## num85 num85 8.565559e-02
## charHash charHash 8.562662e-02
## data data 7.062828e-02
## address address 5.746824e-02
## pm pm 4.582455e-02
## parts parts 4.249470e-02
## charSquarebracket charSquarebracket 4.082814e-02
## conference conference 3.665344e-02
## labs labs 2.119116e-02
## credit credit 1.827047e-02
## lab lab 9.647987e-03
## addresses addresses 6.973161e-03
## direct direct 6.414129e-03
## cs cs 2.986557e-05
## original original 8.272759e-06
## telnet telnet 0.000000e+00
## num857 num857 0.000000e+00
## num415 num415 0.000000e+00
## table table 0.000000e+00
#training predicted probabilities
prob.boost.train<-predict(spam.boost,
newdata=spam.train.boost,
n.trees=5000,
type="response")
#convert probabilities to classes using 0.5 cutoff
pred.boost.train<-ifelse(prob.boost.train>=0.5,
"spam",
"nonspam")
pred.boost.train<-factor(pred.boost.train,
levels=levels(spam.train$type))
conf.mat.boost.train<-table(spam.train$type,
pred.boost.train)
conf.mat.boost.train
## pred.boost.train
## nonspam spam
## nonspam 1852 1
## spam 0 1212
MCR.boost.train<-
1-sum(diag(conf.mat.boost.train))/sum(conf.mat.boost.train)
MCR.boost.train
## [1] 0.0003262643
#holdout predicted probabilities
prob.boost<-predict(spam.boost,
newdata=spam.test.boost,
n.trees=5000,
type="response")
pred.boost<-ifelse(prob.boost>=0.5,
"spam",
"nonspam")
pred.boost<-factor(pred.boost,
levels=levels(spam.test$type))
conf.mat.boost<-table(spam.test$type,
pred.boost)
conf.mat.boost
## pred.boost
## nonspam spam
## nonspam 904 31
## spam 36 565
MCR.boost<-
1-sum(diag(conf.mat.boost))/sum(conf.mat.boost)
MCR.boost
## [1] 0.04361979
#Number incorrectly classified
1536-sum(diag(conf.mat.boost))
## [1] 67
################################################################################
######################## Gini Tree Performance #################################
################################################################################
pred.Gini<-predict(spam.tree.Gini,
newdata=spam.test,
type="class")
conf.mat.Gini<-table(spam.test$type,
pred.Gini)
conf.mat.Gini
## pred.Gini
## nonspam spam
## nonspam 804 131
## spam 248 353
MCR.Gini<-
1-sum(diag(conf.mat.Gini))/sum(conf.mat.Gini)
MCR.Gini
## [1] 0.2467448
################################################################################
######################## Final Comparison ######################################
################################################################################
#Default tree
MCR.tree<-
1-sum(diag(conf.mat))/sum(conf.mat)
#Pruned default tree
MCR.prune<-
1-sum(diag(conf.mat.prune))/sum(conf.mat.prune)
#Large tree
MCR.ctrl<-
1-sum(diag(conf.mat.ctrl))/sum(conf.mat.ctrl)
#Pruned large tree
MCR.prune.ctrl<-
1-sum(diag(conf.mat.prune.ctrl))/sum(conf.mat.prune.ctrl)
results<-data.frame(
Model=c("Default tree",
"Pruned tree",
"Large tree",
"Pruned large tree",
"Gini tree",
"Bagging",
"Random forest",
"Boosting"),
Holdout.Misclassification=c(
MCR.tree,
MCR.prune,
MCR.ctrl,
MCR.prune.ctrl,
MCR.Gini,
MCR.bag,
MCR.rf,
MCR.boost
)
)
results
## Model Holdout.Misclassification
## 1 Default tree 0.09114583
## 2 Pruned tree 0.09114583
## 3 Large tree 0.08854167
## 4 Pruned large tree 0.08789062
## 5 Gini tree 0.24674479
## 6 Bagging 0.05598958
## 7 Random forest 0.04687500
## 8 Boosting 0.04361979
results[order(results$Holdout.Misclassification),]
## Model Holdout.Misclassification
## 8 Boosting 0.04361979
## 7 Random forest 0.04687500
## 6 Bagging 0.05598958
## 4 Pruned large tree 0.08789062
## 3 Large tree 0.08854167
## 1 Default tree 0.09114583
## 2 Pruned tree 0.09114583
## 5 Gini tree 0.24674479
##This is analysis of email vs spam data set
library(tree)
library(kernlab)
data(spam)
class(spam)
## [1] "data.frame"
str(spam)
## 'data.frame': 4601 obs. of 58 variables:
## $ make : num 0 0.21 0.06 0 0 0 0 0 0.15 0.06 ...
## $ address : num 0.64 0.28 0 0 0 0 0 0 0 0.12 ...
## $ all : num 0.64 0.5 0.71 0 0 0 0 0 0.46 0.77 ...
## $ num3d : num 0 0 0 0 0 0 0 0 0 0 ...
## $ our : num 0.32 0.14 1.23 0.63 0.63 1.85 1.92 1.88 0.61 0.19 ...
## $ over : num 0 0.28 0.19 0 0 0 0 0 0 0.32 ...
## $ remove : num 0 0.21 0.19 0.31 0.31 0 0 0 0.3 0.38 ...
## $ internet : num 0 0.07 0.12 0.63 0.63 1.85 0 1.88 0 0 ...
## $ order : num 0 0 0.64 0.31 0.31 0 0 0 0.92 0.06 ...
## $ mail : num 0 0.94 0.25 0.63 0.63 0 0.64 0 0.76 0 ...
## $ receive : num 0 0.21 0.38 0.31 0.31 0 0.96 0 0.76 0 ...
## $ will : num 0.64 0.79 0.45 0.31 0.31 0 1.28 0 0.92 0.64 ...
## $ people : num 0 0.65 0.12 0.31 0.31 0 0 0 0 0.25 ...
## $ report : num 0 0.21 0 0 0 0 0 0 0 0 ...
## $ addresses : num 0 0.14 1.75 0 0 0 0 0 0 0.12 ...
## $ free : num 0.32 0.14 0.06 0.31 0.31 0 0.96 0 0 0 ...
## $ business : num 0 0.07 0.06 0 0 0 0 0 0 0 ...
## $ email : num 1.29 0.28 1.03 0 0 0 0.32 0 0.15 0.12 ...
## $ you : num 1.93 3.47 1.36 3.18 3.18 0 3.85 0 1.23 1.67 ...
## $ credit : num 0 0 0.32 0 0 0 0 0 3.53 0.06 ...
## $ your : num 0.96 1.59 0.51 0.31 0.31 0 0.64 0 2 0.71 ...
## $ font : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num000 : num 0 0.43 1.16 0 0 0 0 0 0 0.19 ...
## $ money : num 0 0.43 0.06 0 0 0 0 0 0.15 0 ...
## $ hp : num 0 0 0 0 0 0 0 0 0 0 ...
## $ hpl : num 0 0 0 0 0 0 0 0 0 0 ...
## $ george : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num650 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ lab : num 0 0 0 0 0 0 0 0 0 0 ...
## $ labs : num 0 0 0 0 0 0 0 0 0 0 ...
## $ telnet : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num857 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ data : num 0 0 0 0 0 0 0 0 0.15 0 ...
## $ num415 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num85 : num 0 0 0 0 0 0 0 0 0 0 ...
## $ technology : num 0 0 0 0 0 0 0 0 0 0 ...
## $ num1999 : num 0 0.07 0 0 0 0 0 0 0 0 ...
## $ parts : num 0 0 0 0 0 0 0 0 0 0 ...
## $ pm : num 0 0 0 0 0 0 0 0 0 0 ...
## $ direct : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ cs : num 0 0 0 0 0 0 0 0 0 0 ...
## $ meeting : num 0 0 0 0 0 0 0 0 0 0 ...
## $ original : num 0 0 0.12 0 0 0 0 0 0.3 0 ...
## $ project : num 0 0 0 0 0 0 0 0 0 0.06 ...
## $ re : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ edu : num 0 0 0.06 0 0 0 0 0 0 0 ...
## $ table : num 0 0 0 0 0 0 0 0 0 0 ...
## $ conference : num 0 0 0 0 0 0 0 0 0 0 ...
## $ charSemicolon : num 0 0 0.01 0 0 0 0 0 0 0.04 ...
## $ charRoundbracket : num 0 0.132 0.143 0.137 0.135 0.223 0.054 0.206 0.271 0.03 ...
## $ charSquarebracket: num 0 0 0 0 0 0 0 0 0 0 ...
## $ charExclamation : num 0.778 0.372 0.276 0.137 0.135 0 0.164 0 0.181 0.244 ...
## $ charDollar : num 0 0.18 0.184 0 0 0 0.054 0 0.203 0.081 ...
## $ charHash : num 0 0.048 0.01 0 0 0 0 0 0.022 0 ...
## $ capitalAve : num 3.76 5.11 9.82 3.54 3.54 ...
## $ capitalLong : num 61 101 485 40 40 15 4 11 445 43 ...
## $ capitalTotal : num 278 1028 2259 191 191 ...
## $ type : Factor w/ 2 levels "nonspam","spam": 2 2 2 2 2 2 2 2 2 2 ...
#The variable "type" is the outcome variable
# I'll produce my own training and test sets of the size described in the book
set.seed(3731)
train.ind<-sort(sample(1:4601, 3065, replace=FALSE))
test.ind<-seq(1,4601)[-train.ind]
spam.train<-spam[train.ind,]
spam.test<-spam[test.ind,]
################################################################################
######################## Default Classification Tree ###########################
################################################################################
#Fit a classification tree to the training data
spam.tree<-tree(type~.,data=spam.train)
plot(spam.tree)
text(spam.tree)

summary(spam.tree)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 15
## Residual mean deviance: 0.4404 = 1343 / 3050
## Misclassification error rate: 0.07732 = 237 / 3065
conf.mat.train<-table(spam.train$type,
predict(spam.tree,type='class'))
conf.mat.train
##
## nonspam spam
## nonspam 1775 78
## spam 159 1053
#How well does this tree perform on the holdout set
pred.tree<-predict(spam.tree,
newdata=spam.test,
type="class")
conf.mat<-table(spam.test$type,pred.tree)
conf.mat
## pred.tree
## nonspam spam
## nonspam 888 47
## spam 93 508
#misclassification rate
1-sum(diag(conf.mat))/sum(conf.mat)
## [1] 0.09114583
1536-sum(diag(conf.mat)) #Number of incorrect calls
## [1] 140
################################################################################
######################## Prune Default Tree ####################################
################################################################################
#Cost complexity pruning the default tree shows misclassification best for full tree
spam.cv<-cv.tree(spam.tree,
FUN=prune.misclass)
par(mfrow=c(1,2))
plot(spam.cv$size,
spam.cv$dev,
type="b")
plot(spam.cv$k,
spam.cv$dev,
type="b")

spam.cv
## $size
## [1] 15 14 9 8 7 6 5 3 2 1
##
## $dev
## [1] 299 299 309 331 333 337 368 435 644 1212
##
## $k
## [1] -Inf 0.0 7.6 12.0 13.0 14.0 34.0 48.0 177.0 591.0
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
min(spam.cv$dev)
## [1] 299
which(spam.cv$dev==min(spam.cv$dev))
## [1] 1 2
spam.cv$size[2] #size of best tree that is smallest in that class
## [1] 14
#prune down and see how the size 14 tree works
#DOUBLE CHECK best above
spam.prune<-prune.misclass(spam.tree,
best=14)
par(mfrow=c(1,2))
plot(spam.tree) #original tree
plot(spam.prune) #pruned tree

#check in-sample error rate of pruned tree
summary(spam.prune)
##
## Classification tree:
## snip.tree(tree = spam.tree, nodes = 12L)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 14
## Residual mean deviance: 0.4563 = 1392 / 3051
## Misclassification error rate: 0.07732 = 237 / 3065
#check out-of-sample error rate of pruned tree
pred.prune<-predict(spam.prune,
newdata=spam.test,
type="class")
conf.mat.prune<-table(spam.test$type,
pred.prune)
conf.mat.prune
## pred.prune
## nonspam spam
## nonspam 888 47
## spam 93 508
#misclassification rate
1-sum(diag(conf.mat.prune))/sum(conf.mat.prune)
## [1] 0.09114583
1536-sum(diag(conf.mat.prune))
## [1] 140
################################################################################
######################## Large Classification Tree #############################
################################################################################
#Build a larger tree for cost complexity pruning
spam.tree.ctrl<-tree(type~.,
data=spam.train,
control=tree.control(nobs=3065,
minsize=2,
mindev=0))
par(mfrow=c(1,1))
plot(spam.tree.ctrl)
text(spam.tree.ctrl)

summary(spam.tree.ctrl)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train, control = tree.control(nobs = 3065,
## minsize = 2, mindev = 0))
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "free" "internet"
## [9] "capitalAve" "you" "edu" "re"
## [13] "capitalTotal" "our" "mail" "all"
## [17] "technology" "will" "your" "charSemicolon"
## [21] "meeting" "address" "num3d" "num650"
## [25] "data" "charRoundbracket" "make" "hpl"
## [29] "over" "email" "charHash" "report"
## [33] "num1999" "business" "receive" "people"
## Number of terminal nodes: 176
## Residual mean deviance: 0.0009597 = 2.773 / 2889
## Misclassification error rate: 0.0003263 = 1 / 3065
#Out of sample misclassification of the large tree
#How well does this tree perform on the holdout set
pred.tree.ctrl<-predict(spam.tree.ctrl,
newdata=spam.test,
type="class")
conf.mat.ctrl<-table(spam.test$type,
pred.tree.ctrl)
conf.mat.ctrl
## pred.tree.ctrl
## nonspam spam
## nonspam 870 65
## spam 71 530
#misclassification rate
1-sum(diag(conf.mat.ctrl))/sum(conf.mat.ctrl)
## [1] 0.08854167
1536-sum(diag(conf.mat.ctrl))
## [1] 136
#Well growing the biggest possible tree overfit relative to the training error rate
#but was superior to the smaller tree in the out-of-sample misclassification rate.
################################################################################
######################## Prune Large Tree ######################################
################################################################################
spam.ctrl.cv<-cv.tree(spam.tree.ctrl,
FUN=prune.misclass)
par(mfrow=c(1,2))
plot(spam.ctrl.cv$size,
spam.ctrl.cv$dev,
type="b")
plot(spam.ctrl.cv$k,
spam.ctrl.cv$dev,
type="b")

spam.ctrl.cv
## $size
## [1] 176 175 170 136 133 125 116 111 104 76 66 64 59 51 39 36 34 30 28
## [20] 24 19 18 16 11 10 9 8 7 6 5 3 2 1
##
## $dev
## [1] 293 293 293 293 293 293 293 293 293 293 293 293 293 293 293
## [16] 293 293 293 293 293 293 296 297 300 296 321 324 328 328 368
## [31] 443 638 1212
##
## $k
## [1] -Inf 0.0000000 0.2000000 0.5000000 0.6666667 0.7500000
## [7] 0.7777778 0.8000000 0.8571429 1.0000000 1.2000000 1.5000000
## [13] 1.6000000 1.7500000 2.0000000 2.3333333 2.5000000 2.7500000
## [19] 3.0000000 3.7500000 4.0000000 5.0000000 6.0000000 7.6000000
## [25] 8.0000000 12.0000000 13.0000000 14.0000000 15.0000000 34.0000000
## [31] 48.0000000 177.0000000 591.0000000
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
min(spam.ctrl.cv$dev)
## [1] 293
which(spam.ctrl.cv$dev==min(spam.ctrl.cv$dev))
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
#Find the smallest tree among those tied for minimum CV error
best.ind<-which(spam.ctrl.cv$dev==
min(spam.ctrl.cv$dev))
best.size<-min(spam.ctrl.cv$size[best.ind])
best.size
## [1] 19
#prune large tree using selected size
spam.prune.ctrl<-prune.misclass(spam.tree.ctrl,
best=best.size)
par(mfrow=c(1,2))
plot(spam.tree.ctrl) #original large tree
plot(spam.prune.ctrl) #pruned tree

#check in-sample error rate of pruned tree
summary(spam.prune.ctrl)
##
## Classification tree:
## snip.tree(tree = spam.tree.ctrl, nodes = c(33L, 73L, 262L, 10L,
## 76L, 263L, 37L, 260L, 12L, 64L, 72L, 261L))
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "num650" "capitalTotal" "free" "business"
## [13] "email"
## Number of terminal nodes: 19
## Residual mean deviance: 0.4056 = 1235 / 3046
## Misclassification error rate: 0.06427 = 197 / 3065
#check out-of-sample error rate of pruned tree
pred.prune.ctrl<-predict(spam.prune.ctrl,
newdata=spam.test,
type="class")
conf.mat.prune.ctrl<-table(spam.test$type,
pred.prune.ctrl)
conf.mat.prune.ctrl
## pred.prune.ctrl
## nonspam spam
## nonspam 882 53
## spam 64 537
#misclassification rate
1-sum(diag(conf.mat.prune.ctrl))/sum(conf.mat.prune.ctrl)
## [1] 0.07617188
1536-sum(diag(conf.mat.prune.ctrl))
## [1] 117
################################################################################
######################## Gini Index Version ####################################
################################################################################
###Revisit the Gini index version
#This unrestricted version reaches maximum depth:
#spam.tree.Gini<-tree(type~.,
# data=spam.train,
# split="gini")
#Use controls to avoid maximum depth error
spam.tree.Gini<-tree(type~.,
data=spam.train,
split="gini",
control=tree.control(nobs=3065,
mincut=100,
minsize=200,
mindev=.01))
summary(spam.tree.Gini)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train, control = tree.control(nobs = 3065,
## mincut = 100, minsize = 200, mindev = 0.01), split = "gini")
## Variables actually used in tree construction:
## [1] "conference" "project" "num415"
## [4] "original" "pm" "technology"
## [7] "num85" "data" "charSquarebracket"
## [10] "report" "charSemicolon" "num1999"
## [13] "hp" "credit" "receive"
## [16] "people" "make" "address"
## [19] "all" "will" "re"
## [22] "charExclamation" "capitalTotal"
## Number of terminal nodes: 24
## Residual mean deviance: 1.051 = 3197 / 3041
## Misclassification error rate: 0.262 = 803 / 3065
summary(spam.tree)
##
## Classification tree:
## tree(formula = type ~ ., data = spam.train)
## Variables actually used in tree construction:
## [1] "charDollar" "remove" "charExclamation" "george"
## [5] "hp" "capitalLong" "edu" "our"
## [9] "capitalTotal"
## Number of terminal nodes: 15
## Residual mean deviance: 0.4404 = 1343 / 3050
## Misclassification error rate: 0.07732 = 237 / 3065
par(mfrow=c(1,1))
plot(spam.tree)
text(spam.tree)

plot(spam.tree.Gini)
text(spam.tree.Gini)

#Compare the various in- and out-of-sample misclassification rates - expectations met?
################################################################################
######################## Gini Tree Performance #################################
################################################################################
pred.Gini<-predict(spam.tree.Gini,
newdata=spam.test,
type="class")
conf.mat.Gini<-table(spam.test$type,
pred.Gini)
conf.mat.Gini
## pred.Gini
## nonspam spam
## nonspam 804 131
## spam 248 353
MCR.Gini<-
1-sum(diag(conf.mat.Gini))/sum(conf.mat.Gini)
MCR.Gini
## [1] 0.2467448
1536-sum(diag(conf.mat.Gini))
## [1] 379
################################################################################
######################## Bagging ################################################
################################################################################
library(randomForest)
#There are 57 candidate predictors
#For bagging, allow all predictors to be considered at every split
set.seed(23784)
spam.bag<-randomForest(type~.,
data=spam.train,
mtry=57,
importance=TRUE,
ntree=500)
#training predictions
pred.bag.train<-predict(spam.bag,
newdata=spam.train,
type="class")
conf.mat.bag.train<-table(spam.train$type,
pred.bag.train)
conf.mat.bag.train
## pred.bag.train
## nonspam spam
## nonspam 1852 1
## spam 0 1212
#training misclassification rate
MCR.bag.train<-
1-sum(diag(conf.mat.bag.train))/sum(conf.mat.bag.train)
MCR.bag.train
## [1] 0.0003262643
#holdout predictions
pred.bag<-predict(spam.bag,
newdata=spam.test,
type="class")
conf.mat.bag<-table(spam.test$type,
pred.bag)
conf.mat.bag
## pred.bag
## nonspam spam
## nonspam 899 36
## spam 50 551
#holdout misclassification rate
MCR.bag<-
1-sum(diag(conf.mat.bag))/sum(conf.mat.bag)
MCR.bag
## [1] 0.05598958
#Number incorrectly classified
1536-sum(diag(conf.mat.bag))
## [1] 86
#variable importance
importance(spam.bag)
## nonspam spam MeanDecreaseAccuracy MeanDecreaseGini
## make 3.4375107 7.1340748 7.482192 4.5451238
## address 8.3856032 7.0315143 11.220811 3.4421350
## all 1.7399876 6.5128243 6.859883 5.8280717
## num3d 11.2614372 -0.6113121 9.583824 2.8370080
## our 25.6120408 24.1997680 33.596296 23.5780096
## over 16.1088210 5.2778569 16.357111 4.9756630
## remove 71.8486869 43.8974403 79.650696 180.6049774
## internet 15.1688083 6.2211822 15.190525 8.0687118
## order 11.4536096 1.9803425 12.250713 3.5688217
## mail 5.7479403 5.5055160 8.060951 7.3231617
## receive 17.2489085 2.0130431 17.464684 4.4653853
## will 2.8378200 11.4937870 11.110846 9.2491864
## people -0.9692594 3.4124512 2.245570 3.9128499
## report 10.3119508 8.1193903 12.179344 3.8215417
## addresses 5.3310266 3.0249328 6.265170 1.1292987
## free 36.5417447 30.3690826 44.990199 55.4029369
## business 19.7444486 9.2738823 20.978700 11.3550531
## email 13.0181662 9.0610999 14.245406 9.4070060
## you 14.4237939 15.8286180 22.036126 28.0117604
## credit 12.8634464 -0.4241135 12.880596 2.5884365
## your 15.2606515 15.2803819 20.378356 27.4350838
## font 15.6207333 12.5478591 17.354343 3.4502325
## num000 21.1644025 3.6890746 21.741198 9.3520811
## money 18.3806807 13.6155787 20.588810 16.2026072
## hp 31.4991029 67.7601565 64.548040 65.5995410
## hpl 0.5631187 28.2139028 28.070692 7.2740788
## george 16.9778364 35.7693596 38.520775 28.9517581
## num650 11.2438092 10.7478919 14.596898 7.3190271
## lab -8.9360976 19.5199376 19.372771 1.9241327
## labs 4.2785020 8.2201427 8.687946 2.1191606
## telnet 2.2491732 8.8969068 9.346173 0.9649704
## num857 0.8190727 7.9052724 8.042649 0.5664608
## data -2.5536741 8.9450087 5.547927 4.4758298
## num415 2.6899538 3.6647482 4.089579 0.2738140
## num85 4.6964260 6.2857471 7.123029 1.2681270
## technology 18.0540808 12.3067954 20.195553 7.4682756
## num1999 10.8959235 26.8137012 27.820515 9.9769696
## parts -0.6516205 8.3552941 6.561458 1.2131948
## pm 5.6746546 13.5804438 14.458598 5.5073597
## direct 4.4844411 2.2614370 4.740264 0.6561704
## cs 0.8271131 2.8452786 3.008445 0.5193167
## meeting 5.2054936 33.3063918 33.510794 9.0726166
## original -1.1300318 3.4338742 2.764312 0.4990267
## project 3.5687084 3.7956868 5.058334 2.0685303
## re 10.1173362 15.4814868 18.254379 10.6315605
## edu 26.0358121 80.1141936 79.113022 33.7404122
## table 1.3425112 0.8639931 1.492275 0.1664714
## conference 7.9498170 22.4471542 22.735973 4.0165862
## charSemicolon 8.5157965 8.1002914 11.720263 4.9244527
## charRoundbracket 8.5072320 17.9463037 19.491600 14.0817382
## charSquarebracket 2.3574388 4.5555113 3.972918 1.9406875
## charExclamation 51.0938315 41.6571227 68.471513 304.2808033
## charDollar 43.0037494 35.3829713 53.606872 326.4922243
## charHash 6.1926457 3.1911337 7.443912 2.7632185
## capitalAve 36.7954884 21.9632386 39.753905 68.3362847
## capitalLong 29.2341462 27.5832542 41.734219 46.1432726
## capitalTotal 24.2365427 14.2889067 29.397600 58.2561668
varImpPlot(spam.bag)

################################################################################
######################## Random Forest #########################################
################################################################################
#For classification random forest,
#sqrt(p) is a common number of variables considered at each split
#sqrt(57) is approximately 7.55
set.seed(23784)
spam.rf<-randomForest(type~.,
data=spam.train,
mtry=7,
importance=TRUE,
ntree=500)
#training predictions
pred.rf.train<-predict(spam.rf,
newdata=spam.train,
type="class")
conf.mat.rf.train<-table(spam.train$type,
pred.rf.train)
conf.mat.rf.train
## pred.rf.train
## nonspam spam
## nonspam 1852 1
## spam 11 1201
#training misclassification rate
MCR.rf.train<-
1-sum(diag(conf.mat.rf.train))/sum(conf.mat.rf.train)
MCR.rf.train
## [1] 0.003915171
#holdout predictions
pred.rf<-predict(spam.rf,
newdata=spam.test,
type="class")
conf.mat.rf<-table(spam.test$type,
pred.rf)
conf.mat.rf
## pred.rf
## nonspam spam
## nonspam 910 25
## spam 47 554
#holdout misclassification rate
MCR.rf<-
1-sum(diag(conf.mat.rf))/sum(conf.mat.rf)
MCR.rf
## [1] 0.046875
#Number incorrectly classified
1536-sum(diag(conf.mat.rf))
## [1] 72
#variable importance
importance(spam.rf)
## nonspam spam MeanDecreaseAccuracy MeanDecreaseGini
## make 4.172762312 7.730498 8.606268 6.4290947
## address 8.775436501 6.989352 10.185876 7.3984319
## all 3.429542992 13.497652 12.122856 12.6517634
## num3d 5.874022376 2.118182 5.905361 1.6734415
## our 17.370010409 21.739281 23.454021 44.9488924
## over 10.425509143 8.677616 12.824353 9.2365711
## remove 34.396460241 28.102982 36.837358 105.2911379
## internet 14.057782133 8.018537 14.480463 15.1322670
## order 8.370562647 6.129955 9.430022 6.6958514
## mail 6.399696975 10.586146 10.791954 11.9998776
## receive 14.498088715 6.968835 15.193493 13.5891028
## will 6.503649346 17.924424 18.668541 14.9806514
## people 3.669521165 7.454563 7.987547 5.8165125
## report 7.887721532 5.645060 9.183213 3.4231855
## addresses 7.237411910 5.752441 8.111511 2.6741580
## free 30.319691024 25.874944 34.191058 96.2002136
## business 15.982405274 11.737043 18.060900 16.9623703
## email 8.688529493 8.775227 11.534552 11.2719564
## you 13.565899965 17.534811 21.589365 36.7619148
## credit 10.476075838 4.319404 10.525087 6.4001370
## your 19.150353774 24.863154 28.951164 85.5409998
## font 10.629564609 8.547643 12.021160 3.7157930
## num000 19.144023742 11.275828 20.222333 35.2129670
## money 16.524854210 13.866646 18.428611 53.4728971
## hp 22.343878699 31.983010 33.900568 55.7070011
## hpl 13.414458885 21.709436 23.222424 26.9939141
## george 17.478682867 26.338498 28.727691 30.0739091
## num650 8.825311841 13.216057 15.344915 8.3044543
## lab -0.760073886 9.238654 9.257735 2.3728877
## labs 5.143684389 9.888046 10.723883 5.8399688
## telnet 3.916863476 8.778323 9.315714 2.7687222
## num857 2.861237911 6.959428 7.326002 1.4336598
## data 3.421868438 8.645324 8.946168 4.2508983
## num415 2.348389027 5.572533 6.056352 1.0793383
## num85 7.396792964 10.900817 12.619649 4.6745368
## technology 12.022905752 10.862526 14.842010 6.0165158
## num1999 17.245118055 23.364987 26.103243 23.4691994
## parts 0.003538345 4.681149 3.588038 0.7465097
## pm 5.695144790 12.052192 12.714622 5.4017764
## direct 5.197500170 2.415312 5.919370 1.7760713
## cs 2.991477790 6.497331 7.019599 1.3724744
## meeting 10.371722194 17.210066 18.679016 7.2812600
## original 1.125585677 6.803357 6.904095 1.6717831
## project 3.953626403 9.158794 9.627615 3.2448685
## re 11.262027858 21.137658 23.386036 14.3692627
## edu 22.139511918 25.827662 29.758170 25.2252029
## table 0.253394991 2.515123 2.396912 0.3314469
## conference 4.741402409 8.485712 9.223826 2.5321774
## charSemicolon 7.934700481 7.790897 11.072341 6.8531019
## charRoundbracket 8.657169191 17.703867 18.257575 17.4500285
## charSquarebracket 8.420872837 8.398282 10.661389 4.5936004
## charExclamation 34.697670170 38.968735 45.600515 182.8879880
## charDollar 32.826689506 28.901154 37.931025 146.1263182
## charHash 7.255782135 6.012007 8.693029 5.0509040
## capitalAve 29.241755112 28.234180 38.750399 89.2816847
## capitalLong 25.383483041 24.178059 33.084177 76.4753412
## capitalTotal 23.536835109 19.603281 30.082176 63.1651356
varImpPlot(spam.rf)

################################################################################
######################## Boosting ##############################################
################################################################################
library(gbm)
#gbm with Bernoulli loss needs a 0/1 outcome
spam.train.boost<-spam.train
spam.test.boost<-spam.test
spam.train.boost$spam01<-
ifelse(spam.train.boost$type=="spam",1,0)
spam.test.boost$spam01<-
ifelse(spam.test.boost$type=="spam",1,0)
#remove original factor outcome so it is not used as a predictor
spam.train.boost$type<-NULL
spam.test.boost$type<-NULL
set.seed(23784)
spam.boost<-gbm(spam01~.,
data=spam.train.boost,
distribution="bernoulli",
n.trees=5000,
interaction.depth=4,
verbose=FALSE)
summary(spam.boost)

## var rel.inf
## charDollar charDollar 1.929183e+01
## charExclamation charExclamation 1.822695e+01
## remove remove 1.018396e+01
## free free 6.207823e+00
## hp hp 5.271819e+00
## capitalAve capitalAve 4.766057e+00
## your your 4.633751e+00
## capitalTotal capitalTotal 4.235524e+00
## capitalLong capitalLong 3.708443e+00
## edu edu 3.013665e+00
## people people 2.795790e+00
## george george 2.761526e+00
## you you 1.911746e+00
## our our 1.770247e+00
## money money 1.582761e+00
## num000 num000 8.428473e-01
## hpl hpl 8.199242e-01
## will will 7.621925e-01
## num1999 num1999 7.398950e-01
## charRoundbracket charRoundbracket 5.606778e-01
## re re 5.510159e-01
## internet internet 5.459915e-01
## num650 num650 5.170344e-01
## meeting meeting 4.873904e-01
## charSemicolon charSemicolon 4.727749e-01
## receive receive 4.080249e-01
## all all 3.513144e-01
## technology technology 3.092416e-01
## business business 2.902337e-01
## email email 2.597703e-01
## mail mail 2.322399e-01
## make make 2.231470e-01
## report report 2.129757e-01
## over over 1.342410e-01
## project project 1.010395e-01
## order order 9.878013e-02
## num3d num3d 9.808688e-02
## font font 9.155955e-02
## num85 num85 8.565559e-02
## charHash charHash 8.562662e-02
## data data 7.062828e-02
## address address 5.746824e-02
## pm pm 4.582455e-02
## parts parts 4.249470e-02
## charSquarebracket charSquarebracket 4.082814e-02
## conference conference 3.665344e-02
## labs labs 2.119116e-02
## credit credit 1.827047e-02
## lab lab 9.647987e-03
## addresses addresses 6.973161e-03
## direct direct 6.414129e-03
## cs cs 2.986557e-05
## original original 8.272759e-06
## telnet telnet 0.000000e+00
## num857 num857 0.000000e+00
## num415 num415 0.000000e+00
## table table 0.000000e+00
#training predicted probabilities
prob.boost.train<-predict(spam.boost,
newdata=spam.train.boost,
n.trees=5000,
type="response")
#convert probabilities to classes using 0.5 cutoff
pred.boost.train<-ifelse(prob.boost.train>=0.5,
"spam",
"nonspam")
pred.boost.train<-factor(pred.boost.train,
levels=levels(spam.train$type))
conf.mat.boost.train<-table(spam.train$type,
pred.boost.train)
conf.mat.boost.train
## pred.boost.train
## nonspam spam
## nonspam 1852 1
## spam 0 1212
#training misclassification rate
MCR.boost.train<-
1-sum(diag(conf.mat.boost.train))/sum(conf.mat.boost.train)
MCR.boost.train
## [1] 0.0003262643
#holdout predicted probabilities
prob.boost<-predict(spam.boost,
newdata=spam.test.boost,
n.trees=5000,
type="response")
pred.boost<-ifelse(prob.boost>=0.5,
"spam",
"nonspam")
pred.boost<-factor(pred.boost,
levels=levels(spam.test$type))
conf.mat.boost<-table(spam.test$type,
pred.boost)
conf.mat.boost
## pred.boost
## nonspam spam
## nonspam 904 31
## spam 36 565
#holdout misclassification rate
MCR.boost<-
1-sum(diag(conf.mat.boost))/sum(conf.mat.boost)
MCR.boost
## [1] 0.04361979
#Number incorrectly classified
1536-sum(diag(conf.mat.boost))
## [1] 67
################################################################################
######################## Final Comparison ######################################
################################################################################
#Default tree
MCR.tree<-
1-sum(diag(conf.mat))/sum(conf.mat)
#Pruned default tree
MCR.prune<-
1-sum(diag(conf.mat.prune))/sum(conf.mat.prune)
#Large tree
MCR.ctrl<-
1-sum(diag(conf.mat.ctrl))/sum(conf.mat.ctrl)
#Pruned large tree
MCR.prune.ctrl<-
1-sum(diag(conf.mat.prune.ctrl))/sum(conf.mat.prune.ctrl)
results<-data.frame(
Model=c("Default tree",
"Pruned tree",
"Large tree",
"Pruned large tree",
"Gini tree",
"Bagging",
"Random forest",
"Boosting"),
Holdout.Misclassification=c(
MCR.tree,
MCR.prune,
MCR.ctrl,
MCR.prune.ctrl,
MCR.Gini,
MCR.bag,
MCR.rf,
MCR.boost
)
)
results
## Model Holdout.Misclassification
## 1 Default tree 0.09114583
## 2 Pruned tree 0.09114583
## 3 Large tree 0.08854167
## 4 Pruned large tree 0.07617188
## 5 Gini tree 0.24674479
## 6 Bagging 0.05598958
## 7 Random forest 0.04687500
## 8 Boosting 0.04361979
#Rank from best to worst
results[order(results$Holdout.Misclassification),]
## Model Holdout.Misclassification
## 8 Boosting 0.04361979
## 7 Random forest 0.04687500
## 6 Bagging 0.05598958
## 4 Pruned large tree 0.07617188
## 3 Large tree 0.08854167
## 1 Default tree 0.09114583
## 2 Pruned tree 0.09114583
## 5 Gini tree 0.24674479