Reputation: 45
I'm trying to parse user input as arguments to a function call (within an expression). It seems like I'm close but !!! is wrapping my arguments in parenthesis which is not working. I'm trying to recreate the following with user inputs:
recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>%
step_log(disp, base = 10, offset = 0) %>%
prep()
library(rlang)
library(tidymodels)
user_dv <- "mpg"
user_idv <- c("cyl", "hp", "wt", "disp")
user_step <- "log"
user_selector <- "disp"
user_args <- "base = 10, offset = 0"
formula <- as.formula(paste(user_dv, paste(user_idv, collapse = " + "), sep = " ~ "))
rcp <- expr(recipe(!!formula,data = mtcars))
add_step <- function(x, step, selector, args){
f <- parse_expr(paste0("step_", step))
vars <- ensym(user_selector)
args <- args %>%
str_replace(",", ";") %>%
parse_exprs()
step_expr <- call2(f, vars, !!!args)
expr(!!x %>% !!step_expr)
}
rcp %>%
add_step(user_step, user_selector, user_args) %>%
eval() %>%
prep()
My expression ends up looking like this:
recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>%
step_log(disp, (base = 10), (offset = 0))
Which does not prep()
Upvotes: 1
Views: 176
Reputation: 13731
After parse_exprs()
, you end up with assignment expressions stored in an unnamed list:
# [[1]]
# base = 10
#
# [[2]]
# offset = 0
while, to get the effect you want with !!!
, they need to be values in a named list:
# $base
# [1] 10
#
# $offset
# [1] 0
With that said, I suggest "forwarding the dots" instead, because it leads to a simpler and more flexible implementation:
add_step <- function(x, step, selector, ...){
f <- parse_expr(paste0("step_", step))
vars <- sym(selector)
step_expr <- call2(f, vars, ...) # <---- forwarding the dots here
expr(!!x %>% !!step_expr)
}
The user can now simply provide the desired arguments directly to add_step()
:
rcp %>% add_step(user_step, user_selector, base=10, offset=0)
# recipe(mpg ~ cyl + hp + wt + disp, data = mtcars) %>%
# step_log(disp, base = 10, offset = 0)
OR store them in a list and use !!!
on your function:
user_args <- list(base = 10, offset=0)
rcp %>% add_step(user_step, user_selector, !!!user_args)
Upvotes: 2