Consider the Gini index, classification error, and entropy in a simple classification setting with two classes. Create a single plot that displays each of these quantities as a function of \(\hat{p}_{m1}\). The x-axis should display \(\hat{p}_{m1}\), ranging from 0 to 1, and the y-axis should display the value of the Gini index, classification error, and entropy.
Hint: In a setting with two classes, \(\hat{p}_{m1}\) = 1 - \(\hat{p}_{m2}\). You could make this plot by hand, but it will be much easier to make in R.
prob_1 <- seq(0, 1, 0.10)
prob_2 <- 1 - prob_1
gini_index <- 2 * prob_1 * prob_2
classification_error <- 1 - pmax(prob_1, prob_2)
entropy <- - (ifelse(prob_1 == 0, 0, prob_1 * log(prob_1)) +
ifelse(prob_2 == 0, 0, prob_2 * log(prob_2)))
plot(prob_1,
entropy,
type = "l",
ylim = c(0,1),
col = "purple",
)
lines(prob_1, gini_index, col = "blue")
lines(prob_1, classification_error, col = "darkgreen")
In the lab, a classification tree was applied to the Carseats data set after converting Sales into a qualitative response variable. Now we will seek to predict Sales using regression trees and related approaches, treating the response as a quantitative variable.
# install.packages("BART")
library(ISLR2)
library(caret)
## Loading required package: ggplot2
## Loading required package: lattice
library(tree)
library(randomForest)
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
library(BART)
## Loading required package: nlme
## Loading required package: survival
##
## Attaching package: 'survival'
## The following object is masked from 'package:caret':
##
## cluster
attach(Carseats)
set.seed(1)
trainindex <- createDataPartition(Carseats$Sales, p = 0.80, list = FALSE)
train_carseat <- Carseats[trainindex, ]
test_carseat <- Carseats[- trainindex, ]
tree.carseats <- tree(Sales ~ ., train_carseat)
summary(tree.carseats)
##
## Regression tree:
## tree(formula = Sales ~ ., data = train_carseat)
## Variables actually used in tree construction:
## [1] "ShelveLoc" "Price" "CompPrice" "Age" "Advertising"
## Number of terminal nodes: 18
## Residual mean deviance: 2.39 = 724.3 / 303
## Distribution of residuals:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -4.9380 -0.9822 0.1214 0.0000 0.9908 3.3760
plot(tree.carseats)
text(tree.carseats, pretty = 0)
tree_pred <- predict(tree.carseats, test_carseat)
mean((tree_pred - test_carseat$Sales) ^ 2)
## [1] 3.947858
From the decision tree, we can see that the test MSE using the training data is about 3.95. Out of the 12 variables, the decision tree made the predictions based on five variables: ShelveLoc, Price, CompPrice, Age, Advertising.
set.seed(1)
cv.carseats <- cv.tree(tree.carseats)
cv.carseats
## $size
## [1] 18 17 16 15 14 13 12 10 9 8 7 6 5 4 3 2 1
##
## $dev
## [1] 1518.114 1544.175 1532.682 1562.012 1564.222 1573.732 1575.487 1629.843
## [9] 1613.281 1758.139 1792.464 1784.681 1773.340 1765.712 1783.284 1954.793
## [17] 2566.119
##
## $k
## [1] -Inf 26.74673 27.28892 32.67834 40.44341 43.06716 43.24045
## [8] 48.48233 51.91873 66.97883 68.81871 71.65623 80.16261 117.47087
## [15] 152.95942 284.39933 619.81164
##
## $method
## [1] "deviance"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
From the cross-validation, we can see that optimal level of tree complexity is at size 18. What this is saying is that pruning the decision tree does not improve the current decision tree. Since the cross-validation said the full model is most efficienct, the MSE has not changed and is also 3.95.
set.seed(1)
bag.carseat <- randomForest(Sales ~ ., data = train_carseat, importance = TRUE)
bag.carseat
##
## Call:
## randomForest(formula = Sales ~ ., data = train_carseat, importance = TRUE)
## Type of random forest: regression
## Number of trees: 500
## No. of variables tried at each split: 3
##
## Mean of squared residuals: 2.785152
## % Var explained: 64.92
set.seed(1)
yhat.bag <- predict(bag.carseat, newdata = test_carseat)
test_sales <- test_carseat$Sales
plot(yhat.bag, test_sales)
abline(0,1)
mean((yhat.bag - test_sales) ^ 2)
## [1] 2.878256
importance(bag.carseat)
## %IncMSE IncNodePurity
## CompPrice 14.7758597775 226.30590
## Income 7.0219098197 189.57453
## Advertising 20.1504739445 232.13827
## Population -0.0006996264 149.40412
## Price 49.3932918406 622.92892
## ShelveLoc 53.3930058581 588.83879
## Age 16.4445354850 250.09345
## Education 3.1977026272 102.48771
## Urban 0.0509883165 22.39921
## US 4.1102636734 33.54182
Using bagging, we can see that the MSE error is about 2.88. And with importance, we can see that ShelveLoc and Price are the two most important variables.
rf.carseat <- randomForest(Sales ~., data = train_carseat,
mtry = 5, importance = TRUE)
rf.carseat
##
## Call:
## randomForest(formula = Sales ~ ., data = train_carseat, mtry = 5, importance = TRUE)
## Type of random forest: regression
## Number of trees: 500
## No. of variables tried at each split: 5
##
## Mean of squared residuals: 2.460876
## % Var explained: 69.01
yhat.rf <- predict(rf.carseat, newdata = test_carseat)
mean((yhat.rf - test_sales) ^ 2)
## [1] 2.568048
importance(rf.carseat)
## %IncMSE IncNodePurity
## CompPrice 20.184934 246.32343
## Income 7.372158 155.33685
## Advertising 23.544142 235.54125
## Population -1.188342 113.81406
## Price 61.300857 680.92064
## ShelveLoc 67.591155 685.01236
## Age 19.124661 232.73986
## Education 3.395507 81.30260
## Urban -2.697134 12.68769
## US 4.634857 21.45461
The test MSE of the random forest is 2.61 and we can see from importance that the variables ShelveLoc and Price are the most important. Since m is equal to 5 this will be the number of variables considered at the split of a random forest. If m is too small then it could lead to a random forest that does not provide significant information. While on the other hand if m is too large then all of the trees could start to be identical to one another.
x <- Carseats[, 2:11]
y <- Carseats[, "Sales"]
xtrain <- x[trainindex, ]
ytrain <- y[trainindex]
xtest <- x[-trainindex, ]
ytest <- y[-trainindex]
set.seed(1)
bartfit <- gbart(xtrain, ytrain, x.test = xtest)
## *****Calling gbart: type=1
## *****Data:
## data:n,p,np: 321, 14, 79
## y1,yn: 1.989065, -1.570935
## x1,x[n*p]: 138.000000, 1.000000
## xp1,xp[np*p]: 117.000000, 1.000000
## *****Number of Trees: 200
## *****Number of Cut Points: 67 ... 1
## *****burn,nd,thin: 100,1000,1
## *****Prior:beta,alpha,tau,nu,lambda,offset: 2,0.95,0.276302,3,0.202195,7.51093
## *****sigma: 1.018826
## *****w (weights): 1.000000 ... 1.000000
## *****Dirichlet:sparse,theta,omega,a,b,rho,augment: 0,0,1,0.5,1,14,0
## *****printevery: 100
##
## MCMC
## done 0 (out of 1100)
## done 100 (out of 1100)
## done 200 (out of 1100)
## done 300 (out of 1100)
## done 400 (out of 1100)
## done 500 (out of 1100)
## done 600 (out of 1100)
## done 700 (out of 1100)
## done 800 (out of 1100)
## done 900 (out of 1100)
## done 1000 (out of 1100)
## time: 1s
## trcnt,tecnt: 1000,1000
yhat.bart <- bartfit$yhat.test.mean
mean((yhat.bart - ytest) ^ 2)
## [1] 1.437575
With BART, the MSE is 1.46. This is good because the BART test is performing better then random forest model but is worse then the bagging model. Of the models test I would recommend to use either the bagging model or BART model.
This problem involves the OJ data set which is part of the ISLR2 package.
attach(OJ)
set.seed(1)
index <- sample(nrow(OJ), size = 800)
train_oj <- OJ[index, ]
test_oj <- OJ[- index, ]
set.seed(1)
tree.oj <- tree(Purchase ~., data = train_oj)
summary(tree.oj)
##
## Classification tree:
## tree(formula = Purchase ~ ., data = train_oj)
## Variables actually used in tree construction:
## [1] "LoyalCH" "PriceDiff" "SpecialCH" "ListPriceDiff"
## [5] "PctDiscMM"
## Number of terminal nodes: 9
## Residual mean deviance: 0.7432 = 587.8 / 791
## Misclassification error rate: 0.1588 = 127 / 800
In the initial tree, it has decided to base it’s decisions on 5 variables: LoyalCH, PriceDiff, SpecialCH, ListPriceDiff, PctDiscMM. Also, the initial test contained 9 terminal nodes. Lastly, the model has a training error rate of 15% almost 16%.
tree.oj
## node), split, n, deviance, yval, (yprob)
## * denotes terminal node
##
## 1) root 800 1073.00 CH ( 0.60625 0.39375 )
## 2) LoyalCH < 0.5036 365 441.60 MM ( 0.29315 0.70685 )
## 4) LoyalCH < 0.280875 177 140.50 MM ( 0.13559 0.86441 )
## 8) LoyalCH < 0.0356415 59 10.14 MM ( 0.01695 0.98305 ) *
## 9) LoyalCH > 0.0356415 118 116.40 MM ( 0.19492 0.80508 ) *
## 5) LoyalCH > 0.280875 188 258.00 MM ( 0.44149 0.55851 )
## 10) PriceDiff < 0.05 79 84.79 MM ( 0.22785 0.77215 )
## 20) SpecialCH < 0.5 64 51.98 MM ( 0.14062 0.85938 ) *
## 21) SpecialCH > 0.5 15 20.19 CH ( 0.60000 0.40000 ) *
## 11) PriceDiff > 0.05 109 147.00 CH ( 0.59633 0.40367 ) *
## 3) LoyalCH > 0.5036 435 337.90 CH ( 0.86897 0.13103 )
## 6) LoyalCH < 0.764572 174 201.00 CH ( 0.73563 0.26437 )
## 12) ListPriceDiff < 0.235 72 99.81 MM ( 0.50000 0.50000 )
## 24) PctDiscMM < 0.196196 55 73.14 CH ( 0.61818 0.38182 ) *
## 25) PctDiscMM > 0.196196 17 12.32 MM ( 0.11765 0.88235 ) *
## 13) ListPriceDiff > 0.235 102 65.43 CH ( 0.90196 0.09804 ) *
## 7) LoyalCH > 0.764572 261 91.20 CH ( 0.95785 0.04215 ) *
Node 8: LoyalCH < 0.0356415: - Split: Any LoyalCH value less then 0.0356415. This is how the model decides how the data will be split. - N: 59. This is the total number of points after the split - Deviance: 10.14. This is deviance of the 59 points in Node 8. - YVal: MM. This is the predicted value for any point that is placed within the node. So if LoyalCH < 0.0356415 then the model will predict the brand as MM (Minute Maid) - (YProb): ( 0.01695 0.98305 ). This is the probability of the predictions for the points in the node. In this example for node 8, there is a 1.695% of a chance the data point in Node 8 is Citrus Hill. While on the other hand there is a 98.305% of a chance the data point in Node 8 is Minute Maid.
plot(tree.oj)
text(tree.oj, pretty = 0)
From the plot, it seems the most important indicator is LoyalCH. Also, it can be see that at the end of the branches majority of the end predictions are Citrus Hill.
tree.oj.pred <- predict(tree.oj, test_oj, type = "class")
table(tree.oj.pred, test_oj$Purchase)
##
## tree.oj.pred CH MM
## CH 160 38
## MM 8 64
(8 + 38) / 270
## [1] 0.1703704
The test error rate of the initial tree is 17.04%.
set.seed(1)
cv.oj <- cv.tree(tree.oj, FUN = prune.misclass)
cv.oj
## $size
## [1] 9 8 7 4 2 1
##
## $dev
## [1] 145 145 146 146 167 315
##
## $k
## [1] -Inf 0.000000 3.000000 4.333333 10.500000 151.000000
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
plot(cv.oj$size, cv.oj$dev, type = "b")
Based on the plot, the lowest cross-validated classification error rate is lowest when the tree size corresponds with 9 or 8. However, the inital tree had nine nodes, so the cross-validation is saying the initial tree performs the best.
prune.oj <- prune.misclass(tree.oj, best = 5)
summary(prune.oj)
##
## Classification tree:
## snip.tree(tree = tree.oj, nodes = c(4L, 10L))
## Variables actually used in tree construction:
## [1] "LoyalCH" "PriceDiff" "ListPriceDiff" "PctDiscMM"
## Number of terminal nodes: 7
## Residual mean deviance: 0.7748 = 614.4 / 793
## Misclassification error rate: 0.1625 = 130 / 800
# pruned tree
prune.pred <- predict(prune.oj, train_oj, type = "class")
table(prune.pred, train_oj$Purchase)
##
## prune.pred CH MM
## CH 441 86
## MM 44 229
(44 + 86) / 800
## [1] 0.1625
The training error of the pruned tree is: 16.25%
# unpruned tree
tree.oj.pred <- predict(tree.oj, train_oj, type = "class")
table(tree.oj.pred, train_oj$Purchase)
##
## tree.oj.pred CH MM
## CH 450 92
## MM 35 223
(35 + 92) / 800
## [1] 0.15875
The training error of the unpruned tree is: 15.88%
The pruned tree has the higher training error rate of the two.
prune.pred <- predict(prune.oj, test_oj, type = "class")
table(prune.pred, test_oj$Purchase)
##
## prune.pred CH MM
## CH 160 36
## MM 8 66
(8 + 36) / 270
## [1] 0.162963
The test error rate of the pruned tree is: 16.30%
tree.oj.pred <- predict(tree.oj, test_oj, type = "class")
table(tree.oj.pred, test_oj$Purchase)
##
## tree.oj.pred CH MM
## CH 160 38
## MM 8 64
(8 + 38) / 270
## [1] 0.1703704
The test error rate of the unpruned tree is 17.04%
The unpruned tree has the higher test error rate of the two.