为什么 !! (bang-bang) 结合 as.name() 给出与 !! 不同的输出还是 as.name() 一个人?
Why does !! (bang-bang) combined with as.name() give a different output compared to !! or as.name() alone?
我使用动态变量(例如 ID
)作为引用列名的方式,该列名将根据我当时正在处理的基因而改变。然后,我在 mutate
中使用 case_when
创建一个新列,该列的值取决于动态列。
我认为 !!
(bang-bang) 是我需要强制评估变量内容的东西;但是,我在新专栏中没有得到预期的输出。只有 !!as.name
给了我预期的输出,我不完全明白为什么。有人可以解释为什么在这种情况下仅使用 !!
是不合适的以及 !!as.name
中发生了什么?
这是我编写的一个简单的可重现示例,用于演示我正在经历的事情:
library(tidyverse)
ID <- "birth_year"
# Correct output
test <- starwars %>%
mutate(FootballLeague = case_when(
!!as.name(ID) < 10 ~ "U10",
!!as.name(ID) >= 10 & !!as.name(ID) < 50 ~ "U50",
!!as.name(ID) >= 50 & !!as.name(ID) < 100 ~ "U100",
!!as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
# Incorrect output
test2 <- starwars %>%
mutate(FootballLeague = case_when(
!!(ID) < 10 ~ "U10",
!!(ID) >= 10 & !!(ID) < 50 ~ "U50",
!!(ID) >= 50 & !!(ID) < 100 ~ "U100",
!!(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
# Incorrect output
test3 <- starwars %>%
mutate(FootballLeague = case_when(
as.name(ID) < 10 ~ "U10",
as.name(ID) >= 10 & as.name(ID) < 50 ~ "U50",
as.name(ID) >= 50 & as.name(ID) < 100 ~ "U100",
as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
identical(test, test2)
# FALSE
identical(test2, test3)
# TRUE
sessionInfo()
#R version 4.0.2 (2020-06-22)
#Platform: x86_64-centos7-linux-gnu (64-bit)
#Running under: CentOS Linux 7 (Core)
# tidyverse_1.3.0
# dplyr_1.0.2
干杯!
您可以将表达式包裹在函数 quo()
中,以在应用 !!
运算符后查看运算结果。为简单起见,我将使用较短的表达式进行演示:
准备工作:
library(tidyverse)
ID <- "birth_year"
## Test without quasiquotation:
starwars %>%
filter(birth_year < 50)
实验一:
quo(
starwars %>%
filter(ID < 50)
)
## result: starwars %>% filter(ID < 50)
我们了解到:filter()
不会将 ID
视为变量,而是“按原样”处理。所以我们需要一种机制来告诉 filter()
它应该把 ID
当作变量,并且它应该使用它的值。
--> !!
运算符可用于告诉 filter()
它应该将表达式视为变量并替换其值。
实验二:
quo(
starwars %>%
filter(!!ID < 50)
)
## result: starwars %>% filter("birth_year" < 50)
我们了解到:!!
运算符确实有效:ID
被替换为它的值。但是:ID
的值是 字符串 "birth_year"
。注意结果中的引号。但是您可能知道,tidyverse 函数不将变量名作为字符串,它们需要原始名称,不带引号。与实验 1 比较:filter()
接受所有“原样”,因此它查找名为 "birth_year"
的列(包括引号!)
函数as.name()
有什么作用?
这是一个基本的 R 函数,它接受一个字符串(或一个包含字符串的变量)和 returns 字符串的 内容 作为变量名。
所以如果你在 base R 中调用 as.name(ID)
,结果是 birth_year
,这次没有引号——就像 tidyverse 期望的那样。那么让我们来试试吧:
实验 3:
quo(
starwars %>%
filter(as.name(ID) < 50)
)
## result: starwars %>% filter(as.name(ID) < 50)
我们了解到:这没有用,因为,filter()
再次“按原样”处理所有内容。所以现在它查找名为 as.name(ID)
的列,当然它不存在。
--> 我们需要结合这两件事来让它工作:
- 使用
as.name()
将字符串转换为变量名。
- 使用
!!
告诉filter()
它不应该“按原样”接受事物,而是替换真实值。
实验四:
quo(
starwars %>%
filter(!!as.name(ID) < 50)
)
## result: starwars %>% filter(birth_year < 50)
现在可以了! :)
我在实验中使用了 filter()
,但它与 mutate()
和其他 tidyverse 函数完全相同。
为了方便起见,您还可以按照@Lionel Henry 在此 . See also rlang 0.4.0 release notes
中的建议使用 .data[[]]
library(tidyverse)
ID <- "birth_year"
# Correct output
test <- starwars %>%
mutate(FootballLeague = case_when(
!!as.name(ID) < 10 ~ "U10",
!!as.name(ID) >= 10 & !!as.name(ID) < 50 ~ "U50",
!!as.name(ID) >= 50 & !!as.name(ID) < 100 ~ "U100",
!!as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
test
使用.data
test2 <- starwars %>%
mutate(FootballLeague = case_when(
.data[[ID]] < 10 ~ "U10",
.data[[ID]] >= 10 & .data[[ID]] < 50 ~ "U50",
.data[[ID]] >= 50 & .data[[ID]] < 100 ~ "U100",
.data[[ID]] >= 100 ~ "Senior",
TRUE ~ "Others"
))
test2
#> # A tibble: 87 x 15
#> name height mass hair_color skin_color eye_color
#> <chr> <int> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywalker 172 77 blond fair blue
#> 2 C-3PO 167 75 <NA> gold yellow
#> 3 R2-D2 96 32 <NA> white, blue red
#> 4 Darth Vader 202 136 none white yellow
#> 5 Leia Organa 150 49 brown light brown
#> 6 Owen Lars 178 120 brown, grey light blue
#> 7 Beru Whitesun lars 165 75 brown light blue
#> 8 R5-D4 97 32 <NA> white, red red
#> 9 Biggs Darklighter 183 84 black light brown
#> 10 Obi-Wan Kenobi 182 77 auburn, white fair blue-gray
#> 11 Anakin Skywalker 188 84 blond fair blue
#> 12 Wilhuff Tarkin 180 NA auburn, grey fair blue
#> 13 Chewbacca 228 112 brown unknown blue
#> 14 Han Solo 180 80 brown fair brown
#> 15 Greedo 173 74 <NA> green black
#> birth_year sex gender homeworld species films vehicles starships
#> <dbl> <chr> <chr> <chr> <chr> <list> <list> <list>
#> 1 19 male masculine Tatooine Human <chr [5]> <chr [2]> <chr [2]>
#> 2 112 none masculine Tatooine Droid <chr [6]> <chr [0]> <chr [0]>
#> 3 33 none masculine Naboo Droid <chr [7]> <chr [0]> <chr [0]>
#> 4 41.9 male masculine Tatooine Human <chr [4]> <chr [0]> <chr [1]>
#> 5 19 female feminine Alderaan Human <chr [5]> <chr [1]> <chr [0]>
#> 6 52 male masculine Tatooine Human <chr [3]> <chr [0]> <chr [0]>
#> 7 47 female feminine Tatooine Human <chr [3]> <chr [0]> <chr [0]>
#> 8 NA none masculine Tatooine Droid <chr [1]> <chr [0]> <chr [0]>
#> 9 24 male masculine Tatooine Human <chr [1]> <chr [0]> <chr [1]>
#> 10 57 male masculine Stewjon Human <chr [6]> <chr [1]> <chr [5]>
#> 11 41.9 male masculine Tatooine Human <chr [3]> <chr [2]> <chr [3]>
#> 12 64 male masculine Eriadu Human <chr [2]> <chr [0]> <chr [0]>
#> 13 200 male masculine Kashyyyk Wookiee <chr [5]> <chr [1]> <chr [2]>
#> 14 29 male masculine Corellia Human <chr [4]> <chr [0]> <chr [2]>
#> 15 44 male masculine Rodia Rodian <chr [1]> <chr [0]> <chr [0]>
#> FootballLeague
#> <chr>
#> 1 U50
#> 2 Senior
#> 3 U50
#> 4 U50
#> 5 U50
#> 6 U100
#> 7 U50
#> 8 Others
#> 9 U50
#> 10 U100
#> 11 U50
#> 12 U100
#> 13 Senior
#> 14 U50
#> 15 U50
#> # ... with 72 more rows
检查它们是否相同
identical(test, test2)
#> [1] TRUE
由 reprex package (v0.3.0)
于 2020-11-26 创建
我使用动态变量(例如 ID
)作为引用列名的方式,该列名将根据我当时正在处理的基因而改变。然后,我在 mutate
中使用 case_when
创建一个新列,该列的值取决于动态列。
我认为 !!
(bang-bang) 是我需要强制评估变量内容的东西;但是,我在新专栏中没有得到预期的输出。只有 !!as.name
给了我预期的输出,我不完全明白为什么。有人可以解释为什么在这种情况下仅使用 !!
是不合适的以及 !!as.name
中发生了什么?
这是我编写的一个简单的可重现示例,用于演示我正在经历的事情:
library(tidyverse)
ID <- "birth_year"
# Correct output
test <- starwars %>%
mutate(FootballLeague = case_when(
!!as.name(ID) < 10 ~ "U10",
!!as.name(ID) >= 10 & !!as.name(ID) < 50 ~ "U50",
!!as.name(ID) >= 50 & !!as.name(ID) < 100 ~ "U100",
!!as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
# Incorrect output
test2 <- starwars %>%
mutate(FootballLeague = case_when(
!!(ID) < 10 ~ "U10",
!!(ID) >= 10 & !!(ID) < 50 ~ "U50",
!!(ID) >= 50 & !!(ID) < 100 ~ "U100",
!!(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
# Incorrect output
test3 <- starwars %>%
mutate(FootballLeague = case_when(
as.name(ID) < 10 ~ "U10",
as.name(ID) >= 10 & as.name(ID) < 50 ~ "U50",
as.name(ID) >= 50 & as.name(ID) < 100 ~ "U100",
as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
identical(test, test2)
# FALSE
identical(test2, test3)
# TRUE
sessionInfo()
#R version 4.0.2 (2020-06-22)
#Platform: x86_64-centos7-linux-gnu (64-bit)
#Running under: CentOS Linux 7 (Core)
# tidyverse_1.3.0
# dplyr_1.0.2
干杯!
您可以将表达式包裹在函数 quo()
中,以在应用 !!
运算符后查看运算结果。为简单起见,我将使用较短的表达式进行演示:
准备工作:
library(tidyverse)
ID <- "birth_year"
## Test without quasiquotation:
starwars %>%
filter(birth_year < 50)
实验一:
quo(
starwars %>%
filter(ID < 50)
)
## result: starwars %>% filter(ID < 50)
我们了解到:filter()
不会将 ID
视为变量,而是“按原样”处理。所以我们需要一种机制来告诉 filter()
它应该把 ID
当作变量,并且它应该使用它的值。
--> !!
运算符可用于告诉 filter()
它应该将表达式视为变量并替换其值。
实验二:
quo(
starwars %>%
filter(!!ID < 50)
)
## result: starwars %>% filter("birth_year" < 50)
我们了解到:!!
运算符确实有效:ID
被替换为它的值。但是:ID
的值是 字符串 "birth_year"
。注意结果中的引号。但是您可能知道,tidyverse 函数不将变量名作为字符串,它们需要原始名称,不带引号。与实验 1 比较:filter()
接受所有“原样”,因此它查找名为 "birth_year"
的列(包括引号!)
函数as.name()
有什么作用?
这是一个基本的 R 函数,它接受一个字符串(或一个包含字符串的变量)和 returns 字符串的 内容 作为变量名。
所以如果你在 base R 中调用 as.name(ID)
,结果是 birth_year
,这次没有引号——就像 tidyverse 期望的那样。那么让我们来试试吧:
实验 3:
quo(
starwars %>%
filter(as.name(ID) < 50)
)
## result: starwars %>% filter(as.name(ID) < 50)
我们了解到:这没有用,因为,filter()
再次“按原样”处理所有内容。所以现在它查找名为 as.name(ID)
的列,当然它不存在。
--> 我们需要结合这两件事来让它工作:
- 使用
as.name()
将字符串转换为变量名。 - 使用
!!
告诉filter()
它不应该“按原样”接受事物,而是替换真实值。
实验四:
quo(
starwars %>%
filter(!!as.name(ID) < 50)
)
## result: starwars %>% filter(birth_year < 50)
现在可以了! :)
我在实验中使用了 filter()
,但它与 mutate()
和其他 tidyverse 函数完全相同。
为了方便起见,您还可以按照@Lionel Henry 在此
.data[[]]
library(tidyverse)
ID <- "birth_year"
# Correct output
test <- starwars %>%
mutate(FootballLeague = case_when(
!!as.name(ID) < 10 ~ "U10",
!!as.name(ID) >= 10 & !!as.name(ID) < 50 ~ "U50",
!!as.name(ID) >= 50 & !!as.name(ID) < 100 ~ "U100",
!!as.name(ID) >= 100 ~ "Senior",
TRUE ~ "Others"
))
test
使用.data
test2 <- starwars %>%
mutate(FootballLeague = case_when(
.data[[ID]] < 10 ~ "U10",
.data[[ID]] >= 10 & .data[[ID]] < 50 ~ "U50",
.data[[ID]] >= 50 & .data[[ID]] < 100 ~ "U100",
.data[[ID]] >= 100 ~ "Senior",
TRUE ~ "Others"
))
test2
#> # A tibble: 87 x 15
#> name height mass hair_color skin_color eye_color
#> <chr> <int> <dbl> <chr> <chr> <chr>
#> 1 Luke Skywalker 172 77 blond fair blue
#> 2 C-3PO 167 75 <NA> gold yellow
#> 3 R2-D2 96 32 <NA> white, blue red
#> 4 Darth Vader 202 136 none white yellow
#> 5 Leia Organa 150 49 brown light brown
#> 6 Owen Lars 178 120 brown, grey light blue
#> 7 Beru Whitesun lars 165 75 brown light blue
#> 8 R5-D4 97 32 <NA> white, red red
#> 9 Biggs Darklighter 183 84 black light brown
#> 10 Obi-Wan Kenobi 182 77 auburn, white fair blue-gray
#> 11 Anakin Skywalker 188 84 blond fair blue
#> 12 Wilhuff Tarkin 180 NA auburn, grey fair blue
#> 13 Chewbacca 228 112 brown unknown blue
#> 14 Han Solo 180 80 brown fair brown
#> 15 Greedo 173 74 <NA> green black
#> birth_year sex gender homeworld species films vehicles starships
#> <dbl> <chr> <chr> <chr> <chr> <list> <list> <list>
#> 1 19 male masculine Tatooine Human <chr [5]> <chr [2]> <chr [2]>
#> 2 112 none masculine Tatooine Droid <chr [6]> <chr [0]> <chr [0]>
#> 3 33 none masculine Naboo Droid <chr [7]> <chr [0]> <chr [0]>
#> 4 41.9 male masculine Tatooine Human <chr [4]> <chr [0]> <chr [1]>
#> 5 19 female feminine Alderaan Human <chr [5]> <chr [1]> <chr [0]>
#> 6 52 male masculine Tatooine Human <chr [3]> <chr [0]> <chr [0]>
#> 7 47 female feminine Tatooine Human <chr [3]> <chr [0]> <chr [0]>
#> 8 NA none masculine Tatooine Droid <chr [1]> <chr [0]> <chr [0]>
#> 9 24 male masculine Tatooine Human <chr [1]> <chr [0]> <chr [1]>
#> 10 57 male masculine Stewjon Human <chr [6]> <chr [1]> <chr [5]>
#> 11 41.9 male masculine Tatooine Human <chr [3]> <chr [2]> <chr [3]>
#> 12 64 male masculine Eriadu Human <chr [2]> <chr [0]> <chr [0]>
#> 13 200 male masculine Kashyyyk Wookiee <chr [5]> <chr [1]> <chr [2]>
#> 14 29 male masculine Corellia Human <chr [4]> <chr [0]> <chr [2]>
#> 15 44 male masculine Rodia Rodian <chr [1]> <chr [0]> <chr [0]>
#> FootballLeague
#> <chr>
#> 1 U50
#> 2 Senior
#> 3 U50
#> 4 U50
#> 5 U50
#> 6 U100
#> 7 U50
#> 8 Others
#> 9 U50
#> 10 U100
#> 11 U50
#> 12 U100
#> 13 Senior
#> 14 U50
#> 15 U50
#> # ... with 72 more rows
检查它们是否相同
identical(test, test2)
#> [1] TRUE
由 reprex package (v0.3.0)
于 2020-11-26 创建