Arvore de Decisão em R

Classificação e Aplicação na Base de Baseball (MLB)

Gabriel Stankevix
17 min readOct 15, 2019

Introdução

Durante a nossa infância, pelo menos na minha geração, tínhamos o costume de brincar de “Advinha” ou “Questionário”. Neste jogo, alguém pensaria em alguma objeto, pessoa famosa, pais, fruta, comida etc. Os demais amiguinhos fariam uma serie de perguntas ate adivinhar o que havia pensado. O jogo seria desta maneira:

João: “Estou pensando em um animal.”

Maria: “O animal é carnívoro?”

João: “Não”

Maria: “O animal é simbolo de alguma marca?”

João: “Sim”

Maria: “Esta marca fabrica chocolate?”

João: “Sim”

Maria: “Animal produz Leite?”

João: “Sim!”

Maria: “Então o animal é uma vaca!”

João: “Acertou!”

Esta publicação tem como fundamento uma explicação base sobre a parte conceitual e funcionamento dos algoritmos mais conhecidos para aplicação em cenários reais de mercado.

O que é uma Arvore de Decisão?

Uma Árvore de Decisão é um modelo preditivo de aprendizado supervisionado que utiliza um conjunto de regras binárias (0,1) para calcular um Target/Alvo.

Sua aplicação serve para classificação (variável-alvo categórica) ou para regressão (variável-alvo contínua), funcionando para variáveis ​​de entrada e saída categóricas e contínuas. Suas aplicações em cenários reais incluem: Modelos de pontuação de crédito para clientes, estudos de marketing para comportamento de mercados, diagnóstico de condições médicas, classificação de clientes etc

Estrutura de uma Arvore de Decisão

Podemos notar que as arvores são um tipo de grafo conexo e acíclico existindo um caminho entre quaisquer dois de seus vértices, portanto, sua estrutura é dividia em partes:

  • Nó Raiz: O nó que executa a primeira divisão. Esta divisão pode dividir o nó em dois ou mais sub-nós
  • Quando um sub-nó se divide em outros sub-nós, denominamos como nó de decisão .
  • Quando removemos sub-nós de um nó de decisão, esse processo é chamado de Poda . O oposto da poda é a divisão .
  • Nós / Folhas do Terminal: nós que preveem o resultado ( não se dividem).
  • Para todo nó dividido em sub-nós, é chamado de nó pai de seus sub-nós, consequentemente seus sub-nos são chamados filhos.
  • Ramos: setas conectando nós, mostrando o fluxo da pergunta para a resposta.

Todo nó raiz é o ponto de partida da árvore e os nós raiz/terminal contêm as perguntas ou critérios que desejamos obter a resposta.

Tipos de Arvore de Decisão

Árvores de regressão

O algoritmo para modelos de árvore de decisão funciona particionando repetidamente os dados em vários subespaços, de forma que os resultados em cada subespaço final sejam o mais homogêneos possível. Essa abordagem é tecnicamente chamada de particionamento recursivo. O resultado obtido consiste em um conjunto de regras usadas para prever a variável de resultado, continua para árvores de regressão e categórica para árvores de classificação.

Árvores de classificação

Uma árvore de classificação é muito semelhante a uma árvore de regressão, exceto que é usada para prever uma resposta qualitativa em vez de quantitativa.

Para uma árvore de classificação, prevemos que cada observação pertence à classe de observações de treinamento mais comum na região à qual pertence. Portanto quando interpretamos os resultados de uma árvore de classificação, muitas vezes estaremos interessados não só na previsão de classe correspondente a região de nó terminal, mas também nas proporções de classe entre as observações de treinamento que pertencem aquela região.

Entropia

Quando o algoritmos toma a decisão de dividir uma árvore, haverá uma definição de pureza em relação ao seu target. A entropia é definida como uma forma de mensurar a pureza de cada subconjunto de uma determinada árvore decisão. Basicamente mede a probabilidade de obter uma ocorrência de evento positivo ( 0 a 1), a partir de uma seleção aleatória do subconjunto de dados.

