为什么整洁的评估会抛出错误,除非打印或强制参数?

Why does tidy evaluation throw an error unless an argument is printed or forced?

代码

## 1. Get Hours into Tibble  
## ---------------------
  hrsThreshold <- function(input_data, group1, group2, group2_String) {

    # print ({{group2}})   # for some ridiculous reason, code doesn't work without this print statement in!
    
  # Hours
    hrs <-
    # thresholds
      input_data %>% 
      mutate( score = rowSums(select(., starts_with("less_than")), na.rm = TRUE)) %>%

    # scores & summary
      group_by({{ group1 }}, {{ group2 }}, score) %>%
      summarise(n_points = n()) %>%
      mutate(percent = n_points / sum(n_points)) %>%
      mutate(score  = as_factor(score)) %>%

    # add percentage labels.
      group_by({{ group1 }}, {{ group2 }}) %>%

      mutate(ymax = cumsum(percent),
             ymin = c(0, head(cumsum(percent), n = -1))) %>%

      mutate(label_pos = (ymax+ymin) / 2,
             label = ifelse(percent < 0.01, "", paste0(round(percent*100, 0), "%"))) %>%

      ungroup()
    
    hrs
    

  # Hours All
    hrs_all <-
      hrs %>%
      drop_na( {{ group2 }} ) %>%
      select(-label) %>%

    # Sum n_points
      group_by({{ group1 }}, score) %>%
      summarise_at(
        vars(n_points),
        .funs = sum) %>%

      mutate(percent = n_points / sum(n_points)) %>%

    # redo geometry of rectangles.
      group_by({{ group1 }}) %>%
      mutate(ymax = cumsum(percent),
             ymin = c(0, head(cumsum(percent), n = -1))) %>%
      mutate(label_pos = (ymax+ymin) / 2,
             label = ifelse(percent < 0.01, "", paste0(round(percent*100, 0), "%"))) %>%

    # Change room_type = All
      mutate({{ group2 }} := "all")


    # Join All with Variable
      hrs_by_room <- full_join(hrs_all, hrs)
      return(hrs_by_room)
  }



  # Debug Code
    analysis_vars <- c("room_type", "tenure")
    analysis_index <- 2
    
  # Dynamic Variable
    mytempVar      <- analysis_vars[analysis_index]

    mytempVarTitle <- mytempVar
    mytempVarTitle <- str_replace(mytempVarTitle, "[_]", " ")
    mytempVarTitle <- str_to_title(mytempVarTitle)
    
    mytempVar_     <- sym(mytempVar)


  # run command
  hrs_by_room   <- hrsThreshold(risk_assigned, hhi, mytempVar_,   c({{mytempVar_}}))

问题描述

上面的代码仅在以下行时有效:

print ({{group2}})

未评论!!!从表面上看,这个命令什么都不做。但是如果我的机器上没有它,就会出现以下错误:

Error: Must group by variables found in `.data`.
* Column `mytempVar_` is not found.
Run `rlang::last_error()` to see where the error occurred.
> rlang::last_error()
<error/rlang_error>
Must group by variables found in `.data`.
* Column `mytempVar_` is not found.
Backtrace:
  1. global::hrsThreshold(...)
 11. dplyr:::group_by.data.frame(...)
 12. dplyr::group_by_prepare(.data, ..., .add = .add, caller_env = caller_env())
Run `rlang::last_trace()` to see the full context.
> rlang::last_trace()
<error/rlang_error>
Must group by variables found in `.data`.
* Column `mytempVar_` is not found.
Backtrace:
     x
  1. +-global::hrsThreshold(...)
  2. | \-`%>%`(...)
  3. +-dplyr::ungroup(.)
  4. +-dplyr::mutate(...)
  5. +-dplyr::mutate(...)
  6. +-dplyr::group_by(...)
  7. +-dplyr::mutate(., score = as_factor(score))
  8. +-dplyr::mutate(., percent = n_points/sum(n_points))
  9. +-dplyr::summarise(., n_points = n())
 10. +-dplyr::group_by(...)
 11. \-dplyr:::group_by.data.frame(...)
 12.   \-dplyr::group_by_prepare(.data, ..., .add = .add, caller_env = caller_env())

我已经确认这是导致我的整个报告应用程序在多个功能中崩溃了好几天的问题。我只是通过不小心放入打印语句来调试问题才最终得到它的工作。于是它神秘地开始工作了。到底是怎么回事?

源文件

