rm(list = ls())
library(rpart)
set.seed(161)
n <- 1272

Generate two random normal variables X1 and X2. When X1 is less than 2, then Y should be 1, when X1 is greater than or equal to 2 and X2 is less than -.5, then Y should be 1. Otherwise, Y should be 0.

x1 <- rnorm(n = n, mean = 3.5) 
x2 <- rnorm(n = n, mean = 1.3)
x1.b <- ifelse(x1 < 2, 1, 0)
x2.b <- ifelse(x1 >= 2 & x2 < -.5, 1, 0)

But let’s add a little random variability to Y and then add some unrelated variables (X3, X4, X5).

y <- ifelse(x1.b == 1 | x2.b == 1, .9, .1) # the .9 and .1 are the probs of getting a 1
y <- rbinom(n = n, size = 1, prob = y)
x3 <- rnorm(n = n, mean = x1) # correlated with x1
x4 <- rnorm(n = n, mean = 2)
x5 <- rnorm(n = n, mean = .4)

Run the tree. Ugh. That is one ugly, overfit, and unuseful tree.

mod <- rpart(y ~ x1 + x2 + x3 + x4 + x5, method = "class", control = rpart.control(xval = 10, cp = 0, minsplit = 20))
plot(mod, margin =.1)
text(mod, cex = .8)

Can we prune our tree with cross-validation and get a slightly less complex tree?

printcp(mod)

Classification tree:
rpart(formula = y ~ x1 + x2 + x3 + x4 + x5, method = "class", 
    control = rpart.control(xval = 10, cp = 0, minsplit = 20))

Variables actually used in tree construction:
[1] x1 x2 x3 x4 x5

Root node error: 203/1272 = 0.15959

n= 1272 

          CP nsplit rel error  xerror     xstd
1 0.31527094      0   1.00000 1.00000 0.064342
2 0.15270936      1   0.68473 0.69458 0.055157
3 0.00394089      2   0.53202 0.56158 0.050184
4 0.00164204      7   0.51232 0.60099 0.051736
5 0.00089566     10   0.50739 0.63547 0.053037
6 0.00000000     21   0.49754 0.64039 0.053219
plotcp(mod, upper = "split")

Hmm, cross-validation says we should have just two splits. What does it look like?

fit2 <- prune(mod, cp = .021)
plot(fit2, margin =.5)
text(fit2)

Great. We recovered the truth (with cross-validation).

LS0tCnRpdGxlOiAiQ0FSVCBvdmVyZml0dGluZyBleGFtcGxlIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCmxpYnJhcnkocnBhcnQpCnNldC5zZWVkKDE2MSkKbiA8LSAxMjcyCmBgYAoKR2VuZXJhdGUgdHdvIHJhbmRvbSBub3JtYWwgdmFyaWFibGVzIFgxIGFuZCBYMi4gV2hlbiBYMSBpcyBsZXNzIHRoYW4gMiwgdGhlbiBZIHNob3VsZCBiZSAxLCB3aGVuIFgxIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byAyIGFuZCBYMiBpcyBsZXNzIHRoYW4gLS41LCB0aGVuIFkgc2hvdWxkIGJlIDEuIE90aGVyd2lzZSwgWSBzaG91bGQgYmUgMC4KYGBge3J9CngxIDwtIHJub3JtKG4gPSBuLCBtZWFuID0gMy41KSAKeDIgPC0gcm5vcm0obiA9IG4sIG1lYW4gPSAxLjMpCngxLmIgPC0gaWZlbHNlKHgxIDwgMiwgMSwgMCkKeDIuYiA8LSBpZmVsc2UoeDEgPj0gMiAmIHgyIDwgLS41LCAxLCAwKQpgYGAKCkJ1dCBsZXQncyBhZGQgYSBsaXR0bGUgcmFuZG9tIHZhcmlhYmlsaXR5IHRvIFkgYW5kIHRoZW4gYWRkIHNvbWUgdW5yZWxhdGVkIHZhcmlhYmxlcyAoWDMsIFg0LCBYNSkuCmBgYHtyfQp5IDwtIGlmZWxzZSh4MS5iID09IDEgfCB4Mi5iID09IDEsIC45LCAuMSkgIyB0aGUgLjkgYW5kIC4xIGFyZSB0aGUgcHJvYnMgb2YgZ2V0dGluZyBhIDEKeSA8LSByYmlub20obiA9IG4sIHNpemUgPSAxLCBwcm9iID0geSkKCngzIDwtIHJub3JtKG4gPSBuLCBtZWFuID0geDEpICMgY29ycmVsYXRlZCB3aXRoIHgxCng0IDwtIHJub3JtKG4gPSBuLCBtZWFuID0gMikKeDUgPC0gcm5vcm0obiA9IG4sIG1lYW4gPSAuNCkKYGBgYAogClJ1biB0aGUgdHJlZS4gVWdoLiBUaGF0IGlzIG9uZSB1Z2x5LCBvdmVyZml0LCBhbmQgdW51c2VmdWwgdHJlZS4KYGBge3J9Cm1vZCA8LSBycGFydCh5IH4geDEgKyB4MiArIHgzICsgeDQgKyB4NSwgbWV0aG9kID0gImNsYXNzIiwgY29udHJvbCA9IHJwYXJ0LmNvbnRyb2woeHZhbCA9IDEwLCBjcCA9IDAsIG1pbnNwbGl0ID0gMjApKQpwbG90KG1vZCwgbWFyZ2luID0uMSkKdGV4dChtb2QsIGNleCA9IC44KQpgYGBgCgpDYW4gd2UgcHJ1bmUgb3VyIHRyZWUgd2l0aCBjcm9zcy12YWxpZGF0aW9uIGFuZCBnZXQgYSBzbGlnaHRseSBsZXNzIGNvbXBsZXggdHJlZT8KYGBge3J9CnByaW50Y3AobW9kKQpwbG90Y3AobW9kLCB1cHBlciA9ICJzcGxpdCIpCmBgYAoKSG1tLCBjcm9zcy12YWxpZGF0aW9uIHNheXMgd2Ugc2hvdWxkIGhhdmUganVzdCB0d28gc3BsaXRzLiBXaGF0IGRvZXMgaXQgbG9vayBsaWtlPwpgYGB7cn0KZml0MiA8LSBwcnVuZShtb2QsIGNwID0gLjAyMSkKcGxvdChmaXQyLCBtYXJnaW4gPS41KQp0ZXh0KGZpdDIpCmBgYGAKCkdyZWF0LiBXZSByZWNvdmVyZWQgdGhlIHRydXRoICh3aXRoIGNyb3NzLXZhbGlkYXRpb24pLgo=