2016-09-18 57 views
2

处理API时会出现很多问题。大多数时候,为了做真正的分析,我想让我的数据集整齐,但通常情况下,这需要针对每种类型树的解决方案,而不是更通用的解决方案。整齐的嵌套json树

我想这将是不错的生成整齐的数据(尽管有许多不同的因子水平一吨NA在深度嵌套树的一个功能。

我有如下,用unlist(..., recursive = FALSE)一个hackish的解决方案+命名约定,

但我想看看是否有人在这里可能有一个更好的解决方案来整理这些类型的列表结构。

##################### 
# Some Test Data 
aNestedTree = 
    list(a = 1, 
     b = 2, 
     c = list(
     a = list(1:5), 
     b = 2, 
     c = list(
      a = 1, 
      d = 3, 
      e = list())), 
     d = list(
     y = 3, 
     z = 2 
     )) 

############################################################ 
# Run through the list and rename all list elements, 
# We unlist once at time, adding "__" at each unlist step 
# until the object is no longer a list 

renameVars <- function(lst, sep = '__') { 
    if(is.list(lst)) { 
    names(lst) <- paste0(names(lst),sep) 
    renameVars(unlist(lst, recursive = FALSE),sep = sep) 
    } else { 
    lst 
    } 
} 

res <- renameVars(aNestedTree) 

我们可以检查输出,看看我们有一个奇怪命名对象, 但有一种方法来这个疯狂。

> res 
    a________  b________ c__.a____1__ c__.a____2__ c__.a____3__ 
      1    2    1    2    3 
c__.a____4__ c__.a____5__ c__.b______ c__.c__.a____ c__.c__.d____ 
      4    5    2    1    3 
    d__.y______ d__.z______ 
      3    2 

现在我把它放在data.table,所以我可以塑造它。

library(data.table) 
dt <- data.table(values = res, name = names(res)) 

# Use some regex to split that name up, along with data.table's tstrsplit 
# function to separate them into as many columns as there are nests 

> dt[,paste0('V',seq_along(s <- tstrsplit(dt$name,'[__]+(\\.|)'))) := s] 
> dt 
    values   name V1 V2 V3 
1:  1  a________ a NA NA 
2:  2  b________ b NA NA 
3:  1 c__.a____1__ c a 1 
4:  2 c__.a____2__ c a 2 
5:  3 c__.a____3__ c a 3 
6:  4 c__.a____4__ c a 4 
7:  5 c__.a____5__ c a 5 
8:  2 c__.b______ c b NA 
9:  1 c__.c__.a____ c c a 
10:  3 c__.c__.d____ c c d 
11:  3 d__.y______ d y NA 
12:  2 d__.z______ d z NA 

然后我就可以过滤出我想要的因子组合(或者dcast/spread)。 (尽管如果它们存在,我可以有效地将表分为最低级别)

我想过要通过bind.c并拔出do_unlist以通过Rcpp创建一个灵活的命名约定,但是我的C++是生锈的,所以我想我会在我做任何事情之前张贴在这里。

+1

你看过'data.tree'吗? [data.tree intro](https://cran.r-project.org/web/packages/data.tree/vignettes/data.tree.html) [data.tree application](https://cran.r -project.org/web/packages/data.tree/vignettes/applications.html)和[这个问题](http://stackoverflow.com/questions/31339805/converting-json-format-to-csv-to-upload -data-table-in-r-to-produce-d3-bubble-cha) – dracodoc

+0

现在翻看它,这看起来很有希望 – Shape

回答

0

我在类似的情况下苦苦挣扎,但tidyjson软件包为我处理嵌套的JSON提供了一段时间。有相当数量的输入需要,但tidyjson函数返回一个整洁的对象。这里的文档:https://github.com/sailthru/tidyjson

+0

你能告诉我一个与树无关的tidyjson的例子吗? (也就是说,你不需要知道深度嵌套关卡的名称,我以前就玩过它,但我认为它通常需要关于树的结构的知识,我希望通过制作树它更整洁 – Shape

+0

嗯,道歉,但我不确定这样的例子存在。在我得到JSON之后,我调用'jsonlite :: prettify()'来查看整个结构,然后开始用'tidyjson :: enter_object ()','tidyjson :: spread_values()'等等。 –

+0

随着我正在使用的json的大小,这并不总是一个简单的选择。我正在寻找一个函数,就像我上面发布的那个函数,可以在任何嵌套树上工作,并将其移动到一个整洁的结构中。 – Shape

0

正如dracodoc指出的那样,data.tree可能会有所帮助。例如。像这样:

library(data.tree) 
aNestedTree = 
    list(a = 1, 
     b = 2, 
     c = list(
     a = list(1:5), 
     b = 2, 
     c = list(
      a = 1, 
      d = 3, 
      e = list())), 
     d = list(
     y = 3, 
     z = 2 
     )) 

tree <- FromListSimple(aNestedTree) 
print(tree) 

这将给:

 levelName z 
1 Root   NA 
2 ¦--c   NA 
3 ¦ ¦--a  NA 
4 ¦ °--c  NA 
5 ¦  °--e NA 
6 °--d   2 

和:

tree$fieldsAll 
[1] "a" "b" "1" "d" "y" "z" 

边注:通常情况下,你可以做这样的事情:

do.call("print", c(tree, tree$fieldsAll)) 

然而,在这里,这不起作用,因为一些节点名称s与字段名称相同。我认为这是一个错误,很快就会修复。

0

我倾向于倾向于tidyjson以及。在tidyverse中,您正在寻找的行为似乎在gather系列中。

我认为tidyjson中的gather功能家族可以做一些改进,使这些帮助者不必要。现在,他们非常“敏感”,错误或丢弃不匹配的类型。无论如何,解决方法并不是太具有挑战性,尽管它绝对缺乏优雅。请注意,bind_rows变体目前来自我的开发版本,并且不是主流。不过,希望这可以说明这个想法。

的方法注意:

  • 所有值都将是数字(我投他们字符之后)
  • 助手收集不同类型的元素,bind_rows堆栈的数据集在一起。
  • 水平由递归

的水平保持轨道的第一定义助手:

recurse_gather <- function(.x,.level) { 
    .x <- tidyjson::bind_rows(
    gobj(.x,.level) 
    , garr(.x,.level) 
    , gpersist(.x,.level) 
) 

    if (any(as.character(json_types(.x,'type')$type) %in% c('object','array'))) { 
    .x <- recurse_gather(.x,.level+1) 
    } 

    return(.x) 
} 
gobj <- function(.x,.level) { 
    .x %>% json_types('type') %>% 
    filter(type=='object') %>% 
    gather_object(paste0('v',.level)) %>% 
    select(-type) 
} 

gpersist <- function(.x,.level) { 
    .x %>% json_types('type') %>% 
    filter(! type %in% c('object','array')) %>% 
    mutate_(.dots=setNames(
     paste0('as.character(NA)') 
     ,paste0('v',.level) 
    )) %>% 
    select(-type) 
} 

garr <- function(.x,.level) { 
    .x %>% json_types('type') %>% 
    filter(type=='array') %>% 
    gather_array('arridx') %>% 
    append_values_number(paste0('v',.level)) %>% 
    mutate_(.dots=setNames(
     paste0('as.character(v',.level,')') 
     ,paste0('v',.level) 
    )) %>% 
    select(-arridx,-type) 
} 

然后使用助手是非常直接的。

library(dplyr) 
library(tidyjson) 

j <- "{\"a\":[1],\"b\":[2],\"c\":{\"a\":[1,2,3,4,5],\"b\":[2],\"c\":{\"a\":[1],\"d\":[3],\"e\":[]}},\"d\":{\"y\":[3],\"z\":[2]}}" 
recurse_gather(j, 1) %>% arrange(v1, v2, v3, v4) %>% tbl_df() 
#> # A tibble: 12 x 5 
#> document.id v1 v2 v3 v4 
#> *  <int> <chr> <chr> <chr> <chr> 
#> 1   1  a  1 <NA> <NA> 
#> 2   1  b  2 <NA> <NA> 
#> 3   1  c  a  1 <NA> 
#> 4   1  c  a  2 <NA> 
#> 5   1  c  a  3 <NA> 
#> 6   1  c  a  4 <NA> 
#> 7   1  c  a  5 <NA> 
#> 8   1  c  b  2 <NA> 
#> 9   1  c  c  a  1 
#> 10   1  c  c  d  3 
#> 11   1  d  y  3 <NA> 
#> 12   1  d  z  2 <NA> 

希望未来在tidyjson封装发展将会使这样的一个简单的问题来解决!