将数据帧转换为树结构列表列表

fel*_*nti 7 tree r list dataframe networkd3

我有一个data.frame,其中两列代表一个分层树,包含父节点和节点.

我希望以一种我可以用作函数输入的方式转换它的结构d3tree,从d3Network包中.

这是我的数据框:

df <- data.frame(c("Canada","Canada","Quebec","Quebec","Ontario","Ontario"),c("Quebec","Ontario","Montreal","Quebec City","Toronto","Ottawa"))
names(df) <- c("parent","child")
Run Code Online (Sandbox Code Playgroud)

我想将它转换为这种结构

Canada_tree <- list(name = "Canada", children = list(
                                                list(name = "Quebec", 
                children = list(list(name = "Montreal"),list(name = "Quebec City"))),
                                                 list(name = "Ontario", 
                children = list(list(name = "Toronto"),list(name = "Ottawa")))))
Run Code Online (Sandbox Code Playgroud)

我使用下面的代码成功地改变了这个特例:

fill_list <- function(df,node) node <- as.character(node)if (is.leaf(df,node)==TRUE){
    return (list(name = node))
  }
  else {
    new_node = df[df[,1] == node,2]

    return (list(name = node, children =  list(fill_list(df,new_node[1]),fill_list(df,new_node[2]))))
  }
Run Code Online (Sandbox Code Playgroud)

问题是,它只适用于每个父节点都有两个子节点的树.您可以看到我将两个子节点(new_node [1]和new_node [2])硬编码为我的递归函数的输入.

我试图找出一种方法,我可以像父节点的子节点一样多次调用递归函数.例:

fill_list(df,new_node[1]),...,fill_list(df,new_node[length(new_node)])
Run Code Online (Sandbox Code Playgroud)

我尝试了这3种可能性,但没有一种可行:

第一步:创建包含所有函数和参数的字符串,然后进行评估.它返回此错误could not find function fill_functional(df,new_node[1]).那是因为我的功能毕竟不是在我调用它的时候创建的.

fill_functional <- function(df,node) {
  node <- as.character(node)
  if (is.leaf(df,node)==TRUE){
    return (list(name = node))
  }
  else {
    new_node = df[df[,1] == node,2]
    level <- length(new_node)
    xxx <- paste0("(df,new_node[",seq(level),"])")
    lapply(xxx,function(x) eval(call(paste("fill_functional",x,sep=""))))

  }
}
Run Code Online (Sandbox Code Playgroud)

第二:使用for循环.但我只得到了我的根节点的孩子.

L <- list()
fill_list <- function(df,node) {
  node <- as.character(node)
  if (is.leaf(df,node)==TRUE){
    return (list(name = node))
  }
  else {
    new_node = df[df[,1] == node,2]

    for (i in 1:length(new_node)){
      L[i] <- (fill_list(df,new_node[i]))
    }

    return (list(name = node, children = L))
  }
}
Run Code Online (Sandbox Code Playgroud)

第三步:创建一个函数,该函数使用作为函数的元素填充列表,并且只更改参数.但我无法完成任何有趣的事情,我担心我会遇到与上述第一次尝试时相同的问题.

MrF*_*ick 9

这是一个递归定义:

maketreelist <- function(df, root = df[1, 1]) {
  if(is.factor(root)) root <- as.character(root)
  r <- list(name = root)
  children = df[df[, 1] == root, 2]
  if(is.factor(children)) children <- as.character(children)
  if(length(children) > 0) {
    r$children <- lapply(children, maketreelist, df = df)
    }
  r
  }

canadalist <- maketreelist(df)
Run Code Online (Sandbox Code Playgroud)

这产生了你想要的东西.此函数假定您传入的data.frame(或matrix)的第一列包含parent列,第二列包含child.它还有一个root参数,允许您指定起点.它将默认为列表中的第一个父级.

但如果你真的有兴趣玩树木.该igraph包可能会感兴趣

library(igraph)
g <- graph.data.frame(df)
plot(g)
Run Code Online (Sandbox Code Playgroud)

igraph中的加拿大树