【问题标题】:Correct way to recursively create a data.frame from a tree从树递归创建data.frame的正确方法
【发布时间】:2018-02-12 02:10:16
【问题描述】:

我想从 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)
  }
}

这个函数可以正常工作,但需要我在函数之外声明一个results data.frame

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

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

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

使用&lt;- 运算符不会(results 在运行walk_tree 后计算为NULL)。

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

【问题讨论】:

  • @RonakShah 是的!愿意发布答案以便我标记它吗?
  • @RonakShah 实际上,我很好奇这是否会达到深度嵌套树的极限。我要先写几个测试。
  • 只要遵循命名约定(“name”和“parent_name”),无论树的深度如何,都应该没问题。

标签: r recursion tree


【解决方案1】:

一种方法是将所有具有“名称”和“父名称”的节点聚集在一起,并与它们一起创建一个数据框。

#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

【讨论】:

    【解决方案2】:

    你不远:),使用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)
    

    【讨论】:

    • 我仔细检查过,它对我来说运行良好。我真的不知道如何调试这个......还有其他人有这个问题吗?
    • 如果您有上述问题,请在投票前发表评论,我不能盲目修复它。
    • 我将此作为正确答案,因为它最容易理解并且带有基本的 R 解决方案。谢谢!
    【解决方案3】:
    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
    

    【讨论】:

      【解决方案4】:

      您可以使用 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相同的原理。

      【讨论】:

      • 我正在从 API 卷曲树数据,所以我不能简单地重写格式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-23
      • 1970-01-01
      • 2019-10-28
      • 2022-01-14
      • 1970-01-01
      相关资源
      最近更新 更多