dfrankow
dfrankow

Reputation: 21357

dplyr group_by and summarize with non-standard evaluation

Suppose I have this code:

> df<-data.frame(a=c(1,1,1,2,2,2), b=c(T,T,F,F,F,T))
> df %>% group_by(a) %>% summarize(trues=sum(b), falses=sum(!b))
# A tibble: 2 x 3
      a trues falses
  <dbl> <int>  <int>
1     1     2      1
2     2     1      2

and I want to use a string variable to give b (in a for loop, for example). It's exactly like this question I think, but I can't seem to get it to work.

Some failed attempts:

var <- 'b'

df %>% group_by(a) %>% summarize_(trues=sum(var), falses=sum(!var))
df %>% group_by(a) %>% summarize(trues=sum({{var}}), falses=sum(!{{var}}))
df %>% group_by(a) %>% summarize_(trues=sum({{var}}), falses=sum(!{{var}}))
foo1 <- function(df, var) {
  df %>% group_by(a) %>% summarize(trues=sum({{var}}), falses=sum(!{{var}}))
}
foo1(df, 'b')
foo2 <- function(df, var) {
  var <- enquo(var)
  df %>% group_by(a) %>% summarize(trues=sum(!!var), falses=sum(! !!var))
}
foo2(df, 'b')

Perhaps I could use the lazyeval package in the other question, but I'd rather know how to do it with just the tidyverse.

Upvotes: 3

Views: 206

Answers (1)

akrun
akrun

Reputation: 886938

In this case, it is better to use ensym as we are passing a string. Also, the ensym works with unquoted argument as well

foo2 <- function(df, var) {
   var <- ensym(var)
   df %>% 
         group_by(a) %>%
         summarize(trues=sum(!!var), 
                   falses=sum(! (!!var)))
  }
foo2(df, 'b')
# A tibble: 2 x 3
#      a trues falses
#* <dbl> <int>  <int>
#1     1     2      1
#2     2     1      2


foo2(df, b)
# A tibble: 2 x 3
#      a trues falses
#* <dbl> <int>  <int>
#1     1     2      1
#2     2     1      2

If the argument passed is an object, evaluate (!!) while passing into the function to avoid the literal evaluation

foo2(df, !!var)
# A tibble: 2 x 3
#      a trues falses
#* <dbl> <int>  <int>
#1     1     2      1
#2     2     1      2

Upvotes: 3

Related Questions