A entropia esta sempre relacionada ao ganho informacional, que é baseado na redução da entropia. Quando construímos uma arvore de decisão, quanto mais homogêneo for os ramos da arvore,menor a entropia, que resulta em maior ganho de informação (chance de acontecer algo novo/diferente é muito baixa).

Overfitting

Existem casos onde uma árvore de decisão pode ficar com uma quantidade de arestas muito grande, aumentando a sua complexidade. Isso pode gerar um problema conhecido como overfitting. Para resolver este tipo de situação existem métodos de “poda” das árvores de decisão.

Vantagens das árvores de decisão

  • É bastante interpretável e fácil de entender.
  • Também pode ser usado para identificar as variáveis ​​mais significativas no seu conjunto de dados
  • Ajudar na tomada de decisão

Arvore de Decisão em R — Classificação

Durante um período do ano é discutido nos EUA sobre a eleição para o Hall da Fama do Beisebol. A votação sempre envolve uma serie de regras e decidi utilizar a famosa base Lahman, para ver como uma árvore de decisão simples funcionaria como classificador e o que poderíamos aprender com isso.

Relembrando, uma arvore de decisão é um modelo que particiona recursivamente um conjunto de dados em subconjuntos cada vez mais “puros”, em relação a alguma variável de resposta binária ou categórica. Nossa variável de resposta binária será: “O jogador foi eleito para o Hall da Fama por meio do BBWAA?”

Utilizaremos a base HallOfFame para sumarizar os jogadores com indicações e nomeações para o HallOfFame criando um índice de performance.

Analise das Bases de dados que utilizaremos:

Hall Of Fame

str(HallOfFame)
'data.frame': 4120 obs. of 9 variables:
$ playerID : chr "cobbty01" "ruthba01" "wagneho01" "mathech01" ...
$ yearID : int 1936 1936 1936 1936 1936 1936 1936 1936 1936 1936 ...
$ votedBy : chr "BBWAA" "BBWAA" "BBWAA" "BBWAA" ...
$ ballots : int 226 226 226 226 226 226 226 226 226 226 ...
$ needed : int 170 170 170 170 170 170 170 170 170 170 ...
$ votes : int 222 215 215 205 189 146 133 111 105 80 ...
$ inducted : Factor w/ 2 levels "N","Y": 2 2 2 2 2 1 1 1 1 1 ...
$ category : Factor w/ 4 levels "Manager","Pioneer/Executive",..: 3 3 3 3 3 3 3 3 3 3 ...
$ needed_note: chr NA NA NA NA ...

Batting

> str(Batting)
'data.frame': 101332 obs. of 22 variables:
$ playerID: chr "abercda01" "addybo01" "allisar01" "allisdo01" ...
$ yearID : int 1871 1871 1871 1871 1871 1871 1871 1871 1871 1871 ...
$ stint : int 1 1 1 1 1 1 1 1 1 1 ...
$ teamID : Factor w/ 149 levels "ALT","ANA","ARI",..: 136 111 39 142 111 56 111 24 56 24 ...
$ lgID : Factor w/ 7 levels "AA","AL","FL",..: 4 4 4 4 4 4 4 4 4 4 ...
$ G : int 1 25 29 27 25 12 1 31 1 18 ...
$ AB : int 4 118 137 133 120 49 4 157 5 86 ...
$ R : int 0 30 28 28 29 9 0 66 1 13 ...
$ H : int 0 32 40 44 39 11 1 63 1 13 ...
$ X2B : int 0 6 4 10 11 2 0 10 1 2 ...
$ X3B : int 0 0 5 2 3 1 0 9 0 1 ...
$ HR : int 0 0 0 2 0 0 0 0 0 0 ...
$ RBI : int 0 13 19 27 16 5 2 34 1 11 ...
$ SB : int 0 8 3 1 6 0 0 11 0 1 ...
$ CS : int 0 1 1 1 2 1 0 6 0 0 ...
$ BB : int 0 4 2 0 2 0 1 13 0 0 ...
$ SO : int 0 0 5 2 1 1 0 1 0 0 ...
$ IBB : int NA NA NA NA NA NA NA NA NA NA ...
$ HBP : int NA NA NA NA NA NA NA NA NA NA ...
$ SH : int NA NA NA NA NA NA NA NA NA NA ...
$ SF : int NA NA NA NA NA NA NA NA NA NA ...
$ GIDP : int NA NA NA NA NA NA NA NA NA NA ...

