为新 class 定义 S3 "Math" 组泛型时出现意外的 log2 错误
Unexpected log2 error when defining S3 "Math" group generics for a new class
我正在尝试使用 S3 "Math" 组泛型进行自定义 class。但是我得到了一个奇怪的结果:log()
有效,而 log2
和 log10
产生错误。下面是一个最小的例子:
# 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
包装器:
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 次通用数学定义并失败了?
如评论 log2
中所述,log10
不在 S3 Math
泛型中。事实上,exp
、expm1
、log
、log10
、log2
和log1p
就是S4 generic 并且是数学组 generic 的成员。
实现您想做的事情的一种方法是将您 class 定义为 S4 class。
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 接口。但是如果你是新手,有一定的学习曲线(取决于你的开发背景)
似乎正在发生的事情是 NextMethod
没有剥离 lame
class,所以当 log2
调用 log
时,它会重新调度lame
方法现在不再有效,因为它使用 base = 2L
调用 log2
,参数 log2
没有。
强制调度正常工作并不需要太多工作——只需删除并重新添加 class。 (另外:Subclasses 应该放在前面,而不是附加。)
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
方法中使用了剥离和重新添加class的成语,可能是因为这个原因:
MASS:::Math.fractions
#> function (x, ...)
#> {
#> x <- unclass(x)
#> fractions(NextMethod())
#> }
#> <bytecode: 0x7ff8782a1558>
#> <environment: namespace:MASS>
我正在尝试使用 S3 "Math" 组泛型进行自定义 class。但是我得到了一个奇怪的结果:log()
有效,而 log2
和 log10
产生错误。下面是一个最小的例子:
# 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
包装器:
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 次通用数学定义并失败了?
如评论 log2
中所述,log10
不在 S3 Math
泛型中。事实上,exp
、expm1
、log
、log10
、log2
和log1p
就是S4 generic 并且是数学组 generic 的成员。
实现您想做的事情的一种方法是将您 class 定义为 S4 class。
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 接口。但是如果你是新手,有一定的学习曲线(取决于你的开发背景)
似乎正在发生的事情是 NextMethod
没有剥离 lame
class,所以当 log2
调用 log
时,它会重新调度lame
方法现在不再有效,因为它使用 base = 2L
调用 log2
,参数 log2
没有。
强制调度正常工作并不需要太多工作——只需删除并重新添加 class。 (另外:Subclasses 应该放在前面,而不是附加。)
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
方法中使用了剥离和重新添加class的成语,可能是因为这个原因:
MASS:::Math.fractions
#> function (x, ...)
#> {
#> x <- unclass(x)
#> fractions(NextMethod())
#> }
#> <bytecode: 0x7ff8782a1558>
#> <environment: namespace:MASS>