Як насправді побудувати зразкове дерево з randomForest :: getTree ()? [зачинено]


62

Кожен отримав бібліотечні чи кодові пропозиції щодо того, як насправді побудувати пару зразкових дерев :

getTree(rfobj, k, labelVar=TRUE)

(Так, я знаю, що ви не повинні цього робити оперативно, РФ - це чорна скринька тощо) як добре працюють кодовані фактори тощо)


Попередні запитання без гідної відповіді:

Я насправді хочу побудувати зразкове дерево . Тому вже не сперечайтесь зі мною. Я не запитую про varImpPlot(Змінна значення важливості) або, partialPlotабо MDSPlot, або інші ці сюжети , у мене вже є, але вони не є заміною для перегляду зразкового дерева. Так, я можу візуально перевірити вихід getTree(...,labelVar=TRUE).

(Я думаю, що plot.rf.tree()внесок був би дуже добре сприйнятий.)


6
Я не бачу необхідності бути превентивно аргументованою, особливо якщо ви просите, щоб хтось зголосився допомогти вам; це не трапляється добре. Резюме має етикетну політику - ви можете прочитати наші поширені запитання .
gung - Відновіть Моніку

9
@gung: кожне попереднє запитання на цю тему занепадало до того, що люди наполягають на тому, що не потрібно було, і справді єретично, будувати зразкове дерево. Прочитайте цитати, які я дав. Я шукаю тут ескіз того, як кодувати побудову графічного дерева.
smci

3
Я бачу деякі відповіді, коли користувачі намагаються бути корисними і вирішують питання, а також деякі коментарі, що ставлять під сумнів ідею (які, я, чесно кажучи, також призначені в корисному дусі). Безумовно, можна визнати, що деякі люди не погоджуються без тестування.
gung - Відновити Моніку

4
Я бачу нульові відповіді, де хто-небудь коли-небудь насадив дерево, за рік. Я шукаю конкретну відповідь на це конкретне питання.
smci

1
Можна побудувати одне дерево, побудоване з cforest(у пакеті партії ). В іншому випадку вам доведеться перетворити data.frameповернутий randomForest::getTreeоб'єкт у tree-подібний.
chl

Відповіді:


44

Перше (і найпростіше) рішення: Якщо ви не прагнете дотримуватися класичної RF, як це реалізовано в Енді Ляу randomForest, ви можете спробувати партійний пакет, який забезпечує іншу реалізацію оригінального алгоритму RF (використання умовних дерев та на основі схеми агрегації на основі на одиницю середньої ваги). Тоді, як повідомляється в цьому довідці про R-довідку , ви зможете побудувати одного члена списку дерев. Наче це працює безперебійно, наскільки я можу сказати. Нижче представлена ​​ділянка одного дерева, породженого cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0)).

введіть тут опис зображення

