¿Cómo trazar realmente un árbol de muestra de randomForest :: getTree ()? [cerrado]

62

Cualquiera recibió sugerencias de biblioteca o código sobre cómo trazar un par de árboles de muestra de:

getTree(rfobj, k, labelVar=TRUE)

(Sí, sé que se supone que no debes hacer esto operacionalmente, RF es una caja negra, etc., etc.) Quiero verificar visualmente el estado de un árbol para ver si alguna variable se comporta de manera no intuitiva, necesito ajustes / combinaciones / discretización / transformación, verificar qué tan bien funcionan mis factores codificados, etc.)


Preguntas anteriores sin una respuesta decente:

De hecho, quiero trazar un árbol de muestra . Así que no discutas conmigo sobre eso, ya. No estoy preguntando sobre varImpPlot(Gráfico de importancia variable) o partialPloto MDSPlot, o estos otros gráficos , ya los tengo, pero no son un sustituto para ver un árbol de muestra. Sí, puedo inspeccionar visualmente la salida de getTree(...,labelVar=TRUE).

(Supongo que una plot.rf.tree()contribución sería muy bien recibida).

smci
fuente
66
No veo la necesidad de ser preventivamente argumentativo, especialmente si está pidiendo que alguien se ofrezca como voluntario para ayudarlo; no se ve bien El CV tiene una política de etiqueta: puede leer nuestras preguntas frecuentes .
gung - Restablece a Monica
99
@gung: todas las preguntas anteriores sobre este tema se han convertido en personas que insisten en que no era necesario, y de hecho herético, trazar un árbol de muestra. Lee las citas que di. Estoy buscando un boceto aquí de cómo codificar el trazado de un árbol rf.
smci
3
Veo algunas respuestas en las que los usuarios intentan ser útiles y abordan la pregunta, junto con algunos comentarios que cuestionan la premisa de la idea (que, sinceramente, creo que también tienen un espíritu útil). Ciertamente es posible reconocer que algunas personas no estarán de acuerdo sin ser irritable.
gung - Restablece a Monica
44
Veo cero respuestas donde alguien ha trazado un árbol, en más de un año. Estoy buscando una respuesta específica a esa pregunta específica.
smci
1
Es posible trazar un solo árbol construido con cforest(en el paquete de fiesta ). De lo contrario, tendrá que convertir el data.framedevuelto randomForest::getTreeen un treeobjeto similar.
chl

Respuestas:

44

Primera (y más fácil) solución: si no está interesado en seguir con la RF clásica, como se implementó en Andy Liaw's randomForest, puede probar el paquete de fiesta que proporciona una implementación diferente del algoritmo RF original (uso de árboles condicionales y esquema de agregación basado en unidades de peso promedio). Luego, como se informó en esta publicación de R-help , puede trazar un solo miembro de la lista de árboles. Parece que funciona sin problemas, por lo que puedo decir. A continuación se muestra un diagrama de un árbol generado por cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0)).

ingrese la descripción de la imagen aquí

