Árvores de Decisão no R
Classificação com c50 e rpart
Holdout
Etapa de particionamento dos dados usando método holdout, através do pacote rminer
library(rminer)
read.csv('credit_data.csv', header = T)
df =
# O C5.0 obriga que a classe seja um factor (nem string serve)
# No caso do nosso dataset, a classe é originalmente um inteiro 0 ou 1.
# Então convertemos para factor primeiro:
$default = as.factor(df$default)
df
# Removendo o ID do cliente, pois não será útil para os modelos.
df[,-1]
df =
# Aplicando particionamento holdout, com 70% treino e 30% teste
holdout(df$default, ratio = 0.7, mode = 'stratified')
data =
# O retorno da função holdout dá as posições das entradas que foram selecionadas.
# Por isso precisamos voltar ao dataframe e selecionar as linhas, conforme abaixo:
df[data$tr, ]
train = df[data$ts, ]
test =
# No nosso dataframe, a classe está na quarta coluna.
train[,-4]
X_train = test[,-4]
X_test =
train[,4]
y_train = test[,4] y_test =
Treinamento e predição dos modelos
Com o pacote c5.0
library(C50)
C5.0(X_train, y_train) # treinamento
tree_model =
# Predição
predict(tree_model, X_test, type = 'class')
outcome = cbind(test, outcome)
outcome =
# Gerando a matriz de confusão
table(outcome$default, outcome$outcome)
cm = cm
0 1
0 510 5
1 5 80
# Computando as métricas manualmente:
(cm[1,1] + cm[2,2]) / sum(cm)
acc1 = cm[2,2] / (cm[2,2] + cm[1,2] )
prec1 = cm[2,2] / (cm[2,2] + cm[2,1] )
rec1 = 2*(prec1*rec1/(prec1 + rec1))
f1sc1 = c(acc1,prec1,rec1,f1sc1) c50_metrics =
Com o pacote rpart
library(rpart)
rpart(default ~ ., data = train, method = 'class')
tree_model2 =
# A notação ~. serve para indicar que o atributo classe é "default" e todas as outras colunas
# serão usadas como atributos preditivos. Também é possível usar apenas colunas específicas
# como atributos preditivos. Por exemplo, se quiséssemos usar apenas loan e age, seria:
# rpart(default ~ loan + age, data = train, method = 'class')
# Predição
predict(tree_model2, X_test, type = 'class')
outcome2 = cbind(test, outcome2)
outcome2 =
# Gerando a matriz de confusão
table(outcome2$default, outcome2$outcome2)
cm2 = cm2
0 1
0 511 4
1 9 76
# Computando as métricas manualmente:
(cm2[1,1] + cm2[2,2]) / sum(cm2)
acc2 = cm2[2,2] / (cm2[2,2] + cm2[1,2] )
prec2 = cm2[2,2] / (cm2[2,2] + cm2[2,1] )
rec2 = 2*(prec2*rec2/(prec2 + rec2))
f1sc2 = c(acc2,prec2,rec2,f1sc2) rpart_metrics =
Comparativo dos modelos
data.frame(cbind(c50_metrics,rpart_metrics))
metrics =rownames(metrics) = c('Accuracy','Precision','Recall','F1')
colnames(metrics) = c('C5.0','rpart')
metrics
C5.0 rpart
Accuracy 0.9833333 0.9783333
Precision 0.9411765 0.9500000
Recall 0.9411765 0.8941176
F1 0.9411765 0.9212121
Visualização das árvores
Verificando a possibilidade de visualização da árvore de forma nativa, sem a necessidade de recursos gráficos adicionais:
Pacote C50
Usando a função summary, é possível obter um resumo completo da estrutura da árvore, incluindo uma representação que lembra um pouco a que é feita na Weka.
summary(tree_model)
Call:
C5.0.default(x = X_train, y = y_train)
C5.0 [Release 2.07 GPL Edition] Mon Dec 14 16:23:53 2020
-------------------------------
Class specified by attribute `outcome'
Read 1400 cases (4 attributes) from undefined.data
Decision tree:
age > 34.91552: 0 (875.3)
age <= 34.91552:
:...loan <= 3650.196:
:...income > 26581.62: 0 (189.4)
: income <= 26581.62:
: :...loan <= 2591.029: 0 (41)
: loan > 2591.029: 1 (13/1)
loan > 3650.196:
:...loan > 7707.241: 1 (82)
loan <= 7707.241:
:...income > 58121.67: 0 (45)
income <= 58121.67:
:...loan > 6339.851: 1 (40)
loan <= 6339.851:
:...income > 46608.37:
:...loan <= 6034.153: 0 (28)
: loan > 6034.153:
: :...loan <= 6127.382: 1 (2)
: loan > 6127.382: 0 (3.4)
income <= 46608.37:
:...income <= 31587.06: 1 (33)
income > 31587.06:
:...loan > 5043.148: 1 (24)
loan <= 5043.148:
:...income > 38053.63: 0 (12)
income <= 38053.63:
:...loan <= 4263.493: 0 (6)
loan > 4263.493: 1 (6/1)
Evaluation on training data (1400 cases):
Decision Tree
----------------
Size Errors
15 2( 0.1%) <<
(a) (b) <-classified as
---- ----
1200 2 (a): class 0
198 (b): class 1
Attribute usage:
99.86% age
37.57% loan
31.71% income
Time: 0.0 secs
Pacote rpart
Já no pacote rpart, a visualização da árvore de forma nativa é pouco amigável, e o uso da função summary não ajuda muito. Seria necessária a instalação de um pacote adicional (rpart.plot) para conseguirmos visualizar melhor a estrutura da árvore.
tree_model2
n= 1400
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 1400 198 0 (0.85857143 0.14142857)
2) age>=34.92062 876 0 0 (1.00000000 0.00000000) *
3) age< 34.92062 524 198 0 (0.62213740 0.37786260)
6) loan< 5654.481 349 55 0 (0.84240688 0.15759312)
12) loan< 3104.628 211 4 0 (0.98104265 0.01895735) *
13) loan>=3104.628 138 51 0 (0.63043478 0.36956522)
26) income>=31346.88 99 13 0 (0.86868687 0.13131313)
52) income>=38026.91 78 3 0 (0.96153846 0.03846154) *
53) income< 38026.91 21 10 0 (0.52380952 0.47619048)
106) loan< 4264.722 10 0 0 (1.00000000 0.00000000) *
107) loan>=4264.722 11 1 1 (0.09090909 0.90909091) *
27) income< 31346.88 39 1 1 (0.02564103 0.97435897) *
7) loan>=5654.481 175 32 1 (0.18285714 0.81714286)
14) loan< 7707.778 93 32 1 (0.34408602 0.65591398)
28) income>=59070.56 25 0 0 (1.00000000 0.00000000) *
29) income< 59070.56 68 7 1 (0.10294118 0.89705882)
58) income>=50005.27 20 7 1 (0.35000000 0.65000000)
116) loan< 6438.409 8 1 0 (0.87500000 0.12500000) *
117) loan>=6438.409 12 0 1 (0.00000000 1.00000000) *
59) income< 50005.27 48 0 1 (0.00000000 1.00000000) *
15) loan>=7707.778 82 0 1 (0.00000000 1.00000000) *