rpart package : recursive partitioning
[함수의 구조] rpart(formula, data, weights, subset, na.action=na.rpart, method, parms, control, cost, …)
[기능] 훈련 데이터를 이용하여 의사결정나무를 생성한다. R의 rpart 오브젝트를 생성한다.
[옵션]
[함수의 구조] rpart.control(minsplit = 20, minbucket = round(minsplit/3), cp=0.01, maxcompete = 4, maxsurrogate=5, xval=10, maxdepth=30, …)
[기능] 의사결정나무를 생성할 때, 분할규칙 등을 정한다.
[옵션]
[함수의 구조] printcp(x, digits = getOption(“digits”) - 2)
[기능] R의 rpart 오브젝트를 대상으로 cp 값에 대한 나무구조 순서를 출력한다.
[함수의 구조] prune(tree, cp, …)
[기능] 의사결정나무의 가지치기를 실시한다.
[함수의 구조] plot(x, uniform=FALSE, branch=1, compress=FALSE, nspace, margin=0, …)
[기능] 생성된 의사결정나무를 나무그림으로 표현한다.
[함수의 구조] text(x, splits=TRUE, all=FALSE, digits=getOption(“digits) - 3, use.n=FLASE, fancy=FALSE, fwidth=0.8, bg=par(”bg”), col, …)
[기능] plot에 의해 그려진 나무구조에 텍스트를 삽입한다.
[함수의 구조] predict(object, newdata, type=c(“vector”, “prob”, “class”, “matrix”), na.action=na.pass, …)
[기능] 생성된 의사결정나무 오브젝트에 새로운 데이터 newdata를 적용하여 예측한다.
의사결정나무 생성 함수인 rpart에 의해 생성된 rpart 오브젝트는 rpart.plot 이라는 패키지를 활용하면 더 보기 좋게 나무 구조를 출력할 수 있다. rpart.plot 패키지의 prp 함수를 소개한다.
[함수의 구조] prp(object, type=2, extra=“auto”, digits=2, box.palette=“auto”, …)
[기능] 생성된 의사결정나무 오브젝트를 나무구조의 형태로 출력한다.
분류의사결정나무를 생성하는 사례분석으로 앞서 사용되었던 와인품질 데이터(‘winequalityCLASS.csv’)를 이용하여 가장 대중적 의사결정나무인 CART 방법을 이용한 의사결정나무를 구축해 본다.
먼저 와인 품질 데이터가 담긴 ‘winequalityCLASS.csv’ 파일을 확인한다. 데이터 파일을 읽고 CART 의사결정나무를 실행하기 위해서 <R 3-1>과 같이 수행하면 된다.
# importing data
wine <- read.csv("./data/winequalityCLASS.csv")
# factorize for classification
wine$quality <- factor(wine$quality)
# classification tree
library(rpart)
set.seed(1234)
my.control <- rpart.control(xval = 10, cp = 0, minsplit = 20)
tree.wine <- rpart(quality ~ ., data = wine, method = "class", control = my.control)
print(tree.wine)
n= 1194
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 1194 549 1 (0.45979899 0.54020101)
2) alcohol< 10.15 613 204 0 (0.66721044 0.33278956)
4) sulphates< 0.575 276 54 0 (0.80434783 0.19565217)
8) chlorides>=0.0795 138 16 0 (0.88405797 0.11594203) *
9) chlorides< 0.0795 138 38 0 (0.72463768 0.27536232)
18) volatile>=0.42 124 29 0 (0.76612903 0.23387097)
36) fixed< 7.65 60 6 0 (0.90000000 0.10000000) *
37) fixed>=7.65 64 23 0 (0.64062500 0.35937500)
74) citric>=0.245 23 4 0 (0.82608696 0.17391304) *
75) citric< 0.245 41 19 0 (0.53658537 0.46341463)
150) freeSD< 12.5 28 9 0 (0.67857143 0.32142857)
300) chlorides>=0.0695 20 4 0 (0.80000000 0.20000000) *
301) chlorides< 0.0695 8 3 1 (0.37500000 0.62500000) *
151) freeSD>=12.5 13 3 1 (0.23076923 0.76923077) *
19) volatile< 0.42 14 5 1 (0.35714286 0.64285714) *
5) sulphates>=0.575 337 150 0 (0.55489614 0.44510386)
10) fixed< 10.05 296 117 0 (0.60472973 0.39527027)
20) volatile>=0.385 254 89 0 (0.64960630 0.35039370)
40) totalSD>=46.5 115 26 0 (0.77391304 0.22608696)
80) volatile>=0.6475 28 1 0 (0.96428571 0.03571429) *
81) volatile< 0.6475 87 25 0 (0.71264368 0.28735632)
162) fixed>=8.7 26 2 0 (0.92307692 0.07692308) *
163) fixed< 8.7 61 23 0 (0.62295082 0.37704918)
326) fixed< 7.85 45 13 0 (0.71111111 0.28888889)
652) citric< 0.205 21 3 0 (0.85714286 0.14285714) *
653) citric>=0.205 24 10 0 (0.58333333 0.41666667)
1306) citric>=0.275 17 4 0 (0.76470588 0.23529412) *
1307) citric< 0.275 7 1 1 (0.14285714 0.85714286) *
327) fixed>=7.85 16 6 1 (0.37500000 0.62500000) *
41) totalSD< 46.5 139 63 0 (0.54676259 0.45323741)
82) pH>=3.535 17 2 0 (0.88235294 0.11764706) *
83) pH< 3.535 122 61 0 (0.50000000 0.50000000)
166) citric>=0.35 7 0 0 (1.00000000 0.00000000) *
167) citric< 0.35 115 54 1 (0.46956522 0.53043478)
334) volatile>=0.555 70 30 0 (0.57142857 0.42857143)
668) totalSD< 23.5 25 4 0 (0.84000000 0.16000000) *
669) totalSD>=23.5 45 19 1 (0.42222222 0.57777778)
1338) density>=0.99685 35 17 0 (0.51428571 0.48571429)
2676) sulphates< 0.655 16 3 0 (0.81250000 0.18750000) *
2677) sulphates>=0.655 19 5 1 (0.26315789 0.73684211) *
1339) density< 0.99685 10 1 1 (0.10000000 0.90000000) *
335) volatile< 0.555 45 14 1 (0.31111111 0.68888889)
670) density< 0.99715 24 12 0 (0.50000000 0.50000000)
1340) citric>=0.2 7 0 0 (1.00000000 0.00000000) *
1341) citric< 0.2 17 5 1 (0.29411765 0.70588235) *
671) density>=0.99715 21 2 1 (0.09523810 0.90476190) *
21) volatile< 0.385 42 14 1 (0.33333333 0.66666667)
42) fixed>=9.1 10 3 0 (0.70000000 0.30000000) *
43) fixed< 9.1 32 7 1 (0.21875000 0.78125000)
86) density< 0.9965 8 3 0 (0.62500000 0.37500000) *
87) density>=0.9965 24 2 1 (0.08333333 0.91666667) *
11) fixed>=10.05 41 8 1 (0.19512195 0.80487805) *
3) alcohol>=10.15 581 140 1 (0.24096386 0.75903614)
6) sulphates< 0.715 392 127 1 (0.32397959 0.67602041)
12) alcohol< 11.03333 222 96 1 (0.43243243 0.56756757)
24) chlorides>=0.097 24 6 0 (0.75000000 0.25000000) *
25) chlorides< 0.097 198 78 1 (0.39393939 0.60606061)
50) sulphates< 0.505 20 5 0 (0.75000000 0.25000000) *
51) sulphates>=0.505 178 63 1 (0.35393258 0.64606742)
102) density>=0.99591 111 47 1 (0.42342342 0.57657658)
204) residsugar< 1.85 16 3 0 (0.81250000 0.18750000) *
205) residsugar>=1.85 95 34 1 (0.35789474 0.64210526)
410) pH>=3.335 35 17 0 (0.51428571 0.48571429)
820) freeSD< 10 13 2 0 (0.84615385 0.15384615) *
821) freeSD>=10 22 7 1 (0.31818182 0.68181818)
1642) sulphates>=0.655 9 4 0 (0.55555556 0.44444444) *
1643) sulphates< 0.655 13 2 1 (0.15384615 0.84615385) *
411) pH< 3.335 60 16 1 (0.26666667 0.73333333)
822) sulphates< 0.625 29 12 1 (0.41379310 0.58620690)
1644) volatile>=0.615 10 3 0 (0.70000000 0.30000000) *
1645) volatile< 0.615 19 5 1 (0.26315789 0.73684211) *
823) sulphates>=0.625 31 4 1 (0.12903226 0.87096774) *
103) density< 0.99591 67 16 1 (0.23880597 0.76119403)
206) totalSD>=43.5 17 8 0 (0.52941176 0.47058824) *
207) totalSD< 43.5 50 7 1 (0.14000000 0.86000000) *
13) alcohol>=11.03333 170 31 1 (0.18235294 0.81764706)
26) fixed< 6.35 28 14 0 (0.50000000 0.50000000)
52) chlorides< 0.0535 9 1 0 (0.88888889 0.11111111) *
53) chlorides>=0.0535 19 6 1 (0.31578947 0.68421053) *
27) fixed>=6.35 142 17 1 (0.11971831 0.88028169)
54) volatile>=0.765 9 4 0 (0.55555556 0.44444444) *
55) volatile< 0.765 133 12 1 (0.09022556 0.90977444) *
7) sulphates>=0.715 189 13 1 (0.06878307 0.93121693)
14) totalSD>=57.5 32 8 1 (0.25000000 0.75000000)
28) alcohol< 11.5 20 8 1 (0.40000000 0.60000000)
56) pH>=3.325 9 3 0 (0.66666667 0.33333333) *
57) pH< 3.325 11 2 1 (0.18181818 0.81818182) *
29) alcohol>=11.5 12 0 1 (0.00000000 1.00000000) *
15) totalSD< 57.5 157 5 1 (0.03184713 0.96815287) *
의사결정나무를 나무형태이 그래프로 그리기 위해서는 prp() 함수를 사용하면 된다.
# display tree
# install.packages("rpart.plot")
library(rpart.plot)
prp(tree.wine, type = 4, extra = 1, digits = 2, box.palette = "auto") # grays
# pruning with c-s.e.
cps <- printcp(tree.wine)
Classification tree:
rpart(formula = quality ~ ., data = wine, method = "class", control = my.control)
Variables actually used in tree construction:
[1] alcohol chlorides citric density fixed freeSD pH residsugar sulphates totalSD volatile
Root node error: 549/1194 = 0.4598
n= 1194
CP nsplit rel error xerror xstd
1 0.3734062 0 1.00000 1.00000 0.031368
2 0.0227687 1 0.62659 0.65392 0.028861
3 0.0077413 4 0.55556 0.61384 0.028329
4 0.0072860 11 0.49362 0.61384 0.028329
5 0.0063752 20 0.41166 0.60291 0.028174
6 0.0036430 24 0.38616 0.58834 0.027960
7 0.0024287 33 0.35155 0.62113 0.028430
8 0.0018215 38 0.33515 0.62842 0.028529
9 0.0000000 44 0.32423 0.62842 0.028529
# pruning with c-s.e.
cps <- printcp(tree.wine)
Classification tree:
rpart(formula = quality ~ ., data = wine, method = "class", control = my.control)
Variables actually used in tree construction:
[1] alcohol chlorides citric density fixed freeSD pH residsugar sulphates totalSD volatile
Root node error: 549/1194 = 0.4598
n= 1194
CP nsplit rel error xerror xstd
1 0.3734062 0 1.00000 1.00000 0.031368
2 0.0227687 1 0.62659 0.65392 0.028861
3 0.0077413 4 0.55556 0.61384 0.028329
4 0.0072860 11 0.49362 0.61384 0.028329
5 0.0063752 20 0.41166 0.60291 0.028174
6 0.0036430 24 0.38616 0.58834 0.027960
7 0.0024287 33 0.35155 0.62113 0.028430
8 0.0018215 38 0.33515 0.62842 0.028529
9 0.0000000 44 0.32423 0.62842 0.028529
k <- which.min(cps[, "xerror"])
err <- cps[k, "xerror"]; se <- cps[k, "xstd"]
c <-1 # 1-s.e.
k1 <- which(cps[, "xerror"] <= err + c * se)[1]
cp.chosen <- cps[k1, "CP"]
tree.pruned.wine <- prune(tree.wine, cp = cp.chosen)
print(tree.pruned.wine)
n= 1194
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 1194 549 1 (0.4597990 0.5402010)
2) alcohol< 10.15 613 204 0 (0.6672104 0.3327896)
4) sulphates< 0.575 276 54 0 (0.8043478 0.1956522) *
5) sulphates>=0.575 337 150 0 (0.5548961 0.4451039)
10) fixed< 10.05 296 117 0 (0.6047297 0.3952703)
20) volatile>=0.385 254 89 0 (0.6496063 0.3503937) *
21) volatile< 0.385 42 14 1 (0.3333333 0.6666667) *
11) fixed>=10.05 41 8 1 (0.1951220 0.8048780) *
3) alcohol>=10.15 581 140 1 (0.2409639 0.7590361) *
# display tree
prp(tree.pruned.wine, type = 4, extra = 1, digits = 2, box.palette = "Grays")
와인품질 데이터에서 목표변수의 분류예측값을 구하고, 정확도를 평가하는 방법에 대해서 알아보자. 우선 분류예측 확률을 구하고, 이를 토대로 목표변수의 범주값을 예측하는 과정을 3-6에서 알 수 있다.
# making predictions - probability prediction
prob.tree.wine <- predict(tree.pruned.wine, newdata = wine, type = "prob")
head(prob.tree.wine, 5)
0 1
1 0.8043478 0.1956522
2 0.6496063 0.3503937
3 0.6496063 0.3503937
4 0.1951220 0.8048780
5 0.8043478 0.1956522
cutoff <- 0.5 # cutoff
yhat.tree.wine <- ifelse(prob.tree.wine[, 2] > cutoff, 1, 0)
# evaluation
tab <- table(wine$quality, yhat.tree.wine, dnn = c("Observed", "Predicted"))
print(tab) # confusion matrix
Predicted
Observed 0 1
0 387 162
1 143 502
sum(diag(tab) / sum(tab)) # accuracy
[1] 0.7445561
tab[2, 2] / sum(tab[2, ]) # sensitivity
[1] 0.7782946
tab[1, 1] / sum(tab[1, ]) # specificity
[1] 0.704918
table() 함수를 이용하여 학습 데이터의 실제 목표변수와 의사결정나무에 의해 예측된 목표변수의 예측값의 일치성을 확인하기 위해 빈도기준으로 분할표를 만들고 있다. 흔히 이 분할표를 ‘confusion matrix’라고 한다. 또한 보통등급(’0’ 범주)과 우수등급(‘1’ 범주)의 예측 결과에 대한 비교를 위해 정확도, 민감도, 특이도를 계산하는 부분이 나온다.
결과에 따르면 보통 등급 와인을 보통 등급 와인으로 예측한 경우는 387번이고, 우수 등급 와인을 우수 등급 와인으로 예측한 경우는 502번이다. 따라서 학습 데이터에 대한 정확도는 (387 + 502) / 1,194 = 74.46%로 계산되었다. 총 645개의 우수등급 와인 중에서 실제로 우수 등급 와인으로 예측한 경우는 502번이다. 따라서 민감도는 502/645 = 77.8%이다. 마지막으로 총 549개의 보통 등급 와인 중에서 실제로 보통 등급 와인으로 예측한 경우는 387번이다. 따라서 특이도는 387/549=70.5%이다.
회귀나무를 생성하는 사례분석으로 앞서 사용되었던 의류생산성 데이터(‘productivityREG.csv’)를 이용하여 CART 방법을 이용한 회귀나무를 구축해 보고자 한다.
먼저 의류생산성 데이터가 담긴 ‘productivityREG.csv’ 파일을 확인한다. CART 의사결정나무를 실행하기 위해서 다음과 같이 수행하면 된다.
# importing data
prod <- read.csv("./data/productivityREG.csv", header = T)
# factorizing predictor variables
prod$quarter <- factor(prod$quarter)
prod$department <- factor(prod$quarter)
prod$day <- factor(prod$day)
prod$team <- factor(prod$team)
library(rpart)
set.seed(1234)
my.control <- rpart.control(xval = 10, cp = 0.01, minsplit = 30)
tree.prod <- rpart(productivity ~ ., data = prod, method = "anova", control = my.control)
print(tree.prod)
n= 936
node), split, n, deviance, yval
* denotes terminal node
1) root 936 19.0783800 0.7745872
2) target< 0.725 256 5.9939720 0.6987081
4) over_time>=3210 147 1.9551960 0.6661212
8) incentive< 28 59 1.1297170 0.5911298 *
9) incentive>=28 88 0.2712242 0.7163995 *
5) over_time< 3210 109 3.6721560 0.7426556
10) numworkers< 9.5 72 2.3460690 0.6953575
20) team=2,6,7,9,10,11 28 0.8858318 0.5938276 *
21) team=1,4,5,8,12 44 0.9879286 0.7599675 *
11) numworkers>=9.5 37 0.8515788 0.8346949
22) team=8,9,11 10 0.2922560 0.6892305 *
23) team=1,2,4,5,6,7,10 27 0.2693539 0.8885707 *
3) target>=0.725 680 11.0555500 0.8031535
6) incentive< 84.5 626 9.5634660 0.7899670
12) smv< 3.4 74 1.4816030 0.6786105
24) numworkers< 8.5 61 0.9512037 0.6481869 *
25) numworkers>=8.5 13 0.2090045 0.8213672 *
13) smv>=3.4 552 7.0412290 0.8048952
26) smv>=30.25 13 0.2204477 0.6076220 *
27) smv< 30.25 539 6.3026620 0.8096532
54) smv>=4.115 383 3.3506460 0.7921739
108) numworkers< 8.5 28 0.9711016 0.7003731 *
109) numworkers>=8.5 355 2.1249660 0.7994145 *
55) smv< 4.115 156 2.5477100 0.8525670
110) over_time< 1020 32 0.9787853 0.7835423
220) team=3,7,9,10 20 0.5727786 0.7122460 *
221) team=1,2,4 12 0.1349048 0.9023694 *
111) over_time>=1020 124 1.3771190 0.8703799 *
7) incentive>=84.5 54 0.1213521 0.9560197 *
의사결정나무를 나무형태의 그래프로 그리기 위해서는 다음과 같이 ‘prp()’ 함수를 사용하면 된다.
# display tree
library(rpart.plot)
prp(tree.prod, type = 4, extra = 1, digits = 2, box.palette = "auto")
위의 의사결정나무는 가지치기 전의 결과이므로 대단히 사이즈가 큰 상태이다. 이처럼 나무 구조가 과도하게 크면 의사결정나무를 해석하는 것이 매우 어려울 것이다.
의사결정나무의 가지치기를 수행하기 위해서는 다음처럼 가지치기 단계별 정보를 파악할 필요가 있다.
# pruning with c-s.e.
cps <- printcp(tree.prod)
Regression tree:
rpart(formula = productivity ~ ., data = prod, method = "anova",
control = my.control)
Variables actually used in tree construction:
[1] incentive numworkers over_time smv target team
Root node error: 19.078/936 = 0.020383
n= 936
CP nsplit rel error xerror xstd
1 0.106343 0 1.00000 1.00131 0.051171
2 0.071847 1 0.89366 0.91542 0.049271
3 0.054545 2 0.82181 0.85369 0.048553
4 0.027157 3 0.76726 0.78994 0.047001
5 0.024380 4 0.74011 0.79369 0.049856
6 0.021192 8 0.64221 0.76571 0.049399
7 0.016846 9 0.62102 0.74842 0.049034
8 0.015199 10 0.60417 0.74757 0.049209
9 0.013344 11 0.58897 0.74632 0.049216
10 0.012132 12 0.57563 0.73686 0.048710
11 0.010000 14 0.55137 0.71037 0.046591
가지치기를 수행하기 위해서 비용복잡함수의 a(alpha)와 해당되는 나무구조의 교차타당성 오류율을 알아야 한다. printcp() 함수에 의해 실행된 결과를 살펴 보자.
다음의 코드 3-11에는 prune() 함수를 이용하여 1-s.e. 법칙에 의한 ‘cp’ 값을 사용하여 가지치기 하는 부분이 포함되어 있다.
# pruning with c-s.e.
k <- which.min(cps[, "xerror"])
err <- cps[k, "xerror"]; se <- cps[k, "xstd"]
c <- 1 # 1-s.e.
k1 <- which(cps[, "xerror"] <= err+c*se)[1]
cp.chosen <- cps[k1, "CP"]
treee.pruned.prod <- prune(tree.prod, cp=cp.chosen)
print(treee.pruned.prod)
n= 936
node), split, n, deviance, yval
* denotes terminal node
1) root 936 19.0783800 0.7745872
2) target< 0.725 256 5.9939720 0.6987081
4) over_time>=3210 147 1.9551960 0.6661212
8) incentive< 28 59 1.1297170 0.5911298 *
9) incentive>=28 88 0.2712242 0.7163995 *
5) over_time< 3210 109 3.6721560 0.7426556
10) numworkers< 9.5 72 2.3460690 0.6953575
20) team=2,6,7,9,10,11 28 0.8858318 0.5938276 *
21) team=1,4,5,8,12 44 0.9879286 0.7599675 *
11) numworkers>=9.5 37 0.8515788 0.8346949 *
3) target>=0.725 680 11.0555500 0.8031535
6) incentive< 84.5 626 9.5634660 0.7899670
12) smv< 3.4 74 1.4816030 0.6786105 *
13) smv>=3.4 552 7.0412290 0.8048952
26) smv>=30.25 13 0.2204477 0.6076220 *
27) smv< 30.25 539 6.3026620 0.8096532
54) smv>=4.115 383 3.3506460 0.7921739 *
55) smv< 4.115 156 2.5477100 0.8525670 *
7) incentive>=84.5 54 0.1213521 0.9560197 *
결과에서는 1-s.e. 법칙에 의해 가지치기한 CART 의사결정나무의 결과가 텍스트 형식으로 출력되어 있다. 노드번호, 분할규칙, 해당 노드의 총 관찰값 수, 해당 노드의 평균 제곱 오차, 해당 노드의 목표변수 예측값 (회귀나무일 때는 평균값)의 순서로 정보가 제공되고 있다. 줄 마지막에 있는 * 표시는 해당 노드가 최종 노드임을 의미한다.
가지치기가 수행된 의사결정나무를 나무형태 그래프로 그리기 위해서 prp() 함수를 이용한다.
# display tree
prp(treee.pruned.prod, type = 4, extra = 1, digits = 2, box.palette = "Grays")
결과 그래프에서 뿌리노드를 살펴 보면 936개의 관찰값이 있고, 목표변수인 생산성의 평균값은 0.787이다. 이 뿌리노드를 좌우로 분할하는 분할규칙은 ’target < 0.73’인지 여부이다. 만약 사실이면 해당되는 관찰값은 좌측노드로 내려가고, 만약 반대의 경우라면 우측노드로 내려가면 된다. 좌측노드로 내려간 경우에는 총 256개의 관찰값이 있고 그 평균이 0.7이므로, 뿌리노드에 비하면 생산성이 낮아진 노드라고 할 수 있다. 반면 우측노드로 내려간 경우에는 680개의 관찰값과 평균 0.8의 생산성을 보이고 있다.
좌측노드를 추가로 ‘over_time’ 값에 의해 분할했는데, 이 변수가 3,210보다 큰지 혹은 작은지를 기준으로 추가로 분할하게 된다. 예를 들어 ’over_time >= 3210’에 의해 좌측노드에 속하게 되는 관찰값들은 생산성의 평균이 0.67에 불과하다. 반면 뿌리노드에서 우측으로 간 노드를 다시 한 번 ’incentive >= 85’에 의해 분할을 하면 총 54개의 관찰값이 속하는데, 그들의 평균 생산성은 0.96으로 매우 높다.
의류생산성 데이터에서 목표변수의 예측값을 구하고 평가하는 방법에 대해서 알아보자.
# making predictions
pred.tree.prod <- predict(treee.pruned.prod, newdata = prod, type = "vector")
head(pred.tree.prod, 5)
1 2 3 4 5
0.9560197 0.8525670 0.7921739 0.7921739 0.7921739
아래는 평균제곱오차(Mean Squared Error, MSE)와 평균절대오차(Mean Absolute Error, MAE)를 계산하는 코드이다. 평균절대오차는 실제값과 계측값 차이의 절댓값들을 평균한 것이다. 출력값에 따르면 회귀나무의 평균제곱오차는 0.0127이고 평균절대오차는 0.0784이다.
# evaluation
mean((prod$productivity - pred.tree.prod)^2) # MSE
[1] 0.01265816
mean(abs(prod$productivity - pred.tree.prod)) # MAE
[1] 0.07839877
다음으로 CART 회귀이사결정나무의 예측값과 실제값의 일치도를 산점도로 그리고자 한다. 결과를 살펴 보면 회귀의사결정나무에서 최종노드 내 관찰값들의 평균값을 예측값으로 사용하기 때문에 동일한 값을 가진 점이 많은 것을 확인할 수 있다.
# observed vs. predicted
plot(prod$productivity, pred.tree.prod, xlab = "Observed Values", ylab = "Fitted Values")
abline(0, 1)