library(tidyverse)
risk_assigned <- read.csv( text="hhi,device_id,rental_type,room,room_type,ts,temp,less_than_21,less_than_18,less_than_16,less_than_12
MyOrg,2C7970,Council Rental,Lounge,living,1598291349,18.3,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598292249,18.3,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598293149,18.3,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598294049,18.3,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598294949,18.2,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598353449,18.5,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598354354,18.5,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598355254,18.5,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598356154,18.5,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598357054,18.5,1,NA,NA,NA
MyOrg,2C7970,Council Rental,Lounge,living,1598357954,18.5,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598358854,18.5,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598359754,18.6,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598360654,18.5,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598361554,18.5,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598362454,18.4,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598363354,18.4,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598364254,18.4,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598365154,18.4,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598366054,18.3,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598366954,18.2,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598367850,18.2,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598368750,18.1,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598369650,18.1,1,NA,NA,NA
MyOrg,2C7971,Private Rental,Lounge,living,1598370550,18,1,NA,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598371450,17.9,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598372350,17.9,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598373250,17.8,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598374150,17.8,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598375050,17.8,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598375950,17.7,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598376850,17.6,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598377750,17.6,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598378650,17.6,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598379553,17.5,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598380453,17.5,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598381353,17.4,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598439851,17.7,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598440751,17.7,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598441651,17.6,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598442551,17.6,1,1,NA,NA
MyOrg,2C7972,Private Rental,Lounge,living,1598443451,17.5,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598444351,17.5,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598445251,17.4,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598446151,17.4,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598447051,17.3,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598447951,17.3,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598448851,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598449751,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598450651,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598451554,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598452454,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598453354,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598454254,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598455154,17.1,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598456054,17,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598456954,17,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598457854,16.9,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598458754,16.9,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598459654,16.9,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598460554,16.9,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598461454,16.8,1,1,NA,NA
MyOrg,2C7973,Council Rental,Lounge,living,1598462354,16.8,1,1,NA,NA
MyOrg,2C7974,Council Rental,Lounge,living,1598463254,16.7,1,1,NA,NA
MyOrg,2C7974,Council Rental,Lounge,living,1598464154,16.8,1,1,NA,NA
MyOrg,2C7974,Council Rental,Lounge,living,1598465051,16.7,1,1,NA,NA
MyOrg,2C7974,Council Rental,Lounge,living,1598465951,16.8,1,1,NA,NA
MyOrg,2C7974,Council Rental,Lounge,living,1598466851,16.7,1,1,NA,NA")

为什么会失败?

这是一个最小的例子:

var <- rlang::sym("cyl")

fn <- function(var, force) {
  if (force) {
    force(var)
  }

  dplyr::group_by(mtcars, {{ var }})
}

预期的行为是:

fn(var, force = FALSE)
#> Error: Must group by variables found in `.data`.
#> * Column `var` is not found.

这是意外行为:

fn(var, force = TRUE)
#> # A tibble: 32 × 11
#> # Groups:   cyl [3]
#>     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1  21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3  22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> 4  21.4     6   258   110  3.08  3.22  19.4     1     0     3     1
#> # … with 28 more rows

造成这种意外行为的原因有两个。

  1. 断章取义使用{{等同于强行争论。所以 print({{ var }}) 等价于 force(var)。参见 https://rlang.r-lib.org/reference/topic-inject-out-of-context.html

  2. 无法化解强行争论。而且因为编译器从 promises 中解包常量,所以我们无法检测到这种情况而抛出错误。这就是为什么强制参数被注入而不是化解的原因。参见 https://rlang.r-lib.org/reference/topic-embrace-non-args.html

我们无法修复 (1) 或 (2),因为 tidy evaluation 不是在核心语言中实现的,而是在它之上实现的。

我该怎么办?

在您的示例中,您似乎希望通过参数将列名传递给您的函数。

首先要注意的是,由于该参数是通过 {{ 传递给 group_by() 的,因此您的函数继承了 group_by() 的所有行为。它采用不带引号的列名并支持所有整洁的评估功能。

当字符串中有列名时,传递它的规范方法是使用 .data 代词:

var <- "cyl"

fn <- function(var) {
  dplyr::group_by(mtcars, {{ var }})
}

fn(var)
#> Error: Must group by variables found in `.data`.
#> * Column `var` is not found.

fn(.data[[var]])
#> # A tibble: 32 × 11
#> # Groups:   cyl [3]
#>     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb
#>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
#> 1  21       6   160   110  3.9   2.62  16.5     0     1     4     4
#> 2  21       6   160   110  3.9   2.88  17.0     0     1     4     4
#> 3  22.8     4   108    93  3.85  2.32  18.6     1     1     4     1
#> 4  21.4     6   258   110  3.08  3.22  19.4     1     0     3     1
#> # … with 28 more rows

参见 https://rlang.r-lib.org/reference/dot-data.html