2016-12-15 5 views
6

Я хотел бы визуализировать некоторые глубоко вложенные данные, используя 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 
     )) 
    )) 
    ) 
) 
) 

Вот является deparsed объект:

> 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.

+0

@ zx8754 Добавлено 'dput (my_list)'. Кроме того, входные данные не являются 'data.frame', а сделать его в' data.frame' непросто ИМО, потому что уровни несовместимы. Вот почему я отметил «рекурсию» и думаю, что это может быть направление. Однако, исправьте меня, если я ошибаюсь. – Boxuan

+1

Нам нужна рекурсивная функция, которая будет принимать данные и раскол на минимальное значение, извините, нет времени для кода на данный момент. Что-то вроде: 'df1 <- data.frame (уровень, значение, строкиAsFactors = FALSE); split (df1, cumsum (df1 $ level == 1)) 'затем удалите значения min и разделите на следующее значение min и т. д. – zx8754

+1

Я тоже об этом думал, но не был уверен, как пометить каждого ребенка на правильные родители. Другими словами, как можно предотвратить помечение значения 2-го уровня 2 первому родителю. – Boxuan

ответ

3

Использование data.table -style слияния:

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 

Теперь мы можем использовать решение от другого post:

x = maketreelist(as.data.frame(dt)) 

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

Это потрясающе. Благодаря! Я пытаюсь понять код, поэтому для вас быстрый вопрос: является ли ваша вторая строка чем-то вроде перекрестного соединения и фильтруется последней строкой уровня? – Boxuan

+1

Np. Вторая строка - это несоединенное объединение, отфильтрованное последним совпадением. См. Https://channel9.msdn.com/Events/useR-international-R-User-conference/useR2016/Efficient-in-memory-non-equi-joins-using-datatable – sirallen

1

В качестве предисловия, ваши данные трудно работать, потому что критически важной информации кодируется в порядке значений в level. Я не знаю, как вы получаете эти значения в этом порядке, но считайте, что может быть лучший способ структурировать эту информацию в первую очередь, что облегчит следующую задачу.

Вот base -y способ преобразования данных в кадр данных, с 2-мя колоннами, parent и child, переходя затем, что в data.tree функции, которые можно легко конвертировать в формат JSON вам нужно ... и затем передать его на к 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() 

и бонуса, текущая версия DEV из networkD3 (v0.4.9000) имеет новую функцию treeNetwork, которая принимает фрейм данных с nodeId и parentId столбцами/переменными, что устраняет необходимость использования fucntions 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") 
Смежные вопросы