Première solution (et la plus simple): si vous ne souhaitez pas vous en tenir à la RF classique, telle qu'implémentée dans Andy Liaw randomForest
, vous pouvez essayer le paquet party qui fournit une implémentation différente de l' algorithme RF ™ original (utilisation d'arbres conditionnels et de schémas d'agrégation). sur le poids moyen des unités). Ensuite, comme indiqué dans cette publication R-help , vous pouvez tracer un seul membre de la liste des arbres. Il semble que tout se passe bien, pour autant que je sache. Ci-dessous, un graphique d’un arbre généré par cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0))
.
Deuxième (presque aussi facile) solution: La plupart des techniques à base d' arbres dans R ( tree
, rpart
, TWIX
, etc.) offre une tree
structure de -comme pour l' impression / traçage d' un seul arbre. L'idée serait de convertir la sortie de randomForest::getTree
en un tel objet R, même si cela n'a aucun sens d'un point de vue statistique. En gros, il est facile d’accéder à l’arborescence à partir d’un tree
objet, comme indiqué ci-dessous. Veuillez noter que cela diffère légèrement en fonction du type de tâche (régression ou classification). Dans ce dernier cas, les probabilités spécifiques à la classe seront ajoutées à la dernière colonne de obj$frame
(qui est 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
Ensuite, il existe des méthodes permettant d’imprimer et de tracer ces objets. Les fonctions de touche sont une tree:::plot.tree
méthode générique (je mets un triple :
qui permet de visualiser directement le code en R) en s'appuyant sur tree:::treepl
(affichage graphique) et tree:::treeco
(calculer les coordonnées des nœuds). Ces fonctions attendent la obj$frame
représentation de l'arbre. Autres problèmes subtils: (1) l’argument type = c("proportional", "uniform")
dans la méthode de tracé par défaut tree:::plot.tree
, aide à gérer la distance verticale entre les nœuds ( proportional
signifie qu’il est proportionnel à la déviance, uniform
qu’il est fixe); (2) vous devez compléter plot(tr)
par un appel à text(tr)
pour ajouter des étiquettes de texte aux noeuds et aux divisions, ce qui signifie dans ce cas que vous devrez également jeter un coup d'œil tree:::text.tree
.
La getTree
méthode de randomForest
renvoie une structure différente, documentée dans l'aide en ligne. Une sortie typique est illustrée ci-dessous, avec les nœuds terminaux indiqués par le status
code (-1). (Là encore, le résultat sera différent selon le type de tâche, mais uniquement sur les colonnes status
et 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 vous parvenez à convertir le tableau ci-dessus en celui généré par tree
, vous pourrez probablement le personnaliser tree:::treepl
, tree:::treeco
et tree:::text.tree
pour l'adapter à vos besoins, bien que je ne possède pas d'exemple de cette approche. En particulier, vous voudrez probablement vous débarrasser de l'utilisation de la déviance, des probabilités de classe, etc., qui n'ont pas de sens dans RF. Tout ce que vous voulez, c'est définir les coordonnées des nœuds et les valeurs fractionnées. Vous pourriez utiliser fixInNamespace()
pour cela, mais, pour être honnête, je ne suis pas sûr que ce soit la bonne façon de faire.
Troisième solution (et certainement astucieuse): écrivez une vraie as.tree
fonction d’assistance qui atténuera tous les "correctifs" ci-dessus. Vous pouvez ensuite utiliser les méthodes de traçage de R ou, probablement mieux, Klimt (directement à partir de R) pour afficher des arbres individuels.