Árvore de decisão em R: árvore de classificação com exemplo

⚡ Resumo Inteligente

As árvores de decisão em R dividem os dados em ramos usando regras simples de sim ou não, até que cada folha contenha uma única classe dominante. Este passo a passo constrói, plota, avalia e ajusta uma árvore de classificação rpart no conjunto de dados de sobrevivência do Titanic.

  • ???? Definição Essencial: Uma árvore particiona recursivamente o espaço preditor, escolhendo em cada nó a divisão que mais reduz a impureza de classe.
  • 🔄 Preparação de dados: Embaralhe o arquivo Titanic ordenado com sample(), remova as colunas de identificadores, converta os fatores e, em seguida, remova as linhas NA.
  • 🛠️ Sintaxe do modelo: Chame rpart(survived~., data = data_train, method = 'class') e renderize o resultado com rpart.plot(fit, extra = 106).
  • ⚙️ Tuning: A função rpart.control() expõe os parâmetros minsplit, minbucket, maxdepth e cp, elevando a precisão para 79.90%.
  • 📊 Avaliação: Construa uma matriz de confusão com a função table(), depois divida a diagonal pelo total para obter uma precisão de teste de 78.47%.

Árvore de decisão em R

O que são árvores de decisão?

Árvores de decisão As árvores de decisão são um algoritmo de aprendizado de máquina versátil que pode executar tarefas de classificação e regressão. São algoritmos muito poderosos, capazes de se ajustar a conjuntos de dados complexos. Além disso, as árvores de decisão são componentes fundamentais das florestas aleatórias, que estão entre os algoritmos de aprendizado de máquina mais poderosos disponíveis atualmente.

Antes de construir uma em código, é útil saber como uma árvore decide onde se dividir.

Como funciona uma árvore de decisão?

Uma árvore de decisão transforma um conjunto de dados em um fluxograma de perguntas de sim ou não, construído a partir de três tipos de nós: o raiz Contém todas as observações de treinamento, um nó interno faz uma pergunta sobre um preditor e divide os dados em dois, e um folha interrompe a divisão e retorna a classe majoritária.

O crescimento segue um procedimento ganancioso chamado particionamento binário recursivo:

  1. Avalie cada divisão de candidatos. Para cada preditor e ponto de corte, meça o grau de impureza dos dois grupos resultantes.
  2. Fique com a melhor. A divisão que mais reduz as impurezas torna-se a questão a ser feita nesse nó.
  3. Repita o processo com cada criança. até que uma regra de controle o impeça: minsplit, minbucket, maxdepth ou cp.
  4. Ameixa seca. Em seguida, o cp remove os ramos que não se pagam, o que impede a árvore de memorizar o conjunto de treinamento.

Como cada pergunta compara uma variável com um limite, o algoritmo nunca precisa de escalonamento ou codificação fictícia.

Índice de Gini versus Entropia em Árvores de Decisão

Essa impureza pode ser medida de duas maneiras, e o rpart permite que você escolha.

Critérios Índice Gini Entropia (Ganho de Informação)
Fórmula 1 – soma dos quadrados das proporções de classe -soma de p vezes log2(p)
Alcance (duas classes) 0 a 0.5 0 a 1
Computação Mais rápido, sem logaritmo Mais lento, utiliza logaritmos
configuração rpart Padrão parms = lista(split = “informação”)
fit_entropy <- rpart(survived~., data = data_train, method = 'class',
    parms = list(split = "information"))

Na prática, ambos os critérios geralmente apresentam a mesma divisão, portanto, o índice de Gini padrão é uma escolha segura.

Vantagens e desvantagens das árvores de decisão

As compensações indicam quando uma única árvore é suficiente e quando é necessário recorrer a um conjunto.

