将数据框转换为JSON数组对象

3
我正在尝试从数据框中获取JSON数组对象,其中每个JSON对象都是数据框的子集。
> x <- 1:5  
> y <-c('a','b','c','d','e')  
> z <-c(1,1,1,2,2)  
> df <-data.frame(x,y,z)  
> df  
    x y z  
  1 1 a 1  
  2 2 b 1  
  3 3 c 1  
  4 4 d 2  
  5 5 e 2  
> rjson::toJSON(df)  
[1] "{\"x\":[1,2,3,4,5],\"y\":[\"a\",\"b\",\"c\",\"d\",\"e\"],\"z\":[1,1,1,2,2]}"  
> df1 = toJSONArray2(na.omit(df), json = F, names = F)  
> rjson::toJSON(df1)  
[1] "[[1,\"a\",1],[2,\"b\",1],[3,\"c\",1],[4,\"d\",2],[5,\"e\",2]]"  

我需要的输出是

[ [ [1,a],[2,b],[3,c] ],[ [4,d],[5,e] ] ]

下面这个方法可以按预期获取数据帧列表,但无法获取所需的JSON输出。请注意保留HTML标记。
> x <- foreach(i=1:2) %do% { subset(df,df$z==i)[c(1,2)]}  
> x  
 [[1]]   
   x y  
 1 1 a  
 2 2 b  
 3 3 c  

 [[2]]
   x y
 4 4 d
 5 5 e

找到了解决方案。
> x <- foreach(i=1:2) %do% {
   tmp <-subset(df,df$z==i)[c(1,2)]  
   toJSONArray2(na.omit(tmp), json = F, names = F)  
   }
> rjson::toJSON(x) 

我需要一种没有使用toJSONArray2的实现方式,因为该方法速度非常慢。

2个回答

5

rCharts中的toJSONArray2函数由于使用了RJSONIO而速度较慢。我正在更新它,使用更快的rjson实现。以下是目前的内容。我从pandas中借鉴了orient参数的想法。

to_json = function(df, orient = "columns", json = T){
  dl = as.list(df)
  dl = switch(orient, 
    columns = dl,
    records = do.call('zip_vectors_', dl),
    values = do.call('zip_vectors_', setNames(dl, NULL))
  )
  if (json){
    dl = rjson::toJSON(dl)
  }
  return(dl)
}

zip_vectors_ = function(..., names = F){
  x = list(...)
  y = lapply(seq_along(x[[1]]), function(i) lapply(x, pluck_(i)))
  if (names) names(y) = seq_along(y)
  return(y)
}

pluck_ = function (element){
  function(x) x[[element]]
}

下面的示例将向您展示to_jsontoJSONArray2快20倍,其中大部分原因是由于使用rjson而不是RJSONIO
N = 10^3

df <- data.frame(
  x = rpois(N, 10),
  y = sample(LETTERS, N, replace = T),
  z = rpois(N, 5)
)

library(microbenchmark)
autoplot(microbenchmark(
  to_json(df, orient = "values", json = T),
  toJSONArray2(df, names = F),
  times = 5
))

输入图像描述

更新:仔细阅读您的问题后,我意识到我们可以通过使用 dplyrto_json 进一步加快速度。

library(dplyr)

dfl = df %.%
  group_by(z) %.%
  do(function(x){
    to_json(x[-3], orient = 'values', json = F)  
  })

非常感谢你,Ramnath。通过 to_json 实现,我能够将执行时间从 370 秒减少到 10 秒 :-) - on_the_shores_of_linux_sea
1
太好了!这对我来说是一个很好的使用案例,可以推动转移到更新的 to_json 实现。 - Ramnath

2

对于其他试图回答的人,toJSONArray [2] 函数在 rCharts 包中。您的解决方案相当紧凑,但可以使用 sapplysplit 来去除循环并将其更加简洁:

library(rjson)
library(rCharts)

x <- 1:5  
y <- c('a', 'b' ,'c' ,'d' ,'e')  
z <- c(1, 1, 1, 2, 2)  

df <- data.frame(x, y, z) 

toJSON(df)

out <- toJSONArray(sapply(split(df[,1:2], df$z), function(x) {
  toJSONArray2(x, names=FALSE, json = FALSE)
}))

# doing gsub only for SO example output
cat(gsub("\\n", "", out))

## [ [ [ 1,"a" ],[ 2,"b" ],[ 3,"c" ] ],[ [ 4,"d" ],[ 5,"e" ] ] ]

按照请求,让我们来看一下中toJSONArray[2]()函数的实现:

toJSONArray <- function(obj, json = TRUE, nonames = TRUE){
  list2keyval <- function(l){
    keys = names(l)
    lapply(keys, function(key){
      list(key = key, values = l[[key]])
    })
  }
  obj2list <- function(df){
    l = plyr::alply(df, 1, as.list)
    if(nonames){ names(l) = NULL }
    return(l)
  }
  if (json){
    toJSON(obj2list(obj))
  } else {
    obj2list(obj)
  }
}

toJSONArray2 <- function(obj, json = TRUE, names = TRUE, ...){
  value = lapply(1:nrow(obj), function(i) {
    res <- as.list(obj[i, ])
    if (!names) names(res) <- NULL  # remove names (e.g. {x = 1, y = 2} => {1, 2})
    return(res)
  })
  if (json){
    return(toJSON(value, .withNames = F, ...))
  } else {
    names(value) <- NULL;
    return(value)
  }
}

那些函数已经相当优化了,但是 toJSONArray2 基本上使用其中一个 apply 函数作为 for 循环,所以让我们看看是否有一个自编码的 JSON 更适合您的需求。以下内容可能会更快,但您可能需要在生产代码中进行进一步调整(如果需要取消整数的引号):
out <- sapply(split(df[,1:2], df$z), function(x) {
  out.2 <- apply(x, 1, function(y) {
    return(paste0(toJSON(unlist(as.list(y)), .withNames = FALSE), sep=",", collapse=""))
  })
  out.2 <- paste(out.2, sep=", ", collapse=" ")
  out.2 <- gsub(",$", "", out.2)
  return(sprintf("[ %s ], ", out.2))
})

cat(sprintf("[ %s ]", gsub(", $", "", paste(unlist(out), collapse=""))))
## [ [ [ "1", "a" ], [ "2", "b" ], [ "3", "c" ] ], [ [ "4", "d" ], [ "5", "e" ] ] ]

它与rCharts实现有一些相似的模式,但完全专注于将因子分割数据框的行打入所需的格式。

谢谢 :-). 除了使用 toJSONArray2,还有其他的实现方式吗?这个函数非常慢(在一个包含100000个元素的数据集上,仅此函数就占据了80%的处理时间,大约300秒)。 - on_the_shores_of_linux_sea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接