11

这个问题是关于将任何非循环同构或异构数据结构集合转换为数据帧的通用机制。这在处理许多 JSON 文档的摄取或处理作为字典数组的大型 JSON 文档时特别有用。

有几个 SO 问题涉及处理深度嵌套的 JSON 结构并使用诸如 、 等功能将它们转换为数据帧plyrlapply我发现的所有问题和答案都是关于特定案例的,而不是提供处理集合的一般方法复杂的 JSON 数据结构。

在 Python 和 Ruby 中,我通过实现通用数据结构展平实用程序得到了很好的服务,该实用程序使用数据结构中叶节点的路径作为展平数据结构中该节点的值的名称。例如,该值my_data[['x']][[2]][['y']]将显示为result[['x.2.y']]

如果一个人拥有这些可能不完全同质的数据结构的集合,那么成功扁平化到数据帧的关键是发现所有可能的数据帧列的名称,例如,通过合并所有的键/名称单独展平的数据结构中的值。

这似乎是一种常见的模式,所以我想知道是否有人已经为 R 构建了这个。如果没有,我会构建它,但是,鉴于 R 独特的基于承诺的数据结构,我会很感激关于实现方法的建议最小化堆抖动。

4

4 回答 4

9

嗨@Sim 我昨天有理由反思你的问题定义:

flatten<-function(x) {
    dumnames<-unlist(getnames(x,T))
    dumnames<-gsub("(*.)\\.1","\\1",dumnames)
    repeat {
        x <- do.call(.Primitive("c"), x)
        if(!any(vapply(x, is.list, logical(1)))){
           names(x)<-dumnames
           return(x)
        }
    }
}
getnames<-function(x,recursive){

    nametree <- function(x, parent_name, depth) {
        if (length(x) == 0) 
            return(character(0))
        x_names <- names(x)
        if (is.null(x_names)){ 
            x_names <- seq_along(x)
            x_names <- paste(parent_name, x_names, sep = "")
        }else{ 
            x_names[x_names==""] <- seq_along(x)[x_names==""]
            x_names <- paste(parent_name, x_names, sep = "")
        }
        if (!is.list(x) || (!recursive && depth >= 1L)) 
            return(x_names)
        x_names <- paste(x_names, ".", sep = "")
        lapply(seq_len(length(x)), function(i) nametree(x[[i]], 
            x_names[i], depth + 1L))
    }
    nametree(x, "", 0L)
}

(getnames改编自AnnotationDbi:::make.name.tree)

flatten改编自这里的讨论How to flatten a list to a list without coercion?

作为一个简单的例子

my_data<-list(x=list(1,list(1,2,y='e'),3))

> my_data[['x']][[2]][['y']]
[1] "e"

> out<-flatten(my_data)
> out
$x.1
[1] 1

$x.2.1
[1] 1

$x.2.2
[1] 2

$x.2.y
[1] "e"

$x.3
[1] 3

> out[['x.2.y']]
[1] "e"

所以结果是一个扁平的列表,大致具有您建议的命名结构。也避免了强制,这是一个优点。

一个更复杂的例子

library(RJSONIO)
library(RCurl)
json.data<-getURL("http://www.reddit.com/r/leagueoflegends/.json")
dumdata<-fromJSON(json.data)
out<-flatten(dumdata)

更新

删除尾随 .1 的幼稚方法

my_data<-list(x=list(1,list(1,2,y='e'),3))
gsub("(*.)\\.1","\\1",unlist(getnames(my_data,T)))

> gsub("(*.)\\.1","\\1",unlist(getnames(my_data,T)))
[1] "x.1"   "x.2.1" "x.2.2" "x.2.y" "x.3"  
于 2012-08-12T01:29:07.613 回答
4

R 有两个处理 JSON 输入的包:rjsonRJSONIO。如果我正确理解“非周期性同构或异构数据结构的集合”的意思,我认为这些包中的任何一个都会将这种结构导入为list.

unlist然后,您可以使用该函数将该列表展平(成向量) 。

如果列表结构适当(每个元素长度相同的非嵌套列表),则as.data.frame可以将列表转换为数据框。

一个例子:

(my_data <- list(x = list('1' = 1, '2' = list(y = 2))))
unlist(my_data)
于 2012-07-19T09:53:24.663 回答
3

jsonlite包是RJSONIO专门设计用于简化 JSON 和数据帧之间的转换的一个分支。您没有提供任何示例json数据,但我认为这可能是您正在寻找的。看看这篇文或小插图

于 2014-02-22T08:42:57.987 回答
1

flatten 和 getnames 函数的好答案。花了几分钟弄清楚从 JSON 字符串向量到 data.frame 所需的所有选项,所以我想我会在这里记录下来。假设 jsonvec 是 JSON 字符串的向量。下面构建一个data.frame(data.table),其中每个字符串有一行,每一列对应于JSON树的不同可能的叶子节点。任何缺少特定叶节点的字符串都用 NA 填充。

library(data.table)
library(jsonlite)
parsed = lapply(jsonvec, fromJSON, simplifyVector=FALSE)
flattened = lapply(parsed, flatten) #using flatten from accepted answer
d = rbindlist(flattened, fill=TRUE)
于 2017-05-22T16:16:32.373 回答