返回嵌套列表及其嵌套级别和值

8

我想使用networkD3可视化一些深度嵌套的数据,但在发送到radialNetwork之前,我无法弄清楚如何将数据格式化正确。

以下是一些示例数据:

level <- c(1, 2, 3, 4, 4, 3, 4, 4, 1, 2, 3)
value <- letters[1:11]

其中level表示嵌套的层级,value是节点的名称。通过使用这两个向量,我需要将数据转换成以下格式:

my_list <- list(
  name = "root",
  children = list(
    list(
      name = value[1], ## a
      children = list(list(
        name = value[2], ## b
        children = list(list(
          name = value[3], ## c
          children = list(
            list(name = value[4]), ## d
            list(name = value[5]) ## e
          )
        ),
        list(
          name = value[6], ## f
          children = list(
            list(name = value[7]), ## g
            list(name = value[8]) ## h
          )
        ))
      ))
    ),
    list(
      name = value[9], ## i
      children = list(list(
        name = value[10], ## j
        children = list(list(
          name = value[11] ## k
        ))
      ))
    )
  )
)

这是反编译后的对象:

> dput(my_list)
# structure(list(name = "root",
#                children = list(
#                  structure(list(
#                    name = "a",
#                    children = list(structure(
#                      list(name = "b",
#                           children = list(
#                             structure(list(
#                               name = "c", children = list(
#                                 structure(list(name = "d"), .Names = "name"),
#                                 structure(list(name = "e"), .Names = "name")
#                               )
#                             ), .Names = c("name",
#                                           "children")), structure(list(
#                                             name = "f", children = list(
#                                               structure(list(name = "g"), .Names = "name"),
#                                               structure(list(name = "h"), .Names = "name")
#                                             )
#                                           ), .Names = c("name",
#                                                         "children"))
#                           )), .Names = c("name", "children")
#                    ))
#                  ), .Names = c("name",
#                                "children")), structure(list(
#                                  name = "i", children = list(structure(
#                                    list(name = "j", children = list(structure(
#                                      list(name = "k"), .Names = "name"
#                                    ))), .Names = c("name",
#                                                    "children")
#                                  ))
#                                ), .Names = c("name", "children"))
#                )),
#           .Names = c("name",
#                      "children"))

然后我可以将其传递给最终的绘图函数:
library(networkD3)
radialNetwork(List = my_list)

输出结果将类似于以下内容:
enter image description here

问题:如何创建嵌套列表?
注意:如@zx8754所指出的,这个SO post中已经有了一个解决方案,但需要将data.frame作为输入。由于我的level不一致,我没有看到将其转换为data.frame的简单方法。

@zx8754 添加了 dput(my_list)。此外,输入数据不是 data.frame,将其转换为 data.frame 在我看来并不容易,因为级别不一致。这就是为什么我打上了 recursion 标签,并认为这可能是正确的方向。但如果我错了,请纠正我。 - Boxuan
1
我们需要一个递归函数,它可以接收数据框并在最小值处进行拆分,很抱歉目前没有时间编写代码。类似于:df1 <- data.frame(level, value, stringsAsFactors = FALSE); split(df1, cumsum(df1$level == 1)) 然后删除最小值,并在下一个最小值处进行拆分,以此类推。 - zx8754
1
我也在考虑这个问题,但不确定如何将每个子元素标记到正确的父元素。换句话说,我们如何防止将第二层级2的值标记为第一层级的父元素。 - Boxuan
2个回答

5
使用类似于data.table的合并方法:
library(data.table)
dt = data.table(idx=1:length(value), level, parent=value)

dt = dt[dt[, .(i=idx, level=level-1, child=parent)], on=.(level, idx < i), mult='last']

dt[is.na(parent), parent:= 'root'][, c('idx','level'):= NULL]

> dt
#     parent child
#  1:   root     a
#  2:      a     b
#  3:      b     c
#  4:      c     d
#  5:      c     e
#  6:      b     f
#  7:      f     g
#  8:      f     h
#  9:   root     i
# 10:      i     j
# 11:      j     k

现在我们可以使用另一篇帖子中提供的解决方案:链接
x = maketreelist(as.data.frame(dt))

> identical(x, my_list)
# [1] TRUE

太棒了,谢谢!我正在尝试理解代码,所以想问你一个问题:你的第二行代码是否类似于交叉连接,并过滤最后一行的level? - Boxuan
1
Np。第二行是根据最后匹配筛选的非等连接。请参阅https://channel9.msdn.com/Events/useR-international-R-User-conference/useR2016/Efficient-in-memory-non-equi-joins-using-datatable。 - sirallen

3
作为前言,你的数据很难处理,因为关键信息被编码在level值的顺序中。我不知道你是如何按照那个顺序获取这些值的,但请考虑是否有更好的方式来首先构建这些信息,这将使下一个任务更容易。
以下是一种将您的数据转换为具有2列parentchild的数据框的基础方法,然后将其传递到可以轻松转换为您所需的JSON格式的data.tree函数中... 然后将其传递给radialNetwork...
level <- c(1, 2, 3, 4, 4, 3, 4, 4, 1, 2, 3)
value <- letters[1:11]

library(data.tree)
library(networkD3)

parent_idx <- sapply(1:length(level), function(n) rev(which(level[1:n] < level[n]))[1])
df <- data.frame(parent = value[parent_idx], child = value, stringsAsFactors = F)
df$parent[is.na(df$parent)] <- ""

list <- ToListExplicit(FromDataFrameNetwork(df), unname = T)
radialNetwork(list)

这是一个使用 tidyverse 的方式来实现相同结果的方法...
level <- c(1, 2, 3, 4, 4, 3, 4, 4, 1, 2, 3)
value <- letters[1:11]

library(tidyverse)
library(data.tree)
library(networkD3)

data.frame(level, value, stringsAsFactors = F) %>%
  mutate(row = row_number()) %>%
  mutate(level2 = level, value2 = value) %>%
  spread(level2, value2) %>%
  mutate(`0` = "") %>%
  arrange(row) %>%
  fill(-level, -value, -row) %>%
  gather(parent_level, parent, -level, -value, -row) %>%
  filter(parent_level == level - 1) %>%
  arrange(row) %>%
  select(parent, child = value) %>%
  data.tree::FromDataFrameNetwork() %>%
  data.tree::ToListExplicit(unname = TRUE) %>%
  radialNetwork()

作为额外的福利,当前版本的networkD3(v0.4.9000)有一个新的treeNetwork函数,它接受一个带有nodeIdparentId列/变量的数据框,这消除了需要使用data.tree函数转换为JSON的必要性,所以像这样的内容就可以工作...

level <- c(1, 2, 3, 4, 4, 3, 4, 4, 1, 2, 3)
value <- letters[1:11]

library(tidyverse)
library(networkD3)

data.frame(level, value, stringsAsFactors = F) %>%
  mutate(row = row_number()) %>%
  mutate(level2 = level, value2 = value) %>%
  spread(level2, value2) %>%
  mutate(`0` = "root") %>%
  arrange(row) %>%
  fill(-level, -value, -row) %>%
  gather(parent_level, parent, -level, -value, -row) %>%
  filter(parent_level == level - 1) %>%
  arrange(row) %>%
  select(nodeId = value, parentId = parent) %>%
  rbind(data.frame(nodeId = "root", parentId = NA)) %>% 
  mutate(name = nodeId) %>% 
  treeNetwork(direction = "radial")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接