Question 3
# (3)
p <- seq(0, 1, 0.01)
err <- 1 - pmax(p, 1 - p)
gini <- 2 * p * (1 - p)
ent <- -p * log(p) - (1 - p) * log(1 - p)
ent[is.na(ent)] <- 0
plot(p, ent, type = "l", col = "red", ylim = c(0, 0.7))
lines(p, gini, col = "blue")
lines(p, err, col = "green")
Question 8
# (8a)
set.seed(1)
train_idx <- sample(1:nrow(Carseats), 0.7 * nrow(Carseats))
train <- Carseats[train_idx, ]
test <- Carseats[-train_idx, ]
# (8b)
tree_fit <- tree(Sales ~ ., data = train)
plot(tree_fit)
text(tree_fit, pretty = 0)

preds <- predict(tree_fit, newdata = test)
mean((test$Sales - preds)^2)
## [1] 4.208383
invisible("Test MSE 4.21")
# (8c)
cv_tree <- cv.tree(tree_fit)
plot(cv_tree$size, cv_tree$dev, type = "b")

best_size <- cv_tree$size[which.min(cv_tree$dev)]
prune_fit <- prune.tree(tree_fit, best = best_size)
prune_preds <- predict(prune_fit, newdata = test)
mean((test$Sales - prune_preds)^2)
## [1] 4.208383
invisible("Pruning the tree does not improve the test MSE")
# (8d)
bag_fit <- randomForest(Sales ~ ., data = train, mtry = 10, importance = TRUE)
bag_preds <- predict(bag_fit, newdata = test)
mean((test$Sales - bag_preds)^2)
## [1] 2.546423
importance(bag_fit)
## %IncMSE IncNodePurity
## CompPrice 33.904069 231.47471
## Income 5.335622 121.33353
## Advertising 17.870018 157.15055
## Population -1.789009 64.69008
## Price 69.684067 690.84486
## ShelveLoc 72.687816 635.79311
## Age 22.489173 235.52049
## Education 1.010342 62.58691
## Urban -1.300710 11.57267
## US 2.778950 10.54935
varImpPlot(bag_fit)

invisible("Bagging has a test MSE of 2.55, with ShelveLoc and Price identified as the most important predictors of sales.")
# (8e)
rf_fit <- randomForest(Sales ~ ., data = train, mtry = 3, importance = TRUE)
rf_preds <- predict(rf_fit, newdata = test)
mean((test$Sales - rf_preds)^2)
## [1] 2.949671
importance(rf_fit)
## %IncMSE IncNodePurity
## CompPrice 16.7327243 203.67364
## Income 2.8719402 162.46321
## Advertising 12.2638672 161.41889
## Population -0.3836743 132.03719
## Price 44.7666458 540.43983
## ShelveLoc 46.4521878 515.63949
## Age 16.7918061 264.58022
## Education 0.5335414 98.80972
## Urban -1.5990201 19.75826
## US 3.7152171 30.35581
invisible("Random forest yields a test MSE of 2.95, with ShelveLoc and Price as the top predictors; reducing m from 10 to 3 decorrelates the trees. Increases the error rate but reduces variance and protects against overfitting.")
# (8f)
xtrain <- train[, -1]
ytrain <- train$Sales
xtest <- test[, -1]
bart_fit <- gbart(xtrain, ytrain, x.test = xtest)
## *****Calling gbart: type=1
## *****Data:
## data:n,p,np: 280, 14, 120
## y1,yn: 2.763393, 0.113393
## x1,x[n*p]: 107.000000, 1.000000
## xp1,xp[np*p]: 113.000000, 1.000000
## *****Number of Trees: 200
## *****Number of Cut Points: 70 ... 1
## *****burn,nd,thin: 100,1000,1
## *****Prior:beta,alpha,tau,nu,lambda,offset: 2,0.95,0.276302,3,0.215848,7.59661
## *****sigma: 1.052663
## *****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: 2s
## trcnt,tecnt: 1000,1000
bart_preds <- bart_fit$yhat.test.mean
mean((test$Sales - bart_preds)^2)
## [1] 1.539667
invisible("BART yields a test MSE of 1.54, outperforming the single tree, bagging, and random forest models.")
Question 9
# (9a)
set.seed(1)
train_indices <- sample(1:nrow(OJ), 800)
train <- OJ[train_indices, ]
test <- OJ[-train_indices, ]
# (9b)
oj_tree <- tree(Purchase ~ ., data = train)
summary(oj_tree)
##
## Classification tree:
## tree(formula = Purchase ~ ., data = 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
invisible("Tree has a training error rate of 15.88% and 9 terminal nodes. It splits primarily on customer loyalty (LoyalCH), alongside price and promotional variables.")
# (9c)
oj_tree
## 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 ) *
invisible("Terminal node 8 contains 59 observations for customers with extremely low loyalty to Citrus Hill (LoyalCH < 0.0356415). Classifies this final leaf as a Minute Maid (MM) purchase with a 98.3% probability.")
# (9d)
plot(oj_tree)
text(oj_tree, pretty = 0)

invisible("The tree plot confirms that customer loyalty (LoyalCH) is the dominant predictor, forming the root split and its primary branches. Low loyalty directs predictions left toward Minute Maid (MM), while high loyalty branches right toward Citrus Hill (CH), with minor adjustments made by price differences and discounts.")
# (9e)
tree_pred <- predict(oj_tree, newdata = test, type = "class")
table(tree_pred, test$Purchase)
##
## tree_pred CH MM
## CH 160 38
## MM 8 64
mean(tree_pred != test$Purchase)
## [1] 0.1703704
invisible("The test error rate is 17.04% (46 out of 270 observations misclassified).")
# (9f)
cv_oj <- cv.tree(oj_tree, FUN = prune.misclass)
cv_oj
## $size
## [1] 9 8 7 4 2 1
##
## $dev
## [1] 150 150 149 158 172 315
##
## $k
## [1] -Inf 0.000000 3.000000 4.333333 10.500000 151.000000
##
## $method
## [1] "misclass"
##
## attr(,"class")
## [1] "prune" "tree.sequence"
invisible("The cross-validation output shows that a tree size of 7 terminal nodes achieves the lowest cross-validation error (deviance of 149). Trees of size 8 and 9 are close behind with an error of 150.")
# (9g)
plot(cv_oj$size, cv_oj$dev, type = "b",
xlab = "Tree Size", ylab = "Cross-Validated Error (Deviance)")

# (9h)
invisible("A tree size of 7 terminal nodes corresponds to the lowest cross-validated classification error rate.")
# (9i)
pruned_oj <- prune.misclass(oj_tree, best = 7)
plot(pruned_oj)
text(pruned_oj, pretty = 0)

# (9j)
invisible("The training error rate of the pruned tree is higher than the unpruned tree. This occurs because pruning removes nodes, reducing the model's capacity to fit the training data perfectly.")
# (9k)
pruned_pred <- predict(pruned_oj, newdata = test, type = "class")
mean(pruned_pred != test$Purchase)
## [1] 0.162963
table(pruned_pred, test$Purchase)
##
## pruned_pred CH MM
## CH 160 36
## MM 8 66
invisible("The unpruned tree has a higher test error rate (17.04%) compared to the pruned tree (16.30%). Pruning successfully reduced overfitting, improving the model's performance.")