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.
Plot quantities as a function of \(\hat{p}_{m1}\)
p <- seq(0, 1, 0.001)
gini <- 2 * p * (1 - p)
class_error <- 1 - pmax(p, 1 - p)
ent <- -(p * log(p) + (1 - p) * log(1 - p))
plot(p, ent, typ = 'l', col = 'Gold', lwd = 4, xlab = 'Probability', ylab = 'Values', col.lab = 'Dark Blue', col.axis = 'Dark Blue')
box(col = 'Dark Blue')
lines(p, class_error, col = 'Red', lwd = 4)
lines(p, gini, col = 'Black', lwd = 4)
title(main = 'Value of Gini Index, Classification Error, and Entropy', col.main = 'Dark Blue')
legend(.33, .2, legend=c('Entropy', 'Gini Index', 'Classification Error'),
col=c('Gold', 'Black', 'Red'), lwd = 3, cex=1, bty = 'n')

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.
(a) Split the data set into a training set and a test set.
Split data into train & test
set.seed(22)
in_train <- sample(nrow(Carseats) * 0.75)
train <- Carseats[in_train, ]
test <- Carseats[-in_train, ]
(b) Fit a regression tree to the training set. Plot the tree, and interpret the results. What test MSE do you obtain?
The test MSE is 5.223. It appears that we might be able to improve the accuracy of the model by pruning some of the branches.
Fit the model and plot the tree
train_tree <- tree(Sales~., data = train)
plot(train_tree, col = 'Light Blue')
text(train_tree, pretty = 1, cex=.6, col = 'Dark Blue')
Determine MSE
set.seed(22)
tree_pred = predict(train_tree, test)
tree_mse <- mean((test$Sales - tree_pred) ^ 2)
tree_mse
## [1] 5.223056
(c) Use cross-validation in order to determine the optimal level of tree complexity. Does pruning the tree improve the test MSE?
Using cross validation and plotting, I determined that 6 would likely give me the best results while reducing the MSE. However, the MSE actually increased significantly when using pruning. I manually ran pruning for 2-17 branches, and it never resulted in a better MSE.
Use cross-validation to determine optimal level of tree complexity
set.seed(22)
cv_train_tree <- cv.tree(train_tree)
plot(cv_train_tree$size,cv_train_tree$dev, xlab = 'Size', ylab = 'Dev', col.lab = 'Dark Blue', col = 'Dark Blue', col.axis = 'Dark Blue')
box(col = 'Dark Blue')
abline(h = min(cv_train_tree$dev) + .2*sd(cv_train_tree$dev), col = 'Red')
Prune the tree to 6
set.seed(22)
prune_tree <- prune.tree(train_tree, best = 6)
plot(prune_tree, col = 'Light Blue')
text(prune_tree,pretty=1, col = 'Dark Blue', cex = .9)
Determine MSE
prune_pred = predict(prune_tree, data = test)
prune_mse <- mean((test$Sales - prune_pred) ^ 2)
prune_mse
## [1] 12.35829
(d) Use the bagging approach in order to analyze this data. What test MSE do you obtain? Use the importance() function to determine which variables are most important.
The bagging method, using 1000 trees, resulted in a test MSE of 2.58, much better than both the simple tree and the pruning methods. The most important predictors are listed below.
Fit the model and determine MSE
set.seed(22)
bag_train_tree <- randomForest(Sales ~ ., data = train, mtry = 10, ntree = 1000, importance = TRUE)
bag_pred <- predict(bag_train_tree, test)
bag_mse <- mean((test$Sales - bag_pred)^2)
bag_mse
## [1] 2.581502
List the important predictors
pander(importance(bag_train_tree))
| CompPrice |
45.94 |
269.6 |
| Income |
18.09 |
134.4 |
| Advertising |
29.09 |
161.5 |
| Population |
2.47 |
87.97 |
| Price |
99.62 |
660.5 |
| ShelveLoc |
104.5 |
739.9 |
| Age |
28.44 |
210.1 |
| Education |
-0.4655 |
59.02 |
| Urban |
-0.7778 |
12.67 |
| US |
3.372 |
8.131 |
(e) Use random forests to analyze this data. What test MSE do you obtain? Use the importance() function to determine which variables are most important. Describe the effect of \({m}\), the number of variables considered at each split, on the error rate obtained.
The random forest method, using 1000 trees, resulted in a test MSE of 2.66, which was worse than the bagging method. The most important predictors are listed below and are the same as found in the bagging method. Looking at the effect of \({m}\), it appears that the last split to have a “significant” impact to the error rate is Age. Interestingly, you do see different impacts on the MSE in both methods, but they are still relatively similar overall.
Fit the model and Determine MSE
set.seed(33)
train_rf <- randomForest(Sales ~ ., data = train, mtry = 10, ntree = 1000,
importance = T)
rf_pred <- predict(train_rf, test)
rf_mse <- mean((test$Sales - rf_pred)^2)
rf_mse
## [1] 2.665686
List the important predictors
pander(importance(train_rf))
| CompPrice |
47.61 |
271.6 |
| Income |
17.83 |
130.4 |
| Advertising |
29.64 |
167.5 |
| Population |
2.85 |
87.21 |
| Price |
104.9 |
667 |
| ShelveLoc |
101.6 |
740.7 |
| Age |
28.27 |
206.2 |
| Education |
-0.652 |
58.6 |
| Urban |
0.3731 |
12.95 |
| US |
4.298 |
8.962 |
(f) Now analyze the data using BART, and report your results.
The BART method, using 1000 trees, returned the lowest MSE out of all the models, with an MSE of 1.37.
Fit the model
set.seed(22)
bart <- gbart(train[, 2:11], train$Sales, x.test = test[, 2:11], ntree = 1000)
## *****Calling gbart: type=1
## *****Data:
## data:n,p,np: 300, 14, 100
## y1,yn: 4.337033, 1.647033
## x1,x[n*p]: 131.000000, 1.000000
## xp1,xp[np*p]: 116.000000, 1.000000
## *****Number of Trees: 1000
## *****Number of Cut Points: 69 ... 1
## *****burn,nd,thin: 100,1000,1
## *****Prior:beta,alpha,tau,nu,lambda,offset: 2,0.95,0.117795,3,0.191717,7.36297
## *****sigma: 0.992077
## *****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: 15s
## trcnt,tecnt: 1000,1000
Determine MSE
bart_mse <- mean((test$Sales - bart$yhat.test.mean)^2)
bart_mse
## [1] 1.373309
This problem involves the OJ data set which is part of the ISLR2 package.
Create a training set containing a random sample of 800 observations, and a test set containing the remaining observations.
Split data into train & test
set.seed(22)
in_train <- sample(nrow(OJ), 800)
train <- OJ[in_train,]
test <- OJ[-in_train, ]
(b) Fit a tree to the training data, with Purchase as the response and the other variables as predictors. Use the summary() function to produce summary statistics about the tree, and describe the results obtained. What is the training error rate? How many terminal nodes does the tree have?
The tree has a training misclassification error rate was .1638, used three predictors (LoyalCH, ListPriceDiff, and PriceDiff), and had 6 terminal nodes.
Fit the model and print the tree summary
train_tree <- tree(Purchase~., data = train)
summary(train_tree)
##
## Classification tree:
## tree(formula = Purchase ~ ., data = train)
## Variables actually used in tree construction:
## [1] "LoyalCH" "ListPriceDiff" "PriceDiff"
## Number of terminal nodes: 6
## Residual mean deviance: 0.7857 = 623.8 / 794
## Misclassification error rate: 0.1638 = 131 / 800
(c) Type in the name of the tree object in order to get a detailed text output. Pick one of the terminal nodes, and interpret the information displayed.
Node 7 had a split for LoyalCH at > .7535. The number of observations at this node was 261 with a deviance of 84.85. 96% of the observations in branch 9 were characterized as MM while 4% were CH.
Print tree object
train_tree
## node), split, n, deviance, yval, (yprob)
## * denotes terminal node
##
## 1) root 800 1073.000 CH ( 0.60625 0.39375 )
## 2) LoyalCH < 0.48285 294 308.100 MM ( 0.21769 0.78231 )
## 4) LoyalCH < 0.035047 51 9.844 MM ( 0.01961 0.98039 ) *
## 5) LoyalCH > 0.035047 243 278.100 MM ( 0.25926 0.74074 ) *
## 3) LoyalCH > 0.48285 506 458.100 CH ( 0.83202 0.16798 )
## 6) LoyalCH < 0.753545 245 301.800 CH ( 0.69388 0.30612 )
## 12) ListPriceDiff < 0.18 66 89.300 MM ( 0.40909 0.59091 ) *
## 13) ListPriceDiff > 0.18 179 179.700 CH ( 0.79888 0.20112 )
## 26) PriceDiff < -0.165 8 6.028 MM ( 0.12500 0.87500 ) *
## 27) PriceDiff > -0.165 171 155.700 CH ( 0.83041 0.16959 ) *
## 7) LoyalCH > 0.753545 261 84.850 CH ( 0.96169 0.03831 ) *
(d) Create a plot of the tree, and interpret the results.
The model shows the most important predictors and their split point values, LoyalCH is the most important predictor with PriceDiff coming next.
Create a plot of the model
plot(train_tree, col = 'Light Blue')
text(train_tree, pretty = 1, cex = .9, col = 'dark blue')

