I want to pass a bunch of columns to pmap()
inside mutate()
. Later, I want to select those same columns.
At the moment, I'm passing a list of column names to pmap()
as a quosure, which works fine, although I have no idea whether this is the "right" way to do it. But I can't figure out how to use the same quosure/list for select()
.
I've got almost no experience with tidyeval, I've only got this far by playing around. I imagine there must be a way to use the same thing both for pmap()
and select()
, preferably without having to put each of my column names in quotation marks, but I haven't found it yet.
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
We can use
quos
when there are more than one element and evaluate with!!!
The object 'df2' can be created with
If we want to use the quosure as in the OP's post
This is a bit tricky because of the mix of semantics involved in this problem.
pmap()
takes a list and passes each element as its own argument to a function (it's kind of equivalent to!!!
in that sense). Your quoting function thus needs to quote its arguments and somehow pass a list of columns topmap()
.Our quoting function can go one of two ways. Either quote (i.e., delay) the list creation, or create an actual list of quoted expressions right away:
Since our first variant does nothing but return a list of quoted inputs, it's actually equivalent to
quos()
:The second version returns a quoted expression that instructs R to create a list with quoted inputs:
There is a subtle but important difference between the two. The first version creates an actual list object:
On the other hand, the second version does not return a list, it returns an expression for creating a list:
Let's find out which version is more appropriate for interfacing with
pmap()
. But first we'll give a name to the pmapped function to make the code clearer and easier to experiment with:Understanding how tidy eval works is hard in part because we usually don't get to observe the unquoting step. We'll use
rlang::qq_show()
to reveal the result of unquotingexpr
(the delayed list) andexprs
(the actual list) with!!
:When we unquote the delayed list,
mutate()
callspmap_int()
withlist(a, b)
, evaluated in the data frame, which is exactly what we need:On the other hand, if we unquote an actual list of quoted expressions, we get an error:
That's because the quoted expressions inside the list are not evaluated in the data frame. In fact, they are not evaluated at all.
pmap()
gets the quoted expressions as is, which it doesn't understand. Recall whatqq_show()
has shown us:Anything inside angular brackets is passed as is. This is a sign that we should somehow have used
!!!
instead, to inline each element of the list of quosures in the surrounding expression. Let's try it:Hmm... Doesn't look right. We're supposed to pass a list to
pmap_int()
, and here it gets each quoted input as separate argument. Indeed we get a type error:That's easy to fix, just splice into a call to
list()
:And voilà!