【问题标题】:Evaluating ... when other function arguments are NULL by default评估 ... 当其他函数参数默认为 NULL
【发布时间】:2020-06-15 23:32:04
【问题描述】:

我想提供一个面向用户的函数,该函数允许将任意分组变量传递给摘要函数,并可选择指定其他参数进行过滤,但默认情况下为 NULL(因此未计算)。

我理解为什么下面的示例会失败(因为它在 homeworld 所属的位置不明确,而另一个 arg 优先),但我不确定在这种情况下正确传递点的最佳方法是什么。理想情况下,下面对fun 的第二次和第三次调用的结果将返回相同的结果。

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
fun <- function(.df, .species = NULL, ...) {

  .group_vars <- rlang::ensyms(...)

  if (!is.null(.species)) {
    .df <- .df %>%
      dplyr::filter(.data[["species"]] %in% .species)  
  }

  .df %>%
    dplyr::group_by(!!!.group_vars) %>%
    dplyr::summarize(
      ht = mean(.data[["height"]], na.rm = TRUE),
      .groups = "drop"
    )

}

fun(.df = starwars, .species = c("Human", "Droid"), species, homeworld)
#> # A tibble: 19 x 3
#>    species homeworld       ht
#>    <chr>   <chr>        <dbl>
#>  1 Droid   Naboo          96 
#>  2 Droid   Tatooine      132 
#>  3 Droid   <NA>          148 
#>  4 Human   Alderaan      176.
#>  5 Human   Bespin        175 
#>  6 Human   Bestine IV    180 
#>  7 Human   Chandrila     150 
#>  8 Human   Concord Dawn  183 
#>  9 Human   Corellia      175 
#> 10 Human   Coruscant     168.
#> 11 Human   Eriadu        180 
#> 12 Human   Haruun Kal    188 
#> 13 Human   Kamino        183 
#> 14 Human   Naboo         168.
#> 15 Human   Serenno       193 
#> 16 Human   Socorro       177 
#> 17 Human   Stewjon       182 
#> 18 Human   Tatooine      179.
#> 19 Human   <NA>          193
fun(.df = starwars, .species = NULL, homeworld)
#> # A tibble: 49 x 2
#>    homeworld         ht
#>    <chr>          <dbl>
#>  1 Alderaan        176.
#>  2 Aleen Minor      79 
#>  3 Bespin          175 
#>  4 Bestine IV      180 
#>  5 Cato Neimoidia  191 
#>  6 Cerea           198 
#>  7 Champala        196 
#>  8 Chandrila       150 
#>  9 Concord Dawn    183 
#> 10 Corellia        175 
#> # … with 39 more rows
fun(.df = starwars, homeworld)
#> Error in fun(.df = starwars, homeworld): object 'homeworld' not found


<sup>Created on 2020-06-15 by the [reprex package](https://reprex.tidyverse.org) (v0.3.0)</sup>

我知道我可以通过以下方式达到预期的结果:

fun <- function(.df, .species = NULL, .groups = NULL) {

  .group_vars <- rlang::syms(purrr::map(.groups, rlang::as_string))

...

}

但我正在寻找使用... 的解决方案,或者允许用户将字符串或符号传递给.groups,例如.groups = c(species, homeworld).groups = c("species", "homeworld")

【问题讨论】:

  • 传递一个空参数怎么样? fun(.df = starwars, ,homeworld) 否则需要在... 中传递.species
  • @RonakShah NULL 默认值背后的一点是,我希望用户不必担心传递给.species 的内容,并且默认行为是不过滤。执行您的建议相当于只要求用户明确指定fun(.df = starwars, .species = NULL, homeworld)
  • 我仍然没有在您的编辑中看到任何可以消除我的答案有效的内容。

标签: r dplyr rlang


【解决方案1】:

您可以移动参数,使.species 出现在点之后。

fun <- function(.df, ..., .species = NULL) {

    .group_vars <- rlang::ensyms(...)

    if (!is.null(.species)) {
        .df <- .df %>%
            dplyr::filter(.data[["species"]] %in% .species)  
    }

    .df %>%
        dplyr::group_by(!!!.group_vars) %>%
        dplyr::summarize(
            ht = mean(.data[["height"]], na.rm = TRUE),
            .groups = "drop"
        )

}

fun(.df = starwars, homeworld)

给了

> fun(.df = starwars, homeworld)
# A tibble: 49 x 3
   homeworld         ht .groups
   <chr>          <dbl> <chr>  
 1 NA              139. drop   
 2 Alderaan        176. drop   
 3 Aleen Minor      79  drop   
 4 Bespin          175  drop   
 5 Bestine IV      180  drop   
 6 Cato Neimoidia  191  drop   
 7 Cerea           198  drop   
 8 Champala        196  drop   
 9 Chandrila       150  drop   
10 Concord Dawn    183  drop   
# ... with 39 more rows

这是你想要发生的。其他示例仍然有效。

【讨论】:

  • 我知道这个解决方案,但我想我觉得它有点不满意,因为它依赖于参数顺序而不是函数定义中参数的受控评估。
  • 你要求的东西颠覆了论证顺序。我不认为真的有更好的解决方案。如果 .species 是第二个参数并且您没有命名参数,那么第二个参数将始终引用 .species。
  • 您的其他示例的工作方式也完全相同。
  • 我能想到的唯一其他选择是使用fun &lt;- function(df, ...) 并手动解析 ... 以查看 .species 是否出现在点列表中并分别处理每种情况。
猜你喜欢
  • 2012-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 2015-04-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多