在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的主要内容,如果未能解决你的问题,请参考以下文章

使用 jq 展平嵌套的 JSON

在雪花中展平嵌套的 JSON

有没有办法在火花流中展平嵌套的 JSON?

用嵌套列表和嵌套字典列表展平一个非常大的 Json

如何在 Azure 流分析中展平嵌套的 json 数据

在pyspark中展平嵌套的json scala代码