r语言 - 从树递归创建 data.frame 的正确方法



我想从 R 中的一棵树创建一个平面data.frame

树由一个列表表示,每个列表都包含一个名为children的键,该键包含更多具有更多子项的列表。

tree <-
list(name="root",
parent_name='None',
children=list(
list(parent_name="root", name="child1", children=list()),
list(parent_name="root", name="child2", children=list(list(parent_name="child2", name="child3", children=c())))
)
)

我想将其"扁平化"为具有以下结构的data.frame

name parent_name
1   root        None
2 child1        root
3 child2        root
4 child3      child2

我可以使用以下递归函数来实现这一点:

walk_tree <- function(node) {
results <<- rbind(
results,
data.frame(
name=node$name,
parent_name=node$parent_name,
stringsAsFactors=FALSE
)
)
for (node in node$children) {
walk_tree(node)
}
}

这个函数工作正常,但需要我在函数外部声明一个resultsdata.frame

results <- NULL
walk_tree(tree)
results # now contains the data.frame as desired

此外,使用<<-运算符会导致在包中将walk_tree函数作为函数包含在内时出现以下警告:

Note: no visible binding for '<<-' assignment to 'results'

使用<-运算符不会(results在运行walk_tree后计算结果为NULL)。

从 R 中的树递归构建data.frame的正确方法是什么?

一种方法是将所有带有"名称"和"parent_name"的节点聚集在一起,并用它们创建一个数据帧。

#Flatten the nested structure
u_tree <- unlist(tree)
#Gather all the indices where name of the node is equal to parent_name
inds <- grepl("parent_name$", names(u_tree))
#Add them in a dataframe
data.frame(name = u_tree[!inds], parent_name = u_tree[inds])
#    name parent_name
#    root        None
#2 child1        root
#3 child2        root
#4 child3      child2

你:)不远,使用dplyr::bind_rows

walk_tree <- function(node) {
dplyr::bind_rows(
data.frame(
name=node$name,
parent_name=node$parent_name,
stringsAsFactors=FALSE),
lapply(node$children,walk_tree)
)
}
walk_tree(tree)
name parent_name
1   root        None
2 child1        root
3 child2        root
4 child3      child2

和基本 R 版本:

walk_tree <- function(node) {
do.call(
rbind,
c(
list(data.frame(
name=node$name,
parent_name=node$parent_name,
stringsAsFactors=FALSE)),
lapply(node$children,walk_tree)
))
}
walk_tree(tree)
rev(data.frame(matrix(stack(tree)[,1],,2,T)))#MHHH seems too easy for the task
X2     X1
1   None   root
2 child1   root
3 child2   root
4 child3 child2
stack(tree)%>%
mutate(new=rep(1:(n()/2),each=2),ind=rep(ind[2:1],n()/2))%>%
spread(ind,values)
new   name parent_name
1   1   None        root
2   2 child1        root
3   3 child2        root
4   4 child3      child2

您可以使用ape包中出色的树结构并以括号格式写入数据(逗号(,)表示顶点,括号表示边缘,您的叶子是"子" - 树以分号(;)结尾)。

## Reading a tree
my_tree <- "(child1, (child2, child3));"
tree <- ape::read.tree(text = my_tree)
## Getting the edge table (your flatten format)
tree$edge
#     [,1] [,2]
#[1,]    4    1
#[2,]    4    5
#[3,]    5    2
#[4,]    5    3

其中4是您的root(树中最深的顶点(叶子数 + 1))。它将"child1"连接到顶点55表示链接"child2""child3"的第一个顶点。 您可以按如下方式可视化此结构(phylo的 S3 绘图方法)

## Plotting the tree
plot(tree)
ape::nodelabels()

您可以向任何子级添加额外的结构(树),如下所示:

child1_children <- ape::read.tree(text = "(child4, (child5, child6));")
## Adding child1_children to the first leave
tree2 <- ape::bind.tree(tree, child1_children, where = 1)
## Plotting the tree
plot(tree2)
ape::nodelabels()
tree2$edge
#     [,1] [,2]
#[1,]    6    7
#[2,]    7    3
#[3,]    7    8
#[4,]    8    4
#[5,]    8    5
#[6,]    6    9
#[7,]    9    1
#[8,]    9    2

或者使用与ape::drop.tip相同的原理删除一些.

相关内容

最新更新