We now review k-fold cross-validation.
K fold CV is essentially taking a training dataset and dividing it into k equally sized folds. For each of these folds, a model is trained using all other folds and tested on that specific fold. This results in k different estimates of test error, and thus the total error is the average of all fold test errors.
K fold CV has a significantly lower variability than validation set, as having the dataset split once can lead to drastically different results (for example, observations of a specific class may not occur in the test split).
However, the validation set approach is less computationally intensive, particularly in very large datasets where training a single model may take up a large amount of resources.
LOOCV will have a higher variance as the testing dataset will be restricted to one value, and thus K fold CV can often have a more accurate test error due to the bias variance tradeoff; LOOCV will have a low bias due to having more data, but the presence of a single response variable will lead to a much higher variance. K fold CV is also much less computationally demanding, as there are significantly fewer models being trained.
In Chapter 4, we used logistic regression to predict the probability of default using income and balance on the Default data set. We will now estimate the test error of this logistic regression model using the validation set approach. Do not forget to set a random seed before beginning your analysis.
library(ISLR2)
set.seed(123)
lr <- glm(default ~ income + balance, family = 'binomial', data=Default)
tr_indices <- createDataPartition(Default$default, p=0.8, list=F)
train_def <- Default[tr_indices,]
test_def <- Default[-tr_indices,]
split_lr <- glm(default ~ income + balance, family = 'binomial', data=Default)
split_preds <- predict(split_lr, newdata=test_def)
split_preds <- factor(ifelse(split_preds >= 0.5, 'Yes', 'No'))
misclassification <- mean(test_def$default != split_preds)
misclassification
## [1] 0.03051526
We see the misclassification rate to be approximately 2.7%.
for (i in 1:3){
# will repeat above code for 3 different seeds
set.seed(i)
tr_indices <- createDataPartition(Default$default, p=0.8, list=F)
train_def <- Default[tr_indices,]
test_def <- Default[-tr_indices,]
split_lr <- glm(default ~ income + balance, family = 'binomial', data=Default)
split_preds <- predict(split_lr, newdata=test_def)
split_preds <- factor(ifelse(split_preds >= 0.5, 'Yes', 'No'))
misclassification <- mean(test_def$default != split_preds)
cat("Misclassification for attempt ", i, " is: ", misclassification * 100, '%\n')
}
## Misclassification for attempt 1 is: 2.901451 %
## Misclassification for attempt 2 is: 2.501251 %
## Misclassification for attempt 3 is: 2.801401 %
After repeating it three times we see misclassification rates in a similar range, between 2.5% and 2.9%.
We will estimate the test error using validation set approach, and will repeat it 25 times.
misclassification_rates <- sapply(1:25, function(i){
set.seed(i)
tr_indices <- createDataPartition(Default$default, p=0.8, list=F)
train_def <- Default[tr_indices,]
test_def <- Default[-tr_indices,]
split_lr <- glm(default ~ income + balance + student, family = 'binomial', data=Default)
split_preds <- predict(split_lr, newdata=test_def)
split_preds <- factor(ifelse(split_preds >= 0.5, 'Yes', 'No'))
misclassification <- mean(test_def$default != split_preds)
return(misclassification)
})
cat("The average misclassification rate is", mean(misclassification_rates) * 100, '%')
## The average misclassification rate is 2.747374 %
We see that the performance is relatively similar, with a misclassification rate of 2.74%. There does not appear to be a large improvement in performance using student.
We continue to consider the use of a logistic regression model to predict the probability of default using income and balance on the Default data set. In particular, we will now compute estimates for the standard errors of the income and balance logistic regression coefficients in two different ways: (1) using the bootstrap, and (2) using the standard formula for computing the standard errors in the glm() function. Do not forget to set a random seed before beginning your analysis.
set.seed(341)
mlr <- glm(default ~ income + balance, data=Default, family = 'binomial')
summary(mlr)
##
## Call:
## glm(formula = default ~ income + balance, family = "binomial",
## data = Default)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.154e+01 4.348e-01 -26.545 < 2e-16 ***
## income 2.081e-05 4.985e-06 4.174 2.99e-05 ***
## balance 5.647e-03 2.274e-04 24.836 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2920.6 on 9999 degrees of freedom
## Residual deviance: 1579.0 on 9997 degrees of freedom
## AIC: 1585
##
## Number of Fisher Scoring iterations: 8
The income coefficient is 2.081e-05 with a standard error of 4.985e-06, while the balance coefficient is 5.647e-03 with a standard error of 2.274e-04.
boot.fn <- function(data, indices){
sample_data <- data[indices,] # sample with replacement
sample_data$default <- as.factor(sample_data$default)
mlr <- glm(default ~ income + balance, data=sample_data, family = 'binomial')
return(mlr$coefficients)
}
boot_data <- boot(Default, boot.fn, 1000)
boot_data
##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = Default, statistic = boot.fn, R = 1000)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* -1.154047e+01 -3.348636e-02 4.274945e-01
## t2* 2.080898e-05 1.364367e-07 4.883897e-06
## t3* 5.647103e-03 1.860915e-05 2.314111e-04
summary(mlr) # for comparison
##
## Call:
## glm(formula = default ~ income + balance, family = "binomial",
## data = Default)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.154e+01 4.348e-01 -26.545 < 2e-16 ***
## income 2.081e-05 4.985e-06 4.174 2.99e-05 ***
## balance 5.647e-03 2.274e-04 24.836 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 2920.6 on 9999 degrees of freedom
## Residual deviance: 1579.0 on 9997 degrees of freedom
## AIC: 1585
##
## Number of Fisher Scoring iterations: 8
Using 1000 bootstrapped samples we can compare the result to what we found in part a). We see the standard errors for both coefficients are close to each other. Income has approximately the same coefficient at 2.081e-0.5 for both, with a slightly lower error for boostrap (4.88e-06 compared to 4.96e-06); balance is similar with an approximate coefficient of 5.647e-03 but a slightly higher standard error for bootstrap (2.31e-04 compared to 2.27e-04).
We will now consider the Boston housing data set, from the ISLR2 library.
head(Boston)
## crim zn indus chas nox rm age dis rad tax ptratio lstat medv
## 1 0.00632 18 2.31 0 0.538 6.575 65.2 4.0900 1 296 15.3 4.98 24.0
## 2 0.02731 0 7.07 0 0.469 6.421 78.9 4.9671 2 242 17.8 9.14 21.6
## 3 0.02729 0 7.07 0 0.469 7.185 61.1 4.9671 2 242 17.8 4.03 34.7
## 4 0.03237 0 2.18 0 0.458 6.998 45.8 6.0622 3 222 18.7 2.94 33.4
## 5 0.06905 0 2.18 0 0.458 7.147 54.2 6.0622 3 222 18.7 5.33 36.2
## 6 0.02985 0 2.18 0 0.458 6.430 58.7 6.0622 3 222 18.7 5.21 28.7
mu_hat <- mean(Boston$medv)
mu_hat
## [1] 22.53281
The estimated mean value of owner occupied homes is around $22,500.
Hint: We can compute the standard error of the sample mean by dividing the sample standard deviation by the square root of thenumber of observations.
std <- sd(Boston$medv)/sqrt(nrow(Boston))
std
## [1] 0.4088611
The standard error is 0.408, or about $408.
boot.fn <- function(data, indices){
sample_data <- data[indices,] # sample with replacement
mean <- mean(sample_data$medv)
return(mean)
}
boot_data <- boot(Boston, boot.fn, 1000)
boot_data
##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = Boston, statistic = boot.fn, R = 1000)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* 22.53281 0.02163241 0.4211374
Using bootstrap, we find a mean of 22.532 and a standard error of 0.421, which is similar to what we saw in the previous section.
Hint: You can approximate a 95 % confidence interval using the formula [mu_hat − 2SE(mu_hat), mu_hat + 2SE(mu_hat)].
Using the mean medv and standard error for it, we find the 95% confidence interval to be 21.690 to 23.374. Using the t.test we see:
t.test(Boston$medv)
##
## One Sample t-test
##
## data: Boston$medv
## t = 55.111, df = 505, p-value < 2.2e-16
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
## 21.72953 23.33608
## sample estimates:
## mean of x
## 22.53281
This is very close to our prediction using boostrap, as expected.
med <- median(Boston$medv)
med
## [1] 21.2
The median value of medv is 21.2, or $21,200.
boot.fn <- function(data, indices){
sample_data <- data[indices,] # sample with replacement
med <- median(sample_data$medv)
return(med)
}
boot_data <- boot(Boston, boot.fn, 1000)
boot_data
##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = Boston, statistic = boot.fn, R = 1000)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* 21.2 -0.0332 0.380378
Using bootrap, we find the standard error of the median to be 0.380, or $380.
mu_hat01 <- quantile(Boston$medv, 0.1)
mu_hat01
## 10%
## 12.75
The 10th percentile value of medv is 12.75, or $12,750.
boot.fn <- function(data, indices){
sample_data <- data[indices,] # sample with replacement
mu_hat01 <- quantile(sample_data$medv, 0.1)
return(mu_hat01)
}
boot_data <- boot(Boston, boot.fn, 1000)
boot_data
##
## ORDINARY NONPARAMETRIC BOOTSTRAP
##
##
## Call:
## boot(data = Boston, statistic = boot.fn, R = 1000)
##
##
## Bootstrap Statistics :
## original bias std. error
## t1* 12.75 0.0146 0.5014013
Using bootstrap we can estimate the standard error to be 0.501, or $501. This is slightly larger than the standard error of the overall median found in part f).