Search code examples
rsubsetr-s4

Overriding the subset method in R for a specific class interferes with other objects


I am working with R and I have created a new subset method for objects of class new_object. Here is my function:

setGeneric('subset', function(x, i) standardGeneric('subset'))
setMethod('subset', 'new_object', function(x, i) { 
    # ... my code ...
    # subset new_object

})

This works perfectly for new_object. However, it seems to interfere with the subset operation of other objects, like seurat objects. I get an error when I try to subset a seurat object after loading my new subset method.

The inheritance method for the 'subset' function with the 'Seurat' tag could not be found.

I would like the subset method to work as usual for all other types of objects, except for new_object, for which I want to use my new subset method.

How can I define a new subset method for a specific class without affecting other objects in R?


Solution

  • In a clean, newly open R, run the subset without the braces to see what this object is

    > subset
    function (x, ...) 
    UseMethod("subset")
    <bytecode: 0x0000019b6e422730>
    <environment: namespace:base>
    

    So this is a S3 generic function with arguments x, ... and resides in package base. Thus, when you call setGeneric('subset', function(x, i) standardGeneric('subset')), you create a new generic function with arguments x, i which is different than the existing arguments x, ... for subset defined in package base.
    The solution is to call setGeneric("subset") to make the existing function (subset in package base) become an S4 generic function.

    > setGeneric("subset")
    > setClass("new_object")
    > setMethod("subset", "new_object", function(x, i, ...) { 
        # ... your code here ...
        # subset new_object
        # below is an example
        print(x)
    })
    
    > subset
    standardGeneric for "subset" defined from package "base"
    
    function (x, ...) 
    standardGeneric("subset")
    <environment: 0x000001f4839ab3b8>
    Methods may be defined for arguments: x
    Use  showMethods(subset)  for currently available ones.
    
    > showMethods("subset", includeDefs = TRUE)
    Function: subset (package base)
    x="ANY"
    function (x, ...) 
    UseMethod("subset")
    
    
    x="new_object"
    function (x, ...) 
    {
        .local <- function (x, i, ...) 
        {
            print(x)
        }
        .local(x, ...)
    }
    

    As you can see from showMethods("subset", includeDefs = TRUE), when you call this S4 subset with x of class new_object, it will dispatch to your custom methods. Calling with any other class, it will utilize the existing S3 dispatch mechanism (i.e., UseMethod).

    Note: A good practice is for your method to have all the arguments of the generic, including … if the generic does, in this case, setMethod("subset", "new_object", function(x, i, ...) {# your code here ...} (many thank for @JDL for introducing me this point).