Pitching

> str(Pitching)
'data.frame': 44139 obs. of 30 variables:
$ playerID: chr "bechtge01" "brainas01" "fergubo01" "fishech01" ...
$ yearID : int 1871 1871 1871 1871 1871 1871 1871 1871 1871 1871 ...
$ stint : int 1 1 1 1 1 1 1 1 1 1 ...
$ teamID : Factor w/ 149 levels "ALT","ANA","ARI",..: 97 142 90 111 90 136 111 56 97 136 ...
$ lgID : Factor w/ 7 levels "AA","AL","FL",..: 4 4 4 4 4 4 4 4 4 4 ...
$ W : int 1 12 0 4 0 0 0 6 18 12 ...
$ L : int 2 15 0 16 1 0 1 11 5 15 ...
$ G : int 3 30 1 24 1 1 3 19 25 29 ...
$ GS : int 3 30 0 24 1 0 1 19 25 29 ...
$ CG : int 2 30 0 22 1 0 1 19 25 28 ...
$ SHO : int 0 0 0 1 0 0 0 1 0 0 ...
$ SV : int 0 0 0 0 0 0 0 0 0 0 ...
$ IPouts : int 78 792 3 639 27 3 39 507 666 747 ...
$ H : int 43 361 8 295 20 1 20 261 285 430 ...
$ ER : int 23 132 3 103 10 0 5 97 113 153 ...
$ HR : int 0 4 0 3 0 0 0 5 3 4 ...
$ BB : int 11 37 0 31 3 0 3 21 40 75 ...
$ SO : int 1 13 0 15 0 0 1 17 15 12 ...
$ BAOpp : num NA NA NA NA NA NA NA NA NA NA ...
$ ERA : num 7.96 4.5 27 4.35 10 0 3.46 5.17 4.58 5.53 ...
$ IBB : int NA NA NA NA NA NA NA NA NA NA ...
$ WP : int NA NA NA NA NA NA NA NA NA NA ...
$ HBP : int NA NA NA NA NA NA NA NA NA NA ...
$ BK : int 0 0 0 0 0 0 0 2 0 0 ...
$ BFP : int NA NA NA NA NA NA NA NA NA NA ...
$ GF : int NA NA NA NA NA NA NA NA NA NA ...
$ R : int 42 292 9 257 21 0 30 243 223 362 ...
$ SH : int NA NA NA NA NA NA NA NA NA NA ...
$ SF : int NA NA NA NA NA NA NA NA NA NA ...
$ GIDP : int NA NA NA NA NA NA NA NA NA NA ...

AwardsPlayers

> str(AwardsPlayers)
'data.frame': 6078 obs. of 6 variables:
$ playerID: chr "bondto01" "hinespa01" "heckegu01" "radboch01" ...
$ awardID : chr "Pitching Triple Crown" "Triple Crown" "Pitching Triple Crown" "Pitching Triple Crown" ...
$ yearID : int 1877 1878 1884 1884 1887 1888 1889 1894 1894 1901 ...
$ lgID : Factor w/ 4 levels "AA","AL","ML",..: 4 4 1 4 1 4 4 4 4 2 ...
$ tie : chr NA NA NA NA ...
$ notes : chr NA NA NA NA ...

Agora, podemos iniciarmos os desenvolvimentos.

