【问题标题】:How can I use purrr:: invoke_map(), on a function that uses enquo()?如何在使用 enquo() 的函数上使用 purrr::invoke_map()?
【发布时间】:2018-06-08 14:38:58
【问题描述】:

抱歉,我知道标题不是最好的。

所以,我正在尝试编写一个函数来检查 2 个函数的输出是否相同。我正在尝试使用purrr::invoke_map() 来做到这一点。但是,我正在努力让它与任何使用enquo() 的函数一起工作。

我认为问题是因为在我希望它们被评估之前正在评估这些参数。

library(tidyverse)

check_output <- function(function_list, param_list = NULL){
  param_list <- list(param_list) 
  output <- invoke_map(.f = function_list, .x = param_list) 
  identical(output[1], output[2])
}

check_output(function_list = list(cumprod, cumsum),  # This works
             param_list = list(x = iris$Sepal.Length))

check_output(function_list = list(cumsum, cumsum), # This works
             param_list = list(x = iris$Sepal.Length))

m <- function(data, col_name){ # Enquo test function
  col_name <- enquo(col_name)
 data %>% 
    select(!!col_name)
}
n <- m

n(iris, Species) # Seeing if the functions work

check_output(function_list = list(m, n),     # The call doesn't work
             param_list = list(data = iris, col_name = Species))

【问题讨论】:

  • 引用输入会抑制评估,并且似乎可以正确解析:check_output(function_list = list(m, n), param_list = list(data = iris, col_name = quo(Species)))
  • 有没有办法在函数调用中不用quo() 包装物种?
  • 还有其他类型的引用,但问题是当check_output 调用list(param_list) 时,会评估参数(为时过早)。引用是避免评估的最直接方法。您可以在check_output 中使用enquo,但如果它也应该处理数字向量等可能会导致问题。
  • 现在正在玩它。有没有办法在没有quo() 的情况下通过将param_list 作为单独的参数而不是参数列表来做到这一点?
  • 如果它最终被传递给invoke_map,如果您在check_output 中使用... 并将它们传递给invoke_map... 参数而不是使用@987654337,它可能会起作用@(如果可以避免的话)。 .x 的问题在于它只需要一个列表,而传递列表中未评估的内容的唯一方法是以某种形式引用。

标签: r lazy-evaluation tidyverse purrr rlang


【解决方案1】:

我通过添加一个名为 add_quo 的辅助函数解决了我的问题,它将适当的参数转换为 quosures。

require(purrr)
require(magrittr) 
require(rlang) # add_quo uses this package

add_quo <- function(...) {
  ## Formats arguments for use in invoke_map()

  # Create a logical vector for use as an index below
  index <- exprs(...) %>%                       # Captures the arguments
    unlist() %>%                                # Makes them mappable
    map_if(negate(is.name), 
           .f = function(x) {return(TRUE)}) %>% # Replaces unnecessary args with TRUE
    map_if(is.name, as_character) %>%           # Converts named arguments to characters
    map_if(is.character, exists) %>%            # Replaces named args that aren't objects as FALSE
    unlist()                                    # To make index mappable

  # Wraps named args that aren't objects in new_quosure()
  exprs(...) %>%                    # Captures arguments
    unlist() %>%                    # Makes them mappable
    map_if(!index, new_quosure,     # Wraps non-object named arg in new_quosure()
           env = global_env()) %>%  # new_quosure() used instead of quo() as env must be global
    map_if(index, eval) %>%         # Removes expr() formatting
    list()                          # Makes arguments usable in invoke_map()
}

is.output.same<- function(.f_list, ...) {
  ## Checks to see if functions produce identical output

  param_list <- add_quo(...) # Enables function with dplyr syntax to work
  invoke_map(.f = .f_list, .x = param_list) %>%  # Pass args to functions
    reduce(identical) # Sees if all outputs are identical
}

check_output(function_list = list(m, n), data = iris, col_name = Species) #now works

【讨论】:

    猜你喜欢
    • 2023-03-06
    • 2020-02-24
    • 2022-06-24
    • 1970-01-01
    • 2018-05-11
    • 2020-05-13
    • 2023-02-19
    • 2019-03-21
    • 2022-01-23
    相关资源
    最近更新 更多