วิธีการพล็อตแผนภูมิตัวอย่างจาก randomForest :: getTree () จริง ๆ ได้อย่างไร? [ปิด]


62

ทุกคนมีคำแนะนำห้องสมุดหรือรหัสเกี่ยวกับวิธีการพล็อตตัวอย่างต้นไม้สองสามต้นจาก:

getTree(rfobj, k, labelVar=TRUE)

(ใช่ฉันรู้ว่าคุณไม่ควรทำสิ่งนี้ในเชิงปฏิบัติ RF เป็นกล่องดำ ฯลฯ ฯลฯ ฉันต้องการให้มีสติตรวจสายตาต้นไม้เพื่อดูว่าตัวแปรใดที่ทำงานผิดพลาดหรือไม่จำเป็นต้องปรับแต่ง / รวม / แยก / แปลง / ตรวจสอบ ปัจจัยที่เข้ารหัสของฉันทำงานได้ดีเพียงใด ฯลฯ )


คำถามก่อนหน้าโดยไม่มีคำตอบที่ดี:

ที่จริงผมอยากจะพล็อตต้นไม้ตัวอย่าง ดังนั้นอย่าเถียงกับฉันเกี่ยวกับเรื่องนี้อยู่แล้ว ฉันไม่ได้ถามเกี่ยวกับvarImpPlot(Variance Importance Plot) หรือpartialPlotหรือMDSPlotหรือแปลงอื่น ๆ เหล่านี้ฉันมีอยู่แล้ว แต่พวกมันไม่ได้ใช้แทนการดูต้นไม้ตัวอย่าง getTree(...,labelVar=TRUE)ใช่ครับผมสายตาสามารถตรวจสอบการส่งออกของ

(ฉันเดาว่าplot.rf.tree()ผลงานจะได้รับการตอบรับดีมาก)


6
ฉันไม่เห็นว่าจำเป็นที่จะต้องมีการโต้แย้งโดยเฉพาะอย่างยิ่งโดยเฉพาะอย่างยิ่งถ้าคุณกำลังขอให้ใครบางคนเป็นอาสาสมัครที่จะช่วยคุณ มันไม่เจอกัน CV มีนโยบายมารยาท - การที่คุณอาจต้องการที่จะอ่านของเราคำถามที่พบบ่อย
gung - Reinstate Monica

9
@ gung: ทุกคำถามก่อนหน้านี้ในหัวข้อนี้ได้สลายไปในคนยืนยันว่ามันไม่จำเป็นและแน่นอนนอกรีตเพื่อพล็อตต้นไม้ตัวอย่าง อ่านการอ้างอิงที่ฉันให้ ฉันกำลังมองหาร่างที่นี่ของวิธีการรหัสการวางแผนต้นไม้ rf
smci

3
ฉันเห็นคำตอบบางอย่างที่ผู้ใช้พยายามที่จะให้ความช่วยเหลือ & ตอบคำถามพร้อมด้วยความคิดเห็นบางส่วนที่ตั้งคำถามเกี่ยวกับแนวคิด (ซึ่งฉันเชื่อโดยสุจริตว่ามีเจตนาที่เป็นประโยชน์เช่นกัน) แน่นอนว่าเป็นไปได้ที่จะยอมรับว่าบางคนจะไม่เห็นด้วยกับการเป็นพยาน
gung - Reinstate Monica

4
ฉันเห็นคำตอบเป็นศูนย์ที่ไม่มีใครได้วางแผนต้นไม้ไว้ในหนึ่งปี ฉันกำลังหาคำตอบเฉพาะสำหรับคำถามนั้น
smci

1
เป็นไปได้ที่จะพล็อตต้นไม้เดียวที่สร้างด้วยcforest(ในแพ็คเกจปาร์ตี้ ) มิฉะนั้นคุณจะต้องแปลงวัตถุที่data.frameส่งคืนโดยrandomForest::getTreeให้treeเหมือนวัตถุ
chl

คำตอบ:


44

