3

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")
CallumH
  • 751
  • 1
  • 7
  • 22

3 Answers3

2

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"
    )
)
Lionel Henry
  • 6,652
  • 27
  • 33
1

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.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • the ````bquote```` method works a treat, thanks so much. – CallumH Oct 20 '20 at 21:22
  • 1
    I recommend using `blast <- function(expr, env = caller_env()) eval_bare(enexpr(expr, env))` instead of `quo` + `eval_tidy`. The latter solution always evaluates in a mask even when there is no data. With `blast()`, you can use quasiquotation with immediate evaluation, e.g. `blast(foo(!!x))`. – Lionel Henry Oct 21 '20 at 07:09
  • 1
    Or equivalently, `expr()` + `eval()`, mirroring the `bquote()` + `eval()` solution. There is no reason to use `quo()` and `eval_tidy()` here. – Lionel Henry Oct 21 '20 at 07:13
  • Thanks for the suggestions @LionelHenry I guess I still have to learn the differences between eval, eval_bare, and eval_tidy. There isn’t a clearer way of just doing this for one step in the pipe chain, right? We will have to wrap the whole chain in blast() (or the equivalent) – MrFlick Oct 21 '20 at 08:01
  • You're correct that QQ can't be enabled with an independent pipe step. There has to be something like bquote that wraps the pipeline. In most cases you can use `eval()` instead of `eval_bare()` actually, so `eval()` is probably the better go-to when you don't need quosure support. There are differences but they are subtle. – Lionel Henry Oct 21 '20 at 11:29
  • Thnx @Lionel Henry. I’m not sure why but the ````bquote```` route didn’t work on a subsequent (similar) issue. I swapped over to ````blast```` and it’s now all good in the hood. – CallumH Oct 25 '20 at 11:55
0

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))
cdalitz
  • 1,019
  • 1
  • 6
  • 7
  • That returns an error in the example though ''''Error: Problem with `mutate()` input `nested.col`. x fun = apply_quarterly not a valid option. i Input `nested.col` is `purrr::map(...)`. i The error occurred in group 1: symbol = "AMZN".'''' – CallumH Oct 20 '20 at 20:10
  • ````apply_quarterly <- get('apply.quarterly') FANG %>% group_by(symbol) %>% tq_transmute(select = adjusted, # mutate_fun = apply.quarterly, mutate_fun = apply_quarterly, FUN = max, col_rename = "max.close")```` – CallumH Oct 20 '20 at 20:11
  • Don't forget to pass `mode = "function"` to `get()` in that case. This way it searches for a function up the chain of environments, even when there are objects of the same name on the way. – Lionel Henry Oct 21 '20 at 07:12