【问题标题】:How do you extend rbind for a data.frame subclass?如何为 data.frame 子类扩展 rbind?
【发布时间】:2018-02-16 01:14:36
【问题描述】:

我的问题是如何扩展 rbind() 以使用 data.frame 子类?我似乎无法正确扩展 rbind() 以使用即使是非常简单的子类。以下示例演示了该问题:

子类和方法定义:

new_df2 <- function(x, ...)
{
  stopifnot(is.data.frame(x))
  structure(x, class = c("df2", "data.frame"), author = "some user")
}

rbind.df2 <- function(..., deparse.level = 1)
{
  NextMethod()
}

我意识到在这种情况下不需要扩展rbind(),但我的宏伟计划是在我的子类上使用rbind.data.frame(),然后在其结果中添加一些额外的检查/属性。

如果您调用以下命令,则会收到错误消息:Error in NextMethod() : generic function not specified

不起作用:

t1 <- data.frame(a = 1:12, b = month.abb)
t2 <- new_df2(t1)
rbind(t2, t2)

我也尝试使用NextMethod(generic = "rbind"),但在这种情况下,您会收到此错误:Error in NextMethod(generic = "rbind") : wrong value for .Method

也不起作用:

rbind.df2 <- function(..., deparse.level = 1)
{
  NextMethod(generic = "rbind")
}

rbind(t2, t2)

我也束手无策,也猜测我对子类/方法的理解有限。感谢您的帮助。

【问题讨论】:

  • 您想要与数据帧相同的行为吗? rbind / rbind.data.frame 应该都可以工作。
  • @F.Privé 问题是在为新类添加rbind 函数时 NextMethod("rbind") 不起作用。

标签: r generics methods rbind r-s3


【解决方案1】:

我将在下面处理rbind() 的具体情况,但我首先要注意,当第一个参数是... 时,我们可以生成其他示例,表明NextMethod() 通常没有问题(关于赏金请求):

f <- function(..., b = 3) UseMethod("f")
f.a <- function(..., b = 3) { print("yes"); NextMethod() }
f.integer <- function(..., b = 4) sapply(list(...), "*", b)
x <- 1:10
class(x) <- c("a", class(x))
f(x)

[1] "yes"
      [,1]
 [1,]    4
 [2,]    8
 [3,]   12
 [4,]   16
 [5,]   20
 [6,]   24
 [7,]   28
 [8,]   32
 [9,]   36
[10,]   40

f(x, b = 5)

[1] "yes"
      [,1]
 [1,]    5
 [2,]   10
 [3,]   15
 [4,]   20
 [5,]   25
 [6,]   30
 [7,]   35
 [8,]   40
 [9,]   45
[10,]   50

那么为什么 rbind.df2 不起作用?

事实证明,rbind()cbind() 不是正常 泛型。首先,它们是内部通用的;请参阅 Hadley Wickham 在 Advanced R 上的旧 S3 页面中的“内部泛型”部分 here,或当前 Advanced R 的摘录:

一些 S3 泛型,例如 [、sum() 和 cbind(),不调用 UseMethod() 因为它们是用 C 实现的。相反,它们调用 C 函数 DispatchGroup() 或 DispatchOrEval()。

这还不足以给我们带来麻烦,正如我们以sum() 为例:

sum.a <- function(x, na.rm = FALSE) { print("yes"); NextMethod() } 
sum(x)

[1] "yes"
[1] 55

但是,对于rbindcbind,它甚至更奇怪,正如在source code 中的 cmets 中所识别的那样(从第 1025 行开始):

/* cbind(deparse.level, ...) and rbind(deparse.level, ...) : */
/* This is a special .Internal */

...(省略部分代码)...

    /* Lazy evaluation and method dispatch based on argument types are
     * fundamentally incompatible notions.  The results here are
     * ghastly.

之后,给出了调度规则的一些解释,但到目前为止我还不能使用这些信息来使NextMethod() 工作。在上面给出的用例中,我会听从 cmets 的 F. Privé 的建议并这样做:

new_df2 <- function(x, ...)
{
    stopifnot(is.data.frame(x))
    structure(x, class = c("df2", "data.frame"))
}

rbind.df2 <- function(..., deparse.level = 1)
{
    print("yes") # Or whatever else you want/need to do
    base::rbind.data.frame(..., deparse.level = deparse.level)
}

t1 <- data.frame(a = 1:12, b = month.abb)
t2 <- new_df2(t1)
rbind(t2, t2)

[1] "yes"
    a   b
1   1 Jan
2   2 Feb
3   3 Mar
4   4 Apr
5   5 May
6   6 Jun
7   7 Jul
8   8 Aug
9   9 Sep
10 10 Oct
11 11 Nov
12 12 Dec
13  1 Jan
14  2 Feb
15  3 Mar
16  4 Apr
17  5 May
18  6 Jun
19  7 Jul
20  8 Aug
21  9 Sep
22 10 Oct
23 11 Nov
24 12 Dec

【讨论】:

  • 感谢您的回复。但是我认为这是不正确的。在您的示例中,您将data.frame 作为第一类。这意味着当您调用rbind(t1, t2) 时,它会调用rbind.data.frame(基于头等舱)。因此,rbind.df2 没有被调用,并且“yes”字符串从未被打印出来。
  • @KarolisKoncevičius 感谢您的评论,我没听懂。将进一步调查,但请参阅有关其他示例的编辑,其中肯定 NextMethod()... 作为第一个参数一起使用。
  • 感谢您的调查。我认为您的示例表明错误不是因为省略号(这只是一个猜测)。不知道为什么rbind 会停止工作。但我真的很想了解如何正确使用 NextMethod 和 rbind。在我的情况下,错误是关于错误的“.Method”规范。也许手动分配“.Method”是可能的?
  • 感谢您深入研究此问题。我不能接受答案,因为我不是问题的作者,但已经给了它+1。将等待更多,看看是否添加了任何其他答案。但是会很乐意将赏金奖励给这个答案(除非有一些优雅的东西可以让 NextMethod 工作)。
【解决方案2】:

答案是扩展rbind2,而不是rbind。来自rbind2的帮助页面:

“这些是具有默认方法的 (S4) 通用函数。

...

cbind2(rbind2)的主要用途是在满足这两个要求时由cbind()递归调用(rbind()):

  • 至少有一个参数是 S4 对象,并且

  • S3 分派失败(请参阅 cbind 下的分派部分)。”

【讨论】:

  • 感谢您的评论。但问题是关于 S3 泛型,而不是 S4。不过,我认为这里有它很好,因为寻找此类答案的人将被告知使用 S4 的可能性。
  • 你可以有 S3 类的 S4 方法;只需先致电setOldClass("myClass")。这是您想要使用 S3 类时的标准技术,但简单的 S3 方法分派不能满足您的需求。
  • 听起来很有趣。但我有点不确定应该在什么时候调用setOldClass。我应该在泛型中调用它并让它在其中切换到另一个 S4 类的泛型吗?
  • setOldClass 被称为独立的,同时您可以调用setClass。 IE。尽早,在你为rbind2定义方法之前。
猜你喜欢
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
  • 2016-07-11
  • 1970-01-01
  • 2014-03-12
  • 1970-01-01
  • 2020-03-21
  • 1970-01-01
相关资源
最近更新 更多