In the final chapter of this course, you’ll learn how to use resamples() to compare multiple models and select (or ensemble) the best one(s).
Why reuse a trainControl?
Answer the question
50 XP
Possible Answers
So you can use the same summaryFunction and tuning parameters for multiple models.
So you don’t have to repeat code when fitting multiple models.
So you can compare models on the exact same training and test data.
All of the above.
As you saw in the video, for this chapter you will focus on a real-world dataset that brings together all of the concepts discussed in the previous chapters.
The churn dataset contains data on a variety of telecom customers and the modeling challenge is to predict which customers will cancel their service (or churn).
In this chapter, you will be exploring two different types of predictive models: glmnet and rf, so the first order of business is to create a reusable trainControl object you can use to reliably compare them.
Instructions
100 XP
churn_x and churn_y are loaded in your workspace.
Use createFolds() to create 5 CV folds on churn_y, your target variable for this exercise.
Pass them to trainControl() to create a reusable trainControl for comparing models.
Churn<-load(file="Churn.RData")
str(Churn) #Churn contains churn_x, churn_y in a list that can be called upon later
chr [1:2] "churn_x" "churn_y"
str(churn_y)
Factor w/ 2 levels "no","yes": 1 1 1 1 1 1 1 1 1 1 ...
str(churn_x)
'data.frame': 250 obs. of 70 variables:
$ stateAK : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAL : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAR : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAZ : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCO : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateDC : int 0 0 0 0 0 0 0 0 0 1 ...
$ stateDE : int 0 0 0 1 0 0 0 0 0 0 ...
$ stateFL : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateGA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateHI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateIA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateID : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateIL : int 0 0 0 0 0 0 0 1 0 0 ...
$ stateIN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateKS : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateKY : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateLA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMD : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateME : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMO : int 0 1 0 0 0 0 0 0 0 0 ...
$ stateMS : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNC : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateND : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNE : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNH : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNJ : int 0 0 0 0 0 0 0 0 1 0 ...
$ stateNM : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNV : int 0 0 0 0 0 0 1 0 0 0 ...
$ stateNY : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOH : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOK : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOR : int 0 0 0 0 0 0 0 0 0 0 ...
$ statePA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateRI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateSC : int 1 0 0 0 0 0 0 0 0 0 ...
$ stateSD : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateTN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateTX : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateUT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateVA : int 0 0 0 0 0 1 0 0 0 0 ...
$ stateVT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateWA : int 0 0 0 0 1 0 0 0 0 0 ...
$ stateWI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateWV : int 0 0 1 0 0 0 0 0 0 0 ...
$ stateWY : int 0 0 0 0 0 0 0 0 0 0 ...
$ account_length : int 137 83 48 67 143 163 100 151 139 17 ...
$ area_codearea_code_415 : int 0 1 1 1 0 1 1 0 1 0 ...
$ area_codearea_code_510 : int 1 0 0 0 1 0 0 0 0 1 ...
$ international_planyes : int 0 0 0 0 0 0 0 0 0 0 ...
$ voice_mail_planyes : int 0 0 1 0 0 0 1 0 1 1 ...
$ number_vmail_messages : int 0 0 34 0 0 0 39 0 43 30 ...
$ total_day_minutes : num 110 197 198 164 133 ...
$ total_day_calls : int 112 117 70 79 107 100 74 106 85 101 ...
$ total_day_charge : num 18.7 33.4 33.7 28 22.7 ...
$ total_eve_minutes : num 224 272 274 110 224 ...
$ total_eve_calls : int 88 89 121 108 117 46 80 87 82 85 ...
$ total_eve_charge : num 19 23.12 23.26 9.38 19.03 ...
$ total_night_minutes : num 248 200 218 204 180 ...
$ total_night_calls : int 96 62 71 102 85 116 89 88 105 130 ...
$ total_night_charge : num 11.14 9 9.81 9.18 8.12 ...
$ total_intl_minutes : num 17.8 10.1 7.6 9.8 10.2 12.8 11.2 11.8 8.3 10.3 ...
$ total_intl_calls : int 2 11 4 2 13 3 4 5 5 2 ...
$ total_intl_charge : num 4.81 2.73 2.05 2.65 2.75 3.46 3.02 3.19 2.24 2.78 ...
$ number_customer_service_calls: int 1 3 1 1 1 5 2 0 2 3 ...
# Create custom indices: myFolds
myFolds <- createFolds(churn_y, k = 5)
# Create reusable trainControl object: myControl
myControl <- trainControl(
summaryFunction = twoClassSummary,
classProbs = TRUE, # IMPORTANT!
verboseIter = TRUE,
savePredictions = TRUE,
index = myFolds
)
Now that you have a reusable trainControl object called myControl, you can start fitting different predictive models to your churn dataset and evaluate their predictive accuracy.
You’ll start with one of my favorite models, glmnet, which penalizes linear and logistic regression models on the size and number of coefficients to help prevent overfitting.
Instructions
100 XP
# Fit glmnet model: model_glmnet
model_glmnet <- train(
x = churn_x, y = churn_y,
metric = "ROC",
method = "glmnet",
trControl = myControl
)
+ Fold1: alpha=0.10, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold1: alpha=0.10, lambda=0.01821
+ Fold1: alpha=0.55, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold1: alpha=0.55, lambda=0.01821
+ Fold1: alpha=1.00, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold1: alpha=1.00, lambda=0.01821
+ Fold2: alpha=0.10, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold2: alpha=0.10, lambda=0.01821
+ Fold2: alpha=0.55, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold2: alpha=0.55, lambda=0.01821
+ Fold2: alpha=1.00, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold2: alpha=1.00, lambda=0.01821
+ Fold3: alpha=0.10, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold3: alpha=0.10, lambda=0.01821
+ Fold3: alpha=0.55, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold3: alpha=0.55, lambda=0.01821
+ Fold3: alpha=1.00, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold3: alpha=1.00, lambda=0.01821
+ Fold4: alpha=0.10, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold4: alpha=0.10, lambda=0.01821
+ Fold4: alpha=0.55, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold4: alpha=0.55, lambda=0.01821
+ Fold4: alpha=1.00, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold4: alpha=1.00, lambda=0.01821
+ Fold5: alpha=0.10, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold5: alpha=0.10, lambda=0.01821
+ Fold5: alpha=0.55, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold5: alpha=0.55, lambda=0.01821
+ Fold5: alpha=1.00, lambda=0.01821
one multinomial or binomial class has fewer than 8 observations; dangerous ground
- Fold5: alpha=1.00, lambda=0.01821
Aggregating results
Selecting tuning parameters
Fitting alpha = 0.55, lambda = 0.0182 on full training set
What’s the drawback of using a random forest model for churn prediction?
Answer the question
50 XP
Possible Answers
Tree-based models are usually less accurate than linear models.
You no longer have model coefficients to help interpret the model. [ans]
Nobody else uses random forests to predict churn.
Another one of my favorite models is the random forest, which combines an ensemble of non-linear decision trees into a highly flexible (and usually quite accurate) model.
Rather than using the classic randomForest package, you’ll be using the ranger package, which is a re-implementation of randomForest that produces almost the exact same results, but is faster, more stable, and uses less memory. I highly recommend it as a starting point for random forest modeling in R.
Instructions
100 XP
churn_x and churn_y are loaded in your workspace.
Fit a random forest model to the churn dataset. Be sure to use myControl as the trainControl like you’ve done before and implement the “ranger” method.
# Fit random forest: model_rf
model_rf <- train(
x = churn_x, y = churn_y,
metric = "ROC",
method = "ranger",
trControl = myControl
)
+ Fold1: mtry= 2, min.node.size=1, splitrule=gini
- Fold1: mtry= 2, min.node.size=1, splitrule=gini
+ Fold1: mtry=36, min.node.size=1, splitrule=gini
- Fold1: mtry=36, min.node.size=1, splitrule=gini
+ Fold1: mtry=70, min.node.size=1, splitrule=gini
- Fold1: mtry=70, min.node.size=1, splitrule=gini
+ Fold1: mtry= 2, min.node.size=1, splitrule=extratrees
- Fold1: mtry= 2, min.node.size=1, splitrule=extratrees
+ Fold1: mtry=36, min.node.size=1, splitrule=extratrees
- Fold1: mtry=36, min.node.size=1, splitrule=extratrees
+ Fold1: mtry=70, min.node.size=1, splitrule=extratrees
- Fold1: mtry=70, min.node.size=1, splitrule=extratrees
+ Fold2: mtry= 2, min.node.size=1, splitrule=gini
- Fold2: mtry= 2, min.node.size=1, splitrule=gini
+ Fold2: mtry=36, min.node.size=1, splitrule=gini
- Fold2: mtry=36, min.node.size=1, splitrule=gini
+ Fold2: mtry=70, min.node.size=1, splitrule=gini
- Fold2: mtry=70, min.node.size=1, splitrule=gini
+ Fold2: mtry= 2, min.node.size=1, splitrule=extratrees
- Fold2: mtry= 2, min.node.size=1, splitrule=extratrees
+ Fold2: mtry=36, min.node.size=1, splitrule=extratrees
- Fold2: mtry=36, min.node.size=1, splitrule=extratrees
+ Fold2: mtry=70, min.node.size=1, splitrule=extratrees
- Fold2: mtry=70, min.node.size=1, splitrule=extratrees
+ Fold3: mtry= 2, min.node.size=1, splitrule=gini
- Fold3: mtry= 2, min.node.size=1, splitrule=gini
+ Fold3: mtry=36, min.node.size=1, splitrule=gini
- Fold3: mtry=36, min.node.size=1, splitrule=gini
+ Fold3: mtry=70, min.node.size=1, splitrule=gini
- Fold3: mtry=70, min.node.size=1, splitrule=gini
+ Fold3: mtry= 2, min.node.size=1, splitrule=extratrees
- Fold3: mtry= 2, min.node.size=1, splitrule=extratrees
+ Fold3: mtry=36, min.node.size=1, splitrule=extratrees
- Fold3: mtry=36, min.node.size=1, splitrule=extratrees
+ Fold3: mtry=70, min.node.size=1, splitrule=extratrees
- Fold3: mtry=70, min.node.size=1, splitrule=extratrees
+ Fold4: mtry= 2, min.node.size=1, splitrule=gini
- Fold4: mtry= 2, min.node.size=1, splitrule=gini
+ Fold4: mtry=36, min.node.size=1, splitrule=gini
- Fold4: mtry=36, min.node.size=1, splitrule=gini
+ Fold4: mtry=70, min.node.size=1, splitrule=gini
- Fold4: mtry=70, min.node.size=1, splitrule=gini
+ Fold4: mtry= 2, min.node.size=1, splitrule=extratrees
- Fold4: mtry= 2, min.node.size=1, splitrule=extratrees
+ Fold4: mtry=36, min.node.size=1, splitrule=extratrees
- Fold4: mtry=36, min.node.size=1, splitrule=extratrees
+ Fold4: mtry=70, min.node.size=1, splitrule=extratrees
- Fold4: mtry=70, min.node.size=1, splitrule=extratrees
+ Fold5: mtry= 2, min.node.size=1, splitrule=gini
- Fold5: mtry= 2, min.node.size=1, splitrule=gini
+ Fold5: mtry=36, min.node.size=1, splitrule=gini
- Fold5: mtry=36, min.node.size=1, splitrule=gini
+ Fold5: mtry=70, min.node.size=1, splitrule=gini
- Fold5: mtry=70, min.node.size=1, splitrule=gini
+ Fold5: mtry= 2, min.node.size=1, splitrule=extratrees
- Fold5: mtry= 2, min.node.size=1, splitrule=extratrees
+ Fold5: mtry=36, min.node.size=1, splitrule=extratrees
- Fold5: mtry=36, min.node.size=1, splitrule=extratrees
+ Fold5: mtry=70, min.node.size=1, splitrule=extratrees
- Fold5: mtry=70, min.node.size=1, splitrule=extratrees
Aggregating results
Selecting tuning parameters
Fitting mtry = 70, splitrule = extratrees, min.node.size = 1 on full training set
Now that you have fit two models to the churn dataset, it’s time to compare their out-of-sample predictions and choose which one is the best model for your dataset.
You can compare models in caret using the resamples() function, provided they have the same training data and use the same trainControl object with preset cross-validation folds. resamples() takes as input a list of models and can be used to compare dozens of models at once (though in this case you are only comparing two models).
Instructions
100 XP
model_glmnet and model_rf are loaded in your workspace.
Create a list() containing the glmnet model as item1 and the ranger model as item2.
Pass this list to the resamples() function and save the resulting object as resamples.
Summarize the results by calling summary() on resamples.
# Create model_list
model_list <- list(item1 = model_glmnet, item2 = model_rf)
# Pass model_list to resamples(): resamples
resamples<-resamples(model_list)
# Summarize the results
summary(resamples)
Call:
summary.resamples(object = resamples)
Models: item1, item2
Number of resamples: 5
ROC
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
item1 0.5250286 0.5711760 0.6129531 0.6179179 0.6756044 0.7048276 0
item2 0.6497569 0.6539429 0.6876923 0.7002315 0.7328691 0.7768966 0
Sens
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
item1 0.9142857 0.9200000 0.9252874 0.9381215 0.9540230 0.9770115 0
item2 0.9022989 0.9257143 0.9712644 0.9529918 0.9714286 0.9942529 0
Spec
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
item1 0.08000000 0.1153846 0.1153846 0.1172308 0.1153846 0.1600000 0
item2 0.03846154 0.0400000 0.0800000 0.1163077 0.1153846 0.3076923 0
caret provides a variety of methods to use for comparing models. All of these methods are based on the resamples() function. My favorite is the box-and-whisker plot, which allows you to compare the distribution of predictive accuracy (in this case AUC) for the two models.
In general, you want the model with the higher median AUC, as well as a smaller range between min and max AUC.
You can make this plot using the bwplot() function, which makes a box and whisker plot of the model’s out of sample scores. Box and whisker plots show the median of each distribution as a line and the interquartile range of each distribution as a box around the median line. You can pass the metric = “ROC” argument to the bwplot() function to show a plot of the model’s out-of-sample ROC scores and choose the model with the highest median ROC.
If you do not specify a metric to plot, bwplot() will automatically plot 3 of them.
Instructions
100 XP
# Create bwplot
bwplot(resamples, metric = "ROC")
Another useful plot for comparing models is the scatterplot, also known as the xy-plot. This plot shows you how similar the two models’ performances are on different folds.
It’s particularly useful for identifying if one model is consistently better than the other across all folds, or if there are situations when the inferior model produces better predictions on a particular subset of the data.
Instructions
100 XP
Pass the resamples object to the xyplot() function. Look at the resulting plot and note how similar the two models’ predictions are (or are not) on the different folds. Be sure to specify which metric you want to plot.
# Create xyplot
xyplot(resamples,metric="ROC")
That concludes the course! As a teaser for a future course on making ensembles of caret models, I’ll show you how to fit a stacked ensemble of models using the caretEnsemble package.
caretEnsemble provides the caretList() function for creating multiple caret models at once on the same dataset, using the same resampling folds. You can also create your own lists of caret models.
In this exercise, I’ve made a caretList for you, containing the glmnet and ranger models you fit on the churn dataset. Use the caretStack() function to make a stack of caret models, with the two sub-models (glmnet and ranger) feeding into another (hopefully more accurate!) caret model.
Instructions
100 XP
Call the caretStack() function with two arguments, model_list and method = “glm”, to ensemble the two models using a logistic regression. Store the result as stack.
Summarize the resulting model with the summary() function.
str(churn_x)
'data.frame': 250 obs. of 70 variables:
$ stateAK : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAL : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAR : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateAZ : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCO : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateCT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateDC : int 0 0 0 0 0 0 0 0 0 1 ...
$ stateDE : int 0 0 0 1 0 0 0 0 0 0 ...
$ stateFL : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateGA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateHI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateIA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateID : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateIL : int 0 0 0 0 0 0 0 1 0 0 ...
$ stateIN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateKS : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateKY : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateLA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMD : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateME : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMO : int 0 1 0 0 0 0 0 0 0 0 ...
$ stateMS : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateMT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNC : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateND : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNE : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNH : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNJ : int 0 0 0 0 0 0 0 0 1 0 ...
$ stateNM : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateNV : int 0 0 0 0 0 0 1 0 0 0 ...
$ stateNY : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOH : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOK : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateOR : int 0 0 0 0 0 0 0 0 0 0 ...
$ statePA : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateRI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateSC : int 1 0 0 0 0 0 0 0 0 0 ...
$ stateSD : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateTN : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateTX : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateUT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateVA : int 0 0 0 0 0 1 0 0 0 0 ...
$ stateVT : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateWA : int 0 0 0 0 1 0 0 0 0 0 ...
$ stateWI : int 0 0 0 0 0 0 0 0 0 0 ...
$ stateWV : int 0 0 1 0 0 0 0 0 0 0 ...
$ stateWY : int 0 0 0 0 0 0 0 0 0 0 ...
$ account_length : int 137 83 48 67 143 163 100 151 139 17 ...
$ area_codearea_code_415 : int 0 1 1 1 0 1 1 0 1 0 ...
$ area_codearea_code_510 : int 1 0 0 0 1 0 0 0 0 1 ...
$ international_planyes : int 0 0 0 0 0 0 0 0 0 0 ...
$ voice_mail_planyes : int 0 0 1 0 0 0 1 0 1 1 ...
$ number_vmail_messages : int 0 0 34 0 0 0 39 0 43 30 ...
$ total_day_minutes : num 110 197 198 164 133 ...
$ total_day_calls : int 112 117 70 79 107 100 74 106 85 101 ...
$ total_day_charge : num 18.7 33.4 33.7 28 22.7 ...
$ total_eve_minutes : num 224 272 274 110 224 ...
$ total_eve_calls : int 88 89 121 108 117 46 80 87 82 85 ...
$ total_eve_charge : num 19 23.12 23.26 9.38 19.03 ...
$ total_night_minutes : num 248 200 218 204 180 ...
$ total_night_calls : int 96 62 71 102 85 116 89 88 105 130 ...
$ total_night_charge : num 11.14 9 9.81 9.18 8.12 ...
$ total_intl_minutes : num 17.8 10.1 7.6 9.8 10.2 12.8 11.2 11.8 8.3 10.3 ...
$ total_intl_calls : int 2 11 4 2 13 3 4 5 5 2 ...
$ total_intl_charge : num 4.81 2.73 2.05 2.65 2.75 3.46 3.02 3.19 2.24 2.78 ...
$ number_customer_service_calls: int 1 3 1 1 1 5 2 0 2 3 ...
# install.packages("caretEnsemble")
# library(caretEnsemble)
# model_list <- caretList(
# ~ ., churn_x,
# trControl = myControl,
# methodList = c("glm", "rpart", "rf", "gbm", "glmnet")
# )
# # Create ensemble model: stack
# stack <- caretStack(model_list,method="glm")
#
# # Look at summary
# summary(stack)