【问题标题】:Using dplyr `enquo` after calling argument在调用参数后使用 dplyr `enquo`
【发布时间】:2017-09-19 20:39:11
【问题描述】:

我有一个大致遵循这个结构的函数:

TestFunc <- function(dat, point) {
    if (!(point %in% c("NW", "SE", "SW"))) 
        stop("point must be one of 'SW', 'SE', 'NW'")

    point <- enquo(point)

    return(dat %>% filter(point == !!point))

问题是当我包含值检查时出现以下错误:

Error in (function (x, strict = TRUE)  : 
  the argument has already been evaluated

当我删除检查时,错误消失了。我怎样才能同时保留两者?

【问题讨论】:

  • 你是如何调用这个函数的?您是否将 point 作为未引用的表达式传递?使用带有示例输入数据的 reproducible example 帮助您更容易,因此我们可以尝试运行代码。
  • 没有可重复示例的最佳猜测:将enquo 移到顶部,并在支票中使用!!point
  • 我认为您根本不需要enquo,只需!!。如果您没有同名的变量和函数参数,我认为您根本不需要任何非标准的评估工具。
  • 仅供参考,您的示例检查功能已经存在于基础 R 中;见?match.arg。不过,我认为使用它并不能解决您的问题。
  • @MrFlick 我用引号传递它。不带引号传递会导致缺少对象错误。

标签: r dplyr tidyeval


【解决方案1】:

关于 quosure 框架要记住的一点是,它是一段非常聪明、复杂的代码,可以撤销另一段非常聪明、复杂的代码,让您回到起点。您可以通过非常简单的方式实现您想要的,无需前往 NSE 并再次返回。

TestFunc <- function(dat, point)
{
    if(!(point %in% c("NW", "SE", "SW")))
        stop("point must be one of 'SW', 'SE', 'NW'")
    dat[dat$point == point, ]
}

(这与使用match.arg 之间的区别,正如@Frank 在评论中所建议的那样,如果没有提供输入,match.arg 将使用第一个值作为默认值,而这将失败。)

如果您想调用其他 dplyr/tidyverse 动词,只需在过滤行后执行此操作即可。

【讨论】:

  • 我的例子不是我实际在做的。实际代码是专有的且更复杂,所以我发布了一个简化的示例。
【解决方案2】:

由于与 R 如何优化代码有关的技术原因,您只能捕获尚未评估的参数。

因此,您首先必须使用enquo() 进行捕获,然后继续检查该值。但是,如果您必须同时使用引用代码和基于值的代码,则通常表明存在设计问题。

正如 Hong 建议的那样,在您的情况下,您似乎可以直接取消引用该值而不捕获它。取消引用将确保找到正确的值(因为您为该变量指定了与数据框中的列相同的名称)。

【讨论】:

    【解决方案3】:

    评估point,以便filter 可以分辨出参数和数据列之间的区别

    aosmith 有一个好主意,所以我把它放在答案的形式,连同一个可重复的例子:

    f <- function(dat, point) {
      if (!(point %in% c("NW", "SE", "SW")))
        stop("point must be one of 'SW', 'SE', 'NW'")
    
      filter(dat, point == !!point)
    }
    
    tbl <- tibble(point = c('SE', 'NW'))
    f(tbl, 'SE')
    f(tbl, 'XX')
    

    如果您将point 作为字符串传递,您只需区分参数point(在这种情况下为=“SE”)和列dat$point(或tbl$point,取决于是否您在函数内部或外部)。来自dplyr programming doc

    在 dplyr(和一般的 tidyeval 中)你使用 !!表示您想取消引用输入以便对其进行评估,而不是引用。

    您想要评估参数point,以便将“SE”传递给filter,这样filter 可以区分列point 和参数SE

    (我也尝试使用 filter(dat, .data$point == point),但 RHS point 仍然指的是列,而不是 f 参数。)

    我希望这个示例对您的真实代码有所帮助。 ?

    【讨论】:

      猜你喜欢
      • 2018-01-20
      • 1970-01-01
      • 2020-01-17
      • 1970-01-01
      • 2017-09-10
      • 2018-04-21
      • 2020-02-01
      • 2017-09-10
      • 1970-01-01
      相关资源
      最近更新 更多