Search code examples
rclassmethodsr-s3

Methods dispatch for S3 classes in R: specifying method for particular subclass of multiple higher classes


I'm working on a collection of scripts and using s3 classes and methods to keep things a little cleaner.

The class structure has three levels.

  • Level 1: data.frame
  • Level 2: sample_report OR fix_report
  • Level 3: stim_report

I want to write a function that ONLY takes data frames of class stim_report, then dispatches a different method depending on whether the stim_report inherits from sample_report or inherits from fix_report.

Obviously, I could do something like

myfunction.stim_report(df)

if ("sample_report" %in% class(df)) {
% do something 
} else if ("fix_report" %in% class(df)) {
% do something 
}

But that kind of defeats the purpose of methods dispatching.

Note that I need things to work so that the function will return an error if the class of the data frame isn't stim_report. So I suppose I could also do:

myfunction.fix_report(df)
if ("stim_report" %in% class(df)) {
% do something 
} else {
stop("No method found")
}

myfunction.sample_report(df)
if ("stim_report" %in% class(df)) {
% do something 
} else {
stop("No method found")
}

But again, this feels like it goes against the whole point of the S3 methods.

Is there a right way to do this?


Solution

  • What about something like this -

    Df1 <- data.frame(
      x = 1:5,
      y = rexp(5))
    ##
    Df2 <- data.frame(
      x = 6:10,
      y = rexp(5))
    ##
    Df3 <- data.frame(
      x = 11:15,
      y = rexp(5))
    ##
    class(Df1) <- c("stim_report","sample_report","data.frame")
    class(Df2) <- c("stim_report","fix_report", "data.frame")
    ##
    foo <- function(x){
      UseMethod("foo",x)
    }
    foo.sample_report <- function(x){
      x[sample(1:nrow(x),3),]
    }
    foo.fix_report <- function(x){
      x[,2] <- cumsum(x[,2])
      x
    }
    ##
    > foo(Df1)
      x         y
    3 3 0.9400994
    5 5 0.3708902
    1 1 0.7521028
    > foo(Df2)
       x        y
    1  6 2.408421
    2  7 2.637971
    3  8 3.465672
    4  9 3.571835
    5 10 5.468710
    > foo(Df3)
    Error in UseMethod("foo", x) : 
      no applicable method for 'foo' applied to an object of class "data.frame"
    

    Where you would change the bodies of foo.sample_report and foo.fix_report to do whatever it is you want them to do. The objects' classes were assigned as c("stim_report","sub_class", "data.frame") rather than just c("stim_report","sub_class") so that they can inherit other S3 generics, like nrow.