Vantagens

  • Totalmente interpretável: O modelo ajustado é um diagrama que qualquer parte interessada pode ler.
  • Pré-processamento mínimo: Não é necessário nenhum ajuste de escala ou normalização, e os fatores funcionam nativamente.
  • Executa ambas as tarefas: O método `method = 'class'` ajusta um classificador e o método `method = 'anova'` ajusta uma árvore de regressão.
  • Treinamento rápido: Grandes conjuntos de dados são processados ​​em segundos, portanto, as árvores de decisão constituem uma primeira linha de base útil.

Desvantagens

  • Alta variância: Uma pequena alteração nos dados de treinamento pode produzir uma árvore completamente diferente.
  • Propenso ao sobreajuste: Uma árvore sem restrições cresce até que cada folha seja pura, a menos que `cp` e `maxdepth` a restrinjam.
  • Apenas divisões paralelas aos eixos: As linhas diagonais exigem muitos cortes em forma de escada.

A solução para as duas primeiras fraquezas é fazer uma média de muitas árvores, que é o que uma floresta aleatória faz.

Como treinar e visualizar uma árvore de decisão em R

Para construir sua primeira árvore de decisão em R, você seguirá sete etapas:

  • Etapa 1: importe os dados
  • Etapa 2: limpe o conjunto de dados
  • Etapa 3: criar conjunto de treinamento/teste
  • Etapa 4: construir o modelo
  • Etapa 5: faça previsões
  • Etapa 6: Avalie o desempenho
  • Etapa 7: ajuste os hiperparâmetros

Etapa 1) Importe os dados

Se você está curioso sobre o destino do Titanic, pode assistir a este vídeo no Youtube. O objetivo deste conjunto de dados é prever quais pessoas têm maior probabilidade de sobreviver após a colisão com o iceberg. O conjunto de dados contém 13 variáveis ​​e 1309 observações. O conjunto de dados é ordenado pela variável X.

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

Saída:

##   X pclass survived                                            name    sex
## 1 1      1        1                   Allen, Miss. Elisabeth Walton female
## 2 2      1        1                  Allison, Master. Hudson Trevor   male
## 3 3      1        0                    Allison, Miss. Helen Loraine female
## 4 4      1        0            Allison, Mr. Hudson Joshua Creighton   male
## 5 5      1        0 Allison, Mrs. Hudson J C (Bessie Waldo Daniels) female
## 6 6      1        1                             Anderson, Mr. Harry   male
##       age sibsp parch ticket     fare   cabin embarked
## 1 29.0000     0     0  24160 211.3375      B5        S
## 2  0.9167     1     2 113781 151.5500 C22 C26        S
## 3  2.0000     1     2 113781 151.5500 C22 C26        S
## 4 30.0000     1     2 113781 151.5500 C22 C26        S
## 5 25.0000     1     2 113781 151.5500 C22 C26        S
## 6 48.0000     0     0  19952  26.5500     E12        S
##                         home.dest
## 1                    St Louis, MO
## 2 Montreal, PQ / Chesterville, ON
## 3 Montreal, PQ / Chesterville, ON
## 4 Montreal, PQ / Chesterville, ON
## 5 Montreal, PQ / Chesterville, ON
## 6                    New York, NY
tail(titanic)

Saída:

##         X pclass survived                      name    sex  age sibsp
## 1304 1304      3        0     Yousseff, Mr. Gerious   male   NA     0
## 1305 1305      3        0      Zabour, Miss. Hileni female 14.5     1
## 1306 1306      3        0     Zabour, Miss. Thamine female   NA     1
## 1307 1307      3        0 Zakarian, Mr. Mapriededer   male 26.5     0
## 1308 1308      3        0       Zakarian, Mr. Ortin   male 27.0     0
## 1309 1309      3        0        Zimmerman, Mr. Leo   male 29.0     0
##      parch ticket    fare cabin embarked home.dest
## 1304     0   2627 14.4583              C          
## 1305     0   2665 14.4542              C          
## 1306     0   2665 14.4542              C          
## 1307     0   2656  7.2250              C          
## 1308     0   2670  7.2250              C          
## 1309     0 315082  7.8750              S

