当函数使用 enquo 列时,如何将多个函数传递给 purr::map(exec 或 invoke_map)?
How can I pass multiple functions to purr::map (exec or invoke_map) when function uses enquo columns?
当我尝试将参数传递给使用 enquo() 的函数时出现以下错误。
“错误:Quosures 只能在 quasiquotation 上下文中取消引用。”
在下面的例子中,
Wrap1 函数使用映射到 运行 单个函数的多个参数集。使用 !!enquo() 传递变量有效。
Wrap 2 尝试传递多个函数和参数,但语法不正确。
我的传递方式有问题吗(exec 或 invoke_map 函数中的顺序,或者列是如何通过 !!enquo(cols) 传递的?
有没有办法将额外的变量传递给 invoke_map 或参数列表之外的所有变量(类似于在下面的 wrap1 代码中使用 partial)?
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"))
错误:Quosures 只能在 quasiquotation 上下文中取消引用。
差:
列表(!!myquosure)
好:
dplyr::mutate(数据,!!myquosure)
调用 rlang::last_error()
查看回溯
调用自:abort(paste_line("Quosures can only be unquoted within a quasiquotation context.",
"", " # 错误:", " list(!!myquosure)",
"", " # Good:", " dplyr::mutate(data, !!myquosure)")
使用 rlang::call2
编写所需的函数调用,然后对其进行评估。
此外,因为您使用的是列名,所以正确的 NSE 动词是 ensym()
,而不是 enquo()
。前者捕获符号名称并处理字符串。后者捕获表达式及其上下文。在这种情况下,上下文是数据框,它已经直接传递给函数,而 enquo()
正在捕获全局环境(这是不正确的)。
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
# ...
当我尝试将参数传递给使用 enquo() 的函数时出现以下错误。
“错误:Quosures 只能在 quasiquotation 上下文中取消引用。”
在下面的例子中,
Wrap1 函数使用映射到 运行 单个函数的多个参数集。使用 !!enquo() 传递变量有效。
Wrap 2 尝试传递多个函数和参数,但语法不正确。
我的传递方式有问题吗(exec 或 invoke_map 函数中的顺序,或者列是如何通过 !!enquo(cols) 传递的?
有没有办法将额外的变量传递给 invoke_map 或参数列表之外的所有变量(类似于在下面的 wrap1 代码中使用 partial)?
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"))
错误:Quosures 只能在 quasiquotation 上下文中取消引用。
差:
列表(!!myquosure)
好:
dplyr::mutate(数据,!!myquosure)
调用 rlang::last_error()
查看回溯
调用自:abort(paste_line("Quosures can only be unquoted within a quasiquotation context.",
"", " # 错误:", " list(!!myquosure)",
"", " # Good:", " dplyr::mutate(data, !!myquosure)")
使用 rlang::call2
编写所需的函数调用,然后对其进行评估。
此外,因为您使用的是列名,所以正确的 NSE 动词是 ensym()
,而不是 enquo()
。前者捕获符号名称并处理字符串。后者捕获表达式及其上下文。在这种情况下,上下文是数据框,它已经直接传递给函数,而 enquo()
正在捕获全局环境(这是不正确的)。
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
# ...