Como realmente plotar uma árvore de amostra de randomForest :: getTree ()? [fechadas]

62

Alguém recebeu sugestões de bibliotecas ou códigos sobre como realmente plotar algumas árvores de amostra de:

getTree(rfobj, k, labelVar=TRUE)

(Sim, eu sei que você não deve fazer isso operacionalmente, a RF é uma caixa preta, etc. etc. Quero verificar visualmente uma árvore para verificar se alguma variável está se comportando de maneira contra-intuitiva, precisa de ajustes / combinação / discretização / transformação, verifique quão bem meus fatores codificados estão funcionando etc.)


Perguntas anteriores sem uma resposta decente:

Na verdade, eu quero plotar uma árvore de amostra . Portanto, não discuta comigo sobre isso, já. Não estou perguntando sobre varImpPlot(Gráfico de Importância Variável) ou partialPlotou MDSPlot, ou esses outros gráficos , eu já os tenho, mas eles não substituem a visualização de uma árvore de amostra. Sim, posso inspecionar visualmente a saída de getTree(...,labelVar=TRUE).

(Eu acho que uma plot.rf.tree()contribuição seria muito bem recebida.)

smci
fonte
6
Não vejo necessidade de ser preventivamente argumentativo, especialmente se você está pedindo para alguém ser voluntário para ajudá-lo; não parece bem. O CV tem uma política de etiqueta - você pode ler nossas Perguntas frequentes .
gung - Restabelece Monica
9
@gung: todas as perguntas anteriores sobre esse tópico se deterioraram e as pessoas insistiram que não era necessário, e de fato herético, plotar uma árvore de amostra. Leia as citações que dei. Estou procurando um esboço aqui de como codificar a plotagem de uma árvore de rf.
SMCI
3
Vejo algumas respostas em que os usuários estão tentando ser úteis e abordam a questão, juntamente com alguns comentários questionando a premissa da ideia (que, sinceramente, também pretendo ser útil). Certamente é possível reconhecer que algumas pessoas discordam de não serem irritáveis.
gung - Restabelece Monica
4
Não vejo respostas onde alguém já tenha tramado uma árvore há mais de um ano. Estou procurando uma resposta específica para essa pergunta específica.
SMCI
11
É possível plotar uma única árvore construída com cforest(no pacote de festa ). Caso contrário, você precisará converter o data.frameretornado por randomForest::getTreeem um treeobjeto semelhante.
quer

Respostas:

44

Primeira (e mais fácil) solução: se você não gosta de usar a RF clássica, como implementado no Andy Liaw randomForest, pode experimentar o pacote de terceiros que fornece uma implementação diferente do algoritmo RF original (uso de árvores condicionais e esquema de agregação baseado em unidades de peso médio). Então, conforme relatado neste post de ajuda R , você pode plotar um único membro da lista de árvores. Parece funcionar sem problemas, até onde eu sei. Abaixo está um gráfico de uma árvore gerada por cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0)).

insira a descrição da imagem aqui

Segundo (quase tão fácil) solução: A maioria das técnicas baseadas em árvores em R ( tree, rpart, TWIX, etc.) oferece uma treeestrutura -como para impressão / plotagem uma única árvore. A idéia seria converter a saída de randomForest::getTreeum objeto desse tipo R, mesmo que isso não faça sentido do ponto de vista estatístico. Basicamente, é fácil acessar a estrutura em árvore a partir de um treeobjeto, como mostrado abaixo. Observe que ela diferirá um pouco, dependendo do tipo de tarefa - regressão versus classificação - onde, no caso posterior, adicionará probabilidades específicas de classe como a última coluna da obj$frame(que é a data.frame).

> library(tree)
> tr <- tree(Species ~ ., data=iris)
> tr
node), split, n, deviance, yval, (yprob)
      * denotes terminal node

 1) root 150 329.600 setosa ( 0.33333 0.33333 0.33333 )  
   2) Petal.Length < 2.45 50   0.000 setosa ( 1.00000 0.00000 0.00000 ) *
   3) Petal.Length > 2.45 100 138.600 versicolor ( 0.00000 0.50000 0.50000 )  
     6) Petal.Width < 1.75 54  33.320 versicolor ( 0.00000 0.90741 0.09259 )  
      12) Petal.Length < 4.95 48   9.721 versicolor ( 0.00000 0.97917 0.02083 )  
        24) Sepal.Length < 5.15 5   5.004 versicolor ( 0.00000 0.80000 0.20000 ) *
        25) Sepal.Length > 5.15 43   0.000 versicolor ( 0.00000 1.00000 0.00000 ) *
      13) Petal.Length > 4.95 6   7.638 virginica ( 0.00000 0.33333 0.66667 ) *
     7) Petal.Width > 1.75 46   9.635 virginica ( 0.00000 0.02174 0.97826 )  
      14) Petal.Length < 4.95 6   5.407 virginica ( 0.00000 0.16667 0.83333 ) *
      15) Petal.Length > 4.95 40   0.000 virginica ( 0.00000 0.00000 1.00000 ) *
