Reputation: 1143
EDIT: The question has changed slightly to why a specific extension doesn't work, rather than why a general method doesn't work.
As indicated by the title, I'm having trouble extending functions to other (S3) classes.
For example:
x <- y <- runif(10)
loessModel = loess(y ~ x)
methods("cor")
## [1] cor.test cor.test.default* cor.test.formula*
cor.loess = function(loessModel, ...) { cor(loessModel$x, loessModel$y, ...) }
cor(loessModel)
## Error in cor(loessModel) : supply both 'x' and 'y' or a matrix-like 'x'
However, the following works:
getCor = function(x, y, ...) { UseMethod("getCor") }
getCor.default = function(x, y, ...) { cor(x, y, ...) }
getCor.loess = function(loessModel, ...) getCor(loessModel$x, loessModel$y, ...)
getCor(loessModel)
## [,1]
## x 1
So... as explained by Josh, the first method of extending cor
didn't work because cor
isn't a generic function. The second method works, but I can't extend it to a class of LoessList
. This is puzzling to me, particularly as it works 'outside the function':
set.seed(13)
df = data.frame(id = rep.int(1:2, 10),
x = runif(20),
y = runif(20))
loessList = structure(dlply(df, "id", loess, formula = as.formula("y ~ x")),
class = "LoessList")
getCor.LoessList = function(loessList, ...) { ldply(loessList, getCor, ...) }
getCor(loessList)
## Error in is.data.frame(y) : argument "y" is missing, with no default
ldply(loessList, getCor)
## id 1
## 1 1 -0.01552707
## 2 2 -0.38997723
On a more general note, are there any good guides to OOP in R? I have been using http://logic.sysbiol.cam.ac.uk/teaching/advancedR/slides.pdf as my main point of reference, but other sources are always appreciated.
Cheers
Upvotes: 1
Views: 916
Reputation: 269481
1. Generics in S3 can be extended but if you want to turn a non-generic into a generic and extend it then you will need S4. Here we define an S4 generic which defaults to stats::cor
. Then we make the S3 method loess
available to S4 and define a loess
method:
setGeneric("cor", function(x, ...) stats::cor(x, ...))
setOldClass("loess") # let S4 use an S3 class
setMethod("cor", list(x = "loess"),
def = function(x, ...) callNextMethod(x$x, x$y, ...))
Now we can do this:
example(loess)
cor(cars.lo)
2. The other approach if you want to stick with S3 is to clobber cor
with your own S3 generic:
cor <- function(x, ...) UseMethod("cor")
cor.default <- stats::cor
cor.loess <- function(x, ...) stats::cor(x$x, x$y, ...)
example(loess)
cor(cars.lo)
3. Of course you could just redefine cor
appropriately and forget about OO dispatch. There are actually some potential problems with this since it makes cor
the parent frame of call to the function doing the real work but in many cases it won't matter. (See lm
source code to find out how to circumvent this.)
cor <- function(x, ...) {
if (inherits(x, "loess")) return(cor(x$x, x$y, ...))
stats::cor(x, ...)
}
Info: For info on S3 see ?UseMethod
and references and links on that page. For info on S4 see ?Methods
and references and links on that page.
UPDATE: Added additional approaches and references.
Upvotes: 4
Reputation: 176648
Pay attention to the warning when you run methods(cor)
.
R> methods("cor")
[1] cor.test cor.test.default* cor.test.formula*
Non-visible functions are asterisked
Warning message:
In methods("cor") : function 'cor' appears not to be generic
cor
isn't generic, so you can't just define a method. You'd need to define a generic too, which is what you did in the solution in your question.
Upvotes: 3