rooplm

How to use match.call() inside an RC class?


I've written my own little linear regression class, similar to lm, and would like to get it to print the "function call". I looked at the lm code and found match.call(), but I believe this only works because lm is defined through a function. I'm using an RC class, which gets initialized through the initialize function. I want to create my function using linreg_mod <- linreg(formula=Petal.Length ~ Species, data=iris) and have defined .self$call <<- match.call(expand.dots=TRUE) inside the initialize function. I know TRUE is the default for expand.dots, but either way it doesn't seem to do it. When I then call linreg_mod$call I get .Object$initialize(formula = ..1, data = ..2). Does anyone know why it's giving me the ..1, ..2, rather than my input? Thanks!


Solution

  • You should be able to use match.call() in the usual way with a reference class. Here's a minimal example:

    lm_wrapper <- setRefClass("lm_wrapper", methods = list(
        match_call_demo = function(...) {
            # This method is just to demonstrate the output
            # of match.call() and not required in your class
            as.list(match.call())
        },
        linreg = function(...) {
            # The actual regression method
            args  <- as.list(match.call())
            lm(formula = args$formula, data = eval.parent(args$data))
        }
    ))
    

    You can then call this with your arguments. First let's call the demo to show the output of match.call(). Note I have used as.list() here as it will be easier to parse the individual arguments. As the R Language Definition states:

    The components of a call object are accessed using a list-like syntax, and may in fact be converted to and from lists using as.list and as.call

    lm_model$match_call_demo(formula = Petal.Length ~ Species, data = iris)
    # [[1]]
    # lm_model$linreg
    
    # $formula
    # Petal.Length ~ Species
    
    # $data
    # iris
    

    Note that with this method it is necessary to eval() the args$data argument in the parent frame, which in this case is the global environment. I initially used get() to do this. However, that works if the argument is a name, e.g. iris, but not if the argument is a call which we want to evaluate, e.g. head(iris). Thanks to Konrad Rudolph for the comment explaining this.

    To run the model we can simply provide the relevant objects of the list to the arguments.

    lm_model$linreg(formula = Petal.Length ~ Species, data = iris)
    # Call:
    # lm(formula = args$formula, data = eval.parent(args$data))
    
    # Coefficients:
    #       (Intercept)  Speciesversicolor   Speciesvirginica  
    #             1.462              2.798              4.090  
    

    Note that alternately, and perhaps preferably, you can also take Konrad Rudolph's suggestion of using standard evaluation.

    lm_wrapper <- setRefClass("lm_wrapper", methods = list(
        linreg = function(...) {
            lm(formula = list(...)$formula, data = list(...)$data)
        }
    ))
    

    I cannot find a huge amount of documentation about RC. The Reference classes chapter of Advanced R by Hadley Wickham contains a little more info, although it appears to be in draft form. Wickham advises that R6 is preferable over RC for various reasons, one of which is that R6 has comprehensive documentation.