调用参数后使用dplyr`enquo`

时间:2017-09-19 20:39:11

标签: r dplyr tidyeval

我有一个大致遵循这种结构的功能:

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

删除支票后错误消失。我怎么能保留两者?

3 个答案:

答案 0 :(得分:2)

关于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, ]
}

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

如果你想调用其他dplyr / tidyverse动词,只需在过滤行后再这样做。

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

评估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(=&#34; SE&#34;,在本例中)和列dat$point(或tbl$point,视您是否在函数内部或外部而定。来自dplyr programming doc

  

在dplyr(和一般的tidyeval)你用!!要说你想要取消引用一个输入,以便它被评估,而不是引用。

您想要评估参数point,以便&#34; SE&#34;传递给filter,这样filter可以区分列point和参数SE

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

我希望这个例子可以帮助您实现真正的代码。