Reputation: 243
I get the following error when I try to pass arguments to a function that uses enquo().
"Error: Quosures can only be unquoted within a quasiquotation context."
In the example below,
Wrap1 function uses map to run multiple argument sets for a single function. Passing the variables with !!enquo() works.
Wrap 2 tries to pass multiple functions and arguments, but I can't get the syntax correct.
Is there an issue with the how I've pass (either order in exec or invoke_map functions, or how the columns are passed with !!enquo(cols)?
Is there a way to pass extra variables to invoke_map or that are common to all outside the param list (similar to using partial in the wrap1 code below)?
library(dplyr)
library(lubridate)
library(purrr)
library(rlang)
library(rkt)
dataset <- tibble(Date= lubridate::decimal_date(seq(as.Date("2000/1/1"), by = "month", length.out = 120)),
Value = rnorm(120),
season = lubridate::month(seq(as.Date("2000/1/1"), by = "month", length.out = 120)))
#inner function
run1_fun <- function(df, yCOL, datesCOL, seasCOL=NULL, ind.obs=TRUE){
y <- df %>% pull(!!enquo(yCOL))
dates <- df %>% pull(!!enquo(datesCOL))
mk <- rlang::quo_is_null(enquo(seasCOL))
if (mk){
seas <- rep(1, length(y)) #just so can get nseas=1 later
smk <- rkt::rkt(date=dates, y=y)
} else {
seas <- df %>% pull(!!enquo(seasCOL))
smk <- rkt::rkt(date=dates, y=y, block=seas, correct = !ind.obs)
}
out <- tibble(tau = smk$tau,
slope = smk$B,
type = ifelse(mk, "MK", "SK"))
out
}
#inner function with by option
run1_fun_by <- function(df, by=NULL, yCOL, datesCOL, seasCOL=NULL, ind.obs=TRUE){
if(is.null(by)) {
df <- run1_fun(df, !!enquo(yCOL), !!enquo(datesCOL), !!enquo(seasCOL),ind.obs)
} else {
df <- plyr::ddply(df, .variables = by,
.fun = run1_fun, !!enquo(yCOL), !!enquo(datesCOL), !!enquo(seasCOL),ind.obs)
}
df
}
#to run:
run1_fun_by(dataset, by="season", Value, Date, ind.obs=TRUE)
#WRAP 1: WORKS
# example of wrap function that passing multiple arguments sets to the above inner function that uses enquo variables.
wrap1 <- function(df, yCOL, datesCOL, seasCOL=NULL, ind.obs=TRUE,
ttype=c("MK", "SK")){
fun_args <- partial(run1_fun, df=df, yCOL = !!enquo(yCOL), datesCOL = !!enquo(datesCOL), ind.obs=ind.obs)
#fun_args - order in "SK", "MK"
keep <- which(c("SK", "MK") %in% ttype)
args <- list(seasCOL = dplyr::vars(!!enquo(seasCOL), NULL)[keep]) # didnt work with alist..
#args <-tibble(seasCOL = dplyr::vars(!!enquo(seasCOL), NULL)[keep]) #works too
res <- purrr::pmap_dfr(args, fun_args)
res
}
#to run:
wrap1(dataset, Value, Date, season, ind.obs=TRUE, ttype=c("MK", "SK"))
#WRAP 2: DOESN'T WORK
#attempt to iterate through multiple functions and argument sets (where functions requires enquo arguments)
using either invoke_map or exec?
wrap2 <- function(df, yCOL, datesCOL, seasCOL=NULL, ind.obs=TRUE,
ttype=c("MK", "SK", "MKSeas")){
t <- c("SK", "MK", "MKSeas")
#HOW CAN I PASS (which are enq in next function)
sim <- tribble(
~f, ~params,
"run1_fun", list(seasCOL = dplyr::vars(!!enquo(seasCOL)), ind.obs=ind.obs), #SK
"run1_fun", list(seasCOL = dplyr::vars(NULL)), #MK
"run1_fun_by", list(by="season")) #MKSeas
#with invoke_map - but looks like this is retired
res <- invoke_map_dfc(sim$f, sim$params, df=df, yCOL=!!enquo(yCOL), datesCOL=!!enquo(datesCOL))
#with exec - new
#res <- map2_dfc(sim$f, sim$params, function(fn, args) exec(fn, !!!args))
res
}
#to run
wrap2(dataset, Value, Date, season, ind.obs=TRUE, ttype=c("MK", "SK", "MKSeas"))
Error: Quosures can only be unquoted within a quasiquotation context.
list(!!myquosure)
dplyr::mutate(data, !!myquosure)
Call rlang::last_error()
to see a backtrace
Called from: abort(paste_line("Quosures can only be unquoted within a quasiquotation context.",
"", " # Bad:", " list(!!myquosure)",
"", " # Good:", " dplyr::mutate(data, !!myquosure)")
Upvotes: 1
Views: 921
Reputation: 13731
Use rlang::call2
to compose the desired function calls, then evaluate them.
Also, because you are working with column names, the proper NSE verb is ensym()
, not enquo()
. The former captures symbolic names and works with strings. The latter captures an expression and its context. In this case, the context is the data frame, which is already being passed directly to the function, whereas enquo()
is capturing the global environment (which is not correct).
wrap2 <- function(df, yCOL, datesCOL, seasCOL=NULL, ind.obs=TRUE,
ttype=c("MK", "SK", "MKSeas")){
sim <- tribble(
~f, ~params,
"run1_fun", list(seasCOL = ensym(seasCOL), ind.obs=ind.obs), #SK
"run1_fun", list(seasCOL = NULL), #MK
"run1_fun_by", list(by="season")) #MKSeas
# Concatenate common arguments to each list of parameters
myArgs <- map( sim$params, c, yCOL=ensym(yCOL), datesCOL=ensym(datesCOL) )
# Compose the function calls
calls <- map2( sim$f, myArgs, ~rlang::call2(.x, !!!.y, df=df) )
# Evaluate the function calls and aggregate the results
map_dfr( calls, eval )
}
wrap2(dataset, Value, Date, season, ind.obs=TRUE, ttype=c("MK", "SK", "MKSeas"))
# # A tibble: 14 x 4
# tau slope type season
# <dbl> <dbl> <chr> <dbl>
# 1 -0.0259 -0.0180 SK NA
# 2 -0.0255 -0.0151 MK NA
# 3 0.2 0.0743 MK 1
# 4 -0.378 -0.162 MK 2
# 5 -0.0222 -0.0505 MK 3
# ...
Upvotes: 1