Na saída head e tail, você pode notar que os dados não são embaralhados. Este é um grande problema! Ao dividir seus dados entre um conjunto de treinamento e um conjunto de teste, você selecionará só o passageiro das classes 1 e 2 (nenhum passageiro da classe 3 está entre os 80% melhores das observações), o que significa que o algoritmo nunca verá as características do passageiro da classe 3. Esse erro levará a uma previsão ruim.

Para superar esse problema, você pode usar a função sample().

shuffle_index <- sample(1:nrow(titanic))
head(shuffle_index)

Explicação do código R da árvore de decisão

  • sample(1:nrow(titanic)): Gera uma lista aleatória de índices de 1 a 1309 (ou seja, o número máximo de linhas).

Saída:

## [1]  288  874 1078  633  887  992

Você usará este índice para embaralhar o conjunto de dados do Titanic.

titanic <- titanic[shuffle_index, ]
head(titanic)

Saída:

##         X pclass survived
## 288   288      1        0
## 874   874      3        0
## 1078 1078      3        1
## 633   633      3        0
## 887   887      3        1
## 992   992      3        1
##                                                           name    sex age
## 288                                      Sutton, Mr. Frederick   male  61
## 874                   Humblen, Mr. Adolf Mathias Nicolai Olsen   male  42
## 1078                                 O'Driscoll, Miss. Bridget female  NA
## 633  Andersson, Mrs. Anders Johan (Alfrida Konstantia Brogren) female  39
## 887                                        Jermyn, Miss. Annie female  NA
## 992                                           Mamee, Mr. Hanna   male  NA
##      sibsp parch ticket    fare cabin embarked           home.dest## 288      0     0  36963 32.3208   D50        S     Haddenfield, NJ
## 874      0     0 348121  7.6500 F G63        S                    
## 1078     0     0  14311  7.7500              Q                    
## 633      1     5 347082 31.2750              S Sweden Winnipeg, MN
## 887      0     0  14313  7.7500              Q                    
## 992      0     0   2677  7.2292              C	

Etapa 2) Limpe o conjunto de dados

Diversas variáveis ​​contêm valores NA. A limpeza é executada em três partes:

  • Remova as variáveis ​​home.dest, cabin, name, X e ticket.
  • Crie variáveis ​​de fator para pclass e sobreviveu
  • Abandone o NA
library(dplyr)
# Drop variables
clean_titanic <- titanic %>%
select(-c(home.dest, cabin, name, X, ticket)) %>% 
#Convert to factor level
	mutate(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()
glimpse(clean_titanic)

Code Explicação

  • select(-c(home.dest, cabine, nome, X, ticket)): Elimina variáveis ​​desnecessárias
  • pclass = factor(pclass, levels = c(1,2,3), labels= c('Upper', 'Middle', 'Lower')): Adiciona um rótulo à variável pclass. 1 torna-se Upper, 2 torna-se Middle e 3 torna-se Lower.
  • factor(survived, levels = c(0,1), labels = c('Não', 'Sim')): Adiciona rótulos à variável survivor. 0 torna-se Não e 1 torna-se Sim.
  • na.omit(): Remove as observações de NA

Saída:

## Observations: 1,045
## Variables: 8
## $ pclass   <fctr> Upper, Lower, Lower, Upper, Middle, Upper, Middle, U...
## $ survived <fctr> No, No, No, Yes, No, Yes, Yes, No, No, No, No, No, Y...
## $ sex      <fctr> male, male, female, female, male, male, female, male...
## $ age      <dbl> 61.0, 42.0, 39.0, 49.0, 29.0, 37.0, 20.0, 54.0, 2.0, ...
## $ sibsp    <int> 0, 0, 1, 0, 0, 1, 0, 0, 4, 0, 0, 1, 1, 0, 0, 0, 1, 1,...
## $ parch    <int> 0, 0, 5, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 2, 0, 4, 0,...
## $ fare     <dbl> 32.3208, 7.6500, 31.2750, 25.9292, 10.5000, 52.5542, ...
## $ embarked <fctr> S, S, S, S, S, S, S, S, S, C, S, S, S, Q, C, S, S, C...		

Etapa 3) Criar conjunto de treinamento/teste

