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))
.
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 tree
estructura -como para la impresión / trazado un solo árbol. La idea sería convertir la salida de randomForest::getTree
tal 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 tree
objeto, 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.tree
mé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$frame
representació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 ( proportional
significa que es proporcional a la desviación, uniform
significa 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 getTree
método de randomForest
devuelve 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 status
código (-1). (Nuevamente, la salida diferirá según el tipo de tarea, pero solo en las columnas status
y 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:::treeco
y tree:::text.tree
para 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.tree
funció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.