如何匹配 R 中函数参数的多种组合?

Posted

技术标签:

【中文标题】如何匹配 R 中函数参数的多种组合?【英文标题】:How to match many combinations of function arguments in R? 【发布时间】:2021-12-11 00:18:55 【问题描述】:

我们可以创建一个二维数组来匹配两个函数参数的组合吗?

例如,如果我编写一个带有 1 个参数的函数(除了数据输入参数):

choose_procedure <- function(x, what_to_do) 
  switch(what_to_do,
         "mean"   = ..., # mean(x) or mean(x, na.rm = TRUE) or weighted.mean(x)
         "median" = ..., # median(x)
         "square" = ..., # x * x or x ^ 2
         "unique" = ..., # unique(x)
         "log"    = ...  # log(x) or log10(x)
  )

我添加了内联 cmets 以暗示每个 what_to_do 输入可能有多个选择。

what_to_do = "mean" 时,应该是mean(x, na.rm = TRUE) 还是mean(x, na.rm = FALSE)? 同样,当what_to_do = "log" 时,应该是log(x) 还是log10(x)?等等。

为了解决这个问题,我想向choose_procedure() 引入另一个参数,称为"scenario"。所以如果对choose_procedure()的调用是:

choose_procedure(x = mtcars$mpg, what_to_do = "log", scenario = "A")

然后它将执行log(mtcars$mpg)。 但是如果调用是

choose_procedure(x = mtcars$mpg, what_to_do = "log", scenario = "B")

然后它将执行log10(mtcars$mpg)


仅包含 "log""scenario" 的示例描述了一个 2x2 数组:

"what_to_do" 有 2 个选项:log()log10() "scenario" 有两个选项:"A""B"

显然,这可以用 4 个 if 语句(每个组合一个)来处理,但如果我们有更多组合(如我打开的 choose_procedure() 示例),编程将变得非常困难。

所以我有两个问题:

    我正在寻找一种可以扩展到任何可能的 n × n 数组的设置。 事实上,也许有一种方法可以泛化到超过n × n?例如,如果我们有 3 个参数:"what_do_to""scenario""sub_scenario"。等等。

【问题讨论】:

了解match.fun 可以,但通常不是一个好主意。您的代码似乎想用字符串模拟 OOP 或函数式编程。请改用适当的工具——即高阶函数、OOP 或其他什么。特别是,您似乎正在重塑策略模式。 @KonradRudolph,reinventing the strategy pattern 是什么意思?我想这个问题确实是关于函数式编程的(虽然我在发帖时没有意识到)。但是 R 中不鼓励 FP 吗? @Emman FP ,但你的代码不是 FP——它使用string typing,而不是在适当的地方使用正确的类型,即函数。 @Emman wurli 的回答是使用字符串时的一个很好的解决方案。但是,是的,更好的解决方案是首先不使用字符串,如另一个答案所示。 【参考方案1】:
choose_procedure <- function(x, FUN, ...)
   if(...length()) FUN(x, ...)
   else FUN(x)
 

x <- c(1,3,5,NA, 10)

choose_procedure(x, mean)
[1] NA

choose_procedure(x, mean, na.rm = TRUE)
[1] 4.75

choose_procedure(x, log)
[1] 0.000000 1.098612 1.609438       NA 2.302585

choose_procedure(x, log10)
[1] 0.0000000 0.4771213 0.6989700        NA 1.0000000

【讨论】:

谢谢。这很有帮助。但是,您的解决方案会处理用户知道预先选择相关功能的特定情况。但我希望输入参数更高级。例如choose_procedure(x = my_df, FUN = "wrangle_data", data_source = c("my_mom", "my_brother"))。所以"wrangle_data" 只是一个标签,而不是实际的函数名。很像switch()。而"wrangle_data" 又调用了一个较低级别的函数(可能是uniquena.omit 或我决定的任何东西,根据data_source arg) @Emman,由于“wrangle_data”将调用较低级别的函数,请考虑将较低级别的函数命名为 wrangle_data。然后您可以使用与上述相同的过程。 @Emman 你也可以调用match.fun。不需要switch。当然,除非函数在列表中。 您能否就您的解决方案的工作原理添加一些解释?我的意思是,你会如何向人类解释if(...length()) FUN(x, ...)?我试过用谷歌搜索它,但没有找到洞察力。或者是否有讨论这种策略的参考/教科书?谢谢! @Emman if 语句声称如果您传递任何附加参数,即length(...)&gt;0,则该函数使用附加参数,因此使用附加参数运行该函数。即均值、中值等。注意log 只接受一个参数,x 因此只需要 FUN(x) 但均值可以是mean(x, na.rm = TRUE),因此FUN(x, na.rm = TRUE) 因此FUN(x, ...) 省略号代表附加参数 【参考方案2】:

这是有很多方法可以解决问题的方法之一,最好的方法很可能取决于您实际想要使用它的上下文。但是,在您概述的情况下,以下是方法我会接近它:

choose_procedure <- function(x, ...) 
  
  # Define a table of options
  choices <- tibble::tribble(
    ~what_to_do,  ~scenario,                ~result,
         "mean",        "A",                   mean,
         "mean",        "B", ~mean(., na.rm = TRUE),
         "mean",        "C",          weighted.mean,
       "median",        "A",                 median,
       "square",        "A",                 ~. * .,
       "square",        "B",                 ~. ^ 2,
       "unique",        "A",                 unique,
          "log",        "A",                    log,
          "log",        "B",                  log10
  )
  
  # Filter the table down to the desired option
  choice <- dplyr::filter(choices, ...)
  
  # Stop if no options available
  if (nrow(choice) == 0) 
    stop("No such option available")
  
  
  # Warn if multiple options available, and use first
  if (nrow(choice) > 1) 
    choice <- head(choices, 1)
    warning("More than one option available, using first scenario")
  
  
  # Transform any purrr-style lambda functions to normal functions
  fun <- rlang::as_function(choice$result[[1]])
  
  # Perform the calculation
  fun(x)
  


choose_procedure(x = mtcars$mpg, what_to_do == "log", scenario == "B")
#>  [1] 1.322219 1.322219 1.357935 1.330414 1.271842 1.257679 1.155336 1.387390
#>  [9] 1.357935 1.283301 1.250420 1.214844 1.238046 1.181844 1.017033 1.017033
#> [17] 1.167317 1.510545 1.482874 1.530200 1.332438 1.190332 1.181844 1.123852
#> [25] 1.283301 1.436163 1.414973 1.482874 1.198657 1.294466 1.176091 1.330414

由reprex package (v2.0.0) 于 2021 年 10 月 25 日创建

【讨论】:

以上是关于如何匹配 R 中函数参数的多种组合?的主要内容,如果未能解决你的问题,请参考以下文章

默认参数取决于 R 中匹配的函数

R语言paste函数

R语言使用caret包的train函数构建多元自适应回归样条(MARS)模型模型调优自定义设置tuneGrid参数多个超参数组合调优trainControl函数自定义调优评估指标

Python笔记 · 函数参数

R语言leaps包中的regsubsets函数实现全集子集回归(all subsets regression)使用调整R方和Mallows Cp统计量筛选最优模型并可视化不同组合参数下的模型指标

如何创建一个从管道和命令行接受多种参数类型的函数?