R:JSON 到 data.frame 的通用展平
Posted
技术标签:
【中文标题】R:JSON 到 data.frame 的通用展平【英文标题】:R: Generic flattening of JSON to data.frame 【发布时间】:2012-07-18 05:02:07 【问题描述】:这个问题是关于将任何非循环同构或异构数据结构集合转换为数据帧的通用机制。这在处理大量 JSON 文档的摄取或作为字典数组的大型 JSON 文档时特别有用。
有几个 SO 问题涉及处理深度嵌套的 JSON 结构并使用 plyr
、lapply
等功能将它们转换为数据帧。我发现的所有问题和答案都是关于特定情况的,而不是提供处理复杂 JSON 数据结构集合的通用方法。
在 Python 和 Ruby 中,我通过实现通用数据结构展平实用程序得到了很好的服务,该实用程序使用数据结构中叶节点的路径作为展平数据结构中该节点的值的名称。例如,值my_data[['x']][[2]][['y']]
将显示为result[['x.2.y']]
。
如果一个人拥有这些可能不完全同质的数据结构的集合,那么成功扁平化到数据帧的关键是发现所有可能的数据帧列的名称,例如,通过合并所有键/单独展平的数据结构中的值的名称。
这似乎是一种常见的模式,所以我想知道是否有人已经为 R 构建了这个。如果没有,我会构建它,但是鉴于 R 独特的基于 promise 的数据结构,我会很感激关于最小化堆抖动的实现方法。
【问题讨论】:
嗯?我(无论如何)英语太多了。建议提供一些可重现的输入和一些(大概)产生你想要的输出的慢代码,然后从那里开始。也许只是我不知道 JSON。您能否提供一些可粘贴到新 R 会话中的内容,该会话从某个地方下载一些 JSON 数据来演示您的问题? How to make a great reproducible example 【参考方案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)
【讨论】:
【参考方案2】:jsonlite
包是RJSONIO
的一个分支,专门用于简化 JSON 和数据帧之间的转换。您没有提供任何示例 json
数据,但我认为这可能是您正在寻找的。看看这个blog post 或the vignette。
【讨论】:
fromJSON(path_to_file, flatten=F)
-- 然后检查输出。这通常很合乎逻辑,尽管您可能需要一些 rbindlist()
和其他整理才能完全打开它。【参考方案3】:
嗨@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
s?
你应该可以重新分配names(flattened_structure)
,对吧?
我同意。现在更干净了。我的问题是关于将作为字典/哈希数组的大型 JSON 文档转换为 data.frame。为此,您必须将列集构建为所有扁平列表名称的联合,对吗?
感谢示例代码。然而,我在一个大型嵌套集上获取 - 'names' 属性 [71556] 必须与向量 [66648] 的长度相同。
很好的答案 - 我只想建议对 flatten 进行一项更改。目前,您在检查列表是否已经展平之前进行展平(.Primitive(“c))。如果您提供已经展平的列表,它似乎会转换一个原子向量,这会丢失所有类型信息。我建议移动展平到重复循环的末尾。【参考方案4】:
R 有两个包用于处理 JSON 输入:rjson 和 RJSONIO。如果我正确理解“非周期性同构或异构数据结构的集合”的意思,我认为这些包中的任何一个都会将这种结构作为list
导入。
然后您可以使用unlist
函数将该列表展平(变成一个向量)。
如果列表结构合理(每个元素长度相同的非嵌套列表),则as.data.frame
提供了将列表转换为数据框的替代方法。
一个例子:
(my_data <- list(x = list('1' = 1, '2' = list(y = 2))))
unlist(my_data)
【讨论】:
反对票是怎么回事?unlist
似乎与 @Sim 想要的“通用数据结构扁平化实用程序”完全一样。事实上,@ttmaccer 链接的类似问题包括广泛使用unlist
的答案。
@ttmaccer:是的,你不能在 R 中同时拥有这两种方式。你要么是具有单一数据类型的平面(向量)数据结构,要么是具有混合类型的嵌套(列表)结构。我认为 R 中有足够的工具,任何 JSON 都可以转换成你想要的任何东西。
@RichieCotton @ttmaccer 我同意unlist
不会以通用方式工作。如果这是最好的 R 开箱即用,我将继续编写我在其他语言中使用过的递归下降扁平化器。以上是关于R:JSON 到 data.frame 的通用展平的主要内容,如果未能解决你的问题,请参考以下文章
为 sort.data.frame 创建通用/方法一致性的最佳方法?
r JSON到CSV转换器。使用`jsonlite` R包,展平所有层次结构并将所有剩余的列表/数组转换为strin