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 pm1. The xaxis should display pm1, 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, pm1 = 1- pm2. You could make this plot by hand, but it will be much easier to make in R.
p=seq(0,1,0.0001)
#Gini
G=2*p*(1-p)
#Classification Error
E=1-pmax(p,1-p)
#Entropy
D=-(p*log(p) + (1-p)*log(1-p))
plot(p,D, col="red",ylab="")
lines(p,E,col='green')
lines(p,G,col='blue')
legend(0.3,0.15,c("Entropy", "Misclassification","Gini"),lty=c(1,1,1),lwd=c(2.5,2.5,2.5),col=c('red','green','blue'))
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.
library(ISLR2)
library(tree)
## Warning: package 'tree' was built under R version 4.6.1
library(randomForest)
## Warning: package 'randomForest' was built under R version 4.6.1
## randomForest 4.7-1.2
## Type rfNews() to see new features/changes/bug fixes.
library(BART)
## Warning: package 'BART' was built under R version 4.6.1
## Loading required package: nlme
## Loading required package: survival
train_idx <- sample(1:nrow(Carseats), nrow(Carseats) * 0.7)
carseats.train <- Carseats[train_idx, ]
carseats.test <- Carseats[-train_idx, ]
tree.carseats <- tree(Sales ~ ., data = carseats.train)
summary(tree.carseats)
##
## Regression tree:
## tree(formula = Sales ~ ., data = carseats.train)
## Variables actually used in tree construction:
## [1] "ShelveLoc" "Price" "Age" "CompPrice" "Advertising"
## [6] "Income"
## Number of terminal nodes: 19
## Residual mean deviance: 2.284 = 596.1 / 261
## Distribution of residuals:
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## -3.86100 -1.01200 -0.09753 0.00000 1.09000 3.88200
plot(tree.carseats)
text(tree.carseats, pretty = 0, cex = 0.8)
pred.tree <- predict(tree.carseats, newdata = carseats.test)
test_mse_tree <- mean((pred.tree - carseats.test$Sales)^2)
cat("Test MSE for the Single Regression Tree:", test_mse_tree, "\n")
## Test MSE for the Single Regression Tree: 4.813943
cv.carseats <- cv.tree(tree.carseats)
plot(cv.carseats$size, cv.carseats$dev, type = "b",
xlab = "Tree Size", ylab = "Deviance (RSS)")
best_size <- cv.carseats$size[which.min(cv.carseats$dev)]
cat("Optimal tree size selected by CV:", best_size, "\n")
## Optimal tree size selected by CV: 19
prune.carseats <- prune.tree(tree.carseats, best = best_size)
pred.prune <- predict(prune.carseats, newdata = carseats.test)
test_mse_prune <- mean((pred.prune - carseats.test$Sales)^2)
cat("Test MSE for the Pruned Tree:", test_mse_prune, "\n")
## Test MSE for the Pruned Tree: 4.813943
set.seed(123)
bag.carseats <- randomForest(Sales ~ ., data = carseats.train, mtry = 10, importance = TRUE)
pred.bag <- predict(bag.carseats, newdata = carseats.test)
test_mse_bag <- mean((pred.bag - carseats.test$Sales)^2)
cat("Test MSE for Bagging:", test_mse_bag, "\n")
## Test MSE for Bagging: 2.528781
importance(bag.carseats)
## %IncMSE IncNodePurity
## CompPrice 24.603481 189.274612
## Income 9.652677 129.305699
## Advertising 19.423534 138.692538
## Population -1.000631 70.267856
## Price 62.020526 585.983120
## ShelveLoc 67.855830 645.735751
## Age 25.090758 222.756237
## Education 1.215032 48.740091
## Urban 2.268797 11.507730
## US 3.650981 5.785804
varImpPlot(bag.carseats)
set.seed(123)
rf.carseats <- randomForest(Sales ~ ., data = carseats.train, mtry = 3, importance = TRUE)
pred.rf <- predict(rf.carseats, newdata = carseats.test)
test_mse_rf <- mean((pred.rf - carseats.test$Sales)^2)
cat("Test MSE for Random Forest:", test_mse_rf, "\n")
## Test MSE for Random Forest: 3.026197
importance(rf.carseats)
## %IncMSE IncNodePurity
## CompPrice 15.229528 195.25268
## Income 5.590697 182.38525
## Advertising 11.877026 170.57448
## Population -1.457289 120.91234
## Price 38.471823 460.11516
## ShelveLoc 43.246974 481.41708
## Age 14.338502 250.10551
## Education 2.255961 81.71434
## Urban 1.160462 18.48680
## US 2.884426 21.35574
Bagging uses m = p of 10 variables, giving an MSE of 2.000448. Reducing m to 3 de-correlates the trees, but in this specific instance, it actually increased the test error rate to 2.6392. Typically, lowering m reduces variance at the expense of bias, but if there are dominant predictors like Price and ShelveLoc, forcing splits on weaker variables can sometimes degrade regression performance.
x.train <- model.matrix(Sales ~ ., data = carseats.train)[, -1]
x.test <- model.matrix(Sales ~ ., data = carseats.test)[, -1]
y.train <- carseats.train$Sales
set.seed(123)
bart.carseats <- gbart(x.train, y.train, x.test = x.test)
## *****Calling gbart: type=1
## *****Data:
## data:n,p,np: 280, 11, 120
## y1,yn: -1.757071, 1.372929
## x1,x[n*p]: 141.000000, 0.000000
## xp1,xp[np*p]: 117.000000, 0.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.287616,3,0.193657,7.48707
## *****sigma: 0.997084
## *****w (weights): 1.000000 ... 1.000000
## *****Dirichlet:sparse,theta,omega,a,b,rho,augment: 0,0,1,0.5,1,11,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: 4s
## trcnt,tecnt: 1000,1000
pred.bart <- bart.carseats$yhat.test.mean
test_mse_bart <- mean((pred.bart - carseats.test$Sales)^2)
cat("Test MSE for BART:", test_mse_bart, "\n")
## Test MSE for BART: 1.391552
This problem involves the OJ data set which is part of the ISLR2 package.
set.seed(1)
train_idx <- sample(1:nrow(OJ), 800)
oj.train <- OJ[train_idx, ]
oj.test <- OJ[-train_idx, ]
tree.oj <- tree(Purchase ~ ., data = oj.train)
summary(tree.oj)
##
## Classification tree:
## tree(formula = Purchase ~ ., data = oj.train)
## 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
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 ) *
plot(tree.oj)
text(tree.oj, pretty = 0)
pred.oj <- predict(tree.oj, newdata = oj.test, type = "class")
conf_matrix <- table(pred.oj, oj.test$Purchase)
print(conf_matrix)
##
## pred.oj CH MM
## CH 160 38
## MM 8 64
test_error_rate <- mean(pred.oj != oj.test$Purchase)
cat("Unpruned Test Error Rate:", test_error_rate, "\n")
## Unpruned Test Error Rate: 0.1703704
Apply the cv.tree() function to the training set in order to determine the optimal tree size.
Produce a plot with tree size on the x-axis and cross-validated classification error rate on the y-axis.
cv.oj <- cv.tree(tree.oj, FUN = prune.misclass)
plot(cv.oj$size, cv.oj$dev, type = "b",
xlab = "Tree Size", ylab = "Cross-Validated Misclassification Classification Error Count")
best_oj_size <- cv.oj$size[which.min(cv.oj$dev)]
cat("The lowest cross-validated classification error corresponds to a size of:", min(best_oj_size), "\n")
## The lowest cross-validated classification error corresponds to a size of: 7
prune.oj <- prune.misclass(tree.oj, best = 7)
plot(prune.oj)
text(prune.oj, pretty = 0)
cat("Unpruned Summary:\n")
## Unpruned Summary:
summary(tree.oj)$misclass
## [1] 127 800
cat("Pruned Summary:\n")
## Pruned Summary:
summary(prune.oj)$misclass
## [1] 130 800
The pruned tree’s training misclassification count 130/800 is higher than the unpruned tree’s 127/800. This makes complete sense because pruning reduces the model’s flexibility on the training data.
pred.pruned.oj <- predict(prune.oj, newdata = oj.test, type = "class")
pruned_test_error <- mean(pred.pruned.oj != oj.test$Purchase)
cat("Unpruned Test Error Rate:", test_error_rate, "\n")
## Unpruned Test Error Rate: 0.1703704
cat("Pruned Test Error Rate:", pruned_test_error, "\n")
## Pruned Test Error Rate: 0.162963
The unpruned tree’s test error rate 0.1704 is higher than the pruned tree’s 0.1630. This shows that pruning successfully reduced overfitting, improving generalization performance on unseen data