R rlang: обрабатывать NULL-аргументы?

Я хочу использовать необязательный аргумент, который имеет значение по умолчанию NULL для функции dplyr (скажем, count()). Если я использую стандартную процедуру с !!enquo(), я получаю сообщение об ошибке: Ошибка: столбец NULL неизвестен.

Интересно, что 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

Создано 4 августа 2019 г. пакетом REPEX (v0.3.0)


person Matifou    schedule 04.08.2019    source источник
comment
1. Я запустил ваш код в новом сеансе консоли r с пустым .GlobalEnv. Последние две строки вызывают следующую ошибку Error: object 'a' not found. 2. При генерации случайных чисел set.seed().   -  person Grada Gukovic    schedule 05.08.2019
comment
Когда я запускаю код, последние две строки выдают ошибку, такую ​​же, как и Matifou.   -  person cuttlefish44    schedule 05.08.2019
comment
Это должно быть проблема версии, {{}} был реализован в rlang версии 0.4, я думаю, у вас есть эта версия и последняя версия dplyr / tidyverse?   -  person Matifou    schedule 05.08.2019


Ответы (1)


Ни group_by, ни count не принимают значения NULL. Итак, вы должны сначала создать объект quosures, используя enquos, и подмножество значений NULL. Поскольку count - это просто оболочка для tally и group_by, мы можем просто группировать и считать вручную, используя версию group_by с ограниченным объемом group_by_at.

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))
  )

}

Обновление на основе комментариев Матифо:

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
person paqmo    schedule 05.08.2019
comment
Отлично, большое спасибо! Я сделал несколько изменений, немного более компактных: с помощью count(!!!grps). И я понял, что можно просто использовать facet_grid(row= enquos(group_var2)), который будет работать в каждом случае! - person Matifou; 05.08.2019
comment
Очень красиво, добавлено в ответ для полноты картины - person paqmo; 05.08.2019
comment
отлично, взял на себя смелость обновить дальше, facet_grid(row= enquos(group_var2)) определенно самый простой! - person Matifou; 05.08.2019