Tidyverse:为什么在将 helper 放置在 cross 内部时,选择 helper 会通过管道传递到 cross() 抛出有关外部向量的注释/警告/错误?

ran*_*ndy 1 r dplyr tidyverse across

将选择助手 ( matches(), contains(), starts_with(), ends_with())管道化(的值)到across()函数的行为与将选择助手放在 的括号内的行为不同across()

  • 为什么会发生这种情况?
  • 这是预期的行为还是错误?

复制

library(dplyr)

# Very simple function: returns input
self = function(x){x}

# Data to manipulate
dtemp = tibble(var = 1:2)

# No note/warning/error when selection helper is inside across()
dtemp %>% mutate(across(matches("var"), self))

# Note/warning/error when selection helper is piped to across()
dtemp %>% mutate(matches("var") %>% across(self))
Run Code Online (Sandbox Code Playgroud)

观察到的行为

最后一行导致 R 打印

Note: Using an external vector in selections is ambiguous.
i Use `all_of(.)` instead of `.` to silence this message.
i See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
This message is displayed once per session.
Run Code Online (Sandbox Code Playgroud)

请注意,此消息每个 session 仅打印一次,因此您必须重新启动 R 才能再次看到它(除非有其他方法可以重置控制此打印的计数器)。

倒数第二个命令(带matches()inside across())不会导致 R 打印注释。

预期行为

最后两个命令的行为相同。

附加信息

  • dplyr 版本:1.0.6
  • tidyverse 版本:1.3.1
> sessionInfo()
R version 4.0.3 (2020-10-10)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19042)

Matrix products: default

locale:
[1] LC_COLLATE=English_United States.1252  LC_CTYPE=English_United States.1252   
[3] LC_MONETARY=English_United States.1252 LC_NUMERIC=C                          
[5] LC_TIME=English_United States.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_1.0.6

loaded via a namespace (and not attached):
 [1] fansi_0.5.0      assertthat_0.2.1 utf8_1.2.1       crayon_1.4.1    
 [5] R6_2.5.0         DBI_1.1.1        lifecycle_1.0.0  magrittr_2.0.1  
 [9] pillar_1.6.1     cli_2.5.0        rlang_0.4.11     rstudioapi_0.13 
[13] vctrs_0.3.8      generics_0.1.0   ellipsis_0.3.2   tools_4.0.3     
[17] glue_1.4.2       purrr_0.3.4      compiler_4.0.3   pkgconfig_2.0.3 
[21] tidyselect_1.1.1 tibble_3.1.2    
Run Code Online (Sandbox Code Playgroud)

Bar*_*iuh 5

matches返回一个字符向量。在最近的dplyr版本中,all_of引入了消除歧义。假设您有一个data.frame包含两列的x并且y 进一步假设您有一个变量x = 'y'。现在如果你select(x),你的意思是列x还是列yall_of(x)将消除这种歧义。因此,当您通过管道连接matches到时across,您似乎正在选择一个名为.. 这样做的原因是评估选择助手本身只是返回一个向量作为 .cols 的参数。这是 R 管道和输入的基本原理。您可以使用rlang::quo. 如果f <- function(x) rlang::quo(x),那么跑步和跑步f(1)是不同的1 %>% f. 首先返回一个带引号的 1,第二个返回一个带引号的.。因此,如果我们评估across(<selection-helper>)this 与评估相同f(1),而<selection-helper> %>% across()与 相同1 %>% f()。因为后者看起来像一个变量.,它有一个包含其值的专用环境(因此它看起来像一个名为..

为了澄清,让我们看看输出如下:

library(magrittr)
f <- function(x) rlang::enquo(x)
f(1)
#> <quosure>
#> expr: ^1
#> env:  empty
1 %>% 
    f()
#> <quosure>
#> expr: ^.
#> env:  00000000166DD798
`%>%`(1,f)
#> <quosure>
#> expr: ^.
#> env:  00000000165176F0
Run Code Online (Sandbox Code Playgroud)

创建于 2021-06-24 由reprex 包( v2.0.0 ) 第一个输出捕获和引号1(它没有环境它不是对象/符号),在使用管道命令时,我们将值存储lhs在一个带有符号/名称的环境.。该值1现在包含在该环境中。这就是管道工作原理的本质。将lhs值存储在名为 的环境中.,然后将其作为第一个参数rhs(除非.放在其他地方)并对其进行评估。

因此,它会抛出警告,因为看起来您正在提供一个符号作为输入,而不是一个值。如果保持<selection-help>insidecross,那它不是符号/对象,它是字符向量,并且字符向量没有歧义(因为它不是符号)。原理与f(1). 我希望这能提供一些清晰度。需要注意的是,一旦我们评估输入,它就不再是.它的真实值。您可以通过print(x)在引用之前添加来查看这一点x。您可以在高级 R 编程https://adv-r.hadley.nz/主要第 7 章中阅读有关此内容的更多信息,以了解元编程部分中 R 引用、承诺和评估中的环境。

  • 事实上,它确实准确地回答了为什么会发生这种情况。管道过程为下一个函数提供一个名为“.”的输入。评估选择助手本身只是返回一个向量作为“.cols”的参数。这是“R”管道和输入的基本原理。您可以使用“rlang::quo”进行测试。if `f &lt;- function(x) rlang::quo(x)` 运行 `f(1)` 与运行 `1 %&gt;% f` 不同。第一个返回引号“1”,第二个返回引号“.”。 (2认同)