library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.2
library(reshape2)
library(ISLR2)
library(tree)
library(rpart)
library(rpart.plot)
## Warning: package 'rpart.plot' was built under R version 4.3.2
library(caret)
## Loading required package: lattice
library(randomForest)
## randomForest 4.7-1.1
## Type rfNews() to see new features/changes/bug fixes.
## 
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
## 
##     margin
library(dbarts)
## Warning: package 'dbarts' was built under R version 4.3.2

Question 3

Consider the Gini index, classification error, and entropy in a simple classification setting with two classes. Create a single plot that dis- plays each of these quantities as a function of pˆm1. The x-axis should display 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, pˆm1 = 1 − pˆm2. You could make this plot by hand, but it will be much easier to make in R.

pm1 <- seq(0, 1, length.out = 500)
gini_index <- 2 * pm1 * (1 - pm1)
classification_error <- pmin(pm1, 1 - pm1)
entropy <- -pm1 * log2(pm1 + 1e-10) - (1 - pm1) * log2(1 - pm1 + 1e-10)

data <- data.frame(pm1, gini_index, classification_error, entropy)

data_melted <- melt(data, id.vars = 'pm1', variable.name = 'Measure', value.name = 'Value')

# Plot
ggplot(data_melted, aes(x = pm1, y = Value, color = Measure)) +
  geom_line() +
labs(x = expression(hat(p)[m1]), y = "Value",
     title = paste("Gini Index, Classification Error, and Entropy as a function of", expression(hat(p)[m1]))) +

  theme_minimal() +
  scale_color_brewer(palette = "Dark2") +
  theme(text = element_text(size=12))

Question 8

In the lab, a classification tree was applied to the Carseats data set af- ter 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.

data("Carseats")
head(Carseats)
##   Sales CompPrice Income Advertising Population Price ShelveLoc Age Education
## 1  9.50       138     73          11        276   120       Bad  42        17
## 2 11.22       111     48          16        260    83      Good  65        10
## 3 10.06       113     35          10        269    80    Medium  59        12
## 4  7.40       117    100           4        466    97    Medium  55        14
## 5  4.15       141     64           3        340   128       Bad  38        13
## 6 10.81       124    113          13        501    72       Bad  78        16
##   Urban  US
## 1   Yes Yes
## 2   Yes Yes
## 3   Yes Yes
## 4   Yes Yes
## 5   Yes  No
## 6    No Yes

(a) Split the data set into a training set and a test set.

set.seed(1)

train = sample(dim(Carseats)[1], dim(Carseats)[1]/2)
Carseats.train = Carseats[train, ]
Carseats.test = Carseats[-train, ]

(b) Fit a regression tree to the training set. Plot the tree, and inter- pret the results. What test MSE do you obtain?

According to my results, my Test Mean Squared Error was 5.16518178751299

# Fit a regression tree model
tree_model <- rpart(Sales ~ ., data = Carseats.train, method = "anova")

# Plot the regression tree
rpart.plot(tree_model, main="Regression Tree for Carseats Sales", extra=1)

predictions <- predict(tree_model, Carseats.test)
# Calculate Mean Squared Error (MSE)
mse <- mean((Carseats.test$Sales - predictions)^2)

print(paste("Test Mean Squared Error:", mse))
## [1] "Test Mean Squared Error: 5.16518178751299"

(c) Use cross-validation in order to determine the optimal level of tree complexity. Does pruning the tree improve the test MSE?

According to my results, my Test Mean Squared Error went from 5.16518178751299 to 8.0613310725

# cross-validation
set.seed(123) # For reproducibility
control <- trainControl(method="cv", number=10)


cpGrid <- expand.grid(.cp=seq(0.001, 0.1, by=0.001))


cv_model <- train(Sales ~ ., data=Carseats.train, method="rpart",
                  trControl=control, tuneGrid=cpGrid)


best_cp <- cv_model$bestTune$.cp


pruned_tree <- rpart(Sales ~ ., data = Carseats.train, method="anova", cp=best_cp)


pruned_predictions <- predict(pruned_tree, Carseats.test)


pruned_mse <- mean((Carseats.test$Sales - pruned_predictions)^2)


print(paste("Pruned Tree Test Mean Squared Error:", pruned_mse))
## [1] "Pruned Tree Test Mean Squared Error: 8.0613310725"

(d) Use the bagging approach in order to analyze this data. What test MSE do you obtain? Use the importance() function to de- termine which variables are most important.

I saw the my Test Mean Squared Error was 2.62216953572064

# bagging model
set.seed(123) 
bagging_model <- randomForest(Sales ~ ., data = Carseats.train, mtry = ncol(Carseats.train) - 1, importance = TRUE)


bagging_predictions <- predict(bagging_model, Carseats.test)


