Prima (e più semplice) soluzione: se non si desidera attenersi alla RF classica, come implementato in Andy Liaw's randomForest
, è possibile provare il pacchetto party che fornisce un'implementazione diversa dell'algoritmo RF ™ originale (uso di alberi condizionali e schema di aggregazione basato sulla media del peso unitario). Quindi, come riportato in questo post R-help , è possibile tracciare un singolo membro dell'elenco di alberi. Sembra funzionare senza intoppi, per quanto ne so. Di seguito è riportato un diagramma di un albero generato da cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0))
.
In secondo luogo (quasi facile) Soluzione: La maggior parte delle tecniche di tree-based in R ( tree
, rpart
, TWIX
, ecc) offre una tree
struttura -come per la stampa / plottaggio un singolo albero. L'idea sarebbe quella di convertire l'output di randomForest::getTree
un tale oggetto R, anche se non ha senso dal punto di vista statistico. Fondamentalmente, è facile accedere alla struttura ad albero da un tree
oggetto, come mostrato di seguito. Si noti che differirà leggermente a seconda del tipo di attività - regressione vs. classificazione - dove in un secondo momento aggiungerà probabilità specifiche della classe come ultima colonna di obj$frame
(che è una 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
Quindi, ci sono metodi per stampare e stampare graziosamente quegli oggetti. Le funzioni chiave sono un tree:::plot.tree
metodo generico (ho messo una tripla :
che consente di visualizzare direttamente il codice in R) basandosi su tree:::treepl
(visualizzazione grafica) e tree:::treeco
(calcolare le coordinate dei nodi). Queste funzioni prevedono la obj$frame
rappresentazione dell'albero. Altre questioni sottili: (1) l'argomento type = c("proportional", "uniform")
nel metodo di stampa predefinito tree:::plot.tree
, aiuta a gestire la distanza verticale tra i nodi ( proportional
significa che è proporzionale alla devianza, uniform
significa che è stato risolto); (2) devi integrare plot(tr)
una chiamata per text(tr)
aggiungere etichette di testo a nodi e divisioni, il che in questo caso significa che dovrai anche dare un'occhiata tree:::text.tree
.
Il getTree
metodo da randomForest
restituisce una struttura diversa, che è documentata nella guida in linea. Di seguito viene mostrato un tipico output, con i nodi terminali indicati dal status
codice (-1). (Anche in questo caso, l'output differirà in base al tipo di attività, ma solo nelle colonne status
e 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 si riesce a convertire la tabella di cui sopra a quello generato da tree
, si sarà probabilmente in grado di personalizzare tree:::treepl
, tree:::treeco
e tree:::text.tree
in base alle proprie esigenze, anche se non ho un esempio di questo approccio. In particolare, probabilmente vorrai sbarazzarti dell'uso della devianza, delle probabilità di classe, ecc. Che non sono significative in RF. Tutto quello che vuoi è impostare le coordinate dei nodi e dividere i valori. Potresti usarlo fixInNamespace()
per questo, ma, ad essere sincero, non sono sicuro che sia la strada giusta da percorrere.
Terza (e sicuramente intelligente) soluzione: Scrivi una vera as.tree
funzione di aiuto che allevierà tutte le "patch" sopra. È quindi possibile utilizzare i metodi di stampa di R o, probabilmente meglio, Klimt (direttamente da R) per visualizzare i singoli alberi.