【问题标题】:Can you create a function in R from text strings by using substitute()?您可以使用substitute() 在R 中从文本字符串创建一个函数吗?
【发布时间】:2021-02-13 00:18:32
【问题描述】:

我正在尝试编写一个函数,允许我输入 fieldoperatorvalue 的文本字符串,并返回一个简单的 dplyr::filter 函数,然后我可以将其应用于数据集。

例子:

library(dplyr)
field <- "Species"
operator <- "=="
value <- "virginica"
myfun <- substitute(
   function(x) filter(x, EXPR(FIELD, VALUE)),
   list(
     FIELD = as.symbol(field),
     EXPR = as.symbol(operator),
     VALUE = value
   )
 )
myfun
function(x) filter(x, Species == "virginica")

到目前为止,一切都很好,对吧?看起来我们都准备好了。但没那么快:

> myfun(iris)
Error in myfun(iris) (from foo.R!10Zf0E#19) : could not find function "myfun"

如果我输入class(myfun),结果证明我创建了一个名为call 的东西。但我真的想要一个功能。有没有办法把调用变成一个函数,或者重写上面的代码,让我实际上最终得到一个工作函数?

【问题讨论】:

    标签: r function dplyr substitution nse


    【解决方案1】:

    嗯,这很容易。解决方案是用eval() 包裹substitute(),如下所示:

    > myfun <- eval(
          substitute(
            function(x) filter(x, EXPR(FIELD, VALUE)),
            list(
              FIELD = as.symbol(field),
              EXPR = as.symbol(operator),
              VALUE = value
            )
          )
        )
    

    然后myfun(iris) 按预期工作。

    【讨论】:

      【解决方案2】:

      更“R”的方式是使用formalsbody

      f <- function() {}
      formals(f) <- alist(x = )
      body(f) <- substitute(
        filter(x, EXPR(FIELD, VALUE)),
        list(FIELD = as.symbol(field), EXPR = as.symbol(operator), VALUE = value)
      )
      f
      # function (x)
      #   filter(x, Species == "virginica")
      f(iris)
      

      此外,您还可以在函数中添加形式和线条:

      formals(f) <- c(formals(f), alist(y = 120))
      f
      # function (x, y = 120)
      #   filter(x, Species == "virginica")
      body(f) <- as.call(c(as.name('{'), quote(x <- head(x, y)), body(f)))
      f
      # function (x, y = 120)
      # {
      #   x <- head(x, y)
      #   filter(x, Species == "virginica")
      # }
      f(iris)
      

      并且还编辑函数的行:

      body(f)[[2]] <- quote(x <- tail(x, y))
      # function (x, y = 120)
      # {
      #   x <- tail(x, y)
      #   filter(x, Species == "virginica")
      # }
      f(iris)
      

      【讨论】:

        【解决方案3】:

        我们可以动态创建过滤器表达式并使用eval(parse())eval(parse_expr())

        myfun <- function(x, field, operator, value) {
             dplyr::filter(x, eval(rlang::parse_expr(sprintf('%s %s "%s"', 
                                  field, operator, value))))
        }
        
        field <- "Species"
        operator <- "=="
        value <- "virginica"
        myfun(iris, field, operator, value)
        
        #   Sepal.Length Sepal.Width Petal.Length Petal.Width   Species
        #1           6.3         3.3          6.0         2.5 virginica
        #2           5.8         2.7          5.1         1.9 virginica
        #3           7.1         3.0          5.9         2.1 virginica
        #4           6.3         2.9          5.6         1.8 virginica
        #5           6.5         3.0          5.8         2.2 virginica
        #6           7.6         3.0          6.6         2.1 virginica
        #7           4.9         2.5          4.5         1.7 virginica
        #8           7.3         2.9          6.3         1.8 virginica
        #9           6.7         2.5          5.8         1.8 virginica
        #10          7.2         3.6          6.1         2.5 virginica
        #...
        #...
        

        在哪里

        sprintf('%s %s "%s"', field, operator, value)
        #[1] "Species == \"virginica\""
        

        【讨论】:

          猜你喜欢
          • 2018-09-17
          • 1970-01-01
          • 1970-01-01
          • 2020-11-28
          • 1970-01-01
          • 1970-01-01
          • 2019-07-24
          • 2020-08-11
          • 2021-03-24
          相关资源
          最近更新 更多