I've got a tibble
with interaction of few factors as a column names (see example with two factors below).
ex <- structure(list(`Monday*FALSE` = 42.74, `Monday*TRUE` = 70.68,
`Tuesday*TRUE` = 44.05, `Tuesday*FALSE` = 51.25, `Wednesday*TRUE` = 35.57,
`Wednesday*FALSE` = 59.24, `Thursday*TRUE` = 85.3, `Thursday*FALSE` = 59.91,
`Friday*TRUE` = 47.27, `Friday*FALSE` = 47.44, `Saturday*TRUE` = 62.28,
`Saturday*FALSE` = 98.8, `Sunday*TRUE` = 57.11, `Sunday*FALSE` = 65.99), class = c("tbl_df",
"tbl", "data.frame"), row.names = c(NA, -1L))
I want to write a function that would allow to gather
this tibble
, but additionally create a key
name based on input names of factors. However, the following doesn't work as intented because paste0
returns a string.
my_gather <- function(data, ...){
vars <- enquos(...)
data %>%
gather(key = paste0(!!!vars, sep = '*'), value = value, factor_key = TRUE)
}
my_gather(ex, day, cond) %>% head()
# A tibble: 6 x 2
`paste0(day, cond, sep = ".")` value
<fct> <dbl>
1 Monday*FALSE 42.7
2 Monday*TRUE 70.7
3 Tuesday*TRUE 44.0
4 Tuesday*FALSE 51.2
5 Wednesday*TRUE 35.6
6 Wednesday*FALSE 59.2
I have tried to replace *
by .
to make a valid synctatic name, then capture paste0
into a sym
with !!
:
my_gather <- function(data, ...){
vars <- enquos(...)
data %>%
gather(key = !!sym(paste0(!!!vars, sep = '.')), value = value, factor_key = TRUE)
}
but it results in an error:
Error in !vars : invalid argument type
gather
seems to quotate the key
and value
arguments if neccessary, so is there any way to evaluate paste0(...)
within key
definition?
This doesn't work because you're double unquoting:
!!sym(paste0(!!!vars, sep = '.'))
Everything inside !!
is evaluated normally, so if you use another unquote operator it needs to be handled by another quasiquoting function. paste0()
does not support !!!
.
As a general rule, it is better to do things in several steps with complex syntax like !!
. It is more readable and there's fewer chances of making mistakes.
The second thing is that you're quoting the inputs with enquos()
. This means they can be any complex expressions instead of column names. If you're expecting bare columns, it's better to use ensyms(...)
instead (or just syms(c(...))
if you prefer to take strings without any quoting).
my_gather <- function(data, ...){
# ensyms() guarantees there can't be complex expressions in `...`
vars <- ensyms(...)
# Let's convert all symbols to strings and return a character vector
keys <- purrr::map_chr(vars, as.character)
# Now we can use paste() the normal way. It doesn't support `!!!`
# but the standard way of dealing with vector inputs is the
# `collapse` argument:
key <- paste0(keys, collapse = '*')
# Equivalently, but weird:
key <- eval(expr(paste(!!!keys, sep = "*")))
# Now the key can be unquoted:
data %>%
gather(key = !!key, value = value, factor_key = TRUE)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With