我在R中编写一个S3类,它只是一个附加了一些属性的整数.如果x1和x2是这个类的对象(称之为"myclass"),那么我希望c(x1,x2)返回myclass对象的向量,原始类定义和属性保持不变.但是,c()的记录行为是删除属性,所以我似乎需要编写自己的c.myclass()方法.我的问题是,我该怎么做?
问题的一个例子:
myclass <- function(x, n) structure(x, class="myclass", n=n)
x1 <- myclass(1, 5)
x2 <- myclass(2, 6)
c(x1, x2)
[1] 1 2
Run Code Online (Sandbox Code Playgroud)
这里的结果只是类numeric的项向量,原来的n属性消失了.
查看各种包的代码,我有时会看到如下代码,其中我们需要保留class属性,但没有别的:
c.myclass <- function(..., recursive = F) {
structure(c(unlist(lapply(list(...), unclass))), class="myclass")
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,我也无法让这个工作.调用c.myclass(x1,x2)的结果是一个向量,其中向量本身具有类"myclass",但是向量中的每个项都有类numeric; 我真的希望向量中的每个项都有类"myclass".在实践中,我还需要升级此方法以保留其他属性(如myclass中的属性"n").
我是 R 中面向对象编程的新手,并且正在努力解决如何正确编写修改对象的函数。
这个例子有效:
store1 <- list(
apples=3,
pears=4,
fruits=7
)
class(store1) <- "fruitstore"
print.fruitstore <- function(x) {
paste(x$apples, "apples and", x$pears, "pears", sep=" ")
}
print(store1)
addApples <- function(x, i) {
x$apples <- x$apples + i
x$fruits <- x$apples + x$pears
return(x)
}
store1 <- addApples(store1, 5)
print(store1)
Run Code Online (Sandbox Code Playgroud)
但我想应该有一种更干净的方法来做到这一点而不返回整个对象:
addApples(store1, 5) # Preferable line...
store1 <- addApples(store1, 5) # ...instead of this line
Run Code Online (Sandbox Code Playgroud)
在 R 中编写修改函数的正确方法是什么?“<<-”?
更新:感谢大家为 R 中的 OOP 编写的 Rosetta Stone。内容非常丰富。我试图解决的问题在流程方面非常复杂,因此参考类的刚性可能会给结构带来帮助。我希望我可以接受所有回复作为答案,而不仅仅是一个。
我想定义自己的行为(方法),用于将data.frame与新S3类的对象相乘.但我无法弄清楚如何让方法调度以找到我的方法.有办法吗?
首先,我定义S3对象'a'(oldClass"A")和'df'(oldClass"data.frame"):
a <- 4
oldClass(a) <- "A"
df <- data.frame(x=1:2,y=3:4)
Run Code Online (Sandbox Code Playgroud)
然后我使用trace(Ops.data.frame,edit = TRUE)在第一行添加print("Ops.data.frame").这样,我知道何时调用Ops.data.frame.这是一个演示:
a*df
# [1] "Ops.data.frame"
# x y
# 1 4 12
# 2 8 16
Run Code Online (Sandbox Code Playgroud)
我可以为类"A"定义一个S3方法.
Ops.A <- function(e1, e2) {
print("Ops.A")
oldClass(e1) <- oldClass(e1)[oldClass(e1) != "A"]
oldClass(e2) <- oldClass(e2)[oldClass(e2) != "A"]
callGeneric(e1, e2)
}
Run Code Online (Sandbox Code Playgroud)
这被称为a而不是 df:
# This successfully calls Ops.A
a*a
# [1] "Ops.A"
# [1] 16
# But this throws an error
a*df
# Error in a * df : non-numeric argument …
Run Code Online (Sandbox Code Playgroud) 给定一个环境x
,一个方便的简写assign(x, value, envir = e)
就是写e[[x]] <- value
.目前,对于一次分配多个对象的子集运算符没有模拟:
> e = new.env(parent = emptyenv())
> e[["a"]] <- 1
> ls(e)
[1] "a"
> e[c("b", "c")] <- c(1,2)
Error in e[c("b", "c")] <- c(1, 2) :
object of type 'environment' is not subsettable
Run Code Online (Sandbox Code Playgroud)
我希望使用内置的S3功能来编写一个[<-
.我注意到的第一个古怪的是,这两个[[<-
和[<-
是原始的功能,尽管模仿S3功能:
> methods("[<-")
[1] [<-.data.frame [<-.Date [<-.environment [<-.factor [<-.POSIXct [<-.POSIXlt [<-.raster* [<-.ts*
Run Code Online (Sandbox Code Playgroud)
通常,S3函数具有body只是调用的格式UseMethod
.例如:
> summary
function (object, ...)
UseMethod("summary")
<bytecode: 0x1a7c3a8>
<environment: namespace:base>
Run Code Online (Sandbox Code Playgroud)
除了赋值运算符是原始的,[[<- …
有没有人放在一起/找到一个很好的方法来列出给定对象可用的所有S3方法?内置函数将为指定的类或指定的泛型函数提供所有可用的方法,但不为对象提供.methods()
我想到的一个例子是一个glm
对象,它是(minor?)类,"glm"
但也继承自"lm"
g <- glm(y~x,data=data.frame(x=1:10,y=1:10))
class(g)
## [1] "glm" "lm"
Run Code Online (Sandbox Code Playgroud)
类"lm"有35种方法,"glm"有22种方法.我对结合了结果的答案感兴趣
lapply(class(g),function(x) methods(class=x))
Run Code Online (Sandbox Code Playgroud)
以一种明智的方式,以便我可以立即看到(例如)有一个glm
特定的方法add1
,但是方法alias
是从lm
类继承的.
有人有一个光滑的方式来做到这一点,还是已经存在?
PS史蒂夫沃克的S3-S4参考类词汇表显示,它可以自动用于参考类,我们必须使用一个对象来获取方法(x$getRefClass()$methods()
).
我有一个关于在 R 中使用 S3 类执行某些操作的“正确”方法的问题。我想要做的是有一个方法可以更改类,然后在新类上调用相同的方法。像这样的东西:
my_func <- function(.x, ...) {
UseMethod("my_func")
}
my_func.character <- function(.x, ...) {
return(paste(".x is", .x, "of class", class(.x)))
}
my_func.numeric <- function(.x, ...) {
.x <- as.character(.x)
res <- my_func(.x) # this should call my_func.character
return(res)
}
Run Code Online (Sandbox Code Playgroud)
这有效。当我执行以下操作时,我会character
同时获得课程
> my_func("hello")
[1] ".x is hello of class character"
> my_func(1)
[1] ".x is 1 of class character"
Run Code Online (Sandbox Code Playgroud)
我的问题是:这是正确的方法吗?在转换类之后重新调用相同的方法(这一行res <- my_func(.x)
)感觉很奇怪。
我觉得NextMethod()
一定是答案,但我读过很多关于它的文档(例如这个和这个),但他们都讨论了这个事情,它跳到类列表中的下一个类,例如例如,data.frame
直到matrix
您拥有class(df) …
根据我的理解,放在里面的任何东西都test_that()
应该被分隔开,这意味着如果我在 中加载一个包test_that()
,它的函数和方法不应该在其他test_that()
调用中可用。
在下面的示例中,有 3 个(空)测试:
\nas.matrix.get_predicted
在命名空间中不可用。insight
在第二个中,我加载提供了方法的包as.matrix.get_predicted
。根据我的理解,此方法应该仅在本次调用中可用test_that()
。library(testthat)\n\ntest_that("foo 1", {\n print("as.matrix.get_predicted" %in% methods(as.matrix))\n})\n#> [1] FALSE\n#> \xe2\x94\x80\xe2\x94\x80 Skip (???): foo 1 \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n#> Reason: empty test\n\ntest_that("foo 2", {\n invisible(insight::get_parameters)\n})\n#> \xe2\x94\x80\xe2\x94\x80 Skip (???): foo 2 \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n#> Reason: empty test\n\ntest_that("foo 3", {\n print("as.matrix.get_predicted" %in% methods(as.matrix))\n})\n#> [1] TRUE\n#> \xe2\x94\x80\xe2\x94\x80 Skip (???): foo 3 \xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\n#> Reason: empty test\n
Run Code Online (Sandbox Code Playgroud)\n这是为什么?有一些解决方法吗?
\n编辑:我正在寻找特定于 的解决方案 …
我的问题是如何扩展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()
}
Run Code Online (Sandbox Code Playgroud)
我意识到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)
Run Code Online (Sandbox Code Playgroud)
我也尝试过使用NextMethod(generic = "rbind")
,但在这种情况下,您会收到此错误:Error in NextMethod(generic = "rbind") : wrong value for .Method
。
也不起作用:
rbind.df2 …
Run Code Online (Sandbox Code Playgroud) 我坚持定义S3方法autoplot
.
我有以下(完整代码在这里):
#' Autoplot for bigobenchmark object
#'
#' @importFrom ggplot2 autoplot
#'
#' @param object
#'
#' @return A ggplot2 plot
#' @export
#'
#' @examples
#' # Create plot for benchmarks
#' library(ggplot2)
#' bench <- bigobenchmark(1:n, for(i in 1:n) for(i in 1:n) 1:n, args=seq(from=1, to=100, length.out = 50))
#' autoplot(bench)
autoplot.bigobenchmark <- function(object) {
plt <- ggplot2::ggplot(data = object$benchmarks, ggplot2::aes(x=arg, y=mean, colour=expr))
plt <- plt + ggplot2::geom_line()
plt <- plt + ggplot2::geom_pointrange(aes(ymin=min, ymax=max)) …
Run Code Online (Sandbox Code Playgroud) \n\ntl;dr:
\nR CMD check
当我为 S3 类实现泛型时会抱怨<-
,因为它认为该函数是参数不正确的替换函数。
我需要定义一组 S3 泛型来遍历未计算的 R 表达式的 AST。
\n出于演示目的,请考虑以下 S3 泛型及其方法:
\nwalk = function (x) UseMethod(\'walk\')\n\nwalk.default = function (x) message(\'default\')\n\nwalk.name = function (x) message(\'name\')\n\nwalk.call = function (x) message(\'call\')\n
Run Code Online (Sandbox Code Playgroud)\n这工作正常:
\ntests = alist(\'2\', c, f(1))\n\ninvisible(lapply(tests, walk))\n
Run Code Online (Sandbox Code Playgroud)\nwalk = function (x) UseMethod(\'walk\')\n\nwalk.default = function (x) message(\'default\')\n\nwalk.name = function (x) message(\'name\')\n\nwalk.call = function (x) message(\'call\')\n
Run Code Online (Sandbox Code Playgroud)\n然而,有相当多的调用表达式的 S3 类是 \xe2\x80\x99t call
;例如:
tests2 = alist(for (x …
Run Code Online (Sandbox Code Playgroud)