使用具有变异函数的多个字符串向量的 Dplyr 标准评估
Dplyr standard evaluation using a vector of multiple strings with mutate function
我正在尝试使用 dplyr
包向 mutate()
调用提供包含多个列名的向量。下面的可重现示例:
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
one_name <- c('jack')
# jack jill jane
# 1 1 1
# NA 2 2
# 2 NA 3
# NA 3 4
# 3 4 5
# NA NA 6
# 4 5 NA
# NA 6 NA
# 5 NA NA
# NA 7 NA
我知道如何使用 "one variable" 版本,但不知道如何将其扩展到多个变量?
# the below works as expected, and is an example of the output I desire
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
# using the one_name variable works if I evaluate it and then convert to
# a name before unquoting it
stackdf %>% rowwise %>% mutate(test = anyNA(!!as.name(eval(one_name))))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 FALSE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA FALSE
10 NA 7 NA TRUE
如何扩展上述方法以便我可以使用 two_names
向量?使用 as.name
只需要一个对象,所以它不起作用。
这里的这个问题是类似的:Pass a vector of variable names to arrange() in dplyr。该解决方案 "works" 因为我可以使用以下代码:
two_names2 <- quos(c(jack, jill))
stackdf %>% rowwise %>% mutate(test = anyNA(!!!two_names2))
但是如果我必须直接键入 c(jack, jill)
而不是使用 two_names
变量,它就达不到目的了。是否有一些类似的程序可以直接使用 two_names
?这个回答 uses rlang::syms
but though this works for selecting variables (ie stackdf %>% select(!!! rlang::syms(two_names))
it does not seem to work for supplying arguments when mutating (ie stackdf %>% rowwise %>% mutate(test = anyNA(!!! rlang::syms(two_names)))
. This answer is similar but does not work:
你可以使用rlang::syms
(由dplyr重新导出;或者直接调用它)将字符串强制转换为quosures,所以
library(dplyr)
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
stackdf %>% rowwise %>% mutate(test = anyNA(c(!!!syms(two_names))))
#> Source: local data frame [10 x 4]
#> Groups: <by row>
#>
#> # A tibble: 10 x 4
#> jack jill jane test
#> <dbl> <dbl> <dbl> <lgl>
#> 1 1. 1. 1. FALSE
#> 2 NA 2. 2. TRUE
#> 3 2. NA 3. TRUE
#> 4 NA 3. 4. TRUE
#> 5 3. 4. 5. FALSE
#> 6 NA NA 6. TRUE
#> 7 4. 5. NA FALSE
#> 8 NA 6. NA TRUE
#> 9 5. NA NA TRUE
#> 10 NA 7. NA TRUE
或者,使用一点基础 R 而不是整齐的评估:
stackdf %>% mutate(test = rowSums(is.na(.[two_names])) > 0)
#> jack jill jane test
#> 1 1 1 1 FALSE
#> 2 NA 2 2 TRUE
#> 3 2 NA 3 TRUE
#> 4 NA 3 4 TRUE
#> 5 3 4 5 FALSE
#> 6 NA NA 6 TRUE
#> 7 4 5 NA FALSE
#> 8 NA 6 NA TRUE
#> 9 5 NA NA TRUE
#> 10 NA 7 NA TRUE
...这可能会快很多,因为迭代 rowwise
进行 n
调用而不是一个矢量化调用。
解决这道题有几个关键:
- 访问字符向量中的字符串并将它们与
dplyr
一起使用
- 提供给与
mutate
一起使用的函数的参数格式,这里是 anyNA
此处的目标是复制此调用,但使用命名变量 two_names
而不是手动输入 c(jack,jill)
。
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
1.在 dplyr
中使用动态变量
使用quo
/quos
:不接受字符串作为输入。使用此方法的解决方案是:
two_names2 <- quos(c(jack, jill))
stackdf %>% rowwise %>% mutate(test = anyNA(!!! two_names2))
请注意,quo
接受单个参数,因此使用 !!
不加引号,对于多个参数,您可以分别使用 quos
和 !!!
。这是不可取的,因为我不使用 two_names
而是必须键入我希望使用的列。
使用as.name
或rlang::sym
/rlang::syms
:as.name
和sym
只接受一个输入,但是syms
将取多个 return 一个 list 符号对象作为输出。
> two_names
[1] "jack" "jill"
> as.name(two_names)
jack
> syms(two_names)
[[1]]
jack
[[2]]
jill
请注意 as.name
会忽略第一个元素之后的所有内容。但是,syms
似乎在这里可以正常工作,所以现在我们需要在 mutate
调用中使用它。
2。使用 anyNA
或其他变量
在 mutate
中使用动态变量
直接使用syms
和anyNA
实际上不会产生正确的结果。
> stackdf %>% rowwise %>% mutate(test = anyNA(!!! syms(two_names)))
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 FALSE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA FALSE
10 NA 7 NA TRUE
查看test
发现这里只考虑了第一个元素,忽略了第二个元素。但是,如果我使用不同的函数,例如 sum
或 paste0
,很明显这两个元素都被使用了:
> stackdf %>% rowwise %>% mutate(test = sum(!!! syms(two_names),
na.rm = TRUE))
jack jill jane test
<dbl> <dbl> <dbl> <dbl>
1 1 1 1 2
2 NA 2 2 2
3 2 NA 3 2
4 NA 3 4 3
5 3 4 5 7
6 NA NA 6 0
7 4 5 NA 9
8 NA 6 NA 6
9 5 NA NA 5
10 NA 7 NA 7
当您查看 anyNA
与 sum
和 sum
的论据时,其原因就变得很清楚了。
function (x, recursive = FALSE) .Primitive("anyNA")
function (..., na.rm = FALSE) .Primitive("sum")
anyNA
需要单个对象 x
,而 sum
可以采用对象的可变列表 (...)
。
只需提供 c()
即可解决此问题(请参阅 alistaire 的回答)。
> stackdf %>% rowwise %>% mutate(test = anyNA(c(!!! syms(two_names))))
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
或者...出于教育目的,可以使用 sapply
、any
和 anyNA
的组合来产生正确的结果。这里我们使用 list
以便将结果作为单个列表对象提供。
# this produces an error an error because the elements of !!!
# are being passed to the arguments of sapply (X =, FUN = )
> stackdf %>% rowwise %>%
mutate(test = any(sapply(!!! syms(two_names), anyNA)))
Error in mutate_impl(.data, dots) :
Evaluation error: object 'jill' of mode 'function' was not found.
提供 list
解决了这个问题,因为它将所有结果绑定到一个对象中。
# the below table is the familiar incorrect result that uses only the `jack`
> stackdf %>% rowwise %>%
mutate(test = any(sapply(X=as.list(!!! syms(two_names)),
FUN=anyNA)))
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 FALSE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA FALSE
10 NA 7 NA TRUE
# this produces the correct answer
> stackdf %>% rowwise %>%
mutate(test = any(X = sapply(list(!!! syms(two_names)),
FUN = anyNA)))
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
当比较他们的行为时,理解为什么这两个表现不同是有道理的!
> as.list(two_names)
[[1]]
[1] "jack"
[[2]]
[1] "jill"
> list(two_names)
[[1]]
[1] "jack" "jill"
我正在尝试使用 dplyr
包向 mutate()
调用提供包含多个列名的向量。下面的可重现示例:
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
one_name <- c('jack')
# jack jill jane
# 1 1 1
# NA 2 2
# 2 NA 3
# NA 3 4
# 3 4 5
# NA NA 6
# 4 5 NA
# NA 6 NA
# 5 NA NA
# NA 7 NA
我知道如何使用 "one variable" 版本,但不知道如何将其扩展到多个变量?
# the below works as expected, and is an example of the output I desire
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
# using the one_name variable works if I evaluate it and then convert to
# a name before unquoting it
stackdf %>% rowwise %>% mutate(test = anyNA(!!as.name(eval(one_name))))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 FALSE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA FALSE
10 NA 7 NA TRUE
如何扩展上述方法以便我可以使用 two_names
向量?使用 as.name
只需要一个对象,所以它不起作用。
这里的这个问题是类似的:Pass a vector of variable names to arrange() in dplyr。该解决方案 "works" 因为我可以使用以下代码:
two_names2 <- quos(c(jack, jill))
stackdf %>% rowwise %>% mutate(test = anyNA(!!!two_names2))
但是如果我必须直接键入 c(jack, jill)
而不是使用 two_names
变量,它就达不到目的了。是否有一些类似的程序可以直接使用 two_names
?这个回答rlang::syms
but though this works for selecting variables (ie stackdf %>% select(!!! rlang::syms(two_names))
it does not seem to work for supplying arguments when mutating (ie stackdf %>% rowwise %>% mutate(test = anyNA(!!! rlang::syms(two_names)))
. This answer is similar but does not work:
你可以使用rlang::syms
(由dplyr重新导出;或者直接调用它)将字符串强制转换为quosures,所以
library(dplyr)
stackdf <- data.frame(jack = c(1,NA,2,NA,3,NA,4,NA,5,NA),
jill = c(1,2,NA,3,4,NA,5,6,NA,7),
jane = c(1,2,3,4,5,6,NA,NA,NA,NA))
two_names <- c('jack','jill')
stackdf %>% rowwise %>% mutate(test = anyNA(c(!!!syms(two_names))))
#> Source: local data frame [10 x 4]
#> Groups: <by row>
#>
#> # A tibble: 10 x 4
#> jack jill jane test
#> <dbl> <dbl> <dbl> <lgl>
#> 1 1. 1. 1. FALSE
#> 2 NA 2. 2. TRUE
#> 3 2. NA 3. TRUE
#> 4 NA 3. 4. TRUE
#> 5 3. 4. 5. FALSE
#> 6 NA NA 6. TRUE
#> 7 4. 5. NA FALSE
#> 8 NA 6. NA TRUE
#> 9 5. NA NA TRUE
#> 10 NA 7. NA TRUE
或者,使用一点基础 R 而不是整齐的评估:
stackdf %>% mutate(test = rowSums(is.na(.[two_names])) > 0)
#> jack jill jane test
#> 1 1 1 1 FALSE
#> 2 NA 2 2 TRUE
#> 3 2 NA 3 TRUE
#> 4 NA 3 4 TRUE
#> 5 3 4 5 FALSE
#> 6 NA NA 6 TRUE
#> 7 4 5 NA FALSE
#> 8 NA 6 NA TRUE
#> 9 5 NA NA TRUE
#> 10 NA 7 NA TRUE
...这可能会快很多,因为迭代 rowwise
进行 n
调用而不是一个矢量化调用。
解决这道题有几个关键:
- 访问字符向量中的字符串并将它们与
dplyr
一起使用
- 提供给与
mutate
一起使用的函数的参数格式,这里是anyNA
此处的目标是复制此调用,但使用命名变量 two_names
而不是手动输入 c(jack,jill)
。
stackdf %>% rowwise %>% mutate(test = anyNA(c(jack,jill)))
# A tibble: 10 x 4
jack jill jane test
<dbl> <dbl> <dbl> <lgl>
1 1 1 1 FALSE
2 NA 2 2 TRUE
3 2 NA 3 TRUE
4 NA 3 4 TRUE
5 3 4 5 FALSE
6 NA NA 6 TRUE
7 4 5 NA FALSE
8 NA 6 NA TRUE
9 5 NA NA TRUE
10 NA 7 NA TRUE
1.在 dplyr
中使用动态变量使用
quo
/quos
:不接受字符串作为输入。使用此方法的解决方案是:two_names2 <- quos(c(jack, jill)) stackdf %>% rowwise %>% mutate(test = anyNA(!!! two_names2))
请注意,
quo
接受单个参数,因此使用!!
不加引号,对于多个参数,您可以分别使用quos
和!!!
。这是不可取的,因为我不使用two_names
而是必须键入我希望使用的列。使用
as.name
或rlang::sym
/rlang::syms
:as.name
和sym
只接受一个输入,但是syms
将取多个 return 一个 list 符号对象作为输出。> two_names [1] "jack" "jill" > as.name(two_names) jack > syms(two_names) [[1]] jack [[2]] jill
请注意
as.name
会忽略第一个元素之后的所有内容。但是,syms
似乎在这里可以正常工作,所以现在我们需要在mutate
调用中使用它。
2。使用 anyNA
或其他变量
mutate
中使用动态变量
直接使用
syms
和anyNA
实际上不会产生正确的结果。> stackdf %>% rowwise %>% mutate(test = anyNA(!!! syms(two_names))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE
查看
test
发现这里只考虑了第一个元素,忽略了第二个元素。但是,如果我使用不同的函数,例如sum
或paste0
,很明显这两个元素都被使用了:> stackdf %>% rowwise %>% mutate(test = sum(!!! syms(two_names), na.rm = TRUE)) jack jill jane test <dbl> <dbl> <dbl> <dbl> 1 1 1 1 2 2 NA 2 2 2 3 2 NA 3 2 4 NA 3 4 3 5 3 4 5 7 6 NA NA 6 0 7 4 5 NA 9 8 NA 6 NA 6 9 5 NA NA 5 10 NA 7 NA 7
当您查看
anyNA
与sum
和sum
的论据时,其原因就变得很清楚了。function (x, recursive = FALSE) .Primitive("anyNA")
function (..., na.rm = FALSE) .Primitive("sum")
anyNA
需要单个对象x
,而sum
可以采用对象的可变列表(...)
。只需提供
c()
即可解决此问题(请参阅 alistaire 的回答)。> stackdf %>% rowwise %>% mutate(test = anyNA(c(!!! syms(two_names)))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE
或者...出于教育目的,可以使用
sapply
、any
和anyNA
的组合来产生正确的结果。这里我们使用list
以便将结果作为单个列表对象提供。# this produces an error an error because the elements of !!! # are being passed to the arguments of sapply (X =, FUN = ) > stackdf %>% rowwise %>% mutate(test = any(sapply(!!! syms(two_names), anyNA))) Error in mutate_impl(.data, dots) : Evaluation error: object 'jill' of mode 'function' was not found.
提供
list
解决了这个问题,因为它将所有结果绑定到一个对象中。# the below table is the familiar incorrect result that uses only the `jack` > stackdf %>% rowwise %>% mutate(test = any(sapply(X=as.list(!!! syms(two_names)), FUN=anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 FALSE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA FALSE 10 NA 7 NA TRUE # this produces the correct answer > stackdf %>% rowwise %>% mutate(test = any(X = sapply(list(!!! syms(two_names)), FUN = anyNA))) jack jill jane test <dbl> <dbl> <dbl> <lgl> 1 1 1 1 FALSE 2 NA 2 2 TRUE 3 2 NA 3 TRUE 4 NA 3 4 TRUE 5 3 4 5 FALSE 6 NA NA 6 TRUE 7 4 5 NA FALSE 8 NA 6 NA TRUE 9 5 NA NA TRUE 10 NA 7 NA TRUE
当比较他们的行为时,理解为什么这两个表现不同是有道理的!
> as.list(two_names) [[1]] [1] "jack" [[2]] [1] "jill" > list(two_names) [[1]] [1] "jack" "jill"