【问题标题】:Strategies for formatting JSON output from R从 R 格式化 JSON 输出的策略
【发布时间】:2012-01-07 12:05:25
【问题描述】:

我正在尝试找出从 R 生成 JSON 文件的最佳方法。我在 R 中有以下数据框 tmp

> tmp
  gender age welcoming proud tidy unique
1      1  30         4     4    4      4
2      2  34         4     2    4      4
3      1  34         5     3    4      5
4      2  33         2     3    2      4
5      2  28         4     3    4      4
6      2  26         3     2    4      3

dput(tmp)的输出如下:

tmp <- structure(list(gender = c(1L, 2L, 1L, 2L, 2L, 2L), age = c(30, 
34, 34, 33, 28, 26), welcoming = c(4L, 4L, 5L, 2L, 4L, 3L), proud = c(4L, 
2L, 3L, 3L, 3L, 2L), tidy = c(4L, 4L, 4L, 2L, 4L, 4L), unique = c(4L, 
4L, 5L, 4L, 4L, 3L)), .Names = c("gender", "age", "welcoming", 
"proud", "tidy", "unique"), na.action = structure(c(15L, 39L, 
60L, 77L, 88L, 128L, 132L, 172L, 272L, 304L, 305L, 317L, 328L, 
409L, 447L, 512L, 527L, 605L, 618L, 657L, 665L, 670L, 708L, 709L, 
729L, 746L, 795L, 803L, 826L, 855L, 898L, 911L, 957L, 967L, 983L, 
984L, 988L, 1006L, 1161L, 1162L, 1224L, 1245L, 1256L, 1257L, 
1307L, 1374L, 1379L, 1386L, 1387L, 1394L, 1401L, 1408L, 1434L, 
1446L, 1509L, 1556L, 1650L, 1717L, 1760L, 1782L, 1814L, 1847L, 
1863L, 1909L, 1930L, 1971L, 2004L, 2022L, 2055L, 2060L, 2065L, 
2082L, 2109L, 2121L, 2145L, 2158L, 2159L, 2226L, 2227L, 2281L
), .Names = c("15", "39", "60", "77", "88", "128", "132", "172", 
"272", "304", "305", "317", "328", "409", "447", "512", "527", 
"605", "618", "657", "665", "670", "708", "709", "729", "746", 
"795", "803", "826", "855", "898", "911", "957", "967", "983", 
"984", "988", "1006", "1161", "1162", "1224", "1245", "1256", 
"1257", "1307", "1374", "1379", "1386", "1387", "1394", "1401", 
"1408", "1434", "1446", "1509", "1556", "1650", "1717", "1760", 
"1782", "1814", "1847", "1863", "1909", "1930", "1971", "2004", 
"2022", "2055", "2060", "2065", "2082", "2109", "2121", "2145", 
"2158", "2159", "2226", "2227", "2281"), class = "omit"), row.names = c(NA, 
6L), class = "data.frame")

使用rjson 包,我运行toJSON(tmp) 行,它会生成以下JSON 文件:

 {"gender":[1,2,1,2,2,2],
 "age":[30,34,34,33,28,26],
 "welcoming":[4,4,5,2,4,3],
 "proud":[4,2,3,3,3,2],
  "tidy":[4,4,4,2,4,4],
  "unique":[4,4,5,4,4,3]}