bagging_mse <- mean((Carseats.test$Sales - bagging_predictions)^2)
print(paste("Bagging Test Mean Squared Error:", bagging_mse))
## [1] "Bagging Test Mean Squared Error: 2.62216953572064"
variable_importance <- importance(bagging_model)
print(variable_importance)
##                %IncMSE IncNodePurity
## CompPrice   26.4012250    171.833476
## Income       5.2986947     91.287059
## Advertising 14.8653864    106.322362
## Population  -1.3016770     58.450270
## Price       61.8972031    508.651053
## ShelveLoc   48.6813291    366.772122
## Age         16.7438295    154.355128
## Education    2.1458731     43.548804
## Urban       -0.4139116      7.678707
## US           5.8826617     15.815135
varImpPlot(bagging_model)

(e) Use random forests to analyze this data. What test MSE do you obtain? Use the importance() function to determine which vari- ables are most important. Describe the effect of m, the number of variables considered at each split, on the error rate obtained.

set.seed(123) 
rf_model <- randomForest(Sales ~ ., data = Carseats.train, mtry = floor(sqrt(ncol(Carseats.train) - 1)), importance = TRUE)

rf_predictions <- predict(rf_model, Carseats.test)


rf_mse <- mean((Carseats.test$Sales - rf_predictions)^2)
print(paste("Random Forest Test Mean Squared Error:", rf_mse))
## [1] "Random Forest Test Mean Squared Error: 3.0346790509252"
rf_variable_importance <- importance(rf_model)
print(rf_variable_importance)
##                %IncMSE IncNodePurity
## CompPrice   15.9798090     154.74702
## Income       3.9262250     133.27867
## Advertising  9.5034904     104.02220
## Population  -1.9168315      93.90404
## Price       34.7891781     396.52698
## ShelveLoc   36.6558536     291.06552
## Age         13.4679974     173.48227
## Education    0.3123937      75.68286
## Urban        0.4711800      15.93681
## US           5.8566680      32.89546
varImpPlot(rf_model)

Question 9

9. This problem involves the OJ data set which is part of the ISLR2 package.

head(OJ)
##   Purchase WeekofPurchase StoreID PriceCH PriceMM DiscCH DiscMM SpecialCH
## 1       CH            237       1    1.75    1.99   0.00    0.0         0
## 2       CH            239       1    1.75    1.99   0.00    0.3         0
## 3       CH            245       1    1.86    2.09   0.17    0.0         0
## 4       MM            227       1    1.69    1.69   0.00    0.0         0
## 5       CH            228       7    1.69    1.69   0.00    0.0         0
## 6       CH            230       7    1.69    1.99   0.00    0.0         0
##   SpecialMM  LoyalCH SalePriceMM SalePriceCH PriceDiff Store7 PctDiscMM
## 1         0 0.500000        1.99        1.75      0.24     No  0.000000
## 2         1 0.600000        1.69        1.75     -0.06     No  0.150754
## 3         0 0.680000        2.09        1.69      0.40     No  0.000000
## 4         0 0.400000        1.69        1.69      0.00     No  0.000000
## 5         0 0.956535        1.69        1.69      0.00    Yes  0.000000
## 6         1 0.965228        1.99        1.69      0.30    Yes  0.000000
##   PctDiscCH ListPriceDiff STORE
## 1  0.000000          0.24     1
## 2  0.000000          0.24     1
## 3  0.091398          0.23     1
## 4  0.000000          0.00     1
## 5  0.000000          0.00     0
## 6  0.000000          0.30     0

(a) Create a training set containing a random sample of 800 obser- vations, and a test set containing the remaining observations.

set.seed(123) 

train_indices <- sample(nrow(OJ), 800)


OJ_train <- OJ[train_indices, ]
OJ_test <- OJ[-train_indices, ]

