R:尝试格式化从 JSON 对象创建的 data.frame 以便我可以使用 write.table

R: Trying to format a data.frame created from a JSON object so that I can use write.table

我正在使用 R 编程语言(和 R Studio)在组织一些我通过 API 提取的数据时遇到问题,因此它可以写入 table。我正在使用 StubHub API 获取 JSON 响应,其中包含特定事件的所有门票列表。我可以成功地调用 StubHub,我得到了成功的响应。这是我用来获取响应的代码:

# get the content part of the response
msgContent = content(response)

# format to JSON object
jsonContent = jsonlite::fromJSON(toJSON(msgContent),flatten=TRUE,simplifyVector=TRUE)

这个 JSON 对象有一个名为“listing”的节点,这是我最感兴趣的,所以我为对象的那部分设置了一个变量:

friListings = jsonContent $listing

检查“friListings”的 class 我看到我有一个 data.frame:

> class(friListings)
[1] "data.frame"

当我在 R Studio 中单击这个变量 — View(friListings) — 它在一个新选项卡中打开,看起来很漂亮,格式也很好。有 21 个变量(列)和 609 个观察值(行)。我看到某些单元格的空值,这是预期的。

我想将此 data.frame 作为 table 写入我计算机上的文件中。当我尝试这样做时,出现此错误。

> write.table(friListings,file="data",row.names=FALSE)
Error in if (inherits(X[[j]], "data.frame") && ncol(xj) > 1L) X[[j]] <- as.matrix(X[[j]]) : 
  missing value where TRUE/FALSE needed

看看其他帖子,这似乎是因为我的 data.frame 实际上不是“扁平”的,而是具有不同 classes 和嵌套的列表列表。我在 friListings 的每一列上通过 str() 验证了这一点……

> str(friListings[1])
'data.frame':   609 obs. of  1 variable:
 $ listingId:List of 609
  ..$ : int 1138579989
  ..$ : int 1138969061
  ..$ : int 1138958138
(this is just the first couple of lines, there are hundreds)

另一个例子:

> str(friListings[6])
'data.frame':   609 obs. of  1 variable:
$ sellerSectionName:List of 609
..$ : chr "Upper 354 - No View"
..$ : chr "Club 303 - Obstructed/No View"
..$ : chr "Middle 254 - Obstructed/No View"
(this is just the first couple of lines, there are hundreds)

这是我尝试使用来自 reproducible example post:

的 dput 分享的 friListings 的负责人
> dput(head(friListings,4))
structure(list(listingId = list(1138579989L, 1138969061L, 1138958138L, 
1139003985L), sectionId = list(1552295L, 1552172L, 1552220L, 
1552289L), row = list("16", "6", "22", "26"), quantity = list(
1L, 2L, 4L, 1L), sellerSectionName = list("Upper 354 - No View", 
"Club 303 - Obstructed/No View", "Middle 254 - Obstructed/No View", 
"353"), sectionName = list("Upper 354 - Obstructed/No View", 
"Club 303 - Obstructed/No View", "Middle 254 - Obstructed/No View", 
"Upper 353 - Obstructed/No View"), seatNumbers = list("21", 
"7,8", "13,14,15,16", "General Admission"), zoneId = list(
232917L, 232909L, 232914L, 232917L), zoneName = list("Upper", 
"Club", "Middle", "Upper"), listingAttributeList = list(structure(c(204L, 
201L), .Dim = c(2L, 1L)), structure(c(4369L, 5370L), .Dim = c(2L, 
1L)), structure(c(4369L, 5989L), .Dim = c(2L, 1L)), structure(c(204L, 
4369L), .Dim = c(2L, 1L))), listingAttributeCategoryList = list(
structure(1L, .Dim = c(1L, 1L)), structure(1L, .Dim = c(1L, 
1L)), structure(1L, .Dim = c(1L, 1L)), structure(1L, .Dim = c(1L, 
1L))), deliveryTypeList = list(structure(5L, .Dim = c(1L, 
1L)), structure(5L, .Dim = c(1L, 1L)), structure(5L, .Dim = c(1L, 
1L)), structure(5L, .Dim = c(1L, 1L))), dirtyTicketInd = list(
FALSE, FALSE, FALSE, FALSE), splitOption = list("0", "0", 
"1", "1"), ticketSplit = list("1", "2", "2", "1"), splitVector = list(
structure(1L, .Dim = c(1L, 1L)), structure(2L, .Dim = c(1L, 
1L)), structure(c(2L, 4L), .Dim = c(2L, 1L)), structure(1L, .Dim = c(1L, 
1L))), sellerOwnInd = list(0L, 0L, 0L, 0L), currentPrice.amount = list(
468.99, 475L, 475L, 550.45), currentPrice.currency = list(
"USD", "USD", "USD", "USD"), faceValue.amount = list(NULL, 
NULL, NULL, NULL), faceValue.currency = list(NULL, NULL, 
NULL, NULL)), .Names = c("listingId", "sectionId", "row", 
"quantity", "sellerSectionName", "sectionName", "seatNumbers", 
"zoneId", "zoneName", "listingAttributeList", "listingAttributeCategoryList", 
"deliveryTypeList", "dirtyTicketInd", "splitOption", "ticketSplit", 
"splitVector", "sellerOwnInd", "currentPrice.amount", "currentPrice.currency", 
"faceValue.amount", "faceValue.currency"), row.names = c(NA, 
4L), class = "data.frame")