我还尝试了RJSONIO 包; toJSON() 的输出是一样的。我想制作的是以下结构:

  {"traits":["gender","age","welcoming","proud", "tidy", "unique"],
   "values":[   
            {"gender":1,"age":30,"welcoming":4,"proud":4,"tidy":4, "unique":4},
            {"gender":2,"age":34,"welcoming":4,"proud":2,"tidy":4, "unique":4},
            ....
            ]

我不确定如何最好地做到这一点。我意识到我可以使用python 逐行解析它,但我觉得可能有更好的方法来做到这一点。我也意识到我在R 中的数据结构并没有反映我的JSON 文件(特别是traits 行)中所需的元信息,但我主要对生成格式类似于行的数据感兴趣

{"gender":1,"age":30,"welcoming":4,"proud":4,"tidy":4, "unique":4}

因为我可以手动添加第一行。


编辑:我发现了一个有用的blog 帖子,作者在其中处理了类似的问题并提供了解决方案。此函数从数据框中生成格式化的 JSON 文件。

toJSONarray <- function(dtf){
clnms <- colnames(dtf)

name.value <- function(i){
quote <- '';
# if(class(dtf[, i])!='numeric'){
if(class(dtf[, i])!='numeric' && class(dtf[, i])!= 'integer'){ # I modified this line so integers are also not enclosed in quotes
quote <- '"';
}

paste('"', i, '" : ', quote, dtf[,i], quote, sep='')
}

objs <- apply(sapply(clnms, name.value), 1, function(x){paste(x, collapse=', ')})
objs <- paste('{', objs, '}')

# res <- paste('[', paste(objs, collapse=', '), ']')
res <- paste('[', paste(objs, collapse=',\n'), ']') # added newline for formatting output

return(res)
}

【问题讨论】:

  • 问这个问题已经过去好几年了。我可以建议查看 library(jsonlite) - 其他人在下面提供了答案。它不需要 apply() 转换并直接产生预期的 JSON 输出。
  • @ripvlan - 将 jsonlite 标记为答案

标签: json r dataframe


【解决方案1】:

在我看来,您可以通过使用适当的 apply 语句将 data.frame 的每一行发送到 JSON 来做到这一点。

对于单行:

library(RJSONIO)

> x <- toJSON(tmp[1, ])
> cat(x)
{
 "gender": 1,
"age":     30,
"welcoming": 4,
"proud": 4,
"tidy": 4,
"unique": 4 
}

整个data.frame

x <- apply(tmp, 1, toJSON)
cat(x)
{
 "gender": 1,
"age":     30,
"welcoming": 4,
"proud": 4,
"tidy": 4,
"unique": 4 
} {

...

} {
 "gender": 2,
"age":     26,
"welcoming": 3,
"proud": 2,
"tidy": 4,
"unique": 3 
}

【讨论】:

  • 谢谢@Andrie,这是个好主意。我试图弄清楚如何使用这样的东西在每行的末尾插入,x &lt;- apply(tmp, 1, function(tmp){paste(toJSON, ',')})。有没有办法在 toJSON 返回的内容上附加一些东西?
  • 刚刚想通了:x &lt;- apply(tmp, 1, function(tmp){paste(toJSON(tmp), ',')})。仍然掌握apply的窍门!
  • @celenius:这看起来很可疑,就像您正在尝试自己编写一些 JSON 代码,这只会以泪水告终。最好将数据处理成调用toJSON 为您提供所需内容的形式。看我的回答。
  • @RichieCotton 我只是在{...}{...} 之间添加,。生成的输出不是有效的 JSON。不过,我总体上同意,这可能不是一种可靠的方法。
  • RJSONIO 库给了我一个我还没有解决的问题。所有值都输出为 1 的数组。例如{ "gender" : [ 2], "age" : [33] }.... 但是, jsonlite::toJSON() 产生了预期的输出。 {“性别”:2,“年龄”:33}。我通过 ddply(..summarise..) 由数据创建 - 但我也尝试使用简单的 data.frame(a=c(1,2,3), b=("a","b","c" )) 并得到相同的结果。
【解决方案2】:

基于 Andrie 的 apply 想法,您可以通过在调用 toJSON 之前修改 tmp 变量来获得您想要的结果。

library(RJSONIO)
modified <- list(
  traits = colnames(tmp),
  values = unname(apply(tmp, 1, function(x) as.data.frame(t(x))))
)
cat(toJSON(modified))

【讨论】:

  • 两种方法(rjsonRJSONIO)都遇到的一个问题是它们将所有数字都转换为字符串。例如,2 变为 "2"。我作为问题的一部分粘贴的代码 sn-p 避免了这个问题,因为它检查了 numericinteger。也许我应该将其添加为答案。
  • @celenius:我的解决方案应用于您的变量tmp 具有整数整数,而不是转换为字符串的整数。你确定你没有做傻事吗?
  • @RichieCotton 嗯。我一定是在做一些愚蠢的事情——当我第一次尝试时,它肯定将整数作为字符串返回。不知道为什么,就像我刚才运行它时它工作得很好(整数作为整数)。我没有覆盖toJSON 函数。
【解决方案3】:

基于 Andrie 和 Richie 的想法,使用 alply 而不是 apply 来避免将数字转换为字符:

library(RJSONIO)
library(plyr)
modified <- list(
  traits = colnames(tmp),
  values = unname(alply(tmp, 1, identity))
)
cat(toJSON(modified))

plyr 的alplyapply 类似,但会自动返回一个列表;而在 Richie Cotton 的答案中没有更复杂的函数,apply 将返回一个向量或数组。而那些额外的步骤,包括t,意味着如果你的数据集有任何非数字列,数字将被转换为字符串。 所以使用alply 可以避免这种担忧。

例如,获取您的 tmp 数据集并添加

tmp$grade <- c("A","B","C","D","E","F")

然后将这段代码(使用alply)与另一个示例(使用apply)进行比较。

【讨论】:

    【解决方案4】:

    另一种选择是使用 split 将您的 data.frame 与 N 行拆分为 N 个具有 1 行的 data.frames。

    library(RJSONIO)
    modified <- list(
       traits = colnames(tmp),
       values = split(tmp, seq_len(nrow(tmp)))
    )
    cat(toJSON(modified))
    

    【讨论】:

      【解决方案5】:

      使用包jsonlite:

      > jsonlite::toJSON(list(traits = names(tmp), values = tmp), pretty = TRUE)
      {
        "traits": ["gender", "age", "welcoming", "proud", "tidy", "unique"],
        "values": [
          {
            "gender": 1,
            "age": 30,
            "welcoming": 4,
            "proud": 4,
            "tidy": 4,
            "unique": 4
          },
          {
            "gender": 2,
            "age": 34,
            "welcoming": 4,
            "proud": 2,
            "tidy": 4,
            "unique": 4
          },
          {
            "gender": 1,
            "age": 34,
            "welcoming": 5,
            "proud": 3,
            "tidy": 4,
            "unique": 5
          },
          {
            "gender": 2,
            "age": 33,
            "welcoming": 2,
            "proud": 3,
            "tidy": 2,
            "unique": 4
          },
          {
            "gender": 2,
            "age": 28,
            "welcoming": 4,
            "proud": 3,
            "tidy": 4,
            "unique": 4
          },
          {
            "gender": 2,
            "age": 26,
            "welcoming": 3,
            "proud": 2,
            "tidy": 4,
            "unique": 3
          }
        ]
      } 
      

      【讨论】:

      • 嗨,Alex - 我最喜欢这个答案。 jsonlite 无需 alply()/apply() 转换即可理解 data.frame 和输出,如上面@civilstat 答案中所述。另外 - 我遇到了一个我无法解决的 RJSONIO 问题,导致所有值都是 1 的数组。jsonlite 没有这个问题。
      • 感谢您的反馈,很高兴您发现它很有用。
      猜你喜欢
      • 2012-10-15
      • 1970-01-01
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-24
      相关资源
      最近更新 更多