(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?

According to my results, I got 8 terminal nodes and I got my training error was 16.5%. The tree model suggests that loyalty and price difference are key determinants.

oj_tree <- tree(Purchase ~ ., data = OJ_train)

summary(oj_tree)
## 
## Classification tree:
## tree(formula = Purchase ~ ., data = OJ_train)
## Variables actually used in tree construction:
## [1] "LoyalCH"   "PriceDiff"
## Number of terminal nodes:  8 
## Residual mean deviance:  0.7625 = 603.9 / 792 
## Misclassification error rate: 0.165 = 132 / 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.

I picked terminal node 8. My ‘LoyalCH’ is < 0.0356415, I had 56 observations fall into it. The goodness of fit is 10.03. My low LoyalCH suggests customers have no loyalty to Citrus Hill. This node’s low deviance suggests that the model fits very well for this particular group of customers.

oj_tree
## node), split, n, deviance, yval, (yprob)
##       * denotes terminal node
## 
##  1) root 800 1071.00 CH ( 0.60875 0.39125 )  
##    2) LoyalCH < 0.5036 350  415.10 MM ( 0.28000 0.72000 )  
##      4) LoyalCH < 0.276142 170  131.00 MM ( 0.12941 0.87059 )  
##        8) LoyalCH < 0.0356415 56   10.03 MM ( 0.01786 0.98214 ) *
##        9) LoyalCH > 0.0356415 114  108.90 MM ( 0.18421 0.81579 ) *
##      5) LoyalCH > 0.276142 180  245.20 MM ( 0.42222 0.57778 )  
##       10) PriceDiff < 0.05 74   74.61 MM ( 0.20270 0.79730 ) *
##       11) PriceDiff > 0.05 106  144.50 CH ( 0.57547 0.42453 ) *
##    3) LoyalCH > 0.5036 450  357.10 CH ( 0.86444 0.13556 )  
##      6) PriceDiff < -0.39 27   32.82 MM ( 0.29630 0.70370 ) *
##      7) PriceDiff > -0.39 423  273.70 CH ( 0.90071 0.09929 )  
##       14) LoyalCH < 0.705326 130  135.50 CH ( 0.78462 0.21538 )  
##         28) PriceDiff < 0.145 43   58.47 CH ( 0.58140 0.41860 ) *
##         29) PriceDiff > 0.145 87   62.07 CH ( 0.88506 0.11494 ) *
##       15) LoyalCH > 0.705326 293  112.50 CH ( 0.95222 0.04778 ) *

(d) Create a plot of the tree, and interpret the results.

I see that LoyalCH is the root node. It means that it’s the most critical factor in determining whether a customer will purchase Citrus Hill or Minute Maid. If split is 0.5036. If LoyalCH is < that then the tree moves to the left child node, otherwise to the right child node.

plot(oj_tree, main = "Decision Tree for OJ Purchase Data")
text(oj_tree, pretty = 0)

(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?

My test error rate was 0.185185185185185.

oj_test_pred <- predict(oj_tree, OJ_test, type = "class")

conf_matrix <- table(OJ_test$Purchase, oj_test_pred)


print(conf_matrix)
##     oj_test_pred
##       CH  MM
##   CH 150  16
##   MM  34  70
test_error_rate <- 1 - sum(diag(conf_matrix)) / sum(conf_matrix)
print(paste("Test error rate:", test_error_rate))
## [1] "Test error rate: 0.185185185185185"

(f) Apply the cv.tree() function to the training set in order to determine the optimal tree size.

cv_oj_tree <- cv.tree(oj_tree, FUN = prune.tree)

print(cv_oj_tree)
## $size
## [1] 8 7 6 5 4 3 2 1
## 
## $dev
## [1]  690.2346  692.8691  683.0423  717.4446  717.4446  743.3604  793.1266
## [8] 1073.0916
## 
## $k
## [1]      -Inf  12.03823  14.92474  25.76707  26.02613  38.91686  50.61655
## [8] 298.68751
## 
## $method
## [1] "deviance"
## 
## attr(,"class")
## [1] "prune"         "tree.sequence"

(g) Produce a plot with tree size on the x-axis and cross-validated classification error rate on the y-axis.

plot(cv_oj_tree$size, cv_oj_tree$dev, type = "b", xlab = "Tree Size", ylab = "CV Deviance")

(h) Which tree size corresponds to the lowest cross-validated classi- fication error rate?

According to my result, size 6 gives the lowest cross-validation error.

(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.

pruned_oj_tree <- prune.tree(oj_tree, best = 6)

summary(pruned_oj_tree)
## 
## Classification tree:
## snip.tree(tree = oj_tree, nodes = c(4L, 14L))
## Variables actually used in tree construction:
## [1] "LoyalCH"   "PriceDiff"
## Number of terminal nodes:  6 
## Residual mean deviance:  0.7945 = 630.9 / 794 
## Misclassification error rate: 0.165 = 132 / 800

(j) Compare the training error rates between the pruned and unpruned trees. Which is higher?

I got the same results for both 0.165

# Predictions on the training data using the unpruned tree
unpruned_train_pred <- predict(oj_tree, OJ_train, type = "class")

# Calculate training error rate for the unpruned tree
unpruned_train_error <- mean(unpruned_train_pred != OJ_train$Purchase)
print(paste("Unpruned Tree Training Error Rate:", unpruned_train_error))
## [1] "Unpruned Tree Training Error Rate: 0.165"
# Predictions on the training data using the pruned tree
pruned_train_pred <- predict(pruned_oj_tree, OJ_train, type = "class")

# Calculate training error rate for the pruned tree
pruned_train_error <- mean(pruned_train_pred != OJ_train$Purchase)
print(paste("Pruned Tree Training Error Rate:", pruned_train_error))
## [1] "Pruned Tree Training Error Rate: 0.165"

(k) Compare the test error rates between the pruned and unpruned trees. Which is higher?

I also got the same results for test error rates 0.185185185185185

# Predictions on the test data using the unpruned tree
unpruned_test_pred <- predict(oj_tree, OJ_test, type = "class")

# Calculate test error rate for the unpruned tree
unpruned_test_error <- mean(unpruned_test_pred != OJ_test$Purchase)
print(paste("Unpruned Tree Test Error Rate:", unpruned_test_error))
## [1] "Unpruned Tree Test Error Rate: 0.185185185185185"
# Predictions on the test data using the pruned tree
pruned_test_pred <- predict(pruned_oj_tree, OJ_test, type = "class")

# Calculate test error rate for the pruned tree
pruned_test_error <- mean(pruned_test_pred != OJ_test$Purchase)
print(paste("Pruned Tree Test Error Rate:", pruned_test_error))
## [1] "Pruned Tree Test Error Rate: 0.185185185185185"