我对 gbm 包如何计算重要性做了一些研究,它基于 ErrorReduction,它包含在结果的 trees 元素中,可以通过 pretty.gbm.trees() 访问。通过对每个变量的所有树求此 ErrorReduction 的总和来获得相对影响。对于多类问题,模型中实际上有 n.trees*num.classes 树。因此,如果有 3 个类,您可以计算每三棵树上每个变量的 ErrorReduction 的总和,以获得一个类的重要性。我编写了以下函数来实现这一点,然后绘制结果:
按类获取变量重要性
RelInf_ByClass <- function(object, n.trees, n.classes, Scale = TRUE){
library(dplyr)
library(purrr)
library(gbm)
Ext_ErrRed<- function(ptree){
ErrRed <- ptree %>% filter(SplitVar != -1) %>% group_by(SplitVar) %>%
summarise(Sum_ErrRed = sum(ErrorReduction))
}
trees_ErrRed <- map(1:n.trees, ~pretty.gbm.tree(object, .)) %>%
map(Ext_ErrRed)
trees_by_class <- split(trees_ErrRed, rep(1:n.classes, n.trees/n.classes)) %>%
map(~bind_rows(.) %>% group_by(SplitVar) %>%
summarise(rel_inf = sum(Sum_ErrRed)))
varnames <- data.frame(Num = 0:(length(object$var.names)-1),
Name = object$var.names)
classnames <- data.frame(Num = 1:object$num.classes,
Name = object$classes)
out <- trees_by_class %>% bind_rows(.id = "Class") %>%
mutate(Class = classnames$Name[match(Class,classnames$Num)],
SplitVar = varnames$Name[match(SplitVar,varnames$Num)]) %>%
group_by(Class)
if(Scale == FALSE){
return(out)
} else {
out <- out %>% mutate(Scaled_inf = rel_inf/max(rel_inf)*100)
}
}
按类绘制变量重要性
在我的实际用途中,我有 40 多个特征,因此我提供了一个选项来指定要绘制的特征数量。如果我想为每个类单独排序图,我也不能使用分面,这就是我使用gridExtra 的原因。
plot_imp_byclass <- function(df, n) {
library(ggplot2)
library(gridExtra)
plot_imp_class <- function(df){
df %>% arrange(rel_inf) %>%
mutate(SplitVar = factor(SplitVar, levels = .$SplitVar)) %>%
ggplot(aes(SplitVar, rel_inf))+
geom_segment(aes(x = SplitVar,
xend = SplitVar,
y = 0,
yend = rel_inf))+
geom_point(size=3, col = "cyan") +
coord_flip()+
labs(title = df$Class[[1]], x = "Variable", y = "Importance")+
theme_classic()+
theme(plot.title = element_text(hjust = 0.5))
}
df %>% top_n(n, rel_inf) %>% split(.$Class) %>%
map(plot_imp_class) %>% map(ggplotGrob) %>%
{grid.arrange(grobs = .)}
}
试试看
gbm_iris <- gbm(Species~., data = iris)
imp_byclass <- RelInf_ByClass(gbm_iris, length(gbm_iris$trees),
gbm_iris$num.classes, Scale = F)
plot_imp_byclass(imp_byclass, 4)
如果对所有类的结果求和,似乎给出的结果与内置的 relative.influence 函数相同。
relative.influence(gbm_iris)
# n.trees not given. Using 100 trees.
# Sepal.Length Sepal.Width Petal.Length Petal.Width
# 0.00000 51.88684 2226.88017 868.71085
imp_byclass %>% group_by(SplitVar) %>% summarise(Overall_rel_inf = sum(rel_inf))
# A tibble: 3 x 2
# SplitVar Overall_rel_inf
# <fct> <dbl>
# 1 Petal.Length 2227.
# 2 Petal.Width 869.
# 3 Sepal.Width 51.9