过时的数据掩码。在 `dplyr::mutate()` 结束后解析 `xxxxxx` 为时已晚

Obsolete data mask. Too late to resolve `xxxxxx` after the end of `dplyr::mutate()`

作为我对 的回答的一部分,我提出了一种完全通用的机制,通过该机制,一个数据帧可以根据存储在另一个数据帧中的条件进行过滤。 OP 叫我出去(该死的!)并要求我实施。

我的解决方案要求我将 函数 存储在过滤器数据框中。这是可能的: 显示如何。

作为一个基本示例,请考虑

library(tidyverse)

longFilterTable <- tribble(
  ~var,   ~value,
  "gear", list(3),
) %>% 
  mutate(
    func=pmap(
      list(value),
      ~function(x) x == ..1[[1]]
    )
  )

longFilterTable
# A tibble: 1 x 3
  var   value      func  
  <chr> <list>     <list>
1 gear  <list [1]> <fn>  

这是一种非常复杂的说法,即“select 只有 mtcars 的那些行 gear3。这行得通:

mtcars %>% filter(longFilterTable$func[[1]](gear)) %>% head(3)
                     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Hornet 4 Drive      21.4   6 258.0 110 3.08 3.215 19.44  1  0    3    1
Hornet Sportabout   18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
Valiant             18.1   6 225.0 105 2.76 3.460 20.22  1  0    3    1
Duster 360          14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
<11 rows deleted for brevity>

现在假设我希望标准更加灵活。例如,我可能想要 select 一个值范围或一个固定值。这似乎是上面过滤器数据集的合理扩展:

longFilterTable <- tribble(
  ~var,   ~value,         ~condition,
  "gear", list(3),        "equal",
  "wt",   list(3,4, 3.9), "range",
) %>% 
  mutate(
    func=pmap(
      list(value, condition),
      ~function(x) {
        case_when(
          condition == "equal" ~ x == ..1[[1]],
          condition == "range" ~ x >= ..1[[1]][1] & x <= ..1[[1]][2],
          TRUE ~ x
        )
      }
    )
  )

longFilterTable
# A tibble: 2 x 4
  var   value      condition func  
  <chr> <list>     <chr>     <list>
1 gear  <list [1]> equal     <fn>  
2 wt    <list [3]> range     <fn>  

但现在当我尝试应用过滤器时,我得到:

mtcars %>% filter(longFilterTable$func[[1]](gear))
 Show Traceback
 
 Rerun with Debug
 Error: Problem with `filter()` input `..1`.
x Obsolete data mask.
x Too late to resolve `condition` after the end of `dplyr::mutate()`.
ℹ Did you save an object that uses `condition` lazily in a column in the `dplyr::mutate()` expression ?
ℹ Input `..1` is `longFilterTable$func[[1]](gear)`.

我试过 deparse()substitute()expression()force()eval() 的各种组合,但无济于事。谁能找到解决方案?

您的问题是 case_when 的所有选项总是被评估并检查正确的输出格式

x <- 1

dplyr::case_when(x < 2 ~ TRUE,
                 x < 0 ~ FALSE)
#> [1] TRUE

dplyr::case_when(x < 2 ~ TRUE,
                 x < 0 ~ stop())
#> Error in eval_tidy(pair$rhs, env = default_env):

在你的例子中,你想使用第一个选项,检查是否相等。然而,范围条件也被评估,但没有第二个值存储在 value 列表中,结果只是 NAs 的向量,因此错误。从 case_when 切换到常规 if else 子句可以解决此问题。

library(purrr)
library(dplyr)
longFilterTable <- tribble(
  ~var,   ~value,         ~condition,
  "gear", list(3),        "equal",
  "wt",   list(3.4, 3.9), "range",
) %>% 
  mutate(
    func=pmap(
      list(value, condition),
      ~function(x) {
        if(..2 == "equal") x == ..1[[1]]
        else if (..2 == "range") x >= ..1[[1]] & x <= ..1[[2]]
        else TRUE
      }
    )
  )


mtcars %>% filter(longFilterTable$func[[2]](drat))
#>                mpg cyl  disp  hp drat    wt  qsec vs am gear carb
#> Mazda RX4     21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
#> Mazda RX4 Wag 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
#> Datsun 710    22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
#> Merc 240D     24.4   4 146.7  62 3.69 3.190 20.00  1  0    4    2
#> Toyota Corona 21.5   4 120.1  97 3.70 2.465 20.01  1  0    3    1