r - 将基于记录的列表/对象展平到数据框中

标签 r

编辑:这个问题已经过时了。 jsonlite包自动变平。

我正在处理具有基于记录的编码的在线数据流,通常是 JSON。对象的结构(即 JSON 中的名称)可从 API 文档中获知,但是,值大多是可选的,并非出现在每条记录中。列表可以包含新列表,并且结构有时非常深。下面是一些 GPS 数据的一个非常简单的例子:http://pastebin.com/raw.php?i=yz6z9t25 .请注意,在下面的行中,"l"由于没有 GPS 信号,物体丢失。

我正在寻找一种优雅的方式将这些对象展平为数据框。我目前正在使用这样的东西:

library(RJSONIO)
library(plyr)

obj <- fromJSON("http://pastebin.com/raw.php?i=yz6z9t25", simplifyWithNames=FALSE, simplify=FALSE)
flatdata <- lapply(obj$data, as.data.frame);
mydf <- rbind.fill(flatdata)

这可以完成工作,但是它很慢并且容易出错。这种方法的一个问题是我没有使用我对数据结构(对象名称)的了解;相反,它是从数据中推断出来的。当每个记录中碰巧不存在某个属性时,这会导致问题。在这种情况下,它根本不会出现在数据框中,而是出现在具有 NA 值的列中。这可能会导致下游问题。例如,我需要处理位置时间戳:
mydf$l.t <- structure(mydf$l.t/1000, class="POSIXct")

但是,如果数据集包含 l$t,这将导致错误。对象不在。此外,as.data.framerbind.fill让事情变得很慢。示例数据集是一个相对较小的数据集。有什么更好的实现建议吗?一个健壮的解决方案总是会产生一个具有相同顺序的相同列的数据帧,并且只有行数不同。

编辑:在具有更多元数据的数据集下方。它的尺寸更大,嵌套更深:
obj <- fromJSON("http://www.stat.ucla.edu/~jeroen/files/output.json", simplifyWithNames=FALSE, simplify=FALSE)

最佳答案

这是一个解决方案,可让您利用您对数据字段名称和类的先验知识。此外,通过避免重复调用 as.data.frame以及对 plyr 的单一调用的 rbind.fill() (都是时间密集型的)它在您的示例数据上运行速度大约快 60 倍。

cols <- c("id", "ls", "ts", "l.lo","l.tz", "l.t", "l.ac", "l.la", "l.pr", "m")   
numcols <- c("l.lo", "l.t", "l.ac", "l.la")

## Flatten each top-level list element, converting it to a character vector.
x <- lapply(obj$data, unlist)
## Extract fields that might be present in each record (returning NA if absent).
y <- sapply(x, function(X) X[cols])
## Convert to a data.frame with columns of desired classes.
z <- as.data.frame(t(y), stringsAsFactors=FALSE)
z[numcols] <- lapply(numcols, function(X) as.numeric(as.character(z[[X]])))

编辑:为了确认我的方法给出的结果与原始问题中的结果相同,我进行了以下测试。 (请注意,在这两种情况下,我都设置了 stringsAsFactors=FALSE 以避免在因子水平的排序中出现无意义的差异。)
flatdata <- lapply(obj$data, as.data.frame, stringsAsFactors=FALSE)
mydf <- rbind.fill(flatdata)
identical(z, mydf)
# [1] TRUE

进一步编辑:

只是为了记录,这里是上面的一个替代版本,另外自动:
  • 查找所有数据字段的名称
  • 确定他们的类/类型
  • 将最终 data.frame 的列强制为正确的类

  • .
    dat <- obj$data
    
    ## Find the names and classes of all fields
    fields <- unlist(lapply(xx, function(X) rapply(X, class, how="unlist")))
    fields <- fields[unique(names(fields))]
    cols <- names(fields)
    
    ## Flatten each top-level list element, converting it to a character vector.
    x <- lapply(dat, unlist)
    ## Extract fields that might be present in each record (returning NA if absent).
    y <- sapply(x, function(X) X[cols])
    ## Convert to a data.frame with columns of desired classes.
    z <- as.data.frame(t(y), stringsAsFactors=FALSE)
    
    ## Coerce columns of z (all currently character) back to their original type
    z[] <- lapply(seq_along(fields), function(i) as(z[[cols[i]]], fields[i]))
    

    关于r - 将基于记录的列表/对象展平到数据框中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11197032/

    相关文章:

    r - 基于最小值的子集数据

    r - 如何生成日期序列格式 dd/mm/yy 或 %d%m%y?

    R - 先验算法的 For 循环

    r - 更改 ggplot 标题中的行间空间?

    r - 使用 summarize() 访问其他 group_by 组

    mysql - 使用 MySQL 或 r,或两者都使用?

    r - 有条件改变或不改变组中的所有变量

    r - 列出数据框中的未列出列

    r - R data.table `.BY` 运算符如何使用?

    r - 如何从多个数据帧创建热图