【问题标题】:How to create a sub-class of data.frame with additional features如何创建具有附加功能的 data.frame 子类
【发布时间】:2013-05-05 18:52:02
【问题描述】:

我想创建一个几乎是数据框的类,具有一些增强功能(额外的功能、额外的属性),我想知道最好的方法是什么。该类基本上是一个数据框,但具有一些附加属性,例如该数据框的架构(以下称为“表单”,自动派生,表示为数据框,用于将数据框转换为正确的类型),和其他几件事。当用户在其他无法识别其特殊类型的函数中使用此对象时,我希望他们处理对象的 data.frame 部分。最好的方法是什么?

我发现的两种方法都不能令人满意;我列出了它们以及我仍然看到并试图解决的问题;问题是:做我想做的事情的最佳方法是什么?

方法一,使用“data.frame”作为“base”槽(inspired by this SO post)

setClass("formhubData", representation(form="data.frame"), contains="data.frame")
fd <- new('formhubData', data.frame(x=c(1,2)), form=data.frame(name='x', type='select one', label='X'))

这个方法让我可以做这样的事情:

fd$x                  >> 1 2
names(fd)             >> "x"

[更新:原来“崩溃”是由我的环境引起的,我在其中使用不同的参数反复调用 setClass('formhubData', ...)。在新的 R 会话中,以下所有功能都按预期工作。]

但它很快就会崩溃:

nrow(fd)              >> NULL
colnames(fd)          >> NULL

与上面链接的帖子不同,即使是简单的is.data.frame 也不适合我

is.data.frame         >> FALSE

方法二,使用“数据”槽(受SP启发)

setClass("formhubData", representation(data="data.frame", form="data.frame"))
fd <- new('formhubData', data=data.frame(x=c(1,2)), form=data.frame(name='x', type='select one', label='X'))

我丢失了默认定义:

fd$x             >> NULL
names(fd)        >> integer(0)

但是,至少我可以重新定义它们中的大多数(仍然需要了解[,[[等):

 dim.formhubData <- function(x) dim(x@data)
 names.formhubData <- function(x) names(x@data)
 nrow(fd)        >> 2
 names(fd)       >> "x"

但是,我似乎无法表达这样一个事实,即对于任何采用 data.frame 的方法,我的类都应该用作其 @data 插槽的传递。我觉得需要像*.formhubData &lt;- function(x, ...) *(x, ...) 这样的东西,而不是试图猜测我班级的客户可能使用的所有功能,并像dim.formhubDatanames.formhubData 等那样定义它们。

有没有办法实现这样的目标?

【问题讨论】:

  • 对于它的价值,nrowcolnames 在“R 版本 3.0.0 已修补 (2013-04-15 r62590)”下的方法 1“为我工作”中。
  • 我在 R 2.15.2 中得到了与 Martin 相同的结果。此外,is.data.frame(fd) 出现了TRUE。但是,方法 2 甚至不会显示 fd!
  • 好的,在你们俩说了同样的话之后,我在一个新的 R 会话中进行了测试,现在我也得到了很好的结果。也许 R 没有处理多次在同一个“formhubData”上调用 setClass,这就是让我搞砸的原因。
  • 我还发现,如果我需要使用方法 1 访问数据帧,它会存储在一个名为 .Data 的特殊命名槽中,这意味着我几乎使用方法 1。谢谢大家!

标签: r s4


【解决方案1】:

虽然这两种方法在某种程度上都有效,但我实际上建议使用方法 2。关于“is-a”与“has-a”设计的“标准”面向对象的考虑通常会偏向于“has-a” .此外,在 R 中,方法可以随时添加到对象中,因此在某些方面,“is-a”是在宣传对您的类执行任意数量的任意事情是有意义的。这是一个很难履行的合同,即使对于像子设置这样的定义功能——大概如果用户在formhubData 中删除/添加行或列到底层data,你想更新@987654323 中的信息@。

相反,您似乎真的很想实现“具有”关系,并利用这个机会将接口限制为有意义的操作。您仍然可以通过简单的分派到底层实现,以最少的新代码获得大量的代码重用,例如,

setMethod(dim, "formhubData", function(x) dim(x@data)

例如,给你nrowncol。对于常见操作(例如,子集),您希望提供尊重数据结构完整性的实现。如果确实是用户应该能够做几乎任意事情的情况,您可以为data 提供简单的“访问器”,也许使用setter 来执行将form 字段对齐所需的任何操作使用用户提供的更新后的 data.frame。

【讨论】:

  • 谢谢。子集化实际上并不是破坏is-a + 数据形式通信合同的操作,而是像改变names 这样的操作。但我正在考虑通过编写validity 函数来解决这个问题,并享受is-a 的便利,而不是has-a 的纯粹性。然而,这个建议是值得的。
猜你喜欢
  • 2020-11-25
  • 2022-06-30
  • 2020-01-19
  • 1970-01-01
  • 2015-03-11
  • 2017-01-08
  • 1970-01-01
  • 1970-01-01
  • 2021-01-12
相关资源
最近更新 更多