Получение quosures для работы внутри вызова карты

Я изо всех сил пытаюсь заставить quosures работать внутри вызова map.

Некоторые данные игрушки:

library(tidyverse)

df <- tibble(
   g1 = letters[1:2] %>% 
     rep(each = 3),
   g2 = letters[3:5] %>% 
     rep(times = 2),
   y = runif(6)
  )

Я могу заставить эту функцию работать, где я enquo переменную перед тем, как передать ее group_by:

sum1 <- function(df, g){

 g <- enquo(g)

 df %>% 
   group_by(!! g) %>% 
   summarize(
     mu = y %>% 
       mean
     )
  }

Вызов этой функции

 sum1(df, g2)

дает мне ожидаемый результат. Но если я хочу map по нескольким переменным группировки (т.е. g1 и g2)

 str_c("g", 1:2) %>% 
   map(
    function(i)
      sum1(df, i)
   )

Возвращает ошибку

  Error in grouped_df_impl(data, unname(vars), drop) : 
   Column `i` is unknown 

Как настроить quosures в вызове map?


person tomw    schedule 18.06.2018    source источник


Ответы (2)


Мы можем использовать group_by_at, и он может принимать строку в качестве аргумента.

library(tidyverse)
sum1 <- function(df, grps){

 map(grps, ~ 
           df %>%
              group_by_at(.x) %>%
              summarise(mu = mean(y))
              )

              }

sum1(df, str_c("g", 1:2))
#[[1]]
# A tibble: 2 x 2
#  g1       mu
#  <chr> <dbl>
#1 a     0.440
#2 b     0.469

#[[2]]
# A tibble: 3 x 2
#  g2       mu
#  <chr> <dbl>
#1 c     0.528
#2 d     0.592
#3 e     0.243

Что касается использования параметров с quosure в функции, неясно, должен ли это быть один параметр или несколько параметров.

В случае, если мы собираемся использовать строку в качестве аргумента, преобразуйте ее в символ (sym), а затем оцените (!!)

sum2 <- function(df, grps){


 map(grps, ~ 
           df %>%
              group_by(!! rlang::sym(.x)) %>%
              summarise(mu = mean(y))
              )

              }

sum2(df, str_c("g", 1:2))
#[[1]]
# A tibble: 2 x 2
#  g1       mu
#  <chr> <dbl>
#1 a     0.440
#2 b     0.469

#[[2]]
# A tibble: 3 x 2
#  g2       mu
#  <chr> <dbl>
#1 c     0.528
#2 d     0.592
#3 e     0.243

Другой с quosure для передачи нескольких групп будет

sum3 <- function(df, ...){

   gs <- enquos(...)
   map(gs, ~ 
         df %>%
            group_by(!! .x) %>%
            summarise(mu = mean(y)))


              }
sum3(df, g1, g2)
#[[1]]
# A tibble: 2 x 2
#  g1       mu
#  <chr> <dbl>
#1 a     0.440
#2 b     0.469

#[[2]]
# A tibble: 3 x 2
#  g2       mu
#  <chr> <dbl>
#1 c     0.528
#2 d     0.592
#3 e     0.243
person akrun    schedule 18.06.2018

str_c("g", 1:2) %>% 
  syms() %>%
  map(sum1, df = df)

syms() превращает character в символы (ожидается sum1).

Переписывание map(function(i) sum1(df, i)) как map(sum1, df = df) предотвращает нежелательную оценку обещания i, которое происходит, когда sum1 заключено в другую функцию.

Переписывание map(function(i) sum1(df, i)) как map(sum1, df = df) позволяет передавать символы g1 и g2 непосредственно в sum1(), а не в символ i.

(В качестве альтернативы работают str_c("g", 1:2) %>% syms() %>% map(function(i) sum1(df, !! i)) или str_c("g", 1:2) %>% map(function(i) sum1(df, !! sym(i))), где !! раскавычивает i перед передачей его sum1().
(На самом деле это немного упрощено: раскавычка не происходит до, но когда вы делаете enquo(g) в тело sum1).

person Aurèle    schedule 18.06.2018