R How to Pass a function as a String Inside another Function

Viewed 230

Any assistance on this little conundrum would be mightily appreciated thanks.

I am trying to pass an argument to the tq_transmute function from the tidyquant package; the value for the argument is a function, however I would like to pass it as a string (out with the scope of the example below I’ll be passing it via a Shiny selectInput).

I have tried every way I can think of to turn the string 'apply.quarterly' into the object apply.quarterly accepted by the mutate_fun argument. The commented lines are my failed attempts.

Ultimately, I would like to extend this concept to the other arguments also i.e. FUN = max to FUN = ‘max’.

library(tidyverse)
library(tidyquant)
library(rlang)

FANG %>%
  group_by(symbol) %>%
  tq_transmute(select     = adjusted, 
               mutate_fun = apply.quarterly,
               # mutate_fun = sym('apply.quarterly'),
               # mutate_fun = syms('apply.quarterly'),
               # mutate_fun = !!sym('apply.quarterly'),
               # mutate_fun = !!!sym('apply.quarterly'),
               # mutate_fun = eval(parse('apply.quarterly')),
               # mutate_fun = eval(substitute('apply.quarterly')),
               # mutate_fun = enquo('apply.quarterly'),
               # mutate_fun = expr(!!enquo('apply.quarterly')),
               # mutate_fun = quo('apply.quarterly'),
               # mutate_fun = enquos('apply.quarterly'),
               # mutate_fun = enquote('apply.quarterly'),
               # mutate_fun = quote('apply.quarterly'),
               # mutate_fun = substitute('apply.quarterly'),
               # mutate_fun = parse('apply.quarterly'),
               # mutate_fun = parse('apply.quarterly'),
               # mutate_fun = ensym('apply.quarterly'),
               # mutate_fun = rlang::as_function('apply.quarterly'),
               # mutate_fun = rlang::as_closure('apply.quarterly'),
               # mutate_fun = rlang::as_quosure('apply.quarterly'),
               # mutate_fun = rlang::as_quosure('apply.quarterly'),
               # mutate_fun = enexpr('apply.quarterly'),
               # mutate_fun = enexprs('apply.quarterly'),
               # mutate_fun = ensym('apply.quarterly'),
               # mutate_fun = ensyms('apply.quarterly'),
               # mutate_fun = eval_tidy('apply.quarterly'),
               # mutate_fun = exprs('apply.quarterly'),
               # mutate_fun = expr_deparse('apply.quarterly'),
               # mutate_fun = expr_label('apply.quarterly'),
               # mutate_fun = expr_label(substitute('apply.quarterly')),
               # mutate_fun = expr_label(quote('apply.quarterly')),
               # mutate_fun = parse_expr('apply.quarterly'),
               # mutate_fun = quasiquotation('apply.quarterly'),
               # mutate_fun = quotation('apply.quarterly'),
               # mutate_fun = quotation('apply.quarterly'),
               FUN        = max, 
               col_rename = "max.close")
3 Answers

You can use blast() for quasiquotation with immediate evaluation

blast <- function(expr, env = caller_env()) {
  eval_bare(enexpr(expr), env)
}

blast(list(!!!1:3))
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3

Then

myfun <- "apply.quarterly"

blast(
  FANG %>%
    group_by(symbol) %>%
    tq_transmute(
      select = adjusted,
      mutate_fun = !!sym(myfun),
      FUN = max,
      col_rename = "max.close"
    )
)

It seems that function is a bit finicky for some reason. One way would be to change the call and then evaulate that. For example

myfun <- "apply.quarterly"
bquote(FANG %>%
  group_by(symbol) %>%
  tq_transmute(select     = adjusted, 
               mutate_fun = .(as.name(myfun)),
               FUN        = max, 
               col_rename = "max.close")) %>% 
  eval()

or if you prefer rlang syntax

myfun <- "apply.quarterly"
quo(FANG %>%
         group_by(symbol) %>%
         tq_transmute(select     = adjusted, 
                      mutate_fun = !!sym(myfun),
                      FUN        = max, 
                      col_rename = "max.close")) %>% 
  eval_tidy()

Note that we have to treat the entire expression as rlang quosure. Unless the tq_transmute function was specifically written to handle rlang features like !! then they won't work by default.

You can obtain the function from the string carrying the name of the function with get. The following works, for example:

s <- get("sum")
s(c(1,2,3))
Related