【问题标题】:R How to Pass a function as a String Inside another FunctionR如何将函数作为字符串传递给另一个函数
【发布时间】:2021-02-03 16:58:10
【问题描述】:

非常感谢您对这个小难题的任何帮助。

我正在尝试从tidyquant 包中将参数传递给tq_transmute 函数;参数的值是一个函数,但是我想将它作为字符串传递(在下面示例的范围内,我将通过 Shiny selectInput 传递它)。

我已经尝试了所有我能想到的方法将字符串“apply.quarterly”转换为apply.quarterly 接受的对象mutate_fun 参数。注释行是我失败的尝试。

最终,我想将此概念扩展到其他参数,即FUN = maxFUN = ‘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")

【问题讨论】:

    标签: r tidyverse symbols tidyeval tidyquant


    【解决方案1】:

    你可以通过get从带有函数名的字符串中获取函数。例如以下工作:

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

    【讨论】:

    • 虽然 ''''Error: Problem with mutate() input nested.col 在示例中返回错误。 x fun = apply_quarterly 不是有效选项。 i 输入nested.colpurrr::map(...)。 i 错误发生在第 1 组:symbol = "AMZN".''''
    • apply_quarterly &lt;- get('apply.quarterly') FANG %&gt;% group_by(symbol) %&gt;% tq_transmute(select = adjusted, # mutate_fun = apply.quarterly, mutate_fun = apply_quarterly, FUN = max, col_rename = "max.close")
    • 在这种情况下不要忘记将mode = "function" 传递给get()。通过这种方式,它会在环境链中向上搜索函数,即使途中有同名对象也是如此。
    【解决方案2】:

    由于某种原因,该功能似乎有点挑剔。一种方法是更改​​呼叫,然后对其进行评估。例如

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

    或者如果你更喜欢 rlang 语法

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

    请注意,我们必须将整个表达式视为rlang quosure。除非 tq_transmute 函数是专门为处理像 !! 这样的 rlang 功能而编写的,否则默认情况下它们将不起作用。

    【讨论】:

    • bquote 方法很有效,非常感谢。
    • 我建议使用blast &lt;- function(expr, env = caller_env()) eval_bare(enexpr(expr, env)) 而不是quo + eval_tidy。即使没有数据,后一种解决方案也总是在掩码中进行评估。使用blast(),您可以使用具有立即评估的准引用,例如blast(foo(!!x)).
    • 或等效地,expr() + eval(),镜像bquote() + eval() 解决方案。这里没有理由使用quo()eval_tidy()
    • 感谢@LionelHenry 的建议我想我仍然需要了解eval、eval_bare 和eval_tidy 之间的区别。没有一种更清晰的方法可以仅在管道链中的一个步骤中执行此操作,对吧?我们必须将整个链包裹在 blast() (或等效的)中
    • 你说的对,QQ不能用独立的管道步骤启用。必须有类似 bquote 的东西来包装管道。在大多数情况下,您实际上可以使用eval() 而不是eval_bare(),因此当您不需要quosure 支持时,eval() 可能是更好的选择。有区别,但很微妙。
    【解决方案3】:

    您可以使用blast() 进行准报价并立即评估

    blast <- function(expr, env = caller_env()) {
      eval_bare(enexpr(expr), env)
    }
    
    blast(list(!!!1:3))
    #> [[1]]
    #> [1] 1
    #>
    #> [[2]]
    #> [1] 2
    #>
    #> [[3]]
    #> [1] 3
    

    然后

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

    【讨论】:

      猜你喜欢
      • 2019-08-19
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 2023-01-12
      • 2013-04-13
      • 2012-05-25
      相关资源
      最近更新 更多