rfunctionwrapperdo.call

base::formals with multiple-option arguments [R]


I trying to extract all arguments and their default options from a function (randomForestSRC::rfsrc) to integrate it within another function. This is what I'm doing:

> mydata <- source("https://pastebin.com/raw/bL8ZHvbt")$value
> myparams <- list(ntree = 500, seed = 333)
> time = "OS"
> event = "Death"

# prepare vars
> aux <- formals(randomForestSRC::rfsrc)[-c(1,2,32,35)] #delete arguments I gonna manually add
> diffs <- setdiff(names(aux), names(myparams))
> method.params <- c(myparams, aux[diffs])

# run function
> f <- as.formula(paste0("Surv(", time, ",", event, ") ~ ."))
> res <- do.call(randomForestSRC::rfsrc, c(list(formula = f, data = mydata), method.params))

Error in (function (formula, data, ntree = 1000, mtry = NULL, ytry = NULL,  : 
  object 'samptype' not found

I guess the error prompts because rfsrc's arguments format: it's not a single value but multiple options... despite of having only one as default option.

> aux$importance #default value for "importance" is "none"
c(FALSE, TRUE, "none", "permute", "random", "anti")

> str(aux)
List of 31
 $ ntree      : num 1000
 $ nodedepth  : NULL
 $ splitrule  : NULL
 $ nsplit     : num 10
 $ importance : language c(FALSE, TRUE, "none", "permute", "random", "anti")
 ...

My question is how can I extract this only default value and not the entire string of options? Thanks in advance.


Solution

  • This will let you alter the defaults of the two parameters you sought to set at 500 and 333 in a new function with the same environment as the original rfsrc. (You can do it in the original function, but that seems more dangerous.)

    library(randomForestSRC)
    rfsrc2 <- rfsrc
    environment(rfsrc2) <- environment(rfsrc)
    params <- formals(rfsrc)
    myparams <- list(ntree = 500, seed = 333)
    params[c("ntree","seed")] <- myparams 
        # more generally could have been `params[names(myparams)]` on LHS
    formals(rfsrc2) <- params
    
    str(rfsrc2)
    #-------
    function (formula, data, ntree = 500, mtry = NULL, ytry = NULL, nodesize = NULL, nodedepth = NULL, splitrule = NULL, nsplit = 10, 
        importance = c(FALSE, TRUE, "none", "permute", "random", "anti"), block.size = if (any(is.element(as.character(importance), 
            c("none", "FALSE")))) NULL else 10, ensemble = c("all", "oob", "inbag"), bootstrap = c("by.root", "none", "by.user"), 
        samptype = c("swor", "swr"), samp = NULL, membership = FALSE, sampsize = if (samptype == "swor") function(x) {
            x * 0.632
        } else function(x) {
            x
        }, na.action = c("na.omit", "na.impute"), nimpute = 1, ntime, cause, proximity = FALSE, distance = FALSE, forest.wt = FALSE, 
        xvar.wt = NULL, yvar.wt = NULL, split.wt = NULL, case.wt = NULL, forest = TRUE, var.used = c(FALSE, "all.trees", "by.tree"), 
        split.depth = c(FALSE, "all.trees", "by.tree"), seed = 333, do.trace = FALSE, statistics = FALSE, ...)  
    
    # test of "function"-ality
    rfsrc2(formula=f, data=mydata)
                             Sample size: 100
                        Number of deaths: 11
                         Number of trees: 500
               Forest terminal node size: 15
           Average no. of terminal nodes: 4.464
    No. of variables tried at each split: 3
                  Total no. of variables: 6
           Resampling used to grow trees: swor
        Resample size used to grow trees: 63
                                Analysis: RSF
                                  Family: surv
                          Splitting rule: logrank *random*
           Number of random split points: 10
                              Error rate: 58.9%
    

    If you wanted to do that with do.call it could have been:

    do.call( rfsrc2, list(formula=f, data=mydata) )
    

    It's crucial that you not remove formula, data and ... from the formals-list since they need to be there when the code in the body of the function seeks to evaluate them. I'm pretty sure you cannot just paste them back (after removing them) by using do.call. You could of course have side-stepped the whole process by just:

    do.call( rfsrc, list(formula=f, data=mydata, ntree=500, seed=333) )
    

    .... but I got the idea you wanted methods to do surgery on a formals-list outside of the function itself.