在R中,我有一个"环绕"向量的S4类.我想将这个类上的许多普通的S3和S4方法转发到它的vec位,其中包含向量.然而,由于[方法的特殊属性,我在使用它时遇到了特别的问题.Edit: I want to forward all arguments to 101 including 103, 104, 105 etc to this inner vector, exactly as they are passed to the outer wrapper object.

以下是我的代码和我try 过的代码的一个简单示例.首先,我们定义这个简单的类并实例化它:

> Foo = setClass("Foo", slots=c(vec = "numeric"))
> foo = Foo(vec=c(1, 2, 3))
> foo
An object of class "Foo"
Slot "vec":
[1] 1 2 3

到目前为止一切正常,接下来我们try 使用正常的...点将所有参数转发到[:

> setMethod("[", signature=c(x="Foo"), function(x, ...){
+   print(list(...))
+   x@vec[...]
+ })
> foo[2]
list()
[1] 1 2 3

显然,这是不起作用的,因为它根本没有子化向量.但不仅如此,print语句显示...没有捕获任何内容,即使这里传递了i=2.所以我放弃了使用....

接下来,我try 明确使用ij:

> setMethod("[", signature=c(x="Foo"), function(x, i, j, ..., drop = TRUE){
+   x@vec[i, j, drop=drop]
+ })
> foo[2]
Error in x@vec[i, j, drop = drop] : incorrect number of dimensions

这不起作用,因为我总是传入j,但R没有 Select 性地传入参数的机制,所以我有点卡在这里了.

接下来,我try match.call:

> setMethod("[", signature=c(x="Foo"), function(x, ...){
+   call = as.list(match.call())
+   fun = call[[1]]
+   str(fun)
+   args = call[-1]
+   str(args)
+   do.call(fun, args)
+ })
> foo[2]
 symbol [
List of 2
 $ x: symbol foo
 $ i: num 2
Error in do.call(fun, args) : 
  'what' must be a function or character string

这很有趣,因为它证明了match.call does捕获了...没有捕获的额外参数,但是因为x[被捕获为符号,而不是它们的实际值,所以它不容易工作,并且不清楚如何解析它们.

那么,我如何将传递给[方法的所有参数转发给另一个对象呢?

推荐答案

仔细阅读?setMethod本书,就能理解这些问题中的许多.我将逐一阐述您的建议,然后添加一些我自己的建议.

1)

setMethod("[", signature = c(x = "Foo"), 
          function(x, ...) { 
              print(list(...))
              x@vec[...]
          })

该方法的形参与泛型函数的形参不匹配.setMethod会自动更正它们,而不会发出警告(叹息).因此,您的actual方法如下所示:

getMethod("[", signature = c(x = "Foo"))
Method Definition:

function (x, i, j, ..., drop = TRUE) 
{
    print(list(...))
    x@vec[...]
}

Signatures:
        x    
target  "Foo"
defined "Foo"

当我们调用foo[2]时,我们发现xfoo匹配,i2匹配,...与零匹配.因此,list(...)为空,x@vec[...]的计算结果为x@vec.

2)

setMethod("[", signature = c(x = "Foo"), 
          function(x, i, j, ..., drop = TRUE) {
              x@vec[i, j, drop = drop]
          })

此方法的形参很好,但您将向量作为数组进行索引,这是错误的.您可以忽略除i之外的所有内容,只返回x@vec[i],但仍然存在trap (例如,可能缺少i).

3)

setMethod("[", signature = c(x = "Foo"), 
          function(x, ...) {
              call <- as.list(match.call())
              fun <- call[[1]]
              str(fun)
              args <- call[-1]
              str(args)
              do.call(fun, args)
          })

这个是错误的,因为(同样)形式参数与泛型函数的形参不匹配,并且正如您所指出的那样:fun是一个符号,args是语言对象(可能还有常量)的列表,这不是do.call所期望的.无论如何,我的观点是,基于match.call的方法过于复杂和脆弱.

我会怎么做

您对类Foo的定义意味着foo@vec没有dim属性.它始终是数值向量,而不是array.

Foo <- setClass("Foo", slots = c(vec = "numeric"))
foo.v <- Foo(vec = c(1, 2, 3))
foo.a <- Foo(vec = toeplitz(1:6))
## Error in validObject(.Object) : 
##   invalid class "Foo" object: invalid object for slot "vec" in class "Foo": got class "matrix", should be or extend class "numeric"

对于这个类似向量的类Foo,我将定义:

setMethod("[", signature = c(x = "Foo", i = "ANY", j = "missing", drop = "missing"), 
          function(x, i, j, ..., drop = TRUE) {
              if (nargs() > 2L)
                  stop("'x' of class Foo must be indexed as x[i]")
              else if (missing(i)) 
                  x@vec 
              else x@vec[i]
          })

这允许x[]x[i],同时禁止x[drop=]x[i, ]x[i, , drop=]x[i, j]x[i, j, drop=]中的所有,这对于类向量类没有意义.

foo.v[]
## [1] 1 2 3

foo.v[-2L]
## [1] 1 3

foo.v[1L, 1L, drop = FALSE]
## Error in foo.v[1L, 1L, drop = FALSE] : object of type 'S4' is not subsettable

这个错误有点不透明.之所以出现这种情况,是因为我们的方法没有被调度用于调用中的签名.在实践中,您可以通过其他方法引发更透明的错误来捕获此类情况.

现在让我们考虑与Foo类似的第二个类Bar,但其vec位可以是数字向量或任何array.

setClassUnion("vectorOrArray", c("numeric", "array"))
Bar <- setClass("Bar", slots = c(vec = "vectorOrArray"))
bar.v <- Bar(vec = c(1, 2, 3))
bar.a <- Bar(vec = toeplitz(1:6))

对于这个向量或类似数组的类Bar,我将定义:

setMethod("[", signature = c(x = "Bar", i = "ANY", j = "ANY", drop = "ANY"), 
          function(x, i, j, ..., drop = TRUE) {
              x <- x@vec
              callGeneric()
          })

其中callGeneric实现了您试图用match.calleval实现的东西,但以更健壮的方式实现.

bar.v[]
## [1] 1 2 3

bar.v[-2L]
## [1] 1 3

bar.v[1L, 1L, drop = FALSE]
## Error in x[i = i, j = j, drop = drop] : incorrect number of dimensions

bar.a[1L, 1L, drop = FALSE]
##      [,1]
## [1,]    1

还可以基于callGenericFoo定义一个方法,但是对于一个纯粹的类似向量的类,callGeneric的开销可能是一个障碍.早期的只使用nargsmissing的方法要快得多,我们通常希望对[的方法进行严格优化,因为它们往往是在循环中调用的.

microbenchmark::microbenchmark(foo.v[-2L], bar.v[-2L], times = 1000L)
## Unit: nanoseconds
##        expr   min    lq      mean median    uq   max neval
##  foo.v[-2L]   861  1148  1385.062   1271  1476 11808  1000
##  bar.v[-2L] 12382 14842 15671.225  15498 16031 77613  1000

备注

上述两个setMethod看涨期权遵循两个良好做法:

  • 方法的形参与泛型函数的形参匹配.
  • 为清楚起见,签名指定了每个正式参数的类别.省略的正式参数在没有警告(叹息)的情况下获得"ANY"类,这让许多刚接触S4的人感到惊讶.

R相关问答推荐

使用split.zoo界定xts物体的降水事件

逐行替代引用前一行的for循环

从多个前置日期中获取最长日期

如何使用shinyChatR包配置聊天机器人

R Tidymodels textercipes-使用spacyR进行标记化-如何从生成的标记列表中删除标点符号

通过使用str_detect对具有相似字符串的组进行分组

在"gt"表中添加第二个"groupname_col",而不连接列值

如何将dygraph调用到R Markdown作为一个shiny 的react 对象的参数?

计算具有奇数日期的运行金额

如果某些列全部为NA,则更改列

计算满足R中条件的连续列

以字符格式导入的ExcelElectron 表格日期列标题

将选定的索引范围与阈值进行比较

悬崖三角洲超大型群数计算导致整数溢出

手动指定从相同数据创建的叠加图的 colored颜色

为什么函数toTitleCase不能处理english(1),而toupper可以?

有没有办法将基于每个值中出现的两个关键字或短语的字符串向量重新编码为具有这两个值的新向量?

快速合并R内的值

如何从嵌套数据中自动创建命名对象?在R中

R-找出存在其他变量的各种大小的所有组合