library(caTools)
library(dplyr)
library(rpart)
library(rpart.plot)
require(Lahman)
require(maptree)
require(rpart)
indicados<- HallOfFame %>%
group_by(playerID) %>%
filter(votedBy %in% c("BBWAA", "Special Election") & category == "Player") %>%
summarise(anosEmVotacao = n(), indicado = sum(inducted == "Y"), melhor = max(votes/ballots)) %>%
arrange(desc(melhor))

head(indicados,5)

Para treinar nosso modelo, precisamos de um conjunto de variáveis exploratórias com os índices de performance durante os jogos dos candidatos. Neste caso, incluiremos apenas duas estatísticas para rebatedores(batting) :

  • Hits (H)
  • Home runs (HR)
rebatedores <-
Batting %>%
group_by(playerID) %>%
summarise(numTemporadas = length(unique(yearID)), ultimaTemporada = max(yearID), somaH = sum(H), somaHR = sum(HR)) %>%
arrange(desc(somaH))
head(rebatedores,5)

Para arremessadores(pitching), incluiremos as seguintes estatísticas:

  • Wins(W)
  • Shutout (SO)
  • Save (SV)
arremessadores <- Pitching %>%
group_by(playerID) %>%
summarise(numTemporadas = length(unique(yearID)), ultimaTemporada = max(yearID),
somaW = sum(W), somaSO = sum(SO), somaSV = sum(SV)) %>%
arrange(desc(somaW))
head(arremessadores,5)

Não menos importante, incluiremos as principais premiações individuais da MLB (MVP, Cy Young e Gold Glove)

premios <- AwardsPlayers %>%
group_by(playerID) %>%
summarise(mvp = sum(awardID == "Most Valuable Player"),
gg = sum(awardID == "Gold Glove"),
cyya = sum(awardID == "Cy Young Award"))
head(premios,5)

Desta forma criamos quatro conjuntos de dados e precisaremos criar um cruzamento com todas essas informações. Lembrando que nesta analise queremos apenas jogadores que já foram nomeados candidatos para o Hall Da Fama portanto devemos unir os dados de acordo com essa regra.

candidatos <- merge(x=rebatedores, y=arremessadores, by="playerID", all=TRUE)
candidatos <- merge(x=candidatos, y=premios, by="playerID", all.x=TRUE)
candidatos <- merge(x=candidatos, y=indicados, by="playerID")
head(candidatos,5)

Como podemos notar, a nossa base possui uma serie de NAs, para solucionar este problema substituiremos os dados por 0 para que possamos executar o algoritmos sem maiores problemas.

candidatos[is.na(candidatos)] <- 0

Criando o Modelo

Para criar a árvores de classificação em R podemos utilizar o pacote rpart. A função rpart criará a árvore de decisão com uma expressão de entrada e a base de dados que utilizaremos para criar o modelo.

model <- rpart(as.factor(indicado) ~ somaH + somaHR + mvp + somaW + somaSO + somaSV + gg+ cyya, data=candidatos)

Com o nosso modelo criado, podemos analisar como ficou a disposição da nossa arvore e os respectivos graus de entropia.

prp(model)

Vemos que a variável com menor entropia (a que gera a primeira divisão nos dados) é a variável somaH que representa a soma todos os Hits executados pelo atleta ao longo de sua carreira. Caso a resposta para a pergunta “Você acumulou mais de 2578 hits em sua carreira?” seja sim/yes, então seguimos para a próxima pergunta, “Você já acumulou mais de 2993 hits em sua carreira?” Se a resposta for sim/yes, então este atleta esta no Hall da Fama.

Podemos também avaliar o outro lado da arvore, onde caso a reposta para a primeira pergunta seja Não, então avaliamos se a soma total de Wins de cada atleta durante a carreira foi maior que 249. Caso a resposta seja sim e o soma total de Shutout for maior que 2490, este atleta estará no Hall da fama.

Desta forma, é possível analisar todas as classes criadas na nossa arvore.

Seguindo o comando abaixo, podemos analisar a quantidade de observações por nó folha existem no modelo criado.