我试图通过遍历 friListings 中的每一列、取消列出该节点、保存到向量然后执行 cbind 将它们拼接在一起来解决这个问题。但是,当我这样做时,由于空值,我得到了不同长度的向量。我将这种方法更进一步,并尝试 class 每列以强制 NA 保留空值,但这是行不通的。而且,无论如何,一定有比这更好的方法。这里有一些输出来说明当我尝试这种方法时会发生什么。

# Take the column zoneId and casting it as numeric to force NA
friListings$zoneId<-lapply(friListings$zoneId, as.numeric)

# check the length
> length(friListings$zoneId)
[1] 609

# unlist and check the length... and I lost 11 items
> zoneid <- unlist(friListings$zoneId, use.names=FALSE)
> length(zoneid)
[1] 598

# here's the tail of the column... (because I happen to know that's where the empty values that are being dropped are)
> tail(friListings$zoneId)
[[1]]
numeric(0)

[[2]]
numeric(0)

[[3]]
numeric(0)

[[4]]
numeric(0)

[[5]]
numeric(0)

[[6]]
numeric(0)

我知道人们一直在使用 JSON 和 R(我显然不是那些人中的一员!),所以也许我遗漏了一些明显的东西。但我花了 5 个小时尝试不同的方法来清理这些数据并在互联网上搜索答案。我也阅读了 JSON 包文档。

我真的只是想 "flatten" 这个对象,这样它就很漂亮,结构也和我做 View(friListings) 时 R Studio 渲染它的方式一样。我已经在上面的 "fromJSON" 调用中传递了 "flatten=TRUE",它似乎没有按照我的预期进行。与 "simplifyVector=TRUE" 相同(根据文档默认为 TRUE,但为清楚起见添加了它)。

感谢您提供的任何见解或指导!!!

您可能想尝试并采用这种方法:

f <- function(x)
  if(is.list(x)) {
    unlist(lapply(x, f))
  } else {
    x[which(is.null(x))] <- NA
    paste(x, collapse = ",")
  }
df <- as.data.frame(do.call(cbind, lapply(friListings, f)))
write.table(df, tf <- tempfile(fileext = "csv"))
df <- read.table(tf)
str(df)
# 'data.frame':  4 obs. of  21 variables:
# $ listingId                   : int  1138579989 1138969061 1138958138 1139003985
# $ sectionId                   : int  1552295 1552172 1552220 1552289
# $ row                         : int  16 6 22 26
# $ quantity                    : int  1 2 4 1
# $ sellerSectionName           : Factor w/ 4 levels "353","Club 303 - Obstructed/No View",..: 4 2 3 1
# $ sectionName                 : Factor w/ 4 levels "Club 303 - Obstructed/No View",..: 4 1 2 3
# $ seatNumbers                 : Factor w/ 4 levels "13,14,15,16",..: 2 3 1 4
# $ zoneId                      : int  232917 232909 232914 232917
# $ zoneName                    : Factor w/ 3 levels "Club","Middle",..: 3 1 2 3
# $ listingAttributeList        : Factor w/ 4 levels "204,201","204,4369",..: 1 3 4 2
# $ listingAttributeCategoryList: int  1 1 1 1
# $ deliveryTypeList            : int  5 5 5 5
# $ dirtyTicketInd              : logi  FALSE FALSE FALSE FALSE
# $ splitOption                 : int  0 0 1 1
# $ ticketSplit                 : int  1 2 2 1
# $ splitVector                 : Factor w/ 3 levels "1","2","2,4": 1 2 3 1
# $ sellerOwnInd                : int  0 0 0 0
# $ currentPrice.amount         : num  469 475 475 550
# $ currentPrice.currency       : Factor w/ 1 level "USD": 1 1 1 1
# $ faceValue.amount            : logi  NA NA NA NA
# $ faceValue.currency          : logi  NA NA NA NA