R中的比例树图
Proportions tree graph in R
我需要建立一个算法,给定一个 data.frame
由 n 个因素组成,returns 一个树图,其中每个节点代表一个因素的水平和按以下分类的行的比例该因素的水平和上层节点的水平(例如,每个节点可以显示: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
参数调整顶点标签的位置。
我需要建立一个算法,给定一个 data.frame
由 n 个因素组成,returns 一个树图,其中每个节点代表一个因素的水平和按以下分类的行的比例该因素的水平和上层节点的水平(例如,每个节点可以显示: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
参数调整顶点标签的位置。