Antes de treinar seu modelo, você precisa realizar duas etapas:

  • Crie um conjunto de treinamento e teste: você treina o modelo no conjunto de treinamento e testa a previsão no conjunto de teste (ou seja, dados não vistos)
  • Instale rpart.plot do console

A prática comum é dividir os dados 80/20, 80% dos dados servem para treinar o modelo e 20% para fazer previsões. Você precisa criar dois quadros de dados separados. Você não quer mexer no conjunto de testes até terminar de construir seu modelo. Você pode criar um nome de função create_train_test() que receba três argumentos.

create_train_test(df, size = 0.8, train = TRUE)
arguments:
-df: Dataset used to train the model.
-size: Size of the split. By default, 0.8. Numerical value
-train: If set to `TRUE`, the function creates the train set, otherwise the test set. Default value sets to `TRUE`. Boolean value.You need to add a Boolean parameter because R does not allow to return two data frames simultaneously.
create_train_test <- function(data, size = 0.8, train = TRUE) {
    n_row = nrow(data)
    total_row = size * n_row
    train_sample <- 1: total_row
    if (train == TRUE) {
        return (data[train_sample, ])
    } else {
        return (data[-train_sample, ])
    }
}

Code Explicação

  • function(data, size=0.8, train = TRUE): Adicione os argumentos na função
  • n_row = nrow(data): conta o número de linhas no conjunto de dados
  • total_row = size*n_row: Retorna a enésima linha para construir o conjunto de trens
  • train_sample <- 1:total_row: Selecione a primeira linha até a enésima linha
  • if (train ==TRUE){ } else { }: Se a condição for definida como verdadeira, retorne o conjunto de trem, caso contrário, o conjunto de teste.

Você pode testar sua função e verificar a dimensão.

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)

Saída:

## [1] 836   8
dim(data_test)

Saída:

## [1] 209   8

O conjunto de dados de treino tem 836 linhas e 8 colunas, enquanto o conjunto de dados de teste tem 209 linhas e as mesmas 8 colunas.

Você usa a função prop.table() combinada com table() para verificar se o processo de randomização está correto.

prop.table(table(data_train$survived))

Saída:

##
##        No       Yes 
## 0.5944976 0.4055024
prop.table(table(data_test$survived))

Saída:

## 
##        No       Yes 
## 0.5789474 0.4210526

Em ambos os conjuntos de dados, a quantidade de sobreviventes é a mesma, cerca de 40%.

Instale rpart.plot

rpart.plot não está disponível nas bibliotecas conda. Você pode instalá-lo a partir do console:

install.packages("rpart.plot")

Etapa 4) Construa o modelo

Você está pronto para construir o modelo. A sintaxe da função de árvore de decisão rpart() é:

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	

Você usa o método de classe porque prevê uma classe.

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

Code Explicação

  • rpart(): Função para ajustar o modelo. Os argumentos são:
    • sobreviveu ~.: Fórmula das Árvores de Decisão
    • dados = data_train: conjunto de dados
    • método = 'classe': ajusta um modelo binário
  • rpart.plot(fit, extra=106): Plota a árvore. O argumento extra é definido como 106, o que exibe a probabilidade da segunda classe mais a porcentagem de observações em cada nó. Você pode consultar o vinheta para obter mais informações sobre as outras opções.

Saída:

 Construa um modelo de árvores de decisão em R

Você começa no nó raiz, no topo do grafo e na profundidade 0 de 3:

  1. No topo, está a probabilidade geral de sobrevivência. Mostra a proporção de passageiros que sobreviveram ao acidente. 41 por cento dos passageiros sobreviveram.
  2. Este nó pergunta se o passageiro é do sexo masculino. Em caso afirmativo, o processo segue para o filho à esquerda da raiz (profundidade 1). 63% são homens, com uma probabilidade de sobrevivência de 21%.
  3. No segundo nó, você pergunta se o passageiro do sexo masculino tem mais de 3.5 anos. Se sim, então a chance de sobrevivência é de 19%.
  4. Você continua assim para entender quais características afetam a probabilidade de sobrevivência.

