在R中展平深层嵌套的json
Posted
技术标签:
【中文标题】在R中展平深层嵌套的json【英文标题】:Flatten deep nested json in R 【发布时间】:2016-05-07 19:41:21 【问题描述】:我正在尝试使用 R 将嵌套的 JSON 文件转换为二维数据框。
我的 JSON 文件具有嵌套结构。但是,不同级别的名称和属性是相同的。
"name":"A", "value":"1", "c":
["name":"a1", "value":"11", "c":
["name":"a11", "value":"111",
"name":"a12", "value":"112"]
,
"name":"a2", "value":"12"]
所需的数据集如下所示。虽然确切的列名可能不同。
name value c__name c_value c_c_name c_c_value
A 1 a1 11 a11 111
A 1 a1 11 a12 112
A 1 a2 12
到目前为止,我的代码将数据展平,但它似乎只适用于第一级(请参阅输出的屏幕截图)。
library(jsonlite)
json_file <- ' "name":"A", "value":"1", "c":
["name":"a1", "value":"11", "c":
["name":"a11", "value":"111",
"name":"a12", "value":"112"]
,
"name":"a2", "value":"12"]
'
data <- fromJSON(json_file, flatten = TRUE)
View(data)
我尝试了多个包,包括 jsonlite 和 RJSONIO,我花了最后 5 个小时 5 个小时调试这个并尝试了各种在线教程,但没有成功。感谢您的帮助!
【问题讨论】:
您看过purrr
包吗?它在减少列表提取痛苦方面做得很好。
【参考方案1】:
首先,这是一些丑陋的 JSON;如果你有办法避免它,那就去做吧。因此,接下来的内容也很丑陋——以至于我通常不会发布它,但我现在这样做是希望其中一些方法可能有用。如果冒犯了你的眼睛,请告诉我,我会删除它。
library(jsonlite) # for fromJSON
library(reshape2) # for melt
library(dplyr) # for inner_join, select
jlist <- fromJSON(json_file)
jdf <- as.data.frame(jlist)
jdf$c.value <- as.numeric(jdf$c.value) # fix type
jdf$L1 <- as.integer(factor(jdf$c.name)) # for use as a key with an artifact of melt later *urg, sorry*
ccdf <- melt(jdf$c.c) # get nested list into usable form
names(ccdf)[1:2] <- c('c.c.name', 'c.c.value') # fix names so they won't cause problems with the join
df3 <- inner_join(jdf[, -5], ccdf) # join, take out nested column
df3$c.c.value <- as.numeric(df3$c.c.value) # fix type
df3 <- df3 %>% select(-L1, -c) # get rid of useless columns
留给你的
> df3
name value c.name c.value c.c.name c.c.value
1 A 1 a1 11 a11 111
2 A 1 a1 11 a12 112
3 A 1 a2 12 <NA> NA
具有合理合理的类型。如果您愿意,可以避免使用的包。
这是可扩展的吗?好吧,不是真的,没有更多同样的混乱。如果其他人有一种不那么讨厌且更具可扩展性的方法来处理讨厌的 JSON,请发布它;我会像 OP 一样感激不尽。
【讨论】:
感谢阿利斯泰尔的分享!我想我找到了一种方法来做到这一点(见我的回答)。我确信那里有一个更简洁的解决方案,但现在这似乎有效。【参考方案2】:我想我找到了一种方法来做到这一点。它似乎适用于较大的树木。这个想法是取消列出 JSON 并使用未列出元素的名称属性。在这个例子中,如果一个节点有一个父节点,名称属性将以“c.”开头,如果它有一个父节点和一个“祖父节点”,它将把它列为“c.c.”......等等。因此,下面的代码使用此结构来查找嵌套级别并将节点放置在适当的列中。其余代码添加父节点的属性并删除生成的额外行。我知道它并不优雅,但我认为它可能对其他人有用。
library(stringr)
library(jsonlite)
json_file <- ' "name":"A", "value":"1", "c":
["name":"a1", "value":"11", "c":
["name":"a11", "value":"111",
"name":"a12", "value":"112"]
,
"name":"a2", "value":"12"]
'
nestedjson <- fromJSON(json_file, simplifyVector = F) #read the json
nAttrPerNode <- 2 #number of attributes per node
strChild <- "c." #determines level of nesting
unnestedjson <- unlist(nestedjson) #convert JSON to unlist
unnestednames <- attr(unnestedjson, "names") #get the names of the cells
depthTree <- (max(str_count(unnestednames, strChild)) + 1) * nAttrPerNode #maximum tree depth
htTree <- length(unnestednames) / nAttrPerNode #maximum tree height (number of branches)
X <- array("", c(htTree, depthTree))
for (nodeht in 1:htTree) #iterate through the branches and place the nodes based on the count of strChild in the name attribute
nodeIndex <- nodeht * nAttrPerNode
nodedepth <- str_count(unnestednames[nodeIndex], strChild) + 1
X[nodeht, nodedepth * nAttrPerNode - 1] <- unnestedjson[nodeIndex - 1]
X[nodeht, nodedepth * nAttrPerNode] <- unnestedjson[nodeIndex]
for (nodeht in 2:htTree) #repeat the parent node attributes for the children
nodedepth <- 0
repeat
nodedepth <- nodedepth + 1
startcol <- nodedepth * nAttrPerNode - 1
endcol <- startcol + nAttrPerNode - 1
if (X[nodeht, startcol] == "" & nodedepth < depthTree/nAttrPerNode)
X[nodeht, startcol:endcol] <- X[nodeht-1, startcol:endcol]
else
break()
deleteRows <- NULL #Finally delete the rows that only have the parent attributes for nodes that have children
strBranches <- apply(X, 1, paste, collapse="")
for (nodeht in 1:(htTree-1))
branch2sub <- substr(strBranches[nodeht+1], 1, nchar(strBranches[nodeht]))
if (strBranches[nodeht]==branch2sub)
deleteRows <- c(deleteRows, nodeht)
deleteRows
X <- X[-deleteRows,]
【讨论】:
以上是关于在R中展平深层嵌套的json的主要内容,如果未能解决你的问题,请参考以下文章