在处理API时出现了很多。
大多数时候,为了进行真正的分析,我想让我的数据集整洁,但通常,这需要为每种类型的树提供解决方案,而不是更通用的方法。
我认为有一个生成整洁数据的函数会很好(尽管在深度嵌套的树中有很多不同的NA,但有许多不同的因子水平。
我有一个hackish解决方案,使用unlist(..., recursive = FALSE)
+一个命名约定,
但我想看看这里有人可能有更好的解决方案来整理这些列表结构。
#####################
# 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 ++生锈了,所以我想在我做任何事之前都会在这里发布激烈。
答案 0 :(得分:0)
我在类似情况下挣扎,但tidyjson
软件包在处理嵌套JSON时一次又一次地保护我。需要大量的输入,但tidyjson
函数返回一个整洁的对象。这里的文档:https://github.com/sailthru/tidyjson
答案 1 :(得分: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))
但是,这里不起作用,因为某些节点名称与字段名称相同。我认为这是一个错误,很快就会修复它。
答案 2 :(得分:0)
我倾向于倾向于tidyjson
。在tidyverse
中,您要查找的行为似乎属于gather
系列。
我认为gather
中的tidyjson
函数系列可以做一些改进,使这些助手不必要。现在,他们非常敏感&#34;和错误或抛出不匹配的类型。无论如何,解决方案并不是太具有挑战性,尽管它绝对缺乏优雅。请注意,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
包的未来发展能够解决这个问题更容易解决的问题!