Observe que uma das muitas qualidades das árvores de decisão é que elas exigem muito pouca preparação de dados. Em particular, eles não exigem dimensionamento ou centralização de recursos.

Por padrão, a função rpart() usa o Gini A medida de impureza é usada para escolher cada divisão. Quanto maior o valor de Gini, mais misturadas estão as classes dentro daquele nó, portanto o algoritmo sempre escolhe a divisão que o reduz ao máximo.

Etapa 5) Faça uma previsão

Você pode prever seu conjunto de dados de teste. Para fazer uma previsão, você pode usar a função prever(). A sintaxe básica da previsão para a árvore de decisão R é:

predict(fitted_model, df, type = 'class')
arguments:
- fitted_model: This is the object stored after model estimation. 
- df: Data frame used to make the prediction
- type: Type of prediction			
    - 'class': for classification			
    - 'prob': to compute the probability of each class			
    - 'vector': Predict the mean response at the node level	

Agora você precisa prever, para cada um dos 209 passageiros no conjunto de teste, se o modelo espera que eles sobrevivam à colisão.

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

Code Explicação

  • prever(fit, data_test, type = 'class'): Prever a classe (0/1) do conjunto de teste

Agora compare as classes previstas com os resultados reais.

table_mat <- table(data_test$survived, predict_unseen)
table_mat

Code Explicação

  • tabela(dados_teste$sobreviveu, prever_não_visto): Constrói uma tabela de contingência das classes previstas em relação ao resultado real

Saída:

##      predict_unseen
##        No Yes
##   No  106  15
##   Yes  30  58

As linhas representam os valores reais e as colunas, as previsões. O modelo identificou corretamente 106 não sobreviventes e 58 sobreviventes, mas classificou 15 não sobreviventes como sobreviventes e 30 sobreviventes como falecidos.

Etapa 6) Medir o desempenho

Você pode calcular uma medida de precisão para tarefa de classificação com o matriz de confusão:

O matriz de confusão é a melhor escolha para avaliar o desempenho da classificação. A ideia geral é contar o número de vezes que instâncias Verdadeiras são classificadas como Falsas.

Medir o desempenho das árvores de decisão em R

Cada linha em uma matriz de confusão representa um alvo real, enquanto cada coluna representa um alvo previsto. A primeira linha desta matriz considera os passageiros que morreram (a classe negativa): 106 foram classificados corretamente como mortos (Verdadeiro negativo), enquanto 15 foram classificados erroneamente como sobreviventes (Falso positivoA segunda linha considera os sobreviventes: 58 foram corretamente identificados (Verdadeiro-positivo), enquanto 30 foram perdidos (Falso negativo).

Você pode calcular o teste de precisão da matriz de confusão:

Medir o desempenho das árvores de decisão em R

É a proporção de verdadeiros positivos e verdadeiros negativos sobre a soma da matriz. Com R, você pode codificar da seguinte forma:

accuracy_Test <- sum(diag(table_mat)) / sum(table_mat)

Code Explicação

  • sum(diag(table_mat)): Soma da diagonal
  • sum(table_mat): Soma da matriz.

Você pode imprimir a precisão do conjunto de teste:

print(paste('Accuracy for test', accuracy_Test))

Saída:

## [1] "Accuracy for test 0.784688995215311"

A precisão no conjunto de teste é de 0.7847, ou seja, 78.47%. Repita o exercício no conjunto de treinamento para verificar o quanto o modelo sofre de sobreajuste.

Etapa 7) Ajuste os hiperparâmetros

