Home > database >  custom `smooth()` method for a custom class
custom `smooth()` method for a custom class

Time:04-25

I'm wondering why a custom smooth() function for a custom class is not taking precedence over stats::smooth(). Here is an example:

create_my_class = function(mydata){
  class(mydata) = c("my_class", "data.frame")
  return(mydata)
}

df = data.frame(x = rnorm(100), y = rnorm(100))
df = create_my_class(df)

Now, I know I can define a plot() function for this class, and it will supersede other plot() methods when I pass a myclass object:

library(ggplot2)

plot.my_class = function(data){
  ggplot(data, aes(x,y))   
    geom_point()
}

plot(df)

enter image description here

But why doesn't this approach work with a function called smooth.my_class()? For instance, this simple smoothing function:

smooth.my_class = function(data){
  lm(y~x, data)
}

when run as smooth(df) throws the error "Error in smooth(df) : attempt to smooth non-numeric values", which is the same if I run stats::smooth(df).

Is there a bigger problem here? Can only certain function names be re-used to take a custom class?

CodePudding user response:

Yes, there is a bigger problem here. If you want to define a specific method to handle a particular class, the function in question needs to be a generic function.

For example, look at the function definition for print:

print
#> function (x, ...) 
#> UseMethod("print")

That's all there is to the function print. When you type print(object) into the console, this function executes, and all it does is look up the class of object, append the class name to the word print and look up that function. For example, if object is of class "data.frame", then UseMethod("print") looks for print.data.frame, and if it exists, will call it with the arguments you supplied to print. If no specific method exists for a class, a generic function has (or should have) a fallback default method.

The reason why your custom smooth function doesn't work is that smooth is not a generic function. When you run smooth, no lookup of a class-specific method occurs, and you just get plain old stats::smooth

You could make smooth a generic function though. The following should dispatch correctly for your custom class and in all other cases default to stats::smooth:

smooth <- function(x, ...) UseMethod("smooth")

smooth.my_class = function(data, ...){
  lm(y~x, data)
}

smooth.default <- function(x, ...) stats::smooth(x, ...)

For example if we try it on your custom class we get an lm

smooth(df)
#> 
#> Call:
#> lm(formula = y ~ x, data = data)
#> 
#> Coefficients:
#> (Intercept)            x  
#>     0.07497      0.09133

But calling it on a numeric vector invokes stats::smooth

smooth(df$x)
#> 3RS3R Tukey smoother resulting from  stats::smooth(x = x) 
#>  used 6 iterations
#>   [1]  0.08976935  0.08976935  0.08976935 -0.19641797 -0.19641797 -0.19641797
#>   [7] -0.19641797  0.02547707  0.08861531  0.08861531 -0.03327923 -0.09422650
#>  [13] -0.09422650 -0.15654766 -0.15654766 -0.37095531 -0.49807061 -0.87394069
#>  [19] -1.18237852 -1.18237852 -0.05479660 -0.05479660 -0.05479660  0.26811991
#>  [25]  0.37611915  0.37611915  0.70314430  0.70314430  0.70314430  0.70314430
#>  [31]  0.70314430  0.24278460  0.24278460  0.24278460  0.51725720  0.51725720
#>  [37]  0.51725720  0.51725720  0.67223056  0.67223056  0.67223056  0.67223056
#>  [43]  0.67223056  0.09257700  0.08426558  0.08426558  0.08426558  0.08426558
#>  [49]  0.08426558  0.51762272  0.51762272  0.51762272  0.51762272  0.51762272
#>  [55]  0.51762272  0.02758035  0.02758035  0.10725655  0.13239083  0.13239083
#>  [61]  0.18802296  0.18802296  0.55088073  0.55088073  0.55088073  0.55088073
#>  [67]  0.55088073  0.76919007  0.76919007  0.76919007  0.48469683  0.48469683
#>  [73] -0.32896249 -0.32896249 -0.31640679 -0.28686752 -0.21660688 -0.21660688
#>  [79] -0.21660688 -0.21660688 -0.87848779 -0.87848779 -0.87848779 -1.69139208
#>  [85] -1.77982364 -1.77982364 -1.22898324  0.33751308  0.44113212  0.44113212
#>  [91]  0.85571511  0.85571511  0.85571511  0.34594270  0.29631435  0.19705764
#>  [97] -0.02076665 -0.02076665 -0.02076665 -0.02076665

Created on 2022-04-24 by the reprex package (v2.0.1)

  •  Tags:  
  • r
  • Related