En segundo lugar (casi tan fácil) Solución: La mayoría de las técnicas basadas en árboles en R ( tree, rpart, TWIX, etc.) ofrece una treeestructura -como para la impresión / trazado un solo árbol. La idea sería convertir la salida de randomForest::getTreetal objeto R, incluso si no tiene sentido desde un punto de vista estadístico. Básicamente, es fácil acceder a la estructura de árbol desde un treeobjeto, como se muestra a continuación. Tenga en cuenta que diferirá ligeramente según el tipo de tarea (regresión frente a clasificación), donde en el último caso agregará probabilidades específicas de la clase como la última columna de la obj$frame(que es 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

Luego, hay métodos para imprimir y trazar bonitos esos objetos. Las funciones clave son un tree:::plot.treemétodo genérico (pongo un triple :que le permite ver el código en R directamente) confiando en tree:::treepl(visualización gráfica) y tree:::treeco(calcular las coordenadas de los nodos). Estas funciones esperan la obj$framerepresentación del árbol. Otros problemas sutiles: (1) el argumento type = c("proportional", "uniform")en el método de trazado predeterminado tree:::plot.tree, ayuda a gestionar la distancia vertical entre los nodos ( proportionalsignifica que es proporcional a la desviación, uniformsignifica que es fijo); (2) debe complementar plot(tr)con una llamada a text(tr)para agregar etiquetas de texto a los nodos y divisiones, lo que en este caso significa que también tendrá que echar un vistazo tree:::text.tree.

El getTreemétodo de randomForestdevuelve una estructura diferente, que se documenta en la ayuda en línea. A continuación se muestra una salida típica, con los nodos terminales indicados por el statuscódigo (-1). (Nuevamente, la salida diferirá según el tipo de tarea, pero solo en las columnas statusy 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

Si usted puede manejar para convertir la tabla anterior a la generada por tree, que probablemente no será capaz de personalizar tree:::treepl, tree:::treecoy tree:::text.treepara satisfacer sus necesidades, aunque no tengo un ejemplo de este enfoque. En particular, probablemente desee deshacerse del uso de desviaciones, probabilidades de clase, etc., que no son significativas en RF. Todo lo que desea es configurar coordenadas de nodos y valores divididos. Podrías usar fixInNamespace()eso, pero, para ser honesto, no estoy seguro de que este sea el camino correcto.

Tercera (y ciertamente inteligente) solución: escriba una verdadera as.treefunción auxiliar que alivie todos los "parches" anteriores. Entonces podría usar los métodos de trazado de R o, probablemente mejor, Klimt (directamente de R) para mostrar árboles individuales.

chl
fuente
40

Llego cuatro años tarde, pero si realmente quieres apegarte al randomForestpaquete (y hay algunas buenas razones para hacerlo), y realmente quieres visualizar el árbol, puedes usar el paquete reprtree .

El paquete no está muy bien documentado (puede encontrar los documentos aquí ), pero todo es bastante sencillo. Para instalar el paquete, consulte initialize.R en el repositorio, así que simplemente ejecute lo siguiente:

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

Luego adelante y haga su modelo y árbol:

library(randomForest)
library(reprtree)

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

reprtree:::plot.getTree(model)

Y ahi tienes! Hermoso y sencillo.

árbol generado a partir de plot.getTree (modelo)

Puede consultar el repositorio de Github para conocer los otros métodos del paquete. De hecho, si marca plot.getTree.R , notará que el autor usa su propia implementación de la as.tree()cual chl ♦ sugirió que podría construir su respuesta. Esto significa que puedes hacer esto:

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

Y luego, potencialmente, utilícelo realtreecon otros paquetes de trazado de árboles, como el árbol .

jgozal
fuente
Muchas gracias, todavía estoy felizmente aceptando respuestas, esta parece ser un área donde las personas están disatistified con las ofertas. Supongo que lo nuevo sería apoyar xgboosttambién.
smci
66
No hay problema. Me llevó horas encontrar la biblioteca / paquete, así que pensé que si no era útil para ti, sería para otras personas que intentan dibujar árboles mientras aún se adhieren al randomForestpaquete.
jgozal
2
Genial hallazgo. Nota: Traza el árbol representativo, en cierto sentido, el árbol del conjunto que, en promedio, es el "más cercano" a todos los demás árboles del conjunto
Chris
2
@Chris La función plot.getTree()traza un árbol individual. La función plot.reprtree()en ese paquete traza un árbol representativo.
Chun Li
1
Obtuve el modelo de Caret y quiero alimentarlo con reptree reprtree:::plot.getTree(mod_rf_1$finalModel), sin embargo, hay un "Error en data.frame (var = fr $ var, splits = as.character (gTree [," punto dividido "]): los argumentos implican diferencias número de filas: 2631, 0 "
HappyCoding
15

He creado algunas funciones para extraer las reglas de un árbol.

#**************************
#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
fuente