> tr$frame
            var   n        dev       yval splits.cutleft splits.cutright yprob.setosa yprob.versicolor yprob.virginica
1  Petal.Length 150 329.583687     setosa          <2.45           >2.45   0.33333333       0.33333333      0.33333333
2        <leaf>  50   0.000000     setosa                                  1.00000000       0.00000000      0.00000000
3   Petal.Width 100 138.629436 versicolor          <1.75           >1.75   0.00000000       0.50000000      0.50000000
6  Petal.Length  54  33.317509 versicolor          <4.95           >4.95   0.00000000       0.90740741      0.09259259
12 Sepal.Length  48   9.721422 versicolor          <5.15           >5.15   0.00000000       0.97916667      0.02083333
24       <leaf>   5   5.004024 versicolor                                  0.00000000       0.80000000      0.20000000
25       <leaf>  43   0.000000 versicolor                                  0.00000000       1.00000000      0.00000000
13       <leaf>   6   7.638170  virginica                                  0.00000000       0.33333333      0.66666667
7  Petal.Length  46   9.635384  virginica          <4.95           >4.95   0.00000000       0.02173913      0.97826087
14       <leaf>   6   5.406735  virginica                                  0.00000000       0.16666667      0.83333333
15       <leaf>  40   0.000000  virginica                                  0.00000000       0.00000000      1.00000000

Depois, existem métodos para imprimir e plotar esses objetos de maneira bonita. As funções das teclas são um tree:::plot.treemétodo genérico (coloquei um triplo :que permite visualizar o código em R diretamente) contando com tree:::treepl(exibição gráfica) e tree:::treeco(coordenadas dos nós de cálculo). Essas funções esperam a obj$framerepresentação da árvore. Outras questões sutis: (1) o argumento type = c("proportional", "uniform")no método de plotagem padrão tree:::plot.treeajuda a gerenciar a distância vertical entre os nós ( proportionalsignifica que é proporcional ao desvio, uniformsignifica que é fixo); (2) você precisa complementar plot(tr)com uma chamada para text(tr)adicionar rótulos de texto a nós e divisões, o que, neste caso, significa que você também precisará dar uma olhada tree:::text.tree.

O getTreemétodo from randomForestretorna uma estrutura diferente, documentada na ajuda online. Uma saída típica é mostrada abaixo, com os nós do terminal indicados pelo statuscódigo (-1). (Novamente, a saída será diferente dependendo do tipo de tarefa, mas apenas nas colunas statuse prediction.)

> library(randomForest)
> rf <- randomForest(Species ~ ., data=iris)
> getTree(rf, 1, labelVar=TRUE)
   left daughter right daughter    split var split point status prediction
1              2              3 Petal.Length        4.75      1       <NA>
2              4              5 Sepal.Length        5.45      1       <NA>
3              6              7  Sepal.Width        3.15      1       <NA>
4              8              9  Petal.Width        0.80      1       <NA>
5             10             11  Sepal.Width        3.60      1       <NA>
6              0              0         <NA>        0.00     -1  virginica
7             12             13  Petal.Width        1.90      1       <NA>
8              0              0         <NA>        0.00     -1     setosa
9             14             15  Petal.Width        1.55      1       <NA>
10             0              0         <NA>        0.00     -1 versicolor
11             0              0         <NA>        0.00     -1     setosa
12            16             17 Petal.Length        5.40      1       <NA>
13             0              0         <NA>        0.00     -1  virginica
14             0              0         <NA>        0.00     -1 versicolor
15             0              0         <NA>        0.00     -1  virginica
16             0              0         <NA>        0.00     -1 versicolor
17             0              0         <NA>        0.00     -1  virginica

Se você pode conseguir converter a tabela acima para o gerado por tree, provavelmente você vai ser capaz de personalizar tree:::treepl, tree:::treecoe tree:::text.treepara atender às suas necessidades, embora eu não tenho um exemplo desta abordagem. Em particular, você provavelmente deseja se livrar do uso de desvio, probabilidades de classe etc. que não são significativas na RF. Tudo o que você deseja é configurar as coordenadas dos nós e dividir os valores. Você pode usar fixInNamespace()isso, mas, para ser sincero, não tenho certeza se esse é o caminho certo a seguir.

Terceira (e certamente inteligente) solução: Escreva uma verdadeira as.treefunção auxiliar que alivie todos os "patches" acima. Você poderia usar os métodos de plotagem de R ou, provavelmente melhor, Klimt (diretamente de R) para exibir árvores individuais.

