11

我想创建一个包含不同变量的 data.frame,包括 S4 类。对于像“POSIXlt”(日期)这样的内置类,这很好用:

as.data.frame(list(id=c(1,2), 
                   date=c(as.POSIXlt('2013-01-01'),as.POSIXlt('2013-01-02'))

但是现在我有一个用户定义的类,让我们说一个带有名称和年龄的“Person”类:

setClass("person", representation(name="character", age="numeric"))

但以下失败:

as.data.frame(list(id=c(1,2), pers=c(new("person", name="John", age=20),
                                     new("person", name="Tom", age=30))))

我还尝试使用 [...]-Operator 为 person 类重载

setMethod(
  f = "[",
  signature="person",
  definition=function(x,i,j,...,drop=TRUE){ 
    initialize(x, name=x@name[i], age = x@age[i])
  }
)

这允许类似向量的行为:

persons = new("person", name=c("John","Tom"), age=c(20,30))
p1 = persons[1]

但是以下仍然失败:

as.data.frame(list(id=c(1,2), pers=persons))

也许我必须重载更多运算符才能将用户定义的类放入数据框中?我敢肯定,一定有办法做到这一点,因为 POSIXlt 是一个 S4 类并且它有效!任何使用新 R5 参考类的解决方案也可以!

我不想把我所有的数据都放到 person 类中(你可能会问,为什么“id”不是 person 的成员,我只是不使用数据框)!这个想法是我的 data.frame 代表数据库中的一个表,其中包含许多不同类型的列,例如字符串、数字……还有日期、间隔、地理对象等……而对于我已经拥有的日期一个解决方案(POSIXlt),用于间隔、地理对象等。我可能需要指定我自己的 S4/R5 类。

提前非常感谢。

4

2 回答 2

9

这是您的课程,对其定义进行“列”解释,而不是行;这对性能很重要;还有日期供参考

setClass("person", representation(name="character", age="numeric"))
pers <- new("person", name=c("John", "Tom"), age=c(20, 30))
date <- as.POSIXct(c('2013-01-01', '2013-01-02'))

一些实验,包括查看methods(class="POSIXct")和注意错误消息导致我实现as.data.frame.personformat.person(后者用于在 data.frame 中显示)作为

as.data.frame.person <-
    function(x, row.names=NULL, optional=FALSE, ...)
{
    if (is.null(row.names))
        row.names <- x@name
    value <- list(x)
    attr(value, "row.names") <- row.names
    class(value) <- "data.frame"
    value
}

format.person <- function(x, ...) paste0(x@name, ", ", x@age)

这让我在 data.frame 中得到我的对象:

> lst <- list(id=1:2, date=date, pers=pers)
> as.data.frame(lst)
     id       date     pers
John  1 2013-01-01 John, 20
Tom   2 2013-01-02  Tom, 30

如果我想子集,那么我需要

setMethod("[", "person", function(x, i, j, ..., drop=TRUE) {
    initialize(x, name=x@name[i], age=x@age[i])
})

我不确定当data.frame遇到更多操作时可能需要哪些其他方法,没有“data.frame 接口”。

在 data.table 中使用矢量化类似乎需要一个长度方法来构造。

> library(data.table)
> data.table(id=1:2, pers=pers)
Error in data.table(id = 1:2, pers = pers) : 
  problem recycling column 2, try a simpler type
> setMethod(length, "person", function(x) length(x@name))
[1] "length"
> data.table(id=1:2, pers=pers)
   id     pers
1:  1 John, 20
2:  2  Tom, 30

也许有一个 data.table 接口?

于 2013-01-30T15:15:40.520 回答
2

从邮件列表上的这个线程来看:

http://tolstoy.newcastle.edu.au/R/e2/devel/06/11/1013.html

...John Chambers 在 2006 年就考虑过这个问题。但我们仍然不能将 S4 对象放在数据帧的列中。我们也不能将复杂的 S3 类放在数据帧的列中。

还有一些其他的表格数据结构可能会这样做 - data.table 可能:

require(data.table)
setClass("geezer", representation(name="character", age="numeric"))
tom=new("geezer",name="Tom",age=20)
dick=new("geezer",name="Dick",age=23)
harry=new("geezer",name="Harry",age=25)
gt = data.table(geezers=c(tom,dick,harry),weapons=c("Gun","Gun","Knife"))
gt
    geezers weapons
1: <geezer>     Gun
2: <geezer>     Gun
3: <geezer>   Knife

data.table 的语义与 data.frame 有点不同,不要期望能够将 data.table 插入任何使用 data.frame 的代码并期望它工作(例如,我怀疑lmglm会摇摇晃晃)。但似乎 data.table 作者允许列中的复合类......

于 2013-01-30T14:43:40.697 回答