Tidyverse、Rlang 和 tidyeval:Bang bang (!!) 函数内部失败,但它似乎在没有引用的情况下工作

Tidyverse, Rlang and tidyeval: Bang bang (!!) failing inside function, but it appears to work without quotation

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

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

原始函数的脚本是:

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)
  }

然后我 运行 函数具有:

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

我已经评论了我遇到错误的部分(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) 但我不明白为什么。通过使用 TRUE 和 FALSE 进行测试,R 可能会将 bang bang 解释为双重否定,并且由于它需要一个布尔值,因此会抛出错误。

感谢您的帮助!

直接使用 idgroup -- 我假设这些是传入的字符串,所以我认为没有必要将 quosure 强制转换为字符串。此外,!! 可以在支持整洁评估的函数内部使用。确定这一点的一个简单的第一步是 "is the function from a base R package"。 as.character() 是,所以它不起作用。

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

By testing with TRUE and FALSE, R might be interpreting bang bang as double negation and, since it's expecting a boolean, it throws out an error.

你的推测是正确的。

The last option (in bold), works even though it has no unquotation and the same is true if I remove the bang bang (!!) when filtering the full_database, by using filter(numid==id_name)

Tidy-evaluation 的核心是在正确的环境中评估符号,或者至少这是我的看法。这个 filter() 之所以有效,是因为它查找符号 id_name,但没有在数据中找到它(它查找的第一个位置),然后在封闭环境中查找,找到它,并对语句求值。

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