将数据框转换为列表的树结构列表

Posted

技术标签:

【中文标题】将数据框转换为列表的树结构列表【英文标题】:Transform a dataframe into a tree structure list of lists 【发布时间】:2014-05-23 22:23:05 【问题描述】:

我有一个 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")

我想把它改造成这种结构

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")))))

我已经使用下面的代码成功地转换了这个特殊情况:

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]))))
  

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

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

fill_list(df,new_node[1]),...,fill_list(df,new_node[length(new_node)])

我尝试了这 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=""))))

  

第二:使用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))
  

第三:创建一个用函数元素填充列表的函数,并且只更改参数。但我没能完成任何有趣的事情,而且我担心我会遇到与上述第一次尝试相同的问题。

【问题讨论】:

【参考方案1】:

这是一个递归定义:

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)

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

但如果你真的有兴趣玩树。 igraph 包可能很有趣

library(igraph)
g <- graph.data.frame(df)
plot(g)

【讨论】:

如果有兴趣,请看这个new similar post。 如果我想避免在我的树中有额外的children 节点怎么办? 解决了我自己的问题,使用 purr 包我将函数更改为返回 flatten(r)

以上是关于将数据框转换为列表的树结构列表的主要内容,如果未能解决你的问题,请参考以下文章

Pyspark 将列表列转换为嵌套结构列

Java通用树结构数据管理

如何将嵌套的json结构转换为数据框

如何将分层列表排序为字典的树/pyrimid 模型?

R列表(结构(列表()))到数据框

复杂的树数据结构