do.call a function in R without loading the package
Asked Answered
G

2

8

I want to do.call an (exported) function from a package and need to specify the package and function in the what argument as a character string (i.e., when the package is not loaded into my environment or there is a potential function name conflict).

The function name is a character string like "lme4::lmer" which specifies both the package and the function.

For example, to conditionally call a function (similar to this question):

FUN = if(TRUE) {
  "lme4::lmer"
} else {
  "nlme::nmle"
}

args.list = list(Reaction ~ Days + (Days | Subject), 
                 quote(lme4::sleepstudy))

do.call(what=FUN, args=args.list)
# Error in `lme4::lmer`(Reaction ~ Days + (Days | Subject), lme4::sleepstudy) : 
#   could not find function "lme4::lmer"

Other ways work but are not what I need:

# Load the package and use only the function string 
library(lme4)
do.call("lmer", ...)

# Or calling the function directly: 
lme4::lmer(...)
Golda answered 16/8, 2016 at 19:41 Comment(11)
Try it without the " quotes.Serle
@Serle No dice. The point is that the what argument to do.call takes a character string, and I'm manipulating the value of that stringGolda
Ok, I don't lmer, but this seems to work: do.call(getFromNamespace("rleid", "data.table"), list(c(1,1), 1:2)) Gotta break your string into its component parts, though (function, then package).Serle
do.call(lme4::"lmer", ...) will work. Read ?"::"Roxanneroxburgh
do.call(stringr::str_c, c(tmp, sep = "")) works! so remove the quotes.Catharina
@Catharina I think the OP is saying that the quotes are there because they (somehow) have the function name as a string and are working with it as such.Serle
@Serle I think OP is thinking he must supply a string there.Catharina
@Jack I think you need to clarify your example.Serle
get("lmer", asNamespace("lme4")), but I don't understand why you can't pass lme4::lmer.Retract
example: importFrom(utils,unzip)Pinnatiped
a <- "abs"; do.call(base::get(a), list(c(-1, -2)))Roxanneroxburgh
H
12

You can't include :: in the string because :: is a function. That string isn't evaluated and parsed. do.call just assumes a function by that name exists. Just like ::, $ is also a function so this doesn't work either

a <- list(f = function(x) x+1)
do.call(a$f, list(4))
# [1] 5
do.call("a$f", list(4))
# Error in do.call("a$f", list(4)) : could not find function "a$f"

If you want to be able to find functions from namespaces, you can write a helper function that parses values when they contain ::

getfun <- function(x) {
    if(length(grep("::", x)) > 0) {
        parts <- strsplit(x, "::")[[1]]
        getExportedValue(parts[1], parts[2])
    } else {
        x
    }
}
getfun("lme4::lmer")

And this should work

do.call(getfun("lme4::lmer"), list(
    Reaction ~ Days + (Days | Subject), 
    quote(lme4::sleepstudy)))
Hughhughes answered 16/8, 2016 at 20:48 Comment(3)
I've never used such arcana as these get*s, but getFromNamespace was what Uwe Ligges recommended a decade ago: r.789695.n4.nabble.com/…Serle
@Serle I just looked at the source of :: and did what it did. I'm sure that would work as well.Hughhughes
Ah ok, I think your way makes more sense. Looks like getFromNamespace is more like ::: (also grabbing unexported funs).Serle
C
4
do.call(eval(parse(text="lme4::lmer")), ...)

eval with parse will work!

> tmp <- expand.grid(letters[1:2], 1:3, c("+", "-"))
> do.call(eval(parse(text='stringr::str_c')), c(tmp, sep = ""))

 [1] "a1+" "b1+" "a2+" "b2+" "a3+" "b3+" "a1-" "b1-" "a2-" "b2-" "a3-" "b3-"

Reference: https://www.r-bloggers.com/converting-a-string-to-a-variable-name-on-the-fly-and-vice-versa-in-r/

Catharina answered 16/8, 2016 at 20:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.