r-在dplyr::summaryze()中设置向量的属性有什么影响



我刚刚遇到dplyr的一些奇怪行为,其中summarize一直引用以前组中的对象。

这里有一个简单的可重复的例子来说明令人惊讶的行为:

library(dplyr, warn.conflicts = FALSE)
tibble(x = rep(letters[1:3], times = 4),
y = rnorm(12)) %>%
group_by(x) %>%
summarize(z1 = sum(y),
z2 = {
attr(y, "test") <- "test"
sum(y)
})
#> # A tibble: 3 × 3
#>   x         z1    z2
#>   <chr>  <dbl> <dbl>
#> 1 a      0.602 0.602
#> 2 b      1.22  0.602
#> 3 c     -0.310 0.602

创建于2022-10-31由reprex包(v2.0.1(

我期望z1z2是相同的。我不明白为什么为向量y设置属性意味着在以后的迭代中,对y的"正确"元素的引用会被遮蔽。

通过在最后一行中使用sum(.data$y)可以很容易地解决这个问题,但我想了解summarize的非标准评估中的作用域规则。任何指向有用文档或解释当前行为在tidyverse非标准评估框架中有意义的指针都将受到赞赏。


我使用的是R 4.1.1和dplyr 1.0.7。

这是一个与作用域有关的问题。如果写入summarize中的变量y,则数据的第一组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

只要我们从本地帧中删除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

或者更好的是,不要写入与数据帧中的变量同名的局部变量:

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

创建于2022-10-31,reprex v2.0.2

相关内容