rpart.plot(model,type = 3)

Mas podemos notar que a árvore obtida esta pouco complicada de interpretar e visualizar, podemos definir que esta arvore esta overfitted (muito ajustada).
Neste caso, como evitar o excesso de ajuste? Alterando o tamanho do parâmetro minbucket, minsplit ou maxdepth.

Um dos benefícios de trabalhar com árvores de decisão, é que podemos interromper o treinamento com base em vários limites. Por exemplo, dada uma árvore de decisão que divide os dados em dois nós de 40 e 4. Provavelmente, 4 é um número muito pequeno para ter como nó terminal.

A opção minbucket fornece o menor número de observações permitidas em um nó terminal. Em processo divisão de nós, os dados em um nó estiver com menos do que o minbucket definido, então não será aceito.

O parâmetro minsplit é o menor número de observações em que o nó pai pode ser dividido. O padrão é 20. Se você tiver menos de 20 registros em um nó pai, ele será rotulado como um nó terminal.

Finalmente, o parâmetro maxdepth impede que a árvore cresça além de uma certa profundidade / altura sendo o valor padrão como 30 .

Decidi por controlar o parâmetro de profundidade da arvore conforme o código abaixo. Esta definição fica a critério de cada analista, de acordo com a sua interpretação do problema.

model <- rpart(as.factor(indicado) ~ somaH + somaHR + mvp + somaW + somaSO + somaSV + gg+ cyya, data=candidatos, maxdepth = 5)prp(model)
rpart.plot(model, type = 3)

Como podemos notar, a maioria dos atletas não estão no Hall Da Fama da MLB, pois os agrupamentos onde existem atletas pertencentes a este grupo chega ao máximo de 2% de representatividade (por classe) para toda a base. Portanto existe uma chance bem remota do atleta entrar o Hall da Fama, desde que ele possua ótimos rendimentos a longo da sua carreira, o que demanda muitos anos para acumular tais números relacionados a quantidade de Hits, Home Runs e Shutouts. Não é surpreendente ver que os prêmios como Cy Young não seja considerado como significativo no modelo por conta da relação tempo x carreira.

Predição com Arvore de Classificação

Agora vamos tentar prever os atletas que foram nomeados para o Hall Da Fama usando todas as variáveis que temos disponíveis no nosso modelo.

Dividimos os dados em dois conjunto de treinamento e teste.

set.seed(123)
split = sample.split(candidatos$indicado, SplitRatio = 0.7)
train = subset(candidatos, split==TRUE)
nrow(train)
test = subset(candidatos, split==FALSE)
nrow(test)

Os dados de treinamento são um subconjunto dos dados de Candidatos, onde a divisão é VERDADEIRA. E os dados de teste são o subconjunto dos dados de Candidatos, onde a divisão é FALSE.

model_pred = rpart(as.factor(indicado) ~ somaH + somaHR + mvp + gg + somaW + somaSO + somaSV + cyya, 
data=train,maxdepth = 5)
prp(tree)
rpart.plot(model_pred,type = 3)

Utilizando a função summary, é possível entrar no detalhe do modelo previsto para analisar o grau de importância de cada variável e as proporções para cada classe.