По- друге (майже так само легко) Рішення: Більшість методів дерева на основі в R ( tree, rpart, TWIXі т.д.) пропонує tree-like структуру для друку / прокладки одного дерева. Ідея полягала б у перетворенні результату randomForest::getTreeна такий R-об'єкт, навіть якщо він є безглуздим із статистичної точки зору. В основному, легко отримати доступ до структури дерева з treeоб'єкта, як показано нижче. Зверніть увагу, що вона дещо відрізнятиметься в залежності від типу завдання - регресія та класифікація - де в пізнішому випадку вона додасть імовірності, характерні для класу, як останній стовпець obj$frame(який є а 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

Потім існують методи гарного друку та побудови графіків цих об'єктів. Ключові функції - це загальний tree:::plot.treeметод (я поклав потрійний, :що дозволяє вам переглядати код в R), спираючись на tree:::treepl(графічний дисплей) та tree:::treeco(обчислити координати вузлів). Ці функції очікують obj$frameподання дерева. Інші тонкі питання: (1) аргумент type = c("proportional", "uniform")у методі побудови графіку за замовчуванням,, tree:::plot.treeдопомагає керувати вертикальною відстані між вузлами ( proportionalозначає, що вона пропорційна відхиленню, uniformозначає, що вона фіксована); (2) вам потрібно доповнити plot(tr)дзвінок, щоб text(tr)додати текстові мітки до вузлів та розщеплень, що в цьому випадку означає, що вам також доведеться поглянути tree:::text.tree.

getTreeМетод з randomForestповернень іншої структури, яка описана в інтерактивній довідці. Типовий висновок показаний нижче, кінцеві вузли позначені statusкодом (-1). (Знову ж таки, результат буде відрізнятися залежно від типу завдання, але лише для стовпців statusта 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

Якщо вам вдасться перетворити таблицю вище в одній генеруються tree, ви, ймовірно , будете мати можливість налаштувати tree:::treepl, tree:::treecoі tree:::text.treeвідповідно з вашими потребами, хоча я не приклад такого підходу. Зокрема, ви, мабуть, хочете позбутися від використання відхилень, ймовірностей класів тощо, які не мають сенсу в РФ. Все, що вам потрібно, це встановити координати вузлів та розділити значення. Ви можете використати fixInNamespace()для цього, але, чесно кажучи, я не впевнений, що це правильний шлях.

Третє (і, безумовно, розумне) рішення: Напишіть справжню as.treeфункцію помічника, яка полегшить усі перераховані вище "патчі". Потім можна використовувати методи побудови R, або, можливо, краще, Клімт (безпосередньо з R) для відображення окремих дерев.


40

Мені запізнюється чотири роки, але якщо ви дійсно хочете дотримуватися randomForestпакету (і є кілька вагомих причин для цього), і хочете насправді візуалізувати дерево, ви можете скористатись пакетом репрі .

Пакет не дуже добре задокументований (ви можете знайти тут документи ), але все досить просто. Щоб встановити пакет, зверніться до ініціалізації.R у репо, просто виконайте наступне:

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

Тоді вперед і зробіть свою модель та дерево:

library(randomForest)
library(reprtree)

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

reprtree:::plot.getTree(model)

І ось ти йдеш! Красиво і просто.

дерево, створене з plot.getTree (модель)

Ви можете перевірити github repo, щоб дізнатися про інші способи в упаковці. Насправді, якщо ви перевірите plot.getTree.R , ви помітите, що автор використовує власну реалізацію, as.tree()яку chl ♦ припустив, що ви могли побудувати себе у своїй відповіді. Це означає, що ви могли це зробити:

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

А потім потенційно використовувати realtreeз іншими пакетами для побудови графіків дерева, такими як дерево .


Велике спасибі, я все ще із задоволенням приймаю відповіді, це здається сферою, де люди невдоволені пропозиціями. Я думаю, що новим було б також підтримати xgboost.
smci

6
нема проблем. Щоб знайти бібліотеку / пакет, мені знадобилося кілька годин, тож я зрозумів, що якщо вам це не стане в нагоді, це буде іншим людям, які намагаються намалювати дерева, все ще дотримуючись randomForestпакунку.
jgozal

2
Класна знахідка. Примітка: вона накреслює репрезентативне дерево, в деякому сенсі дерево в ансамблі, яке в середньому є "найближчим" до всіх інших дерев в ансамблі
Кріс

2
@Chris Функція побудує plot.getTree()окреме дерево. Функція plot.reprtree()в цьому пакеті побудує представницьке дерево.
Чун Лі

1
я отримав модель від caret і хочу подати у reptree reprtree:::plot.getTree(mod_rf_1$finalModel), однак, є "Помилка data.frame (var = fr $ var, splits = as.character (gTree [," точка розколу "])): аргументи передбачають різницю кількість рядків: 2631, 0 "
HappyCoding

15

Я створив деякі функції для вилучення правил дерева.

#**************************
#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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.