6

I've been studying the "Programming with dplyr" vignette because I want to create functions that use dplyr functions. I would like to use the functions I make in both shiny applications and interactive R work. For use in shiny, I would like these functions to take string arguments and convert them to symbols using the sym function. For use in interactive sessions, I would like these functions to have the option to not have to use strings. So the functions I make will need a way to tell if certain arguments are strings or not.

I figured out a way to this this. I'm just curious if there is a better and/or more elegant way to do it.

I made a simple function, "my_summarise", as an example. It is a different version of the function "my_summarise" from the vignette. It uses tryCatch to check if the group_var argument is a string.

library(dplyr)
df <- data.frame(g1 = c(1, 1, 2, 2, 2),
  g2 = c(1, 2, 1, 2, 1),
  a = c(1, 5, 4, 3, 2),
  b = c(3, 1, 2, 5, 4))

# df:

#  g1 g2 a b
#  1  1 1 3
#  1  2 5 1
#  2  1 4 2
#  2  2 3 5
#  2  1 2 4

my_summarise <- function(df, group_var) {

  is_string <- tryCatch(sym(group_var), error = function(group_var) group_var)

  if ("error" %in% class(is_string)) { 
    group_var <- enquo(group_var)      
  } else {
    group_var <- sym(group_var)   
  }

  df %>% group_by(!! group_var) %>% 
    summarise(a = mean(a))
}

my_summarise(df, g1)
# g1     a
# 1     3
# 2     3

my_summarise(df, "g1")
# g1     a
# 1     3
# 2     3

Edit: Onyambu's answer is perfect. I just tweaked it to use a few rlang functions instead of their base equivalents.

my_summarise <- function(df, group_var) {

  group_var <- enexpr(group_var)

  if(!is_symbol(group_var)) group_var <- sym(group_var) # instead of is.name and as.name you can use is.symbol and as.symbol or a mixture. 

  group_var <- enquo(group_var)      

  df %>% group_by(!! group_var) %>% 
    summarise(a = mean(a))
}
Dave Rosenman
  • 1,252
  • 9
  • 13

2 Answers2

6
my_summarise <- function(df, group_var) {

  group_var <- substitute(group_var)

 if(!is.name(group_var)) group_var <- as.name(group_var) # instead of is.name and as.name you can use is.symbol and as.symbol or a mixture. 

  group_var <- enquo(group_var)      

  df %>% group_by(!! group_var) %>% 
    summarise(a = mean(a))
}

You can also ignore the if condition altogether :

my_summarise <- function(df, group_var) {

group_var<- as.name(substitute(group_var))

  group_var <- enquo(group_var)      

  df %>% group_by(!! group_var) %>% 
    summarise(a = mean(a))
}
Onyambu
  • 67,392
  • 3
  • 24
  • 53
  • 1
    Interesting! Works! I tweaked it a little bit so that it uses rlang functions (enexpr instead of substitute, is_symbol instead of is.name, and sym instead of as.name). my_summarise <- function(df, group_var) { group_var <- enexpr(group_var) if(!is_symbol(group_var)) group_var <- sym(group_var) # instead of is.name and as.name you can use is.symbol and as.symbol or a mixture. group_var <- enquo(group_var) df %>% group_by(!! group_var) %>% summarise(a = mean(a)) } – Dave Rosenman Jun 07 '18 at 23:52
  • Just noticed I didn't think you for your answer! Thanks!! Super helpful Just to make sure I understand how it works... substitute(group_var) captures the argument group_var without evaluating it. Then is_symbol(group_var) evaluates the argument and returns FALSE if it is a string. So if(!is_symbol(group_var)) is true if the argument group_var is a string, and the string is turned into a symbol? – Dave Rosenman Jun 08 '18 at 00:01
3

We can do this with parse_expr

my_summarise <- function(df, group_var) {    


  group_var <- parse_expr(quo_name(enquo(group_var)))   

  df %>%
     group_by(!! group_var) %>% 
     summarise(a = mean(a))
}

-checking

my_summarise(df, g1)
# A tibble: 2 x 2
#     g1     a
#  <dbl> <dbl>
#1     1     3
#2     2     3

my_summarise(df, 'g1')
# A tibble: 2 x 2
#     g1     a
#  <dbl> <dbl>
#1     1     3
#2     2     3
akrun
  • 874,273
  • 37
  • 540
  • 662