chl
fonte
40

Estou com quatro anos de atraso, mas se você realmente deseja manter o randomForestpacote (e existem algumas boas razões para fazê-lo) e realmente deseja visualizar a árvore, pode usar o pacote reprtree .

O pacote não está super bem documentado (você pode encontrar os documentos aqui ), mas tudo é bem direto. Para instalar o pacote, consulte initialize.R no repositório, então simplesmente execute o seguinte:

options(repos='http://cran.rstudio.org')
have.packages <- installed.packages()
cran.packages <- c('devtools','plotrix','randomForest','tree')
to.install <- setdiff(cran.packages, have.packages[,1])
if(length(to.install)>0) install.packages(to.install)

library(devtools)
if(!('reprtree' %in% installed.packages())){
  install_github('araastat/reprtree')
}
for(p in c(cran.packages, 'reprtree')) eval(substitute(library(pkg), list(pkg=p)))

Então vá em frente e faça seu modelo e árvore:

library(randomForest)
library(reprtree)

model <- randomForest(Species ~ ., data=iris, importance=TRUE, ntree=500, mtry = 2, do.trace=100)

reprtree:::plot.getTree(model)

E lá vai você! Bonito e simples.

árvore gerada a partir de plot.getTree (modelo)

Você pode verificar o repositório do github para aprender sobre os outros métodos no pacote. De fato, se você verificar plot.getTree.R , perceberá que o autor usa sua própria implementação, da as.tree()qual chl ♦ sugeriu que você pudesse construir sua resposta. Isso significa que você pode fazer isso:

tree <- getTree(model, k=1, labelVar=TRUE)
realtree <- reprtree:::as.tree(tree, model)

E, potencialmente, use realtreecom outros pacotes de plotagem de árvores, como a árvore .

jgozal
fonte
Muito obrigado, continuo aceitando com satisfação respostas, esta parece ser uma área em que as pessoas são desatistificadas com as ofertas. Eu acho que a nova novidade seria apoiar xgboosttambém.
smci 22/10
6
sem problemas. Levei horas para encontrar a biblioteca / pacote, então imaginei que, se não lhe fosse útil, seria para outras pessoas que tentavam desenhar árvores enquanto ainda grudavam no randomForestpacote.
jgozal
2
Descoberta legal. Nota: Plota a árvore representativa, em certo sentido, a árvore do conjunto que é, em média, a "mais próxima" de todas as outras árvores do conjunto.
Chris
2
@ Chris A função plot.getTree()plota uma árvore individual. A função plot.reprtree()nesse pacote plota uma árvore representativa.
Chun Li
11
Eu tenho modelo de caret e deseja alimentar reptree com reprtree:::plot.getTree(mod_rf_1$finalModel), no entanto, há um "Erro no data.frame (var = fr $ var, divide = as.character (gTree [," ponto de divisão "])),: argumentos implicam diferenças número de linhas: 2631, 0 "
HappyCoding
15

Eu criei algumas funções para extrair as regras de uma árvore.

#**************************
#return the rules of a tree
#**************************
getConds<-function(tree){
  #store all conditions into a list
  conds<-list()
  #start by the terminal nodes and find previous conditions
  id.leafs<-which(tree$status==-1)
	  j<-0
	  for(i in id.leafs){
		j<-j+1
		prevConds<-prevCond(tree,i)
		conds[[j]]<-prevConds$cond
		while(prevConds$id>1){
		  prevConds<-prevCond(tree,prevConds$id)
		  conds[[j]]<-paste(conds[[j]]," & ",prevConds$cond)
        }
		if(prevConds$id==1){
			conds[[j]]<-paste(conds[[j]]," => ",tree$prediction[i])
    }
    }

  }

  return(conds)
}

#**************************
#find the previous conditions in the tree
#**************************
prevCond<-function(tree,i){
  if(i %in% tree$right_daughter){
		id<-which(tree$right_daughter==i)
		cond<-paste(tree$split_var[id],">",tree$split_point[id])
	  }
	  if(i %in% tree$left_daughter){
    id<-which(tree$left_daughter==i)
		cond<-paste(tree$split_var[id],"<",tree$split_point[id])
  }

  return(list(cond=cond,id=id))
}

#remove spaces in a word
collapse<-function(x){
  x<-sub(" ","_",x)

  return(x)
}


data(iris)
require(randomForest)
mod.rf <- randomForest(Species ~ ., data=iris)
tree<-getTree(mod.rf, k=1, labelVar=TRUE)
#rename the name of the column
colnames(tree)<-sapply(colnames(tree),collapse)
rules<-getConds(tree)
print(rules)
Dalpozz
fonte