【问题标题】:Unexpected log2 error when defining S3 "Math" group generics for a new class为新类定义 S3“数学”组泛型时出现意外的 log2 错误
【发布时间】:2018-08-27 16:29:28
【问题描述】:

我正在尝试将 S3“数学”组泛型用于自定义类。但是我得到了一个奇怪的结果:log() 有效,而 log2log10 产生错误。下面是一个最小的例子:

# simple class with just the new name
lameclass <- function(x) {
  class(x) <- append(class(x), "lame")
  x
}

# It prints something when Math generics methods are used
Math.lame <- function(x, ...) {
  print("I am lame")
  NextMethod()
}

# an object of the class
lamevector <- lameclass(1:10)

> class(lamevector)
[1] "integer" "lame"

现在尝试拨打log

log(lamevector)
[1] "I am lame"
[1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101 2.0794415 2.1972246 2.3025851

以 2 为基数:

log(lamevector, 2)
[1] "I am lame"
[1] 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000 3.169925 3.321928

以上所有工作。但是现在log2 wrapper:

log2(lamevector)
[1] "I am lame"
[1] "I am lame"
Error in log2.default(1:10, 2) :
  2 arguments passed to 'log2' which requires 1

也许有人可以帮助我弄清楚这里发生了什么? log2 是否真的经历了 2 次通用数学定义并失败了?

【问题讨论】:

  • log2log10 不在 S3 数学组中。

标签: r class oop generics r-s3


【解决方案1】:

似乎正在发生的事情是NextMethod 没有剥离lame 类,所以当log2 调用log 时,它会重新调度到lame 方法,该方法现在不再有效,因为它用base = 2L 调用log2,参数log2 没有。

强制调度正常工作不需要太多工作——只需剥离并重新添加类。 (旁白:子类应该被添加,而不是被添加。)

lameclass <- function(x) {
    class(x) <- c("lame", class(x))    # prepend new class
    x
}

Math.lame <- function(x, ...) {
    print("I am lame")
    class(x) <- class(x)[class(x) != "lame"]    # strip lame class
    lameclass(NextMethod())    # re-add lame class to result
}

lamevector <- lameclass(1:5)

log(lamevector)
#> [1] "I am lame"
#> [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379
#> attr(,"class")
#> [1] "lame"    "numeric"
log(lamevector, 2)
#> [1] "I am lame"
#> [1] 0.000000 1.000000 1.584963 2.000000 2.321928
#> attr(,"class")
#> [1] "lame"    "numeric"
log2(lamevector)
#> [1] "I am lame"
#> [1] 0.000000 1.000000 1.584963 2.000000 2.321928
#> attr(,"class")
#> [1] "lame"    "numeric"

我不确定为什么它会这样发送。组泛型有点奇怪,在oldClass 而不是class 上发送,这可能是问题的一部分,也可能不是问题的一部分。这可能只是一个错误。在其他Math 方法中使用了剥离和重新添加类的习惯用法,可能是因为这个原因:

MASS:::Math.fractions
#> function (x, ...) 
#> {
#>     x <- unclass(x)
#>     fractions(NextMethod())
#> }
#> <bytecode: 0x7ff8782a1558>
#> <environment: namespace:MASS>

【讨论】:

  • 谢谢。剥离和重新添加类的出色工作以及来自 MASS 的一个很好的例子。 +1 并接受。最后一个问题你 - 你认为剥离和重新添加其他组泛型的类是否有意义(以防万一)?我现在有点偏执,因为还有其他功能与 log2 有相同的问题。
  • 可能,是的;质量确实如此。 units:::Math.units 采用更特定于功能的方法,这似乎更难编写。在其他组泛型中,Ops 可能需要更多关注,因为它使用双重调度。无论您使用什么方法,都要进行严格的测试。
【解决方案2】:

正如评论中提到的log2log10 不在S3 Math 泛型中。事实上,expexpm1loglog10log2log1pS4 泛型,并且是 Math 组泛型的成员。

实现您想要做的事情的一种方法是将您的类定义为 S4 类。

setClass("lame4", slots = c(x = "numeric"))

并定义方法Math group generic:

setMethod("Math","lame4",function(x) {
                x@x <- callGeneric(x@x)
                x
          }) 
## pretty print 
setMethod("show", "lame4",function(object)print(object@x))

现在让我们测试一下:

l1 <- new("lame4",x=1:10)

然后:

log2(l1)
 [1] 0.000000 1.000000 1.584963 2.000000 2.321928 2.584963 2.807355 3.000000 3.169925 3.321928
> log10(l1)
 [1] 0.0000000 0.3010300 0.4771213 0.6020600 0.6989700 0.7781513 0.8450980 0.9030900 0.9542425
[10] 1.0000000 

这当然不是对您问题的直接回答,而是解释了为什么您的实施不起作用。在这里,我认为使用S4 范式是一个好主意,因为您将拥有更强的打字能力,这对数学非常有帮助。 S4 方法也适用于 R.C/Rcpp 接口。但如果您是新手,则有一定的学习曲线(取决于您的开发背景)

【讨论】:

  • 非常感谢@agstudy 的回复。感谢 S4 的建议,但我必须指出——我根本不是在寻找那个。我实际完成的实际任务没有使用 S3 的数学组泛型(因为 log 和 log2 都保留了我的对象的类)。这个问题更令人惊讶的是 Math.generics 打破了 log2。如果数学没有涵盖 log2,我希望它不会受到它的影响,而不是中断。我想知道这是 R 基础中的故意还是某种错误。
  • 但是 +1 因为我认为这可能对将来有类似问题的人有用(即知道他们可以转向 S4)。但是我仍然认为这并不能真正解释 log2 的行为 - 许多其他函数不包含在 Math 泛型下,但是一旦定义了 Math 泛型,它们就不会中断。所以我觉得还是少了点什么。
  • @KarolisKoncevičius您是否可以在问题中添加 Math 中的泛型不会破坏您的 S3 类实现?这将阐明示例。
  • 就我而言,我定义了一个自定义类,我想要从泛型中得到的只是它们保留了我的类。原来它根本没有实际定义数学组泛型就可以工作:)所以没有这样的“工作实现”......
  • 我的看法是这样的:OOP 有 S3 系统。它具有组泛型。我实现了一组泛型,它破坏了一些功能(甚至不在该泛型类别中)。据我所知,没有办法在不破坏这些函数的情况下实现数学泛型。这不会被认为是一个错误吗?这可能是故意的,但在这种情况下,是在实现 Math 后将被破坏的所有函数的列表。应该在某处可用。
猜你喜欢
  • 1970-01-01
  • 2021-05-19
  • 1970-01-01
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
  • 2018-12-29
  • 1970-01-01
  • 2017-03-16
相关资源
最近更新 更多