วิธีแก้ปัญหาแรก (และง่ายที่สุด):หากคุณไม่กระตือรือร้นที่จะใช้ RF แบบคลาสสิกดังที่นำมาใช้ใน Andy Liaw's randomForestคุณสามารถลองแพ็คเกจปาร์ตี้ซึ่งนำเสนอการใช้งานที่แตกต่างกันของอัลกอริทึมRF ดั้งเดิม(ใช้ต้นไม้ตามเงื่อนไข ในหน่วยน้ำหนักเฉลี่ย) จากนั้นตามที่รายงานไว้ในโพสต์ความช่วยเหลือ Rนี้คุณสามารถลงจุดสมาชิกรายชื่อต้นไม้ได้ ดูเหมือนว่าจะทำงานได้อย่างราบรื่นเท่าที่ฉันสามารถบอกได้ cforest(Species ~ ., data=iris, controls=cforest_control(mtry=2, mincriterion=0))ด้านล่างเป็นพล็อตของต้นไม้ต้นหนึ่งที่สร้างขึ้นโดย

ป้อนคำอธิบายรูปภาพที่นี่

ประการที่สอง (เกือบเป็นเรื่องง่าย) วิธีการ:ที่สุดของเทคนิคที่มีต้นไม้ขึ้นอยู่ใน R ( tree, rpart, TWIXฯลฯ ) มีtreeเหมือนโครงสร้างสำหรับการพิมพ์ / วางแผนต้นไม้เดียว ความคิดจะแปลงผลลัพธ์ของ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เพื่อให้เหมาะกับความต้องการของคุณ แต่ฉันไม่ได้มีตัวอย่างของวิธีการนี้ โดยเฉพาะอย่างยิ่งคุณอาจต้องการกำจัดการใช้ความเบี่ยงเบนความน่าจะเป็นในชั้นเรียน ฯลฯ ซึ่งไม่มีความหมายใน RF สิ่งที่คุณต้องการคือการตั้งค่าพิกัดโหนดและค่าแยก คุณสามารถใช้fixInNamespace()สำหรับสิ่งนั้น แต่เพื่อความซื่อสัตย์ฉันไม่แน่ใจว่านี่เป็นวิธีที่เหมาะสมที่จะไป

โซลูชันที่สาม (และฉลาดอย่างแน่นอน):เขียนas.treeฟังก์ชันตัวช่วยจริงซึ่งจะช่วยบรรเทา "แพทช์" ด้านบนทั้งหมด จากนั้นคุณสามารถใช้วิธีการวางแผนของ R หรืออาจจะดีกว่าKlimt (โดยตรงจาก R) เพื่อแสดงต้นไม้แต่ละต้น


40

ฉันล่าช้าสี่ปี แต่ถ้าคุณต้องการยึดติดกับrandomForestแพคเกจ (และมีเหตุผลที่ดีที่จะทำเช่นนั้น) และต้องการเห็นภาพต้นไม้จริงๆคุณสามารถใช้แพ็กเกจreprtree

แพคเกจไม่ใช่เอกสารที่ดีมาก (คุณสามารถหาเอกสารได้ที่นี่ ) แต่ทุกอย่างค่อนข้างตรงไปตรงมา ในการติดตั้งแพคเกจอ้างอิงถึงinitialize.Rใน repo ดังนั้นให้เรียกใช้สิ่งต่อไปนี้:

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

คุณสามารถตรวจสอบ repo GitHub เพื่อเรียนรู้เกี่ยวกับวิธีการอื่น ๆ ในแพคเกจ ในความเป็นจริงถ้าคุณตรวจสอบ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
ค้นหาเด็ด ๆ หมายเหตุ: มันแปลงต้นไม้ตัวแทนในบางแง่มุมต้นไม้ในชุดซึ่งโดยเฉลี่ย "ใกล้" กับต้นไม้อื่น ๆ ในชุด
Chris

2
@Chris ฟังก์ชั่นplot.getTree()แปลงต้นไม้แต่ละต้น ฟังก์ชั่นplot.reprtree()ในแพคเกจนั้นจะทำการแผนผังทรี
Chun Li

1
ฉันได้โมเดลจากคาเร็ตและต้องการป้อนเข้าไปใน 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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.