lindelof
lindelof

Reputation: 35240

Can I define S4 methods that dispatch on more than one argument from an S3 generic?

I'd like to define a wrapper class that will encapsulate an actual model, and let the user call predict() with either new data frames or a model matrix:

raw_model <- ...
model <- Model(raw_model)
X <- matrix(...)
predict(model, X)
df <- data.frame(...)
predict(model, df)

I thought it would simply be a matter of defining two methods for predict(), dispatching on the types of the first two arguments:

library(methods)

Model <- setClass("Model", slots = "model")

setMethod("predict", signature("Model", "matrix"),
  function(object, newdata, ...) {
    stats::predict(object@model, newdata)
  })

setMethod("predict", signature("Model", "data.frame"),
  function(object, newdata, ...) {
    matrix <- model.matrix(newdata) # or something like that
    stats::predict(object@model, matrix)

})

However, both calls to setMethod fail with

Error in matchSignature(signature, fdef) : 
  more elements in the method signature (2) than in the generic signature (1) for function ‘predict’

I understand that an S4 generic is created from the S3 generic predict, whose signature takes just one named argument object, but is there a way to have the S4 methods dispatch on more than just that first argument?

Upvotes: 0

Views: 283

Answers (1)

JDL
JDL

Reputation: 1654

You can make S4 generics dispatch on more than one argument, but you cannot (currently) dispatch on a named argument and .... This is the problem with predict — the only named argument is object.

You can still achieve what you want though, by defining your own generic "one level down" along the lines of

predict2 <- function(model,newdata){stats::predict(model,newdata)}
setGeneric("predict2",signature=c("model","newdata"))

setMethod(
  "predict2",
  signature=c("Model","data.frame"),
  definition=function(model,newdata){
    matrix <- model.matrix(newdata) # or something like that
    stats::predict(object@model, matrix)
  }
)

Now you modify predict.Model (and the S4 method for predict with the signature model="Model") to call predict2(model,newdata).

Upvotes: 1

Related Questions