Tidyeval:将列列表作为 quosure 传递给 select()
Tidyeval: pass list of columns as quosure to select()
我想将一堆列传递给 mutate()
中的 pmap()
。稍后,我想 select 那些相同的列。
目前,我将列名列表作为一个 quosure 传递给 pmap()
,这工作正常,虽然我不知道这是否是 "right" 的方式.但是我不知道如何为 select()
.
使用相同的 quosure/list
我几乎没有使用 tidyeval 的经验,我只是通过玩弄才走到这一步。我想一定有一种方法可以对 pmap()
和 select()
使用相同的东西,最好不必将我的每个列名都放在引号中,但我还没有找到它。
library(dplyr)
library(rlang)
library(purrr)
df <- tibble(a = 1:3,
b = 101:103) %>%
print
#> # A tibble: 3 x 2
#> a b
#> <int> <int>
#> 1 1 101
#> 2 2 102
#> 3 3 103
cols_quo <- quo(list(a, b))
df2 <- df %>%
mutate(outcome = !!cols_quo %>%
pmap_int(function(..., word) {
args <- list(...)
# just to be clear this isn't what I actually want to do inside pmap
return(args[[1]] + args[[2]])
})) %>%
print()
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106
# I get why this doesn't work, but I don't know how to do something like this that does
df2 %>%
select(!!cols_quo)
#> Error in .f(.x[[i]], ...): object 'a' not found
当有多个元素时,我们可以使用quos
,并用!!!
求值
cols_quo <- quos(a, b)
df2 %>%
select(!!!cols_quo)
对象 'df2' 可以用
创建
df %>%
mutate(output = list(!!! cols_quo) %>%
reduce(`+`))
如果我们想像OP的post
那样使用quosure
cols_quo <- quo(list(a, b))
df2 %>%
select(!!! as.list(quo_expr(cols_quo))[-1])
# A tibble: 3 x 2
# a b
# <int> <int>
#1 1 101
#2 2 102
#3 3 103
这有点棘手,因为这个问题涉及语义的混合。 pmap()
接受一个列表并将每个元素作为其自己的参数传递给一个函数(在这个意义上它相当于 !!!
)。因此,您的引用函数需要引用其参数并以某种方式将列列表传递给 pmap()
.
我们的报价功能可以采用两种方式之一。要么引用(即延迟)列表创建,要么立即创建引用表达式的实际列表:
quoting_fn1 <- function(...) {
exprs <- enquos(...)
# For illustration purposes, return the quoted inputs instead of
# doing something with them. Normally you'd call `mutate()` here:
exprs
}
quoting_fn2 <- function(...) {
expr <- quo(list(!!!enquos(...)))
expr
}
因为我们的第一个变体只做 return 一个引用输入的列表,它实际上等同于 quos()
:
quoting_fn1(a, b)
#> <list_of<quosure>>
#>
#> [[1]]
#> <quosure>
#> expr: ^a
#> env: global
#>
#> [[2]]
#> <quosure>
#> expr: ^b
#> env: global
第二个版本return是一个引用表达式,指示 R 创建一个包含引用输入的列表:
quoting_fn2(a, b)
#> <quosure>
#> expr: ^list(^a, ^b)
#> env: 0x7fdb69d9bd20
两者之间存在细微但重要的区别。第一个版本创建一个实际的列表对象:
exprs <- quoting_fn1(a, b)
typeof(exprs)
#> [1] "list"
另一方面,第二个版本没有return一个列表,它return是一个创建列表的表达式:
expr <- quoting_fn2(a, b)
typeof(expr)
#> [1] "language"
让我们找出哪个版本更适合与 pmap()
交互。但首先我们将为 pmapped 函数命名,以使代码更清晰,更易于试验:
myfunction <- function(..., word) {
args <- list(...)
# just to be clear this isn't what I actually want to do inside pmap
args[[1]] + args[[2]]
}
了解 tidy eval 的工作原理很困难,部分原因是我们通常无法观察取消引用的步骤。我们将使用 rlang::qq_show()
来显示取消引用 expr
(延迟列表)和 exprs
(实际列表)与 !!
:
的结果
rlang::qq_show(
mutate(df, outcome = pmap_int(!!expr, myfunction))
)
#> mutate(df, outcome = pmap_int(^list(^a, ^b), myfunction))
rlang::qq_show(
mutate(df, outcome = pmap_int(!!exprs, myfunction))
)
#> mutate(df, outcome = pmap_int(<S3: quosures>, myfunction))
当我们取消引用延迟列表时,mutate()
使用 list(a, b)
调用 pmap_int()
,在数据框中进行评估,这正是我们需要的:
mutate(df, outcome = pmap_int(!!expr, myfunction))
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106
另一方面,如果我们取消对引用表达式的实际列表的引用,则会出现错误:
mutate(df, outcome = pmap_int(!!exprs, myfunction))
#> Error in mutate_impl(.data, dots) :
#> Evaluation error: Element 1 is not a vector (language).
那是因为列表中引用的表达式没有在数据框中求值。事实上,他们根本没有被评估。 pmap()
按原样获取引用的表达式,它不理解。回想一下 qq_show()
向我们展示的内容:
#> mutate(df, outcome = pmap_int(<S3: quosures>, myfunction))
angular 括号内的任何内容都按原样传递。这表明我们应该以某种方式使用 !!!
来代替周围表达式中的 quosures 列表的每个元素。让我们试试看:
rlang::qq_show(
mutate(df, outcome = pmap_int(!!!exprs, myfunction))
)
#> mutate(df, outcome = pmap_int(^a, ^b, myfunction))
嗯...看起来不对。我们应该将一个列表传递给 pmap_int()
,在这里它将每个引用的输入作为单独的参数获取。事实上,我们得到了一个类型错误:
mutate(df, outcome = pmap_int(!!!exprs, myfunction))
#> Error in mutate_impl(.data, dots) :
#> Evaluation error: `.x` is not a list (integer).
这很容易修复,只需拼接到对 list()
:
的调用即可
rlang::qq_show(
mutate(df, outcome = pmap_int(list(!!!exprs), myfunction))
)
#> mutate(df, outcome = pmap_int(list(^a, ^b), myfunction))
瞧瞧!
mutate(df, outcome = pmap_int(list(!!!exprs), myfunction))
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106
我想将一堆列传递给 mutate()
中的 pmap()
。稍后,我想 select 那些相同的列。
目前,我将列名列表作为一个 quosure 传递给 pmap()
,这工作正常,虽然我不知道这是否是 "right" 的方式.但是我不知道如何为 select()
.
我几乎没有使用 tidyeval 的经验,我只是通过玩弄才走到这一步。我想一定有一种方法可以对 pmap()
和 select()
使用相同的东西,最好不必将我的每个列名都放在引号中,但我还没有找到它。
library(dplyr)
library(rlang)
library(purrr)
df <- tibble(a = 1:3,
b = 101:103) %>%
print
#> # A tibble: 3 x 2
#> a b
#> <int> <int>
#> 1 1 101
#> 2 2 102
#> 3 3 103
cols_quo <- quo(list(a, b))
df2 <- df %>%
mutate(outcome = !!cols_quo %>%
pmap_int(function(..., word) {
args <- list(...)
# just to be clear this isn't what I actually want to do inside pmap
return(args[[1]] + args[[2]])
})) %>%
print()
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106
# I get why this doesn't work, but I don't know how to do something like this that does
df2 %>%
select(!!cols_quo)
#> Error in .f(.x[[i]], ...): object 'a' not found
当有多个元素时,我们可以使用quos
,并用!!!
cols_quo <- quos(a, b)
df2 %>%
select(!!!cols_quo)
对象 'df2' 可以用
创建df %>%
mutate(output = list(!!! cols_quo) %>%
reduce(`+`))
如果我们想像OP的post
那样使用quosurecols_quo <- quo(list(a, b))
df2 %>%
select(!!! as.list(quo_expr(cols_quo))[-1])
# A tibble: 3 x 2
# a b
# <int> <int>
#1 1 101
#2 2 102
#3 3 103
这有点棘手,因为这个问题涉及语义的混合。 pmap()
接受一个列表并将每个元素作为其自己的参数传递给一个函数(在这个意义上它相当于 !!!
)。因此,您的引用函数需要引用其参数并以某种方式将列列表传递给 pmap()
.
我们的报价功能可以采用两种方式之一。要么引用(即延迟)列表创建,要么立即创建引用表达式的实际列表:
quoting_fn1 <- function(...) {
exprs <- enquos(...)
# For illustration purposes, return the quoted inputs instead of
# doing something with them. Normally you'd call `mutate()` here:
exprs
}
quoting_fn2 <- function(...) {
expr <- quo(list(!!!enquos(...)))
expr
}
因为我们的第一个变体只做 return 一个引用输入的列表,它实际上等同于 quos()
:
quoting_fn1(a, b)
#> <list_of<quosure>>
#>
#> [[1]]
#> <quosure>
#> expr: ^a
#> env: global
#>
#> [[2]]
#> <quosure>
#> expr: ^b
#> env: global
第二个版本return是一个引用表达式,指示 R 创建一个包含引用输入的列表:
quoting_fn2(a, b)
#> <quosure>
#> expr: ^list(^a, ^b)
#> env: 0x7fdb69d9bd20
两者之间存在细微但重要的区别。第一个版本创建一个实际的列表对象:
exprs <- quoting_fn1(a, b)
typeof(exprs)
#> [1] "list"
另一方面,第二个版本没有return一个列表,它return是一个创建列表的表达式:
expr <- quoting_fn2(a, b)
typeof(expr)
#> [1] "language"
让我们找出哪个版本更适合与 pmap()
交互。但首先我们将为 pmapped 函数命名,以使代码更清晰,更易于试验:
myfunction <- function(..., word) {
args <- list(...)
# just to be clear this isn't what I actually want to do inside pmap
args[[1]] + args[[2]]
}
了解 tidy eval 的工作原理很困难,部分原因是我们通常无法观察取消引用的步骤。我们将使用 rlang::qq_show()
来显示取消引用 expr
(延迟列表)和 exprs
(实际列表)与 !!
:
rlang::qq_show(
mutate(df, outcome = pmap_int(!!expr, myfunction))
)
#> mutate(df, outcome = pmap_int(^list(^a, ^b), myfunction))
rlang::qq_show(
mutate(df, outcome = pmap_int(!!exprs, myfunction))
)
#> mutate(df, outcome = pmap_int(<S3: quosures>, myfunction))
当我们取消引用延迟列表时,mutate()
使用 list(a, b)
调用 pmap_int()
,在数据框中进行评估,这正是我们需要的:
mutate(df, outcome = pmap_int(!!expr, myfunction))
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106
另一方面,如果我们取消对引用表达式的实际列表的引用,则会出现错误:
mutate(df, outcome = pmap_int(!!exprs, myfunction))
#> Error in mutate_impl(.data, dots) :
#> Evaluation error: Element 1 is not a vector (language).
那是因为列表中引用的表达式没有在数据框中求值。事实上,他们根本没有被评估。 pmap()
按原样获取引用的表达式,它不理解。回想一下 qq_show()
向我们展示的内容:
#> mutate(df, outcome = pmap_int(<S3: quosures>, myfunction))
angular 括号内的任何内容都按原样传递。这表明我们应该以某种方式使用 !!!
来代替周围表达式中的 quosures 列表的每个元素。让我们试试看:
rlang::qq_show(
mutate(df, outcome = pmap_int(!!!exprs, myfunction))
)
#> mutate(df, outcome = pmap_int(^a, ^b, myfunction))
嗯...看起来不对。我们应该将一个列表传递给 pmap_int()
,在这里它将每个引用的输入作为单独的参数获取。事实上,我们得到了一个类型错误:
mutate(df, outcome = pmap_int(!!!exprs, myfunction))
#> Error in mutate_impl(.data, dots) :
#> Evaluation error: `.x` is not a list (integer).
这很容易修复,只需拼接到对 list()
:
rlang::qq_show(
mutate(df, outcome = pmap_int(list(!!!exprs), myfunction))
)
#> mutate(df, outcome = pmap_int(list(^a, ^b), myfunction))
瞧瞧!
mutate(df, outcome = pmap_int(list(!!!exprs), myfunction))
#> # A tibble: 3 x 3
#> a b outcome
#> <int> <int> <int>
#> 1 1 101 102
#> 2 2 102 104
#> 3 3 103 106