2016-09-18 51 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封裝發展將會使這樣的一個簡單的問題來解決!