> summary(model_pred)
Call:
rpart(formula = as.factor(indicado) ~ somaH + somaHR + mvp +
gg + somaW + somaSO + somaSV + cyya, data = train, maxdepth = 5)
n= 779
CP nsplit rel error xerror xstd
1 0.15662651 0 1.0000000 1.0000000 0.10375209
2 0.07228916 1 0.8433735 0.9156627 0.09977865
3 0.06024096 2 0.7710843 0.9036145 0.09919051
4 0.04819277 4 0.6506024 0.9036145 0.09919051
5 0.01000000 7 0.5060241 0.8554217 0.09678298
Variable importance
somaH somaSO somaW mvp somaHR cyya gg somaSV
36 22 13 10 9 5 3 1
Node number 1: 779 observations, complexity param=0.1566265
predicted class=0 expected loss=0.1065469 P(node) =1
class counts: 696 83
probabilities: 0.893 0.107
left son=2 (726 obs) right son=3 (53 obs)
Primary splits:
somaH < 2602 to the left, improve=30.29461, (0 missing)
mvp < 0.5 to the left, improve=24.85874, (0 missing)
somaHR < 402.5 to the left, improve=21.97757, (0 missing)
somaSO < 2206.5 to the left, improve=16.49767, (0 missing)
somaW < 249 to the left, improve=15.30574, (0 missing)
Surrogate splits:
somaHR < 474 to the left, agree=0.937, adj=0.075, (0 split)
Node number 2: 726 observations, complexity param=0.07228916
predicted class=0 expected loss=0.06887052 P(node) =0.9319641
class counts: 676 50
probabilities: 0.931 0.069
left son=4 (692 obs) right son=5 (34 obs)
Primary splits:
somaSO < 2206.5 to the left, improve=19.243520, (0 missing)
somaW < 249 to the left, improve=17.636230, (0 missing)
mvp < 1.5 to the left, improve=10.289770, (0 missing)
cyya < 1.5 to the left, improve= 9.683577, (0 missing)
somaHR < 474 to the left, improve= 8.783919, (0 missing)
Surrogate splits:
somaW < 263 to the left, agree=0.972, adj=0.412, (0 split)
cyya < 1.5 to the left, agree=0.963, adj=0.206, (0 split)
gg < 13 to the left, agree=0.956, adj=0.059, (0 split)
Node number 3: 53 observations, complexity param=0.04819277
predicted class=1 expected loss=0.3773585 P(node) =0.06803594
class counts: 20 33
probabilities: 0.377 0.623
left son=6 (37 obs) right son=7 (16 obs)
Primary splits:
somaH < 3021.5 to the left, improve=4.5441740, (0 missing)
somaHR < 83.5 to the left, improve=4.4033350, (0 missing)
mvp < 0.5 to the left, improve=2.5062350, (0 missing)
gg < 4 to the left, improve=0.8870268, (0 missing)
somaSO < 1.5 to the left, improve=0.1354740, (0 missing)
Node number 4: 692 observations, complexity param=0.04819277
predicted class=0 expected loss=0.0433526 P(node) =0.8883184
class counts: 662 30
probabilities: 0.957 0.043
left son=8 (680 obs) right son=9 (12 obs)
Primary splits:
mvp < 1.5 to the left, improve=9.489040, (0 missing)
somaHR < 474 to the left, improve=9.366310, (0 missing)
somaH < 2085 to the left, improve=3.759138, (0 missing)
gg < 7.5 to the left, improve=1.533544, (0 missing)
somaW < 223.5 to the left, improve=1.398844, (0 missing)
Surrogate splits:
gg < 9.5 to the left, agree=0.986, adj=0.167, (0 split)
Node number 5: 34 observations, complexity param=0.06024096
predicted class=1 expected loss=0.4117647 P(node) =0.0436457
class counts: 14 20
probabilities: 0.412 0.588
left son=10 (24 obs) right son=11 (10 obs)
Primary splits:
somaHR < 4.5 to the left, improve=4.803922, (0 missing)
somaH < 21.5 to the left, improve=3.442017, (0 missing)
somaW < 284.5 to the left, improve=3.442017, (0 missing)
somaSO < 3116.5 to the left, improve=3.348059, (0 missing)
somaSV < 24 to the left, improve=2.989107, (0 missing)
Surrogate splits:
somaH < 113 to the left, agree=0.941, adj=0.8, (0 split)
somaW < 354.5 to the left, agree=0.824, adj=0.4, (0 split)
somaSV < 24 to the left, agree=0.794, adj=0.3, (0 split)
gg < 8 to the left, agree=0.735, adj=0.1, (0 split)
Node number 6: 37 observations, complexity param=0.04819277
predicted class=0 expected loss=0.4864865 P(node) =0.04749679
class counts: 19 18
probabilities: 0.514 0.486
left son=12 (22 obs) right son=13 (15 obs)
Primary splits:
somaHR < 296.5 to the left, improve=3.0743650, (0 missing)
mvp < 0.5 to the left, improve=1.6980250, (0 missing)
somaH < 2869.5 to the left, improve=0.7032697, (0 missing)
somaSO < 0.5 to the left, improve=0.3916589, (0 missing)
gg < 1.5 to the left, improve=0.1134706, (0 missing)
Surrogate splits:
mvp < 0.5 to the left, agree=0.730, adj=0.333, (0 split)
somaH < 2858 to the left, agree=0.649, adj=0.133, (0 split)
gg < 2.5 to the left, agree=0.649, adj=0.133, (0 split)
Node number 7: 16 observations
predicted class=1 expected loss=0.0625 P(node) =0.02053915
class counts: 1 15
probabilities: 0.062 0.938
Node number 8: 680 observations
predicted class=0 expected loss=0.03235294 P(node) =0.872914
class counts: 658 22
probabilities: 0.968 0.032
Node number 9: 12 observations
predicted class=1 expected loss=0.3333333 P(node) =0.01540436
class counts: 4 8
probabilities: 0.333 0.667
Node number 10: 24 observations, complexity param=0.06024096
predicted class=0 expected loss=0.4166667 P(node) =0.03080873
class counts: 14 10
probabilities: 0.583 0.417
left son=20 (16 obs) right son=21 (8 obs)
Primary splits:
somaSO < 3135 to the left, improve=5.0416670, (0 missing)
cyya < 1.5 to the left, improve=3.8347340, (0 missing)
somaW < 285 to the left, improve=2.6666670, (0 missing)
somaSV < 1.5 to the left, improve=0.6666667, (0 missing)
Surrogate splits:
somaW < 285 to the left, agree=0.917, adj=0.750, (0 split)
cyya < 1.5 to the left, agree=0.792, adj=0.375, (0 split)
Node number 11: 10 observations
predicted class=1 expected loss=0 P(node) =0.01283697
class counts: 0 10
probabilities: 0.000 1.000
Node number 12: 22 observations
predicted class=0 expected loss=0.3181818 P(node) =0.02824134
class counts: 15 7
probabilities: 0.682 0.318
Node number 13: 15 observations
predicted class=1 expected loss=0.2666667 P(node) =0.01925546
class counts: 4 11
probabilities: 0.267 0.733
Node number 20: 16 observations
predicted class=0 expected loss=0.1875 P(node) =0.02053915
class counts: 13 3
probabilities: 0.812 0.187
Node number 21: 8 observations
predicted class=1 expected loss=0.125 P(node) =0.01026958
class counts: 1 7
probabilities: 0.125 0.875

Vamos agora medir o desempenho do modelo no conjunto de testes.

predictTrain <- predict(model_pred,type="class", newdata = test)
table(test$indicado,predictTrain)
predictTrain
0 1
0 296 2
1 18 17

Portanto, a acurácia do modelo é (296 + 17) / (296+17+18+2) = 94%! Nada mal, né?

Resultados

  • As variáveis Hits, Home Run são realmente importantes
  • O premio de MVP é significativamente importante em caso de baixa performance nas outras variáveis
  • A variável ShutOuts aparece duas vezes, isso quer dizer que, de alguma forma esta variável é não linear. Pois se o valor for maior que um montante ou menor, atua de maneira distinta.
  • Apesar da alta acurácia, a variável recall tem o valor de 50%. Necessário ajustes no modelo.

Conclusão

Neste artigo, pude discorrer e compartilhar um pouco sobre o conhecimento arvores de decisão. Espero que ajude a criar novos insights e auxiliar na resolução de problemas do dia a dia.

Em breve pretendo criar um post sobre casos de uso com random forests.

--

--

Gabriel Stankevix

Cientista da Computação, flautista, postcrosser e DIY. Data Science for the win.