R:JSON到data.frame的通用展平

时间:2012-07-19 03:39:54

标签: json r dataframe plyr data.table

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

有几个SO问题涉及操纵深层嵌套的JSON结构并使用plyrlapply等功能将它们转换为数据框架。我发现的所有问题和答案都是关于具体的与提供处理复杂JSON数据结构集合的通用方法相反的情况。

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

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

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

4 个答案:

答案 0 :(得分:8)

你好@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"  

答案 1 :(得分:4)

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

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

如果列表具有适当的结构(非嵌套列表,其中每个元素的长度相同),则as.data.frame提供一种替代方法,将列表转换为数据帧。

一个例子:

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

答案 2 :(得分:2)

jsonlite包是RJSONIO的一个分支,专门用于简化JSON和数据框之间的转换。您没有提供任何示例json数据,但我认为这可能是您正在寻找的。请查看此blog postthe vignette

答案 3 :(得分: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)