Tidyverse、Rlang 和 tidyeval:Bang bang (!!) 在函数内部失败,但它似乎无需引用即可工作

fil*_*fig 5 r tidyr tidyeval rlang

我正在一个长数据库(full_database)上运行一个函数,该数据库有两个主要组,我需要在每个组的多个子集上执行各种线性模型。

然后,我将 R^2、调整后的 R^2 和 p.value 提取到数据框中,其中每一行对应于一次比较。由于有 30 种不同的情况,我有另一个小标题,其中列出了函数参数所在的所有可能性( possibility )。

原始函数的脚本是:

database_correlation <-  function(id, group) {

    require(dplyr)
    require(tidyr)
    require(rlang)

    id_name <- quo_name(id)
    id_var <- enquo(id)
    group_name <- quo_name(group)
    group_var <- enquo(group)

    corr_db <- full_database %>%
      filter(numid==!!id_name) %>%
      filter(major_group==!!group_name) %>%
      droplevels()

    correlation <- summary(lm(yvar~xvar, corr_db))

    id.x <- as.character(!!id_var) #Gives out an error: "invalid argument type"
    group.x <- as.character(!!group_var) #Gives out an error: "invalid argument type"
    r_squared <- correlation$r.squared
    r_squared_adj <- correlation$adj.r.squared
    p_value <- correlation$coefficients[2,4]

    data.frame(id.x, group.x, r_squared, r_squared_adj, p_value, stringsAsFactors=FALSE)
  }
Run Code Online (Sandbox Code Playgroud)

然后我运行该函数:

correlation_all <- lapply(seq(nrow(possibilities)), function(index) {
    current <- possibilities[index,]
    with(current, database_correlation(id, database))
  }) %>%
    bind_rows()
Run Code Online (Sandbox Code Playgroud)

我已经评论了出现错误的部分(id.x 和 group.x 分配),并且我尝试了多种替代方案(我将使用 id.x 作为示例):

  1. id_var <- enquo(id) & id.x <- print(!!id_var)
  2. id_var <- sym(id) & id.x <- as.character(!!id_var)
  3. id_var <- sym(id) & id.x <- print(!!id_var)
  4. 没有 id_var & id.x <- !!id_name
  5. 没有 id_var & id.x <- id_name

最后一个选项(粗体)即使没有取消引号也可以工作,如果我在过滤 full_database 时直接使用filter(numid==id_name)删除 bang bang (!!) ,情况也是如此,但我可以'不明白为什么。通过使用 TRUE 和 FALSE 进行测试,R 可能会将 bang bang 解释为双重否定,并且由于它需要布尔值,因此会抛出错误。

感谢您的帮助!

smi*_*son 2

直接使用idand group- 我假设这些是传入的字符串,所以我认为不需要将 quosure 强制为字符串。此外, !!可以在支持整洁评估的函数内部使用。确定这一点的简单第一步是“该函数来自基本 R 包”。as.character()是,所以它不起作用。

如果您决定将 quosure 转换为字符串,则可以使用 将rlang::as_name()相应的符号作为字符串检索。这是推荐的方法。

通过使用 TRUE 和 FALSE 进行测试,R 可能会将 bang bang 解释为双重否定,并且由于它需要布尔值,因此会抛出错误。

你的假设是正确的。

最后一个选项(粗体)即使没有取消引号也可以工作,如果我在过滤 full_database 时使用 filter(numid==id_name) 删除 bang bang (!!) ,情况也是如此

整洁评估的核心是在正确的环境中评估符号,或者至少这是我的看法。这filter()是有效的,因为它查找符号id_name,在数据中(它查找的第一个位置)没有找到它,然后在封闭环境中查找,找到它,并评估该语句。

id_name想象一下,如果您有一个在数据中命名的列。您如何区分数据id_name和封闭环境中的数据。好吧,如果您想要数据的值,您可以使用.data$id_name(另一个 rlang 构造)。如果您想要数据之外的值,请使用!!。这告诉支持整洁评估的函数查看规则。quosure 标识了它是在哪个环境中定义的。然后它会在该环境中评估该符号,确保不会与数据中的名称发生冲突。