【问题标题】:R rlang: handle NULL arguments?R rlang:处理 NULL 参数?
【发布时间】:2023-07-19 02:00:01
【问题描述】:

我想对dplyr 函数(比如count())使用具有默认NULL 值的可选参数。如果我对!!enquo() 使用标准过程,我会收到错误消息:error: Column NULL is unknown

有趣的是,rlang/tidyverse 允许缺失值,所以一个技巧可能是在 NULL 时转换为缺失,但看起来很脏(尤其是如果我想在之后使用 facet_grid,它接受 NULL 但不缺失)。

library(tidyverse)
df <- tibble(a = sample(LETTERS[1:2], 100, replace = TRUE), 
             b = sample(LETTERS[3:4], 100, replace = TRUE), 
             value = rnorm(100,5,1))

f2 <- function(df, group_var1=a,  group_var2=NULL, group_var3) {
  res <- df %>%
    count({{group_var1}}, {{group_var2}}, {{group_var3}})

  print(res)
  ggplot(aes(x=a, y=n), data = res)+
    geom_col() +
    facet_grid(row= enquo(group_var2))
}

f2(df, group_var1 = a, group_var2=b)
#> # A tibble: 4 x 3
#>   a     b         n
#>   <chr> <chr> <int>
#> 1 A     C        26
#> 2 A     D        29
#> 3 B     C        16
#> 4 B     D        29


f2(df, group_var1 = a)
#> Error: Column `NULL` is unknown

reprex package (v0.3.0) 于 2019-08-04 创建

【问题讨论】:

  • 1 。我在带有空 .GlobalEnv 的 r 控制台的新会话中运行了您的代码。最后两行抛出以下错误Error: object 'a' not found。 2.生成随机数时set.seed().
  • 当我运行代码时,最后两行抛出与 Matifou 相同的错误。
  • 这一定是版本问题,{{}} 是在 rlang 0.4 版本中实现的,我相信,你有那个版本,以及最新版本的 dplyr/tidyverse 吗?

标签: r tidyverse rlang


【解决方案1】:

group_bycount 都不会接受 NULL 值。因此,您必须首先使用enquos 创建一个quosures 对象并将NULL 值设置为子集。由于count 只是tallygroup_by 的包装,我们可以使用group_by_at 范围版本的group_by 手动分组和计数。

f2 <- function(df, group_var1=a,  group_var2=NULL, group_var3) {

grps <- enquos(a = group_var1, b = group_var2, c = group_var3, .ignore_empty = "all")

  # this removes the NULL values

  grps <- grps[map_lgl(grps, ~ !quo_is_null(.))]

  res <- df %>%
    group_by_at(grps) %>% 
    tally() %>% 
    ungroup()

  print(res) 
}

这很好地创建了res 数据框:

> f2(df, group_var1 = a, group_var2=b)
# A tibble: 4 x 3
  a     b         n
  <chr> <chr> <int>
1 A     C        20
2 A     D        30
3 B     C        22
4 B     D        28
> f2(df, group_var1 = a)
# A tibble: 2 x 2
  a         n
  <chr> <int>
1 A        50
2 B        50

但是,我们在尝试创建情节时再次遇到问题。 enquo 创建了一个带引号的对象,所以NULL 变成了"NULL"(更准确地说是`NULL`),所以ggplot 不知道如何处理它。所以我认为条件语句是要走的路:

f2 <- function(df, group_var1=a,  group_var2=NULL, group_var3) {

  grps <- enquos(a = group_var1, b = group_var2, c = group_var3, .ignore_empty = "all")

  grps <- grps[map_lgl(grps, ~ !quo_is_null(.))]

  res <- df %>%
    group_by_at(grps) %>% 
    tally() %>% 
    ungroup()

  print(res)

  if (quo_is_null(enquo(group_var2))) {
    ggplot(aes(x=a, y=n), data = res)+
      geom_col()
  } else(
    ggplot(aes(x=a, y=n), data = res)+
      geom_col() +
      facet_grid(row= enquo(group_var2))
  )

}

基于 Matifo 的 cmets 的更新:

library(tidyverse)
library(rlang)
df <- tibble(a = sample(LETTERS[1:2], 100, replace = TRUE), 
             b = sample(LETTERS[3:4], 100, replace = TRUE), 
             value = rnorm(100,5,1))

f2 <- function(df, group_var1=a,  group_var2=NULL, group_var3) {

  grps <- enquos(a = group_var1, b = group_var2, c = group_var3, .ignore_empty = "all")
  grps <- grps[map_lgl(grps, ~ !quo_is_null(.))]

  res <- df %>%
    count(!!!grps) 

  print(res)

  ggplot(aes(x=a, y=n), data = res)+
    geom_col() +
    facet_grid(row= enquos(group_var2))
}

f2(df, group_var1 = a, group_var2=b)
#> # A tibble: 4 x 3
#>   a     b         n
#>   <chr> <chr> <int>
#> 1 A     C        29
#> 2 A     D        33
#> 3 B     C        18
#> 4 B     D        20

f2(df, group_var1 = a)
#> # A tibble: 2 x 2
#>   a         n
#>   <chr> <int>
#> 1 A        62
#> 2 B        38

【讨论】:

  • 太好了,非常感谢!我做了一些改动,稍微紧凑了一点:使用count(!!!grps)。我意识到可以简单地使用facet_grid(row= enquos(group_var2)),这在每种情况下都可以使用!
  • 非常好,为了完整起见,添加到答案中
  • 太棒了,我冒昧地进一步更新,facet_grid(row= enquos(group_var2)) 绝对是最简单的!