我在使用rpart构建分类树。我希望基于叶节点的某些标准开发自己的修剪函数。例如,如果某个叶节点在某些标准上表现不佳(在我这里是参数估计的稳定性),我想在树结构中向上查找该叶节点的父节点(即使这个节点不是终端节点)。为此,我想使用路径遍历树,我需要获取叶节点及其父节点,以便在必要时向上遍历树。
让我们看一个例子:
fit <- rpart(Kyphosis ~ Age + Number + Start, data = kyphosis)> fitn= 81 node), split, n, loss, yval, (yprob) * denotes terminal node1) root 81 17 absent (0.79012346 0.20987654) 2) Start>=8.5 62 6 absent (0.90322581 0.09677419) 4) Start>=14.5 29 0 absent (1.00000000 0.00000000) *5) Start< 14.5 33 6 absent (0.81818182 0.18181818) 10) Age< 55 12 0 absent (1.00000000 0.00000000) *11) Age>=55 21 6 absent (0.71428571 0.28571429) 22) Age>=111 14 2 absent (0.85714286 0.14285714) *23) Age< 111 7 3 present (0.42857143 0.57142857) *3) Start< 8.5 19 8 present (0.42105263 0.57894737) *
通过fit$frame,我可以获取关于叶节点的信息:
fit$frame var n wt dev yval complexity ncompete nsurrogate yval2.V1 yval2.V2 yval2.V3 yval2.V4 yval2.V51 Start 81 81 17 1 0.17647059 2 1 1.00000000 64.00000000 17.00000000 0.79012346 0.209876542 Start 62 62 6 1 0.01960784 2 2 1.00000000 56.00000000 6.00000000 0.90322581 0.096774194 <leaf> 29 29 0 1 0.01000000 0 0 1.00000000 29.00000000 0.00000000 1.00000000 0.000000005 Age 33 33 6 1 0.01960784 2 2 1.00000000 27.00000000 6.00000000 0.81818182 0.1818181810 <leaf> 12 12 0 1 0.01000000 0 0 1.00000000 12.00000000 0.00000000 1.00000000 0.0000000011 Age 21 21 6 1 0.01960784 2 0 1.00000000 15.00000000 6.00000000 0.71428571 0.2857142922 <leaf> 14 14 2 1 0.01000000 0 0 1.00000000 12.00000000 2.00000000 0.85714286 0.1428571423 <leaf> 7 7 3 2 0.01000000 0 0 2.00000000 3.00000000 4.00000000 0.42857143 0.571428573 <leaf> 19 19 8 2 0.01000000 0 0 2.00000000 8.00000000 11.00000000 0.42105263 0.57894737
我可以使用fit$where获取数据表中各行的对应叶节点:
现在我想获取叶节点的父节点。我知道path.rpart可以给我所有为了得到叶节点而进行的分裂。例如,对于叶节点23:
> path.rpart(fit, 23)node number: 23 root Start>=8.5 Start< 14.5 Age>=55 Age< 111
我想得到的是节点23的父节点的路径编号?我该如何进行这种关联?
提前感谢您。
回答:
您不需要关于树的任何信息,因为所有节点都有相同的模式。让我们拟合一个更有趣的树:
(fit <- rpart(Kyphosis ~ Age + Number + Start, data = kyphosis, cp = .0001, minsplit = 5))# n= 81 # # node), split, n, loss, yval, (yprob)# * denotes terminal node# # 1) root 81 17 absent (0.79012346 0.20987654) # 2) Start>=8.5 62 6 absent (0.90322581 0.09677419) # 4) Start>=14.5 29 0 absent (1.00000000 0.00000000) *# 5) Start< 14.5 33 6 absent (0.81818182 0.18181818) # 10) Age< 55 12 0 absent (1.00000000 0.00000000) *# 11) Age>=55 21 6 absent (0.71428571 0.28571429) # 22) Age>=98 16 2 absent (0.87500000 0.12500000) *# 23) Age< 98 5 1 present (0.20000000 0.80000000) *# 3) Start< 8.5 19 8 present (0.42105263 0.57894737) # 6) Age< 11.5 2 0 absent (1.00000000 0.00000000) *# 7) Age>=11.5 17 6 present (0.35294118 0.64705882) # 14) Start< 5.5 12 6 absent (0.50000000 0.50000000) # 28) Age>=130.5 2 0 absent (1.00000000 0.00000000) *# 29) Age< 130.5 10 4 present (0.40000000 0.60000000) # 58) Age< 93 6 2 absent (0.66666667 0.33333333) # 116) Number< 4.5 3 0 absent (1.00000000 0.00000000) *# 117) Number>=4.5 3 1 present (0.33333333 0.66666667) *# 59) Age>=93 4 0 present (0.00000000 1.00000000) *# 15) Start>=5.5 5 0 present (0.00000000 1.00000000) *
如果可能,每个节点将被分成两个,并编号为node * 2 + 0:1
,所以如果您有一个编号为5的节点,它的子节点将是5 * 2 + 0:1
。还要注意,使用这种模式,偶数编号的节点不会有子节点。
因此,给定任何节点编号,我们可以回溯找到父节点:
parent(23)# [1] 1 2 5 11 23## 同一节点的子节点应该有相同的路径identical(head(parent(28), -1), head(parent(29), -1))# [1] TRUEparent <- function(x) { if (x[1] != 1) c(Recall(if (x %% 2 == 0L) x / 2 else (x - 1) / 2), x) else x}