我需要构建一个算法,给定一个由 n 个因素组成的 data.frame
,返回一个树形图,其中每个节点代表一个因素的级别以及按该因素分类的行的比例该因子的级别以及上层节点的级别(例如,每个节点可以显示:factorX.levelY=30%)。
第一个节点将表示总行数,并将作为基数 (100)。树的第二层将有 k 个节点,对应于第一个因子的 k 个级别,第三层将有 k*m 个节点,其中 m 将是第二个因子的级别。等等。
用作函数输入的“data.frame”的列将以充当节点层次结构的方式排序。例如,data[,1]
将是树中的上层因子,data[,2]
等等。
以下是用作输入的 data.frame
示例:
df<-data.frame( f1=factor( rep( LETTERS[1:2], each=50)),
f2=rep( letters[1:4], each=25),
f3=rep( colors(1)[1:2], 25, each=2))
图表看起来像这样,但节点内的格式之前指出:(factorX.levelY=30%)
我注意到 rpart
包可以生成类似的图表,但函数接受的唯一输入是模型对象类型。
最佳答案
这是一种递归方法。首先,有一个函数来构建树结构,将每个分割级别的比例收集到一个命名的嵌套列表中。其次,有一个函数可以将嵌套列表转换为边缘列表以与 igraph 一起使用。最后,igraph
提供绘图功能。
## Create tree structure in nested list
makePtree <- function(data, prev=1) {
tab <- (t <- table(data[,1L]))[t>0] / nrow(data)*prev # calculate proportions at current level
ns <- sprintf("%s.%s=%.2f", names(data)[1L], names(tab), unname(c(tab))) # names
if (NCOL(data) < 2L) return( ns ) # we are done, return names only
setNames(mapply(makePtree, split(data[,-1L,drop=F], data[,1L], drop=T),
tab, SIMPLIFY = F), ns) # recurse
}
## Create edgelist from nested list for igraph::graph_from_data_frame
lst2edge <- function(lst) {
if (!is.list(lst)) return( data.frame(a=character(0), b=character(0)) )
do.call(rbind,
c(lapply(names(lst), function(x) {
if (!is.list(lst[[x]])) return( data.frame(a=x, b=lst[[x]]) )
data.frame(a=x, b=names(lst[[x]]))
}), lapply(lst, lst2edge)))
}
## Apply functions
lst <- makePtree(df) # nested list
dat <- lst2edge(lst) # edgelist
dat <- rbind(dat, data.frame(a="root", b=names(lst))) # add a root node
## Make an igraph
library(igraph)
g <- graph_from_data_frame(dat)
plot(g, layout=layout.reingold.tilford(g, root="root"))
如果您希望单独表示最终节点,您可以更改它们的名称,以便 igraph
分别指向它们。在这里,我修改了 lst2edge
函数,为最终关卡生成更长的名称。然后使用一些正则表达式来缩短它们以获得最终的数字。
## Create edgelist from nested list for igraph::graph_from_data_frame
lst2edge <- function(lst) {
if (!is.list(lst)) return( data.frame(a=character(0), b=character(0)) )
do.call(rbind,
c(lapply(names(lst), function(x) {
if (!is.list(lst[[x]])) return( data.frame(a=x, b=paste0(x, lst[[x]])) )
data.frame(a=x, b=names(lst[[x]]))
}), lapply(lst, lst2edge)))
}
## Apply functions
lst <- makePtree(df) # nested list
dat <- lst2edge(lst) # edgelist
dat <- rbind(dat, data.frame(a="root", b=names(lst))) # add a root node
## Make an igraph
g <- graph_from_data_frame(dat)
## Fix the names of the last level (they are lengthened in lst2edge
## so igraph doesn't show multiple incoming arrows to single nodes)
V(g)$name <- gsub(".*?([^\\.]+=[^=]+$)", "\\1", V(g)$name)
plot(g, layout=layout.reingold.tilford(g, root="root"),
vertex.label.dist=-0.1, vertex.label.degree=c(rep(pi/2, 7), rep(c(pi/2, 3*pi/2), 4)))
您可以使用绘图函数的 vertex.label. Degree
参数调整顶点标签的位置。
关于r - R 中的比例树形图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32033835/