con*_*-ae 7 r dplyr non-standard-evaluation
我刚刚遇到了一些奇怪的行为,其中dplyr不断summarize引用前一组中的对象。
这是一个简单的可重复示例来说明令人惊讶的行为:
\nlibrary(dplyr, warn.conflicts = FALSE)\ntibble(x = rep(letters[1:3], times = 4),\n y = rnorm(12)) %>%\n group_by(x) %>%\n summarize(z1 = sum(y),\n z2 = {\n attr(y, "test") <- "test"\n sum(y)\n })\n#> # A tibble: 3 \xc3\x97 3\n#> x z1 z2\n#> <chr> <dbl> <dbl>\n#> 1 a 0.602 0.602\n#> 2 b 1.22 0.602\n#> 3 c -0.310 0.602\nRun Code Online (Sandbox Code Playgroud)\n由reprex 包于 2022 年 10 月 31 日创建(v2.0.1)
\n我期望的是z1和z2是相同的。我不明白为什么为向量设置属性y意味着在以后的迭代中,对“正确”元素的引用y被隐藏。
通过在最后一行使用可以轻松解决该问题sum(.data$y),但我想了解summarize. 任何指向有用文档或解释为什么当前行为在 tidyverse 非标准评估框架中有意义的指针都是值得赞赏的。
我正在使用 R 4.1.1 和 dplyr 1.0.7。
\n这是一个与范围界定相关的问题。如果您写入yinside变量summarize,则数据y变量的第一个分组将被复制到一个名为 的局部变量中y,该变量与y数据框中的 不同。因为它是局部变量,所以它在y传递的数据帧中之前的搜索路径上找到。由于内部后续组的计算使用相同的环境summarize,因此该局部变量对于每个组都保持不变。
如果我们这样做,我们就可以看到这一点:
library(dplyr, warn.conflicts = FALSE)
set.seed(1)
tibble(x = rep(letters[1:3], times = 4),
y = rnorm(12)) %>%
group_by(x) %>%
summarize(z1 = sum(y),
z2 = {
y <- y
sum(y)
})
#> # A tibble: 3 x 3
#> x z1 z2
#> <chr> <dbl> <dbl>
#> 1 a 1.15 1.15
#> 2 b 2.76 1.15
#> 3 c -0.690 1.15
Run Code Online (Sandbox Code Playgroud)
只要我们y从本地框架中删除变量的本地副本,这种情况就不会发生:
library(dplyr, warn.conflicts = FALSE)
set.seed(1)
tibble(x = rep(letters[1:3], times = 4),
y = rnorm(12)) %>%
group_by(x) %>%
summarize(z1 = sum(y),
z2 = {
attr(y, "test") <- "test"
x <- sum(y)
rm(y)
x
})
#> # A tibble: 3 x 3
#> x z1 z2
#> <chr> <dbl> <dbl>
#> 1 a 1.15 1.15
#> 2 b 2.76 2.76
#> 3 c -0.690 -0.690
Run Code Online (Sandbox Code Playgroud)
或者更好的是,不要写入与数据框中的变量同名的局部变量:
tibble(x = rep(letters[1:3], times = 4),
y = rnorm(12)) %>%
group_by(x) %>%
summarize(z1 = sum(y),
z2 = {
new_y <- y
attr(new_y, "test") <- "test"
sum(new_y)
})
#> # A tibble: 3 x 3
#> x z1 z2
#> <chr> <dbl> <dbl>
#> 1 a 1.15 1.15
#> 2 b 2.76 2.76
#> 3 c -0.690 -0.690
Run Code Online (Sandbox Code Playgroud)
创建于 2022 年 10 月 31 日,使用reprex v2.0.2