(e) Predict the response on the test data, and produce a confusion matrix comparing the test labels to the predicted test labels. What is the test error rate?
The model predicted 50 of the 270 observations incorrectly, for an error rate of 18.5% (50/270).
Train model and create confusion matrix
oj_pred <- predict(train_tree, test, type = 'class')
(table(test$Purchase, oj_pred))
## oj_pred
## CH MM
## CH 132 36
## MM 14 88
(f) Apply the cv.tree() function to the training set in order to determine the optimal tree size.
Train model and apply cross-validation
set.seed(22)
cv_train_tree <- cv.tree(train_tree)
summary(cv_train_tree)
## Length Class Mode
## size 6 -none- numeric
## dev 6 -none- numeric
## k 6 -none- numeric
## method 1 -none- character
(g) Produce a plot with tree size on the x-axis and cross-validated classification error rate on the y-axis.
plot(cv_train_tree$size, cv_train_tree$dev, xlab = 'Size', ylab = 'Dev', col.lab = 'Dark Blue', col = 'Dark Blue', col.axis = 'Dark Blue')
box(col = 'Dark Blue')
abline(h = min(cv_train_tree$dev) + .2*sd(cv_train_tree$dev), col = 'Red')

(h) Which tree size corresponds to the lowest cross-validated classification error rate?
A tree size of 6 had the smallest CV classification error rate, which means pruning is not necessary for my tree, I will use a best rate of 5 for question 9i..
Print cross validation object
(cv_train_tree)
## $size
## [1] 6 5 4 3 2 1
##
## $dev
## [1] 699.9453 725.8748 722.5750 733.4394 791.1645 1074.3597
##
## $k
## [1] -Inf 17.97797 20.11905 32.82322 71.43514 306.43464
##
## $method
## [1] "deviance"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
(i) Produce a pruned tree corresponding to the optimal tree size obtained using cross-validation. If cross-validation does not lead to selection of a pruned tree, then create a pruned tree with five terminal nodes.
Prune the tree to 5
set.seed(22)
prune_tree <- prune.tree(train_tree, best = 5)
plot(prune_tree, col = 'Light Blue')
text(prune_tree,pretty=1, col = 'Dark Blue', cex = .9)

(j) Compare the training error rates between the pruned and unpruned trees. Which is higher?
The pruned tree had a slightly higher error rate of .1713 when compared to the original tree’s error rate of .1638.
Print summary of the pruned tree
summary(prune_tree)
##
## Classification tree:
## snip.tree(tree = train_tree, nodes = 13L)
## Variables actually used in tree construction:
## [1] "LoyalCH" "ListPriceDiff"
## Number of terminal nodes: 5
## Residual mean deviance: 0.8073 = 641.8 / 795
## Misclassification error rate: 0.1713 = 137 / 800
(k) Compare the test error rates between the pruned and unpruned trees. Which is higher?
The pruned tree predicted 54 of the observations incorrectly for an error rate of 20% (54/270); higher than the original tree’s error rate of 18.5%
Create confusion matrix for the pruned tree
prune_pred <- predict(prune_tree, test, type = 'class')
(table(test$Purchase, prune_pred))
## prune_pred
## CH MM
## CH 132 36
## MM 18 84