Á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)

df = read.csv('credit_data.csv', header = T)

# 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: 
df$default = as.factor(df$default)

# Removendo o ID do cliente, pois não será útil para os modelos.
df = df[,-1]

# Aplicando particionamento holdout, com 70% treino e 30% teste
data = holdout(df$default, ratio = 0.7, mode = 'stratified')

# 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:
train = df[data$tr, ]
test = df[data$ts, ]

# No nosso dataframe, a classe está na quarta coluna.
X_train = train[,-4]
X_test = test[,-4]

y_train = train[,4]
y_test = test[,4]

Treinamento e predição dos modelos

Com o pacote c5.0

library(C50)
tree_model = C5.0(X_train, y_train)  # treinamento

# Predição
outcome = predict(tree_model, X_test, type = 'class')
outcome = cbind(test, outcome)

# Gerando a matriz de confusão
cm = table(outcome$default, outcome$outcome)
cm
   
      0   1
  0 510   5
  1   5  80
# Computando as métricas manualmente:
acc1 = (cm[1,1] + cm[2,2]) / sum(cm)
prec1 = cm[2,2] / (cm[2,2] + cm[1,2] )
rec1 = cm[2,2] / (cm[2,2] + cm[2,1] )
f1sc1 = 2*(prec1*rec1/(prec1 + rec1))
c50_metrics = c(acc1,prec1,rec1,f1sc1)

Com o pacote rpart

library(rpart)

tree_model2 = rpart(default ~ ., data = train, method = 'class')

# 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
outcome2 = predict(tree_model2, X_test, type = 'class')
outcome2 = cbind(test, outcome2)

# Gerando a matriz de confusão
cm2 = table(outcome2$default, outcome2$outcome2)
cm2
   
      0   1
  0 511   4
  1   9  76
# Computando as métricas manualmente:
acc2 = (cm2[1,1] + cm2[2,2]) / sum(cm2)
prec2 = cm2[2,2] / (cm2[2,2] + cm2[1,2] )
rec2 = cm2[2,2] / (cm2[2,2] + cm2[2,1] )
f1sc2 = 2*(prec2*rec2/(prec2 + rec2))
rpart_metrics = c(acc2,prec2,rec2,f1sc2)

Comparativo dos modelos

metrics = data.frame(cbind(c50_metrics,rpart_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) *