A árvore de decisão em R possui vários parâmetros que controlam aspectos do ajuste. Na biblioteca de árvore de decisão rpart, você pode controlar os parâmetros usando a função rpart.control(). No código a seguir, você introduz os parâmetros que irá ajustar. Você pode consultar o vinheta para outros parâmetros.

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 a terminal node, i.e. the leaf
-maxdepth: Set the maximum depth of any node of the final tree. The root node is treated as depth 0

Procederemos da seguinte forma:

  • Construir função para retornar precisão
  • Ajuste a profundidade máxima
  • Ajuste o número mínimo de amostras que um nó deve ter antes de poder ser dividido
  • Ajuste o número mínimo de amostras que um nó folha deve ter

Você pode escrever uma função para exibir a precisão. Você simplesmente agrupa o código que usou antes:

  1. prever: prever_unseen <- prever (ajuste, teste de dados, tipo = 'classe')
  2. Produzir tabela: table_mat <- table(data_test$survived, predict_unseen)
  3. Precisão do cálculo: precisão_Test <- sum(diag(table_mat))/sum(table_mat)
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
}

Agora, ajuste os parâmetros e veja se consegue melhorar o modelo padrão. Lembrando que você precisa superar uma precisão de 0.7847.

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)

Saída:

## [1] 0.7990431

Com o seguinte parâmetro:

minsplit = 4
minbucket = round(5/3)
maxdepth = 3
cp = 0

A precisão aumenta de 0.7847 para 0.7990, portanto a árvore otimizada supera a configuração padrão em cerca de 1.4 pontos percentuais.

Árvores de decisão em R: Guia rápido de funções

A tabela abaixo lista todas as funções usadas nas sete etapas acima, juntamente com o pacote que as fornece e os parâmetros que elas esperam. R.

Biblioteca Objetivo função Aula Parâmetros Técnicos Detalhes
parte Árvore de classificação de trens em R parte() classe fórmula, df, método
parte Treinar árvore de regressão parte() Anova fórmula, df, método
parte Trace as árvores rpart.plot() modelo ajustado
base predizer prever() classe modelo equipado, tipo
base predizer prever() prov modelo equipado, tipo
base predizer prever() vetor modelo equipado, tipo
parte parâmetros de controle rpart.control() divisão mínima Defina o número mínimo de observações no nó antes que o algoritmo execute uma divisão
minbucket Defina o número mínimo de observações em um nó terminal, ou seja, a folha.
profundidade máxima Defina a profundidade máxima de qualquer nó da árvore final. O nó raiz é tratado como tendo profundidade 0.
parte Modelo de trem com parâmetro de controle parte() fórmula, df, método, controle

Nota: Treine o modelo em dados de treinamento e teste o desempenho em um conjunto de dados não visto, ou seja, conjunto de teste.

Perguntas Frequentes

Ambas as funções permitem ajustar árvores de classificação e regressão. A função `rpart()` implementa a árvore de classificação e regressão (CART) com poda validada cruzadamente por meio de `cp` e é compatível com `rpart.plot` para gerar diagramas claros, o que a torna a opção mais comum.

O parâmetro `cp` define a melhoria mínima que uma divisão deve proporcionar para ser mantida. Valores maiores resultam em podas mais agressivas e árvores menores. Use as funções `printcp()` e `plotcp()` para encontrar o valor com o menor erro de validação cruzada.

Sim. A função `rpart()` usa divisões substitutas para direcionar as observações com preditores ausentes para o ramo mais semelhante. Este tutorial usa `na.omit()` em vez disso, simplesmente para manter o conjunto de dados de exemplo simples.

Árvores de decisão impulsionam a IA explicável em crédito, seguros e saúde, onde um órgão regulador pode exigir o raciocínio exato por trás de uma decisão. Elas também formam a base dos algoritmos de aprendizado em modelos de gradient boosting e random forest.

Sim. Os assistentes de IA podem traduzir regras de divisão para linguagem simples, sugerir valores de cp para teste e sinalizar sobreajuste na saída de printcp(). Verifique cada sugestão com seus próprios resultados de validação cruzada antes de agir de acordo com ela.

Resuma esta postagem com: