Decision Tree in R | Classification Tree & Code in R

Decision Trees are versatile Machine Learning algorithm that can perform both classification and regression tasks. They are very powerful algorithms, capable of fitting complex datasets. Besides, decision trees are fundamental components of random forests, which are among the most potent Machine Learning algorithms available today.

Step 1) Import the data

set.seed(678)
path <- 'https://raw.githubusercontent.com/guru99-edu/R-Programming/master/titanic_data.csv'
titanic <-read.csv(path)
head(titanic)
tail(titanic)

From the head and tail output, you can notice the data is not shuffled. This is a big issue! When you will split your data between a train set and test set, you will select only the passenger from class 1 and 2 (No passenger from class 3 are in the top 80 percent of the observations), which means the algorithm will never see the features of passenger of class 3. This mistake will lead to poor prediction.

shuffle_index <- sample(1:nrow(titanic))
head(shuffle_index)
[1]   57  774  796 1044  681  920
titanic <- titanic[shuffle_index, ]
head(titanic)
View(titanic)

Step 2) Clean the dataset

The structure of the data shows some variables have NA’s. Data clean up to be done as follows

  • Drop variables home.dest,cabin, name, X and ticket
  • Create factor variables for pclass and survived
  • Drop the NA
library(tidyverse)
# Drop variables
clean_titanic <- titanic %>%
select(-c(home.dest, cabin, name, x, ticket)) %>% 
#Convert to factor level
    mutate(age = as.numeric(age), fare= as.numeric(fare), pclass = factor(pclass, levels = c(1, 2, 3), labels = c('Upper', 'Middle', 'Lower')),
    survived = factor(survived, levels = c(0, 1), labels = c('No', 'Yes'))) %>% na.omit()
Warning: Problem while computing `age = as.numeric(age)`.
i NAs introduced by coercion
Warning: Problem while computing `fare = as.numeric(fare)`.
i NAs introduced by coercion
glimpse(clean_titanic)
Rows: 1,045
Columns: 8
$ pclass   <fct> Upper, Lower, Lower, Middle, Lower, Middle, Lower, Lower, Upper, Middle, Upper, Middle, Middle, Middle, Upper, Upper,~
$ survived <fct> Yes, No, No, No, No, No, No, No, Yes, No, Yes, No, No, Yes, No, No, No, No, No, No, No, No, Yes, No, No, Yes, No, Yes~
$ sex      <chr> "male", "male", "male", "male", "female", "female", "male", "male", "female", "male", "female", "male", "female", "fe~
$ age      <dbl> 36.0, 42.0, 18.5, 44.0, 19.0, 26.0, 23.0, 28.5, 64.0, 36.5, 45.0, 42.0, 22.0, 20.0, 17.0, 52.0, 24.0, 35.0, 18.0, 27.~
$ sibsp    <int> 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, ~
$ parch    <int> 2, 0, 0, 0, 0, 1, 0, 0, 2, 2, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, ~
$ fare     <dbl> 120.0000, 8.6625, 7.2292, 13.0000, 16.1000, 26.0000, 7.8542, 16.1000, 83.1583, 26.0000, 164.8667, 13.0000, 21.0000, 2~
$ embarked <chr> "S", "S", "C", "S", "S", "S", "S", "S", "C", "S", "S", "S", "S", "S", "S", "S", "S", "S", "Q", "S", "S", "S", "S", "S~

Step 3) Create train/test set

Before you train your model, you need to perform two steps:

Create a train and test set: You train the model on the train set and test the prediction on the test set (i.e. unseen data) Install rpart.plot from the console

The common practice is to split the data 80/20, 80 percent of the data serves to train the model, and 20 percent to make predictions. You need to create two separate data frames. You don’t want to touch the test set until you finish building your model. You can create a function name create_train_test() that takes three arguments.

create_train_test <- function(data, size = 0.8, train = TRUE) {
    n_row = nrow(data)
    total_row = size * n_row
    train_sample = c(1: total_row)
    if (train == TRUE) {
        return (data[train_sample, ])
    } else {
        return (data[-train_sample, ])
    }
}
data_train <- create_train_test(clean_titanic, 0.8, train = TRUE)
data_test <- create_train_test(clean_titanic, 0.8, train = FALSE)

dim(data_train)
[1] 836   8
dim(data_test)
[1] 209   8
str(data_train)
'data.frame':   836 obs. of  8 variables:
 $ pclass  : Factor w/ 3 levels "Upper","Middle",..: 1 3 3 2 3 2 3 3 1 2 ...
 $ survived: Factor w/ 2 levels "No","Yes": 2 1 1 1 1 1 1 1 2 1 ...
 $ sex     : chr  "male" "male" "male" "male" ...
 $ age     : num  36 42 18.5 44 19 26 23 28.5 64 36.5 ...
 $ sibsp   : int  1 0 0 0 1 1 0 0 0 0 ...
 $ parch   : int  2 0 0 0 0 1 0 0 2 2 ...
 $ fare    : num  120 8.66 7.23 13 16.1 ...
 $ embarked: chr  "S" "S" "C" "S" ...
 - attr(*, "na.action")= 'omit' Named int [1:264] 3 4 5 8 9 14 19 44 49 54 ...
  ..- attr(*, "names")= chr [1:264] "796" "1044" "681" "1019" ...

The train dataset has 1046 rows while the test dataset has 262 rows.

You use the function prop.table() combined with table() to verify if the randomization process is correct.

prop.table(table(data_train$survived))

       No       Yes 
0.6004785 0.3995215 
prop.table(table(data_test$survived))

       No       Yes 
0.5550239 0.4449761 

Step 4) Build the model

You are ready to build the model. The syntax for Rpart decision tree function is:

rpart(formula, data=, method=’’) arguments:
- formula: The function to predict - data: Specifies the data frame- method:
- “class” for a classification tree
- “anova” for a regression tree

library(rpart)
library(rpart.plot)
fit <- rpart(survived~., data = data_train, method = 'class')
rpart.plot(fit, extra = 106)

Step 5) Make a prediction

You can predict your test dataset. To make a prediction, you can use the predict() function. The basic syntax of predict for R decision tree is:

predict_unseen <-predict(fit, data_test, type = 'class')

table_mat <- table(data_test$survived, predict_unseen)
table_mat
     predict_unseen
       No Yes
  No  106  10
  Yes  34  59

Step 6) Measure performance

You can compute an accuracy measure for classification task with the confusion matrix:

The confusion matrix is a better choice to evaluate the classification performance. The general idea is to count the number of times True instances are classified are False.

accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)
print(paste('Accuracy for test', accuracy_Test))
[1] "Accuracy for test 0.789473684210526"

Step 7) Tune the hyper-parameters

Decision tree in R has various parameters that control aspects of the fit. In rpart decision tree library, you can control the parameters using the rpart.control() function. In the following code, you introduce the parameters you will tune. You can refer to the vignette for other parameters.

rpart.control(minsplit = 20, minbucket = round(minsplit/3), maxdepth = 30) Arguments: -minsplit: Set the minimum number of observations in the node before the algorithm perform a split -minbucket: Set the minimum number of observations in the final note i.e. the leaf -maxdepth: Set the maximum depth of any node of the final tree. The root node is treated a depth 0

accuracy_tune <- function(fit) {
    predict_unseen <- predict(fit, data_test, type = 'class')
    table_mat <- table(data_test$survived, predict_unseen)
    accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)
    accuracy_Test
}

You can try to tune the parameters and see if you can improve the model over the default value. As a reminder, you need to get an accuracy higher than 0.78

control <- rpart.control(minsplit = 4,
    minbucket = round(5 / 3),
    maxdepth = 3,
    cp = 0)
tune_fit <- rpart(survived~., data = data_train, method = 'class', control = control)
accuracy_tune(tune_fit)
[1] 0.7942584

You get a higher performance than the previous model. Congratulation!

R Random Forest Tutorial with Example

Random forests are based on a simple idea: ‘the wisdom of the crowd’. Aggregate of the results of multiple predictors gives a better prediction than the best individual predictor. A group of predictors is called an ensemble. Thus, this technique is called Ensemble Learning.

In earlier tutorial, you learned how to use Decision trees to make a binary prediction. To improve our technique, we can train a group of Decision Tree classifiers, each on a different random subset of the train set. To make a prediction, we just obtain the predictions of all individuals trees, then predict the class that gets the most votes. This technique is called Random Forest.

Step 1) Import the data

To make sure you have the same dataset as in the tutorial for decision trees, the train test and test set are stored on the internet. You can import them without make any change.

Step 2) Train the model

One way to evaluate the performance of a model is to train it on a number of different smaller datasets and evaluate them over the other smaller testing set. This is called the F-fold cross-validation feature. R has a function to randomly split number of datasets of almost the same size. For example, if k=9, the model is evaluated over the nine folder and tested on the remaining test set. This process is repeated until all the subsets have been evaluated. This technique is widely used for model selection, especially when the model has parameters to tune.

Now that we have a way to evaluate our model, we need to figure out how to choose the parameters that generalized best the data.

Random forest chooses a random subset of features and builds many Decision Trees. The model averages out all the predictions of the Decisions trees.

RandomForest(formula, ntree=n, mtry=FALSE, maxnodes = NULL) Arguments: - Formula: Formula of the fitted model - ntree: number of trees in the forest - mtry: Number of candidates draw to feed the algorithm. By default, it is the square of the number of columns. - maxnodes: Set the maximum amount of terminal nodes in the forest - importance=TRUE: Whether independent variables importance in the random forest be assessed

Tuning a model is very tedious work. There are lot of combination possible between the parameters. You don’t necessarily have the time to try all of them. A good alternative is to let the machine find the best combination for you. There are two methods available:

  • Random Search
  • Grid Search We will define both methods but during the tutorial, we will train the model using grid search

Grid Search definition

The grid search method is simple, the model will be evaluated over all the combination you pass in the function, using cross-validation.

For instance, you want to try the model with 10, 20, 30 number of trees and each tree will be tested over a number of mtry equals to 1, 2, 3, 4, 5. Then the machine will test 15 different models:

library(randomForest)
library(caret)
library(e1071)

Default setting K-fold cross validation is controlled by the trainControl() function

trainControl(method = “cv”, number = n, search =“grid”) arguments - method = “cv”: The method used to resample the dataset. - number = n: Number of folders to create - search = “grid”: Use the search grid method. For randomized method, use “grid”

Random Search definition The big difference between random search and grid search is, random search will not evaluate all the combination of hyperparameter in the searching space. Instead, it will randomly choose combination at every iteration. The advantage is it lower the computational cost.

Set the control parameter You will proceed as follow to construct and evaluate the model:

  • Evaluate the model with the default setting
  • Find the best number of mtry
  • Find the best number of maxnodes
  • Find the best number of ntrees
  • Evaluate the model on the test dataset
# Define the control
trControl <- trainControl(method = "cv",
    number = 10,
    search = "grid")

You will use caret library to evaluate your model. The library has one function called train() to evaluate almost all machine learning algorithm. Say differently, you can use this function to train other algorithms.

  • The basic syntax is: train(formula, df, method = “rf”, metric= “Accuracy”, trControl = trainControl(), tuneGrid = NULL) argument
  • formula: Define the formula of the algorithm
  • method: Define which model to train.
  • metric = “Accuracy”: Define how to select the optimal model
  • trControl = trainControl(): Define the control parameters
  • tuneGrid = NULL: Return a data frame with all the possible combination
set.seed(1234)
# Run the model
rf_default <- train(survived ~.,
    data = data_train,
    method = "rf",
    metric = "Accuracy",
    trControl = trControl)
# Print the results
print(rf_default)
Random Forest 

836 samples
  7 predictor
  2 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 752, 753, 752, 753, 752, 753, ... 
Resampling results across tuning parameters:

  mtry  Accuracy   Kappa    
   2    0.7906483  0.5457050
   6    0.7894435  0.5515548
  10    0.7643431  0.5030373

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 2.

Step 2) Search best mtry

You can test the model with values of mtry from 1 to 10

set.seed(1234)
tuneGrid <- expand.grid(.mtry = c(1: 10))
rf_mtry <- train(survived~.,
    data = data_train,
    method = "rf",
    metric = "Accuracy",
    tuneGrid = tuneGrid,
    trControl = trControl,
    importance = TRUE,
    nodesize = 14,
    ntree = 300)
print(rf_mtry)
Random Forest 

836 samples
  7 predictor
  2 classes: 'No', 'Yes' 

No pre-processing
Resampling: Cross-Validated (10 fold) 
Summary of sample sizes: 752, 753, 752, 753, 752, 753, ... 
Resampling results across tuning parameters:

  mtry  Accuracy   Kappa    
   1    0.7667097  0.4778802
   2    0.7990103  0.5617566
   3    0.7966581  0.5600419
   4    0.7942915  0.5567493
   5    0.7978772  0.5662198
   6    0.7966724  0.5636781
   7    0.7930579  0.5570674
   8    0.7943058  0.5600845
   9    0.7894578  0.5492753
  10    0.7918962  0.5555188

Accuracy was used to select the optimal model using the largest value.
The final value used for the model was mtry = 2.
rf_mtry$bestTune$mtry
[1] 2
max(rf_mtry$results$Accuracy)
[1] 0.7990103
best_mtry <- rf_mtry$bestTune$mtry 
best_mtry
[1] 2

Step 3) Search the best maxnodes

You need to create a loop to evaluate the different values of maxnodes. In the following code, you will:

  • Create a list
  • Create a variable with the best value of the parameter mtry; Compulsory
  • Create the loop
  • Store the current value of maxnode
  • Summarize the results
store_maxnode <- list()
tuneGrid <- expand.grid(.mtry = best_mtry)
for (maxnodes in c(5: 15)) {
    set.seed(1234)
    rf_maxnode <- train(survived~.,
        data = data_train,
        method = "rf",
        metric = "Accuracy",
        tuneGrid = tuneGrid,
        trControl = trControl,
        importance = TRUE,
        nodesize = 14,
        maxnodes = maxnodes,
        ntree = 300)
    current_iteration <- toString(maxnodes)
    store_maxnode[[current_iteration]] <- rf_maxnode
}
results_mtry <- resamples(store_maxnode)
summary(results_mtry)

Call:
summary.resamples(object = results_mtry)

Models: 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 
Number of resamples: 10 

Accuracy 
        Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
5  0.7469880 0.7597533 0.7738095 0.7727338 0.7850688 0.8072289    0
6  0.7500000 0.7710843 0.7857143 0.7894435 0.8095238 0.8313253    0
7  0.7380952 0.7597533 0.7843517 0.7870052 0.8089501 0.8571429    0
8  0.7590361 0.7641997 0.7843517 0.7929862 0.8048264 0.8690476    0
9  0.7500000 0.7657057 0.7916667 0.7930293 0.8162651 0.8333333    0
10 0.7500000 0.7641997 0.7904475 0.7966294 0.8273810 0.8571429    0
11 0.7380952 0.7777180 0.7976190 0.7966150 0.8162651 0.8452381    0
12 0.7500000 0.7831325 0.7857143 0.7942197 0.8138626 0.8571429    0
13 0.7590361 0.7678571 0.7904475 0.7930149 0.8138626 0.8452381    0
14 0.7590361 0.7738095 0.7963282 0.7989530 0.8208907 0.8571429    0
15 0.7590361 0.7951807 0.8035714 0.8049627 0.8208907 0.8690476    0

Kappa 
        Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
5  0.4339071 0.4610718 0.5005875 0.4939166 0.5236180 0.5662965    0
6  0.4473684 0.4849300 0.5222538 0.5337185 0.5876485 0.6246770    0
7  0.4122137 0.4584416 0.5284378 0.5291909 0.5799157 0.6939891    0
8  0.4601542 0.4725903 0.5284378 0.5421289 0.5645372 0.7083333    0
9  0.4417722 0.4776392 0.5415944 0.5428190 0.5918055 0.6474820    0
10 0.4417722 0.4725903 0.5471631 0.5510684 0.6313285 0.6800000    0
11 0.4181360 0.5040234 0.5583671 0.5521731 0.5918055 0.6591760    0
12 0.4417722 0.5138337 0.5330832 0.5456990 0.5853127 0.6800000    0
13 0.4578707 0.4790076 0.5468076 0.5435401 0.5858432 0.6591760    0
14 0.4638243 0.4923274 0.5601550 0.5581549 0.6169907 0.6800000    0
15 0.4638243 0.5470799 0.5689730 0.5701457 0.6072453 0.7083333    0

The last value of maxnode has the highest accuracy. You can try with higher values to see if you can get a higher score.

store_maxnode <- list()
tuneGrid <- expand.grid(.mtry = best_mtry)
for (maxnodes in c(20: 30)) {
    set.seed(1234)
    rf_maxnode <- train(survived~.,
        data = data_train,
        method = "rf",
        metric = "Accuracy",
        tuneGrid = tuneGrid,
        trControl = trControl,
        importance = TRUE,
        nodesize = 14,
        maxnodes = maxnodes,
        ntree = 300)
    key <- toString(maxnodes)
    store_maxnode[[key]] <- rf_maxnode
}
results_node <- resamples(store_maxnode)
summary(results_node)

Call:
summary.resamples(object = results_node)

Models: 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 
Number of resamples: 10 

Accuracy 
        Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
20 0.7590361 0.7738095 0.7976190 0.7918531 0.8048264 0.8313253    0
21 0.7590361 0.7747418 0.8024240 0.7966150 0.8168388 0.8333333    0
22 0.7590361 0.7678571 0.7916667 0.7977912 0.8162651 0.8690476    0
23 0.7590361 0.7747418 0.7976190 0.7954389 0.8089501 0.8333333    0
24 0.7590361 0.7648810 0.7857143 0.7906483 0.8089501 0.8452381    0
25 0.7590361 0.7740964 0.7916667 0.7977912 0.8184524 0.8571429    0
26 0.7590361 0.7648810 0.8024240 0.7966150 0.8214286 0.8333333    0
27 0.7590361 0.7648810 0.8024240 0.7954102 0.8168388 0.8452381    0
28 0.7469880 0.7767857 0.7857143 0.7906483 0.8089501 0.8313253    0
29 0.7380952 0.7657057 0.7916667 0.7918531 0.8178787 0.8333333    0
30 0.7469880 0.7619048 0.7857143 0.7870625 0.8089501 0.8333333    0

Kappa 
        Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
20 0.4578707 0.4935681 0.5584709 0.5431912 0.5830589 0.6246770    0
21 0.4638243 0.5010434 0.5791086 0.5551867 0.5940465 0.6508314    0
22 0.4638243 0.4832188 0.5510870 0.5574394 0.5929211 0.7179487    0
23 0.4578707 0.5010434 0.5583671 0.5498851 0.5968977 0.6308851    0
24 0.4638243 0.4754773 0.5349377 0.5416141 0.5929211 0.6666667    0
25 0.4638243 0.5051482 0.5471290 0.5567316 0.6160751 0.6800000    0
26 0.4638243 0.4827514 0.5664209 0.5549536 0.6233991 0.6350093    0
27 0.4638243 0.4742366 0.5744215 0.5510311 0.5940465 0.6591760    0
28 0.4400899 0.4983097 0.5371585 0.5417438 0.5901742 0.6246770    0
29 0.4181360 0.4832188 0.5536855 0.5459419 0.6142639 0.6429872    0
30 0.4400899 0.4782746 0.5311582 0.5353611 0.5929211 0.6350093    0

The highest accuracy score is obtained with a value of maxnode equals to 22.

Step 4) Search the best ntrees

Now that you have the best value of mtry and maxnode, you can tune the number of trees. The method is exactly the same as maxnode.

store_maxtrees <- list()
for (ntree in c(250, 300, 350, 400, 450, 500, 550, 600, 800, 1000, 2000)) {
    set.seed(5678)
    rf_maxtrees <- train(survived~.,
        data = data_train,
        method = "rf",
        metric = "Accuracy",
        tuneGrid = tuneGrid,
        trControl = trControl,
        importance = TRUE,
        nodesize = 14,
        maxnodes = 24,
        ntree = ntree)
    key <- toString(ntree)
    store_maxtrees[[key]] <- rf_maxtrees
}
results_tree <- resamples(store_maxtrees)
summary(results_tree)

Call:
summary.resamples(object = results_tree)

Models: 250, 300, 350, 400, 450, 500, 550, 600, 800, 1000, 2000 
Number of resamples: 10 

Accuracy 
          Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
250  0.7500000 0.7747418 0.7904475 0.7967011 0.8268072 0.8554217    0
300  0.7500000 0.7761403 0.7904475 0.7955106 0.8178787 0.8452381    0
350  0.7500000 0.7648810 0.7891566 0.7943201 0.8178787 0.8452381    0
400  0.7500000 0.7648810 0.7891566 0.7931297 0.8178787 0.8433735    0
450  0.7380952 0.7648810 0.7891566 0.7931297 0.8268072 0.8433735    0
500  0.7380952 0.7738095 0.7891566 0.7955106 0.8268072 0.8433735    0
550  0.7380952 0.7738095 0.7844951 0.7943058 0.8268072 0.8433735    0
600  0.7380952 0.7738095 0.7844951 0.7954963 0.8268072 0.8452381    0
800  0.7380952 0.7717656 0.7844951 0.7954963 0.8343373 0.8452381    0
1000 0.7380952 0.7717656 0.7844951 0.7943058 0.8268072 0.8452381    0
2000 0.7500000 0.7717656 0.7844951 0.7966867 0.8343373 0.8452381    0

Kappa 
          Min.   1st Qu.    Median      Mean   3rd Qu.      Max. NA's
250  0.4302326 0.5126573 0.5517463 0.5553591 0.6178311 0.6885553    0
300  0.4302326 0.5179021 0.5517463 0.5524474 0.5999075 0.6643857    0
350  0.4473684 0.4972937 0.5444663 0.5503549 0.5999075 0.6643857    0
400  0.4473684 0.4972937 0.5444663 0.5479382 0.5999075 0.6643857    0
450  0.4181360 0.4972937 0.5444663 0.5474048 0.6178311 0.6643857    0
500  0.4181360 0.5145985 0.5444663 0.5526736 0.6178311 0.6643857    0
550  0.4181360 0.5068326 0.5378591 0.5503167 0.6178311 0.6643857    0
600  0.4181360 0.5068326 0.5378591 0.5527334 0.6178311 0.6643857    0
800  0.4181360 0.5068326 0.5378591 0.5525672 0.6359562 0.6643857    0
1000 0.4181360 0.5056400 0.5378591 0.5492948 0.6178311 0.6643857    0
2000 0.4417722 0.5068326 0.5378591 0.5549308 0.6359562 0.6643857    0

You have your final model. You can train the random forest with the following parameters:

  • ntree =800: 800 trees will be trained
  • mtry = 4: 4 features is chosen for each iteration
  • maxnodes = 24: Maximum 24 nodes in the terminal nodes (leaves)
fit_rf <- train(survived~.,
    data_train,
    method = "rf",
    metric = "Accuracy",
    tuneGrid = tuneGrid,
    trControl = trControl,
    importance = TRUE,
    nodesize = 14,
    ntree = 800,
    maxnodes = 24)

Step 5) Evaluate the model

The library caret has a function to make prediction. predict(model, newdata= df) argument - model: Define the model evaluated before. - newdata: Define the dataset to make prediction

prediction <-predict(fit_rf, data_test)

confusionMatrix(prediction, data_test$survived)
Confusion Matrix and Statistics

          Reference
Prediction  No Yes
       No  109  32
       Yes   7  61
                                          
               Accuracy : 0.8134          
                 95% CI : (0.7539, 0.8638)
    No Information Rate : 0.555           
    P-Value [Acc > NIR] : 3.115e-15       
                                          
                  Kappa : 0.6119          
                                          
 Mcnemar's Test P-Value : 0.0001215       
                                          
            Sensitivity : 0.9397          
            Specificity : 0.6559          
         Pos Pred Value : 0.7730          
         Neg Pred Value : 0.8971          
             Prevalence : 0.5550          
         Detection Rate : 0.5215          
   Detection Prevalence : 0.6746          
      Balanced Accuracy : 0.7978          
                                          
       'Positive' Class : No              
                                          

You have an accuracy of 0.8134 percent, which is higher than the default value

Step 6) Visualize Result

Lastly, you can look at the feature importance with the function varImp(). It seems that the most important features are the sex and age. That is not surprising because the important features are likely to appear closer to the root of the tree, while less important features will often appear closed to the leaves.

varImp(fit_rf)
rf variable importance
LS0tDQp0aXRsZTogIk1hY2hpbmUgTGVhcm5pbmcgTm90ZXMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIERlY2lzaW9uIFRyZWUgaW4gUiB8IENsYXNzaWZpY2F0aW9uIFRyZWUgJiBDb2RlIGluIFINCkRlY2lzaW9uIFRyZWVzIGFyZSB2ZXJzYXRpbGUgTWFjaGluZSBMZWFybmluZyBhbGdvcml0aG0gdGhhdCBjYW4gcGVyZm9ybSBib3RoIGNsYXNzaWZpY2F0aW9uIGFuZCByZWdyZXNzaW9uIHRhc2tzLiBUaGV5IGFyZSB2ZXJ5IHBvd2VyZnVsIGFsZ29yaXRobXMsIGNhcGFibGUgb2YgZml0dGluZyBjb21wbGV4IGRhdGFzZXRzLiBCZXNpZGVzLCBkZWNpc2lvbiB0cmVlcyBhcmUgZnVuZGFtZW50YWwgY29tcG9uZW50cyBvZiByYW5kb20gZm9yZXN0cywgd2hpY2ggYXJlIGFtb25nIHRoZSBtb3N0IHBvdGVudCBNYWNoaW5lIExlYXJuaW5nIGFsZ29yaXRobXMgYXZhaWxhYmxlIHRvZGF5Lg0KDQojIyBTdGVwIDEpIEltcG9ydCB0aGUgZGF0YQ0KYGBge3J9DQpzZXQuc2VlZCg2NzgpDQpwYXRoIDwtICdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZ3VydTk5LWVkdS9SLVByb2dyYW1taW5nL21hc3Rlci90aXRhbmljX2RhdGEuY3N2Jw0KdGl0YW5pYyA8LXJlYWQuY3N2KHBhdGgpDQpoZWFkKHRpdGFuaWMpDQp0YWlsKHRpdGFuaWMpDQpgYGANCg0KDQpGcm9tIHRoZSBoZWFkIGFuZCB0YWlsIG91dHB1dCwgeW91IGNhbiBub3RpY2UgdGhlIGRhdGEgaXMgbm90IHNodWZmbGVkLiBUaGlzIGlzIGEgYmlnIGlzc3VlISBXaGVuIHlvdSB3aWxsIHNwbGl0IHlvdXIgZGF0YSBiZXR3ZWVuIGEgdHJhaW4gc2V0IGFuZCB0ZXN0IHNldCwgeW91IHdpbGwgc2VsZWN0IG9ubHkgdGhlIHBhc3NlbmdlciBmcm9tIGNsYXNzIDEgYW5kIDIgKE5vIHBhc3NlbmdlciBmcm9tIGNsYXNzIDMgYXJlIGluIHRoZSB0b3AgODAgcGVyY2VudCBvZiB0aGUgb2JzZXJ2YXRpb25zKSwgd2hpY2ggbWVhbnMgdGhlIGFsZ29yaXRobSB3aWxsIG5ldmVyIHNlZSB0aGUgZmVhdHVyZXMgb2YgcGFzc2VuZ2VyIG9mIGNsYXNzIDMuIFRoaXMgbWlzdGFrZSB3aWxsIGxlYWQgdG8gcG9vciBwcmVkaWN0aW9uLg0KDQpgYGB7cn0NCnNodWZmbGVfaW5kZXggPC0gc2FtcGxlKDE6bnJvdyh0aXRhbmljKSkNCmhlYWQoc2h1ZmZsZV9pbmRleCkNCnRpdGFuaWMgPC0gdGl0YW5pY1tzaHVmZmxlX2luZGV4LCBdDQpoZWFkKHRpdGFuaWMpDQpWaWV3KHRpdGFuaWMpDQpgYGANCg0KDQoNCiMjIFN0ZXAgMikgQ2xlYW4gdGhlIGRhdGFzZXQNClRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgc2hvd3Mgc29tZSB2YXJpYWJsZXMgaGF2ZSBOQeKAmXMuIERhdGEgY2xlYW4gdXAgdG8gYmUgZG9uZSBhcyBmb2xsb3dzDQoNCiAgLSBEcm9wIHZhcmlhYmxlcyBob21lLmRlc3QsY2FiaW4sIG5hbWUsIFggYW5kIHRpY2tldA0KICAtIENyZWF0ZSBmYWN0b3IgdmFyaWFibGVzIGZvciBwY2xhc3MgYW5kIHN1cnZpdmVkDQogIC0gRHJvcCB0aGUgTkENCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCiMgRHJvcCB2YXJpYWJsZXMNCmNsZWFuX3RpdGFuaWMgPC0gdGl0YW5pYyAlPiUNCnNlbGVjdCgtYyhob21lLmRlc3QsIGNhYmluLCBuYW1lLCB4LCB0aWNrZXQpKSAlPiUgDQojQ29udmVydCB0byBmYWN0b3IgbGV2ZWwNCgltdXRhdGUoYWdlID0gYXMubnVtZXJpYyhhZ2UpLCBmYXJlPSBhcy5udW1lcmljKGZhcmUpLCBwY2xhc3MgPSBmYWN0b3IocGNsYXNzLCBsZXZlbHMgPSBjKDEsIDIsIDMpLCBsYWJlbHMgPSBjKCdVcHBlcicsICdNaWRkbGUnLCAnTG93ZXInKSksDQoJc3Vydml2ZWQgPSBmYWN0b3Ioc3Vydml2ZWQsIGxldmVscyA9IGMoMCwgMSksIGxhYmVscyA9IGMoJ05vJywgJ1llcycpKSkgJT4lIG5hLm9taXQoKQ0KDQpnbGltcHNlKGNsZWFuX3RpdGFuaWMpDQoNCg0KYGBgDQoNCg0KIyMgU3RlcCAzKSBDcmVhdGUgdHJhaW4vdGVzdCBzZXQNCkJlZm9yZSB5b3UgdHJhaW4geW91ciBtb2RlbCwgeW91IG5lZWQgdG8gcGVyZm9ybSB0d28gc3RlcHM6DQoNCiAgQ3JlYXRlIGEgdHJhaW4gYW5kIHRlc3Qgc2V0OiBZb3UgdHJhaW4gdGhlIG1vZGVsIG9uIHRoZSB0cmFpbiBzZXQgYW5kIHRlc3QgdGhlIHByZWRpY3Rpb24gb24gdGhlIHRlc3Qgc2V0IChpLmUuIHVuc2VlbiBkYXRhKQ0KICBJbnN0YWxsIHJwYXJ0LnBsb3QgZnJvbSB0aGUgY29uc29sZQ0KDQpUaGUgY29tbW9uIHByYWN0aWNlIGlzIHRvIHNwbGl0IHRoZSBkYXRhIDgwLzIwLCA4MCBwZXJjZW50IG9mIHRoZSBkYXRhIHNlcnZlcyB0byB0cmFpbiB0aGUgbW9kZWwsIGFuZCAyMCBwZXJjZW50IHRvIG1ha2UgcHJlZGljdGlvbnMuIFlvdSBuZWVkIHRvIGNyZWF0ZSB0d28gc2VwYXJhdGUgZGF0YSBmcmFtZXMuIFlvdSBkb27igJl0IHdhbnQgdG8gdG91Y2ggdGhlIHRlc3Qgc2V0IHVudGlsIHlvdSBmaW5pc2ggYnVpbGRpbmcgeW91ciBtb2RlbC4gWW91IGNhbiBjcmVhdGUgYSBmdW5jdGlvbiBuYW1lIGNyZWF0ZV90cmFpbl90ZXN0KCkgdGhhdCB0YWtlcyB0aHJlZSBhcmd1bWVudHMuDQoNCmBgYHtyfQ0KY3JlYXRlX3RyYWluX3Rlc3QgPC0gZnVuY3Rpb24oZGF0YSwgc2l6ZSA9IDAuOCwgdHJhaW4gPSBUUlVFKSB7DQogICAgbl9yb3cgPSBucm93KGRhdGEpDQogICAgdG90YWxfcm93ID0gc2l6ZSAqIG5fcm93DQogICAgdHJhaW5fc2FtcGxlID0gYygxOiB0b3RhbF9yb3cpDQogICAgaWYgKHRyYWluID09IFRSVUUpIHsNCiAgICAgICAgcmV0dXJuIChkYXRhW3RyYWluX3NhbXBsZSwgXSkNCiAgICB9IGVsc2Ugew0KICAgICAgICByZXR1cm4gKGRhdGFbLXRyYWluX3NhbXBsZSwgXSkNCiAgICB9DQp9DQpgYGANCg0KDQpgYGB7cn0NCmRhdGFfdHJhaW4gPC0gY3JlYXRlX3RyYWluX3Rlc3QoY2xlYW5fdGl0YW5pYywgMC44LCB0cmFpbiA9IFRSVUUpDQpkYXRhX3Rlc3QgPC0gY3JlYXRlX3RyYWluX3Rlc3QoY2xlYW5fdGl0YW5pYywgMC44LCB0cmFpbiA9IEZBTFNFKQ0KDQpkaW0oZGF0YV90cmFpbikNCg0KZGltKGRhdGFfdGVzdCkNCmBgYA0KDQpgYGB7cn0NCnN0cihkYXRhX3RyYWluKQ0KYGBgDQoNCg0KDQpUaGUgdHJhaW4gZGF0YXNldCBoYXMgMTA0NiByb3dzIHdoaWxlIHRoZSB0ZXN0IGRhdGFzZXQgaGFzIDI2MiByb3dzLg0KDQpZb3UgdXNlIHRoZSBmdW5jdGlvbiBwcm9wLnRhYmxlKCkgY29tYmluZWQgd2l0aCB0YWJsZSgpIHRvIHZlcmlmeSBpZiB0aGUgcmFuZG9taXphdGlvbiBwcm9jZXNzIGlzIGNvcnJlY3QuDQoNCmBgYHtyfQ0KcHJvcC50YWJsZSh0YWJsZShkYXRhX3RyYWluJHN1cnZpdmVkKSkNCg0KcHJvcC50YWJsZSh0YWJsZShkYXRhX3Rlc3Qkc3Vydml2ZWQpKQ0KYGBgDQoNCg0KIyMgU3RlcCA0KSBCdWlsZCB0aGUgbW9kZWwNCllvdSBhcmUgcmVhZHkgdG8gYnVpbGQgdGhlIG1vZGVsLiBUaGUgc3ludGF4IGZvciBScGFydCBkZWNpc2lvbiB0cmVlIGZ1bmN0aW9uIGlzOg0KDQpycGFydChmb3JtdWxhLCBkYXRhPSwgbWV0aG9kPScnKQ0KYXJndW1lbnRzOgkJCQ0KLSBmb3JtdWxhOiBUaGUgZnVuY3Rpb24gdG8gcHJlZGljdA0KLSBkYXRhOiBTcGVjaWZpZXMgdGhlIGRhdGEgZnJhbWUtIG1ldGhvZDogCQkJDQotICJjbGFzcyIgZm9yIGEgY2xhc3NpZmljYXRpb24gdHJlZSAJCQkNCi0gImFub3ZhIiBmb3IgYSByZWdyZXNzaW9uIHRyZWUJDQoNCmBgYHtyfQ0KbGlicmFyeShycGFydCkNCmxpYnJhcnkocnBhcnQucGxvdCkNCmZpdCA8LSBycGFydChzdXJ2aXZlZH4uLCBkYXRhID0gZGF0YV90cmFpbiwgbWV0aG9kID0gJ2NsYXNzJykNCnJwYXJ0LnBsb3QoZml0LCBleHRyYSA9IDEwNikNCmBgYA0KDQoNCiMjIFN0ZXAgNSkgTWFrZSBhIHByZWRpY3Rpb24NCllvdSBjYW4gcHJlZGljdCB5b3VyIHRlc3QgZGF0YXNldC4gVG8gbWFrZSBhIHByZWRpY3Rpb24sIHlvdSBjYW4gdXNlIHRoZSBwcmVkaWN0KCkgZnVuY3Rpb24uIFRoZSBiYXNpYyBzeW50YXggb2YgcHJlZGljdCBmb3IgUiBkZWNpc2lvbiB0cmVlIGlzOg0KDQpgYGB7cn0NCnByZWRpY3RfdW5zZWVuIDwtcHJlZGljdChmaXQsIGRhdGFfdGVzdCwgdHlwZSA9ICdjbGFzcycpDQoNCnRhYmxlX21hdCA8LSB0YWJsZShkYXRhX3Rlc3Qkc3Vydml2ZWQsIHByZWRpY3RfdW5zZWVuKQ0KdGFibGVfbWF0DQpgYGANCg0KDQoNCiMjIFN0ZXAgNikgTWVhc3VyZSBwZXJmb3JtYW5jZQ0KWW91IGNhbiBjb21wdXRlIGFuIGFjY3VyYWN5IG1lYXN1cmUgZm9yIGNsYXNzaWZpY2F0aW9uIHRhc2sgd2l0aCB0aGUgY29uZnVzaW9uIG1hdHJpeDoNCg0KVGhlIGNvbmZ1c2lvbiBtYXRyaXggaXMgYSBiZXR0ZXIgY2hvaWNlIHRvIGV2YWx1YXRlIHRoZSBjbGFzc2lmaWNhdGlvbiBwZXJmb3JtYW5jZS4gVGhlIGdlbmVyYWwgaWRlYSBpcyB0byBjb3VudCB0aGUgbnVtYmVyIG9mIHRpbWVzIFRydWUgaW5zdGFuY2VzIGFyZSBjbGFzc2lmaWVkIGFyZSBGYWxzZS4NCg0KYGBge3J9DQphY2N1cmFjeV9UZXN0IDwtIHN1bShkaWFnKHRhYmxlX21hdCkpIC8gc3VtKHRhYmxlX21hdCkNCnByaW50KHBhc3RlKCdBY2N1cmFjeSBmb3IgdGVzdCcsIGFjY3VyYWN5X1Rlc3QpKQ0KYGBgDQoNCiMjIFN0ZXAgNykgVHVuZSB0aGUgaHlwZXItcGFyYW1ldGVycw0KRGVjaXNpb24gdHJlZSBpbiBSIGhhcyB2YXJpb3VzIHBhcmFtZXRlcnMgdGhhdCBjb250cm9sIGFzcGVjdHMgb2YgdGhlIGZpdC4gSW4gcnBhcnQgZGVjaXNpb24gdHJlZSBsaWJyYXJ5LCB5b3UgY2FuIGNvbnRyb2wgdGhlIHBhcmFtZXRlcnMgdXNpbmcgdGhlIHJwYXJ0LmNvbnRyb2woKSBmdW5jdGlvbi4gSW4gdGhlIGZvbGxvd2luZyBjb2RlLCB5b3UgaW50cm9kdWNlIHRoZSBwYXJhbWV0ZXJzIHlvdSB3aWxsIHR1bmUuIFlvdSBjYW4gcmVmZXIgdG8gdGhlIHZpZ25ldHRlIGZvciBvdGhlciBwYXJhbWV0ZXJzLg0KDQpycGFydC5jb250cm9sKG1pbnNwbGl0ID0gMjAsIG1pbmJ1Y2tldCA9IHJvdW5kKG1pbnNwbGl0LzMpLCBtYXhkZXB0aCA9IDMwKQ0KQXJndW1lbnRzOg0KICAtbWluc3BsaXQ6IFNldCB0aGUgbWluaW11bSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIHRoZSBub2RlIGJlZm9yZSB0aGUgYWxnb3JpdGhtIHBlcmZvcm0gYSBzcGxpdA0KICAtbWluYnVja2V0OiAgU2V0IHRoZSBtaW5pbXVtIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gdGhlIGZpbmFsIG5vdGUgaS5lLiB0aGUgbGVhZg0KICAtbWF4ZGVwdGg6IFNldCB0aGUgbWF4aW11bSBkZXB0aCBvZiBhbnkgbm9kZSBvZiB0aGUgZmluYWwgdHJlZS4gVGhlIHJvb3Qgbm9kZSBpcyB0cmVhdGVkIGEgZGVwdGggMA0KDQpgYGB7cn0NCmFjY3VyYWN5X3R1bmUgPC0gZnVuY3Rpb24oZml0KSB7DQogICAgcHJlZGljdF91bnNlZW4gPC0gcHJlZGljdChmaXQsIGRhdGFfdGVzdCwgdHlwZSA9ICdjbGFzcycpDQogICAgdGFibGVfbWF0IDwtIHRhYmxlKGRhdGFfdGVzdCRzdXJ2aXZlZCwgcHJlZGljdF91bnNlZW4pDQogICAgYWNjdXJhY3lfVGVzdCA8LSBzdW0oZGlhZyh0YWJsZV9tYXQpKSAvIHN1bSh0YWJsZV9tYXQpDQogICAgYWNjdXJhY3lfVGVzdA0KfQ0KYGBgDQoNCllvdSBjYW4gdHJ5IHRvIHR1bmUgdGhlIHBhcmFtZXRlcnMgYW5kIHNlZSBpZiB5b3UgY2FuIGltcHJvdmUgdGhlIG1vZGVsIG92ZXIgdGhlIGRlZmF1bHQgdmFsdWUuIEFzIGEgcmVtaW5kZXIsIHlvdSBuZWVkIHRvIGdldCBhbiBhY2N1cmFjeSBoaWdoZXIgdGhhbiAwLjc4DQoNCmBgYHtyfQ0KY29udHJvbCA8LSBycGFydC5jb250cm9sKG1pbnNwbGl0ID0gNCwNCiAgICBtaW5idWNrZXQgPSByb3VuZCg1IC8gMyksDQogICAgbWF4ZGVwdGggPSAzLA0KICAgIGNwID0gMCkNCnR1bmVfZml0IDwtIHJwYXJ0KHN1cnZpdmVkfi4sIGRhdGEgPSBkYXRhX3RyYWluLCBtZXRob2QgPSAnY2xhc3MnLCBjb250cm9sID0gY29udHJvbCkNCmFjY3VyYWN5X3R1bmUodHVuZV9maXQpDQpgYGANCllvdSBnZXQgYSBoaWdoZXIgcGVyZm9ybWFuY2UgdGhhbiB0aGUgcHJldmlvdXMgbW9kZWwuIENvbmdyYXR1bGF0aW9uIQ0KDQoNCg0KIyBSIFJhbmRvbSBGb3Jlc3QgVHV0b3JpYWwgd2l0aCBFeGFtcGxlDQpSYW5kb20gZm9yZXN0cyBhcmUgYmFzZWQgb24gYSBzaW1wbGUgaWRlYTog4oCYdGhlIHdpc2RvbSBvZiB0aGUgY3Jvd2TigJkuIEFnZ3JlZ2F0ZSBvZiB0aGUgcmVzdWx0cyBvZiBtdWx0aXBsZSBwcmVkaWN0b3JzIGdpdmVzIGEgYmV0dGVyIHByZWRpY3Rpb24gdGhhbiB0aGUgYmVzdCBpbmRpdmlkdWFsIHByZWRpY3Rvci4gQSBncm91cCBvZiBwcmVkaWN0b3JzIGlzIGNhbGxlZCBhbiBlbnNlbWJsZS4gVGh1cywgdGhpcyB0ZWNobmlxdWUgaXMgY2FsbGVkIEVuc2VtYmxlIExlYXJuaW5nLg0KDQpJbiBlYXJsaWVyIHR1dG9yaWFsLCB5b3UgbGVhcm5lZCBob3cgdG8gdXNlIERlY2lzaW9uIHRyZWVzIHRvIG1ha2UgYSBiaW5hcnkgcHJlZGljdGlvbi4gVG8gaW1wcm92ZSBvdXIgdGVjaG5pcXVlLCB3ZSBjYW4gdHJhaW4gYSBncm91cCBvZiBEZWNpc2lvbiBUcmVlIGNsYXNzaWZpZXJzLCBlYWNoIG9uIGEgZGlmZmVyZW50IHJhbmRvbSBzdWJzZXQgb2YgdGhlIHRyYWluIHNldC4gVG8gbWFrZSBhIHByZWRpY3Rpb24sIHdlIGp1c3Qgb2J0YWluIHRoZSBwcmVkaWN0aW9ucyBvZiBhbGwgaW5kaXZpZHVhbHMgdHJlZXMsIHRoZW4gcHJlZGljdCB0aGUgY2xhc3MgdGhhdCBnZXRzIHRoZSBtb3N0IHZvdGVzLiBUaGlzIHRlY2huaXF1ZSBpcyBjYWxsZWQgUmFuZG9tIEZvcmVzdC4NCg0KIyMgU3RlcCAxKSBJbXBvcnQgdGhlIGRhdGENClRvIG1ha2Ugc3VyZSB5b3UgaGF2ZSB0aGUgc2FtZSBkYXRhc2V0IGFzIGluIHRoZSB0dXRvcmlhbCBmb3IgZGVjaXNpb24gdHJlZXMsIHRoZSB0cmFpbiB0ZXN0IGFuZCB0ZXN0IHNldCBhcmUgc3RvcmVkIG9uIHRoZSBpbnRlcm5ldC4gWW91IGNhbiBpbXBvcnQgdGhlbSB3aXRob3V0IG1ha2UgYW55IGNoYW5nZS4NCg0KIyMgU3RlcCAyKSBUcmFpbiB0aGUgbW9kZWwNCk9uZSB3YXkgdG8gZXZhbHVhdGUgdGhlIHBlcmZvcm1hbmNlIG9mIGEgbW9kZWwgaXMgdG8gdHJhaW4gaXQgb24gYSBudW1iZXIgb2YgZGlmZmVyZW50IHNtYWxsZXIgZGF0YXNldHMgYW5kIGV2YWx1YXRlIHRoZW0gb3ZlciB0aGUgb3RoZXIgc21hbGxlciB0ZXN0aW5nIHNldC4gVGhpcyBpcyBjYWxsZWQgdGhlIEYtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGZlYXR1cmUuIFIgaGFzIGEgZnVuY3Rpb24gdG8gcmFuZG9tbHkgc3BsaXQgbnVtYmVyIG9mIGRhdGFzZXRzIG9mIGFsbW9zdCB0aGUgc2FtZSBzaXplLiBGb3IgZXhhbXBsZSwgaWYgaz05LCB0aGUgbW9kZWwgaXMgZXZhbHVhdGVkIG92ZXIgdGhlIG5pbmUgZm9sZGVyIGFuZCB0ZXN0ZWQgb24gdGhlIHJlbWFpbmluZyB0ZXN0IHNldC4gVGhpcyBwcm9jZXNzIGlzIHJlcGVhdGVkIHVudGlsIGFsbCB0aGUgc3Vic2V0cyBoYXZlIGJlZW4gZXZhbHVhdGVkLiBUaGlzIHRlY2huaXF1ZSBpcyB3aWRlbHkgdXNlZCBmb3IgbW9kZWwgc2VsZWN0aW9uLCBlc3BlY2lhbGx5IHdoZW4gdGhlIG1vZGVsIGhhcyBwYXJhbWV0ZXJzIHRvIHR1bmUuDQoNCk5vdyB0aGF0IHdlIGhhdmUgYSB3YXkgdG8gZXZhbHVhdGUgb3VyIG1vZGVsLCB3ZSBuZWVkIHRvIGZpZ3VyZSBvdXQgaG93IHRvIGNob29zZSB0aGUgcGFyYW1ldGVycyB0aGF0IGdlbmVyYWxpemVkIGJlc3QgdGhlIGRhdGEuDQoNClJhbmRvbSBmb3Jlc3QgY2hvb3NlcyBhIHJhbmRvbSBzdWJzZXQgb2YgZmVhdHVyZXMgYW5kIGJ1aWxkcyBtYW55IERlY2lzaW9uIFRyZWVzLiBUaGUgbW9kZWwgYXZlcmFnZXMgb3V0IGFsbCB0aGUgcHJlZGljdGlvbnMgb2YgdGhlIERlY2lzaW9ucyB0cmVlcy4NCg0KUmFuZG9tRm9yZXN0KGZvcm11bGEsIG50cmVlPW4sIG10cnk9RkFMU0UsIG1heG5vZGVzID0gTlVMTCkNCkFyZ3VtZW50czoNCiAgLSBGb3JtdWxhOiBGb3JtdWxhIG9mIHRoZSBmaXR0ZWQgbW9kZWwNCiAgLSBudHJlZTogbnVtYmVyIG9mIHRyZWVzIGluIHRoZSBmb3Jlc3QNCiAgLSBtdHJ5OiBOdW1iZXIgb2YgY2FuZGlkYXRlcyBkcmF3IHRvIGZlZWQgdGhlIGFsZ29yaXRobS4gQnkgZGVmYXVsdCwgaXQgaXMgdGhlIHNxdWFyZSBvZiB0aGUgbnVtYmVyIG9mIGNvbHVtbnMuDQogIC0gbWF4bm9kZXM6IFNldCB0aGUgbWF4aW11bSBhbW91bnQgb2YgdGVybWluYWwgbm9kZXMgaW4gdGhlIGZvcmVzdA0KICAtIGltcG9ydGFuY2U9VFJVRTogV2hldGhlciBpbmRlcGVuZGVudCB2YXJpYWJsZXMgaW1wb3J0YW5jZSBpbiB0aGUgcmFuZG9tIGZvcmVzdCBiZSBhc3Nlc3NlZA0KDQoNClR1bmluZyBhIG1vZGVsIGlzIHZlcnkgdGVkaW91cyB3b3JrLiBUaGVyZSBhcmUgbG90IG9mIGNvbWJpbmF0aW9uIHBvc3NpYmxlIGJldHdlZW4gdGhlIHBhcmFtZXRlcnMuIFlvdSBkb27igJl0IG5lY2Vzc2FyaWx5IGhhdmUgdGhlIHRpbWUgdG8gdHJ5IGFsbCBvZiB0aGVtLiBBIGdvb2QgYWx0ZXJuYXRpdmUgaXMgdG8gbGV0IHRoZSBtYWNoaW5lIGZpbmQgdGhlIGJlc3QgY29tYmluYXRpb24gZm9yIHlvdS4gVGhlcmUgYXJlIHR3byBtZXRob2RzIGF2YWlsYWJsZToNCg0KICAtIFJhbmRvbSBTZWFyY2gNCiAgLSBHcmlkIFNlYXJjaA0KV2Ugd2lsbCBkZWZpbmUgYm90aCBtZXRob2RzIGJ1dCBkdXJpbmcgdGhlIHR1dG9yaWFsLCB3ZSB3aWxsIHRyYWluIHRoZSBtb2RlbCB1c2luZyBncmlkIHNlYXJjaA0KDQojIyMgR3JpZCBTZWFyY2ggZGVmaW5pdGlvbg0KVGhlIGdyaWQgc2VhcmNoIG1ldGhvZCBpcyBzaW1wbGUsIHRoZSBtb2RlbCB3aWxsIGJlIGV2YWx1YXRlZCBvdmVyIGFsbCB0aGUgY29tYmluYXRpb24geW91IHBhc3MgaW4gdGhlIGZ1bmN0aW9uLCB1c2luZyBjcm9zcy12YWxpZGF0aW9uLg0KDQpGb3IgaW5zdGFuY2UsIHlvdSB3YW50IHRvIHRyeSB0aGUgbW9kZWwgd2l0aCAxMCwgMjAsIDMwIG51bWJlciBvZiB0cmVlcyBhbmQgZWFjaCB0cmVlIHdpbGwgYmUgdGVzdGVkIG92ZXIgYSBudW1iZXIgb2YgbXRyeSBlcXVhbHMgdG8gMSwgMiwgMywgNCwgNS4gVGhlbiB0aGUgbWFjaGluZSB3aWxsIHRlc3QgMTUgZGlmZmVyZW50IG1vZGVsczoNCg0KYGBge3J9DQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KGUxMDcxKQ0KYGBgDQoNCioqRGVmYXVsdCBzZXR0aW5nKioNCkstZm9sZCBjcm9zcyB2YWxpZGF0aW9uIGlzIGNvbnRyb2xsZWQgYnkgdGhlIHRyYWluQ29udHJvbCgpIGZ1bmN0aW9uDQoNCnRyYWluQ29udHJvbChtZXRob2QgPSAiY3YiLCBudW1iZXIgPSBuLCBzZWFyY2ggPSJncmlkIikNCmFyZ3VtZW50cw0KLSBtZXRob2QgPSAiY3YiOiBUaGUgbWV0aG9kIHVzZWQgdG8gcmVzYW1wbGUgdGhlIGRhdGFzZXQuIA0KLSBudW1iZXIgPSBuOiBOdW1iZXIgb2YgZm9sZGVycyB0byBjcmVhdGUNCi0gc2VhcmNoID0gImdyaWQiOiBVc2UgdGhlIHNlYXJjaCBncmlkIG1ldGhvZC4gRm9yIHJhbmRvbWl6ZWQgbWV0aG9kLCB1c2UgImdyaWQiDQoNCioqUmFuZG9tIFNlYXJjaCBkZWZpbml0aW9uKioNClRoZSBiaWcgZGlmZmVyZW5jZSBiZXR3ZWVuIHJhbmRvbSBzZWFyY2ggYW5kIGdyaWQgc2VhcmNoIGlzLCByYW5kb20gc2VhcmNoIHdpbGwgbm90IGV2YWx1YXRlIGFsbCB0aGUgY29tYmluYXRpb24gb2YgaHlwZXJwYXJhbWV0ZXIgaW4gdGhlIHNlYXJjaGluZyBzcGFjZS4gSW5zdGVhZCwgaXQgd2lsbCByYW5kb21seSBjaG9vc2UgY29tYmluYXRpb24gYXQgZXZlcnkgaXRlcmF0aW9uLiBUaGUgYWR2YW50YWdlIGlzIGl0IGxvd2VyIHRoZSBjb21wdXRhdGlvbmFsIGNvc3QuDQoNCioqU2V0IHRoZSBjb250cm9sIHBhcmFtZXRlcioqDQpZb3Ugd2lsbCBwcm9jZWVkIGFzIGZvbGxvdyB0byBjb25zdHJ1Y3QgYW5kIGV2YWx1YXRlIHRoZSBtb2RlbDoNCg0KICAtIEV2YWx1YXRlIHRoZSBtb2RlbCB3aXRoIHRoZSBkZWZhdWx0IHNldHRpbmcNCiAgLSBGaW5kIHRoZSBiZXN0IG51bWJlciBvZiBtdHJ5DQogIC0gRmluZCB0aGUgYmVzdCBudW1iZXIgb2YgbWF4bm9kZXMNCiAgLSBGaW5kIHRoZSBiZXN0IG51bWJlciBvZiBudHJlZXMNCiAgLSBFdmFsdWF0ZSB0aGUgbW9kZWwgb24gdGhlIHRlc3QgZGF0YXNldA0KDQpgYGB7cn0NCiMgRGVmaW5lIHRoZSBjb250cm9sDQp0ckNvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsDQogICAgbnVtYmVyID0gMTAsDQogICAgc2VhcmNoID0gImdyaWQiKQ0KYGBgDQoNCllvdSB3aWxsIHVzZSBjYXJldCBsaWJyYXJ5IHRvIGV2YWx1YXRlIHlvdXIgbW9kZWwuIFRoZSBsaWJyYXJ5IGhhcyBvbmUgZnVuY3Rpb24gY2FsbGVkIHRyYWluKCkgdG8gZXZhbHVhdGUgYWxtb3N0IGFsbCBtYWNoaW5lIGxlYXJuaW5nIGFsZ29yaXRobS4gU2F5IGRpZmZlcmVudGx5LCB5b3UgY2FuIHVzZSB0aGlzIGZ1bmN0aW9uIHRvIHRyYWluIG90aGVyIGFsZ29yaXRobXMuDQoNCiAgLSBUaGUgYmFzaWMgc3ludGF4IGlzOg0KdHJhaW4oZm9ybXVsYSwgZGYsIG1ldGhvZCA9ICJyZiIsIG1ldHJpYz0gIkFjY3VyYWN5IiwgdHJDb250cm9sID0gdHJhaW5Db250cm9sKCksIHR1bmVHcmlkID0gTlVMTCkNCmFyZ3VtZW50DQotIGBmb3JtdWxhYDogRGVmaW5lIHRoZSBmb3JtdWxhIG9mIHRoZSBhbGdvcml0aG0NCi0gYG1ldGhvZGA6IERlZmluZSB3aGljaCBtb2RlbCB0byB0cmFpbi4gDQotIGBtZXRyaWNgID0gIkFjY3VyYWN5IjogRGVmaW5lIGhvdyB0byBzZWxlY3QgdGhlIG9wdGltYWwgbW9kZWwNCi0gYHRyQ29udHJvbCA9IHRyYWluQ29udHJvbCgpYDogRGVmaW5lIHRoZSBjb250cm9sIHBhcmFtZXRlcnMNCi0gYHR1bmVHcmlkID0gTlVMTGA6IFJldHVybiBhIGRhdGEgZnJhbWUgd2l0aCBhbGwgdGhlIHBvc3NpYmxlIGNvbWJpbmF0aW9uDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTIzNCkNCiMgUnVuIHRoZSBtb2RlbA0KcmZfZGVmYXVsdCA8LSB0cmFpbihzdXJ2aXZlZCB+LiwNCiAgICBkYXRhID0gZGF0YV90cmFpbiwNCiAgICBtZXRob2QgPSAicmYiLA0KICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsDQogICAgdHJDb250cm9sID0gdHJDb250cm9sKQ0KIyBQcmludCB0aGUgcmVzdWx0cw0KcHJpbnQocmZfZGVmYXVsdCkNCmBgYA0KDQojIyBTdGVwIDIpIFNlYXJjaCBiZXN0IG10cnkNCg0KWW91IGNhbiB0ZXN0IHRoZSBtb2RlbCB3aXRoIHZhbHVlcyBvZiBtdHJ5IGZyb20gMSB0byAxMA0KDQpgYGB7cn0NCnNldC5zZWVkKDEyMzQpDQp0dW5lR3JpZCA8LSBleHBhbmQuZ3JpZCgubXRyeSA9IGMoMTogMTApKQ0KcmZfbXRyeSA8LSB0cmFpbihzdXJ2aXZlZH4uLA0KICAgIGRhdGEgPSBkYXRhX3RyYWluLA0KICAgIG1ldGhvZCA9ICJyZiIsDQogICAgbWV0cmljID0gIkFjY3VyYWN5IiwNCiAgICB0dW5lR3JpZCA9IHR1bmVHcmlkLA0KICAgIHRyQ29udHJvbCA9IHRyQ29udHJvbCwNCiAgICBpbXBvcnRhbmNlID0gVFJVRSwNCiAgICBub2Rlc2l6ZSA9IDE0LA0KICAgIG50cmVlID0gMzAwKQ0KcHJpbnQocmZfbXRyeSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmZfbXRyeSRiZXN0VHVuZSRtdHJ5DQoNCm1heChyZl9tdHJ5JHJlc3VsdHMkQWNjdXJhY3kpDQoNCmJlc3RfbXRyeSA8LSByZl9tdHJ5JGJlc3RUdW5lJG10cnkgDQpiZXN0X210cnkNCmBgYA0KDQoNCiMjIFN0ZXAgMykgU2VhcmNoIHRoZSBiZXN0IG1heG5vZGVzDQpZb3UgbmVlZCB0byBjcmVhdGUgYSBsb29wIHRvIGV2YWx1YXRlIHRoZSBkaWZmZXJlbnQgdmFsdWVzIG9mIG1heG5vZGVzLiBJbiB0aGUgZm9sbG93aW5nIGNvZGUsIHlvdSB3aWxsOg0KDQogICogQ3JlYXRlIGEgbGlzdA0KICAqIENyZWF0ZSBhIHZhcmlhYmxlIHdpdGggdGhlIGJlc3QgdmFsdWUgb2YgdGhlIHBhcmFtZXRlciBtdHJ5OyBDb21wdWxzb3J5DQogICogQ3JlYXRlIHRoZSBsb29wDQogICogU3RvcmUgdGhlIGN1cnJlbnQgdmFsdWUgb2YgbWF4bm9kZQ0KICAqIFN1bW1hcml6ZSB0aGUgcmVzdWx0cw0KDQpgYGB7cn0NCnN0b3JlX21heG5vZGUgPC0gbGlzdCgpDQp0dW5lR3JpZCA8LSBleHBhbmQuZ3JpZCgubXRyeSA9IGJlc3RfbXRyeSkNCmZvciAobWF4bm9kZXMgaW4gYyg1OiAxNSkpIHsNCiAgICBzZXQuc2VlZCgxMjM0KQ0KICAgIHJmX21heG5vZGUgPC0gdHJhaW4oc3Vydml2ZWR+LiwNCiAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sDQogICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsDQogICAgICAgIHR1bmVHcmlkID0gdHVuZUdyaWQsDQogICAgICAgIHRyQ29udHJvbCA9IHRyQ29udHJvbCwNCiAgICAgICAgaW1wb3J0YW5jZSA9IFRSVUUsDQogICAgICAgIG5vZGVzaXplID0gMTQsDQogICAgICAgIG1heG5vZGVzID0gbWF4bm9kZXMsDQogICAgICAgIG50cmVlID0gMzAwKQ0KICAgIGN1cnJlbnRfaXRlcmF0aW9uIDwtIHRvU3RyaW5nKG1heG5vZGVzKQ0KICAgIHN0b3JlX21heG5vZGVbW2N1cnJlbnRfaXRlcmF0aW9uXV0gPC0gcmZfbWF4bm9kZQ0KfQ0KcmVzdWx0c19tdHJ5IDwtIHJlc2FtcGxlcyhzdG9yZV9tYXhub2RlKQ0Kc3VtbWFyeShyZXN1bHRzX210cnkpDQpgYGANCg0KVGhlIGxhc3QgdmFsdWUgb2YgbWF4bm9kZSBoYXMgdGhlIGhpZ2hlc3QgYWNjdXJhY3kuIFlvdSBjYW4gdHJ5IHdpdGggaGlnaGVyIHZhbHVlcyB0byBzZWUgaWYgeW91IGNhbiBnZXQgYSBoaWdoZXIgc2NvcmUuDQoNCmBgYHtyfQ0Kc3RvcmVfbWF4bm9kZSA8LSBsaXN0KCkNCnR1bmVHcmlkIDwtIGV4cGFuZC5ncmlkKC5tdHJ5ID0gYmVzdF9tdHJ5KQ0KZm9yIChtYXhub2RlcyBpbiBjKDIwOiAzMCkpIHsNCiAgICBzZXQuc2VlZCgxMjM0KQ0KICAgIHJmX21heG5vZGUgPC0gdHJhaW4oc3Vydml2ZWR+LiwNCiAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4sDQogICAgICAgIG1ldGhvZCA9ICJyZiIsDQogICAgICAgIG1ldHJpYyA9ICJBY2N1cmFjeSIsDQogICAgICAgIHR1bmVHcmlkID0gdHVuZUdyaWQsDQogICAgICAgIHRyQ29udHJvbCA9IHRyQ29udHJvbCwNCiAgICAgICAgaW1wb3J0YW5jZSA9IFRSVUUsDQogICAgICAgIG5vZGVzaXplID0gMTQsDQogICAgICAgIG1heG5vZGVzID0gbWF4bm9kZXMsDQogICAgICAgIG50cmVlID0gMzAwKQ0KICAgIGtleSA8LSB0b1N0cmluZyhtYXhub2RlcykNCiAgICBzdG9yZV9tYXhub2RlW1trZXldXSA8LSByZl9tYXhub2RlDQp9DQpyZXN1bHRzX25vZGUgPC0gcmVzYW1wbGVzKHN0b3JlX21heG5vZGUpDQpzdW1tYXJ5KHJlc3VsdHNfbm9kZSkNCmBgYA0KDQpUaGUgaGlnaGVzdCBhY2N1cmFjeSBzY29yZSBpcyBvYnRhaW5lZCB3aXRoIGEgdmFsdWUgb2YgbWF4bm9kZSBlcXVhbHMgdG8gMjIuDQoNCg0KIyMgU3RlcCA0KSBTZWFyY2ggdGhlIGJlc3QgbnRyZWVzDQpOb3cgdGhhdCB5b3UgaGF2ZSB0aGUgYmVzdCB2YWx1ZSBvZiBtdHJ5IGFuZCBtYXhub2RlLCB5b3UgY2FuIHR1bmUgdGhlIG51bWJlciBvZiB0cmVlcy4gVGhlIG1ldGhvZCBpcyBleGFjdGx5IHRoZSBzYW1lIGFzIG1heG5vZGUuDQoNCmBgYHtyfQ0Kc3RvcmVfbWF4dHJlZXMgPC0gbGlzdCgpDQpmb3IgKG50cmVlIGluIGMoMjUwLCAzMDAsIDM1MCwgNDAwLCA0NTAsIDUwMCwgNTUwLCA2MDAsIDgwMCwgMTAwMCwgMjAwMCkpIHsNCiAgICBzZXQuc2VlZCg1Njc4KQ0KICAgIHJmX21heHRyZWVzIDwtIHRyYWluKHN1cnZpdmVkfi4sDQogICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLA0KICAgICAgICBtZXRob2QgPSAicmYiLA0KICAgICAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLA0KICAgICAgICB0dW5lR3JpZCA9IHR1bmVHcmlkLA0KICAgICAgICB0ckNvbnRyb2wgPSB0ckNvbnRyb2wsDQogICAgICAgIGltcG9ydGFuY2UgPSBUUlVFLA0KICAgICAgICBub2Rlc2l6ZSA9IDE0LA0KICAgICAgICBtYXhub2RlcyA9IDI0LA0KICAgICAgICBudHJlZSA9IG50cmVlKQ0KICAgIGtleSA8LSB0b1N0cmluZyhudHJlZSkNCiAgICBzdG9yZV9tYXh0cmVlc1tba2V5XV0gPC0gcmZfbWF4dHJlZXMNCn0NCnJlc3VsdHNfdHJlZSA8LSByZXNhbXBsZXMoc3RvcmVfbWF4dHJlZXMpDQpzdW1tYXJ5KHJlc3VsdHNfdHJlZSkNCmBgYA0KDQpZb3UgaGF2ZSB5b3VyIGZpbmFsIG1vZGVsLiBZb3UgY2FuIHRyYWluIHRoZSByYW5kb20gZm9yZXN0IHdpdGggdGhlIGZvbGxvd2luZyBwYXJhbWV0ZXJzOg0KDQogICogbnRyZWUgPTgwMDogODAwIHRyZWVzIHdpbGwgYmUgdHJhaW5lZA0KICAqIG10cnkgPSA0OiA0IGZlYXR1cmVzIGlzIGNob3NlbiBmb3IgZWFjaCBpdGVyYXRpb24NCiAgKiBtYXhub2RlcyA9IDI0OiBNYXhpbXVtIDI0IG5vZGVzIGluIHRoZSB0ZXJtaW5hbCBub2RlcyAobGVhdmVzKQ0KDQpgYGB7cn0NCmZpdF9yZiA8LSB0cmFpbihzdXJ2aXZlZH4uLA0KICAgIGRhdGFfdHJhaW4sDQogICAgbWV0aG9kID0gInJmIiwNCiAgICBtZXRyaWMgPSAiQWNjdXJhY3kiLA0KICAgIHR1bmVHcmlkID0gdHVuZUdyaWQsDQogICAgdHJDb250cm9sID0gdHJDb250cm9sLA0KICAgIGltcG9ydGFuY2UgPSBUUlVFLA0KICAgIG5vZGVzaXplID0gMTQsDQogICAgbnRyZWUgPSA4MDAsDQogICAgbWF4bm9kZXMgPSAyNCkNCmBgYA0KDQoNCiMjIFN0ZXAgNSkgRXZhbHVhdGUgdGhlIG1vZGVsDQpUaGUgbGlicmFyeSBjYXJldCBoYXMgYSBmdW5jdGlvbiB0byBtYWtlIHByZWRpY3Rpb24uDQpwcmVkaWN0KG1vZGVsLCBuZXdkYXRhPSBkZikNCmFyZ3VtZW50DQogIC0gYG1vZGVsYDogRGVmaW5lIHRoZSBtb2RlbCBldmFsdWF0ZWQgYmVmb3JlLiANCiAgLSBgbmV3ZGF0YWA6IERlZmluZSB0aGUgZGF0YXNldCB0byBtYWtlIHByZWRpY3Rpb24NCg0KDQoNCmBgYHtyfQ0KcHJlZGljdGlvbiA8LXByZWRpY3QoZml0X3JmLCBkYXRhX3Rlc3QpDQoNCmNvbmZ1c2lvbk1hdHJpeChwcmVkaWN0aW9uLCBkYXRhX3Rlc3Qkc3Vydml2ZWQpDQpgYGANCllvdSBoYXZlIGFuIGFjY3VyYWN5IG9mIDAuODEzNCAgcGVyY2VudCwgd2hpY2ggaXMgaGlnaGVyIHRoYW4gdGhlIGRlZmF1bHQgdmFsdWUNCg0KDQojIyBTdGVwIDYpIFZpc3VhbGl6ZSBSZXN1bHQNCkxhc3RseSwgeW91IGNhbiBsb29rIGF0IHRoZSBmZWF0dXJlIGltcG9ydGFuY2Ugd2l0aCB0aGUgZnVuY3Rpb24gdmFySW1wKCkuIEl0IHNlZW1zIHRoYXQgdGhlIG1vc3QgaW1wb3J0YW50IGZlYXR1cmVzIGFyZSB0aGUgc2V4IGFuZCBhZ2UuIFRoYXQgaXMgbm90IHN1cnByaXNpbmcgYmVjYXVzZSB0aGUgaW1wb3J0YW50IGZlYXR1cmVzIGFyZSBsaWtlbHkgdG8gYXBwZWFyIGNsb3NlciB0byB0aGUgcm9vdCBvZiB0aGUgdHJlZSwgd2hpbGUgbGVzcyBpbXBvcnRhbnQgZmVhdHVyZXMgd2lsbCBvZnRlbiBhcHBlYXIgY2xvc2VkIHRvIHRoZSBsZWF2ZXMuDQoNCmBgYHtyfQ0KdmFySW1wKGZpdF9yZikNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=