Tidyeval functions with character inputs

Hi All,

I have a tidyeval question that is puzzling me, which I'd be grateful for any help or thoughts.

Is it possible to use this grouped_mean function with character variable inputs?

library(tidyverse)

grouped_mean <- function(data, group_var, summary_var) {
  group_var <- enquo(group_var)
  summary_var <- enquo(summary_var)
  
  data %>%
    group_by(!!group_var) %>%
    summarise(mean = mean(!!summary_var))
}

grouped_mean(mtcars, cyl, mpg) # this works
grouped_mean(mtcars, "cyl", "mpg") # but this doesn't work, and I would like it to

Ideally I want to not modify the grouped_mean function, but instead modify how the character variable input comes into the function

The reason I ask is that I have made some similar tidyeval functions for graphing and mapping that I would like to use shiny inputs

Thanks!
Dave

Hi @davidhodge931. You may try to use use !!as.name.

library(tidyverse)

grouped_mean <- function(data, group_var, summary_var) {
  group_var <- enquo(group_var)
  summary_var <- enquo(summary_var)
  
  data %>%
    group_by(!!group_var) %>%
    summarise(mean = mean(!!summary_var))
}

grouped_mean(mtcars, cyl, mpg) # this works
#> # A tibble: 3 x 2
#>     cyl  mean
#>   <dbl> <dbl>
#> 1     4  26.7
#> 2     6  19.7
#> 3     8  15.1
grouped_mean(mtcars, !!as.name("cyl"), !!as.name("mpg"))
#> # A tibble: 3 x 2
#>     cyl  mean
#>   <dbl> <dbl>
#> 1     4  26.7
#> 2     6  19.7
#> 3     8  15.1

Created on 2019-09-17 by the reprex package (v0.3.0)

1 Like

Not modifying your function would require to recreate symbol to pass to the function.
using !!sym() from tidyeval would work I guess

library(dplyr)
#> 
#> Attachement du package : 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union

grouped_mean <- function(data, group_var, summary_var) {
  group_var <- enquo(group_var)
  summary_var <- enquo(summary_var)
  
  data %>%
    group_by(!!group_var) %>%
    summarise(mean = mean(!!summary_var))
}

grouped_mean(mtcars, cyl, mpg)
#> # A tibble: 3 x 2
#>     cyl  mean
#>   <dbl> <dbl>
#> 1     4  26.7
#> 2     6  19.7
#> 3     8  15.1
grouped_mean(mtcars, !!sym("cyl"), !!sym("mpg"))
#> # A tibble: 3 x 2
#>     cyl  mean
#>   <dbl> <dbl>
#> 1     4  26.7
#> 2     6  19.7
#> 3     8  15.1

Created on 2019-09-17 by the reprex package (v0.3.0)

If you accept to modify the function, you could try to accept both type of input.

2 Likes

Thanks @raytong and @cderv that is really helpful, and solves out my problem!

Out of curiousity @cderv how would I modify the function to accept quoted or non-quoted variables?

I'm hoping someone comes along with a recommended way for a function to take both bare symbols and strings as inputs since I haven't found anything that seems particularly straightforward.

One way, which seems pretty roundabout, is to convert everything (symbols or strings) to strings with rlang::as_name(). Then sym() can be used to convert the strings to symbols (as I said, roundabout :stuck_out_tongue_winking_eye:).

grouped_mean = function(data, group_var, summary_var) {
    group_var = sym( rlang::as_name( enquo(group_var) ) )
    summary_var = sym( rlang::as_name( enquo(summary_var) ) )

    data %>%
        group_by(!!group_var) %>%
        summarise(mean = mean(!!summary_var))
}

Then using either strings or symbols as inputs returns the same output.

grouped_mean(mtcars, "cyl", "mpg")
# A tibble: 3 x 2
#    cyl  mean
#  <dbl> <dbl>
#1     4  26.7
#2     6  19.7
#3     8  15.1
> grouped_mean(mtcars, cyl, mpg)
# A tibble: 3 x 2
#    cyl  mean
#  <dbl> <dbl>
#1     4  26.7
#2     6  19.7
#3     8  15.1

Edited to add another approach
I thought there should be some way to use a conditional statement within the function. This would be based on checking whether the input was a symbol or not and then use enquo() or sym() based on that. It looks like rlang::quo_is_symbol() is the predicate symbol that can do the checking.

grouped_mean = function(data, group_var, summary_var) {
    if(rlang::quo_is_symbol( enquo(group_var) ) ) {
        group_var = enquo(group_var)
    } else { 
        group_var = sym(group_var) 
    }
    
    if(rlang::quo_is_symbol( enquo(summary_var) ) ) {
        summary_var = enquo(summary_var)
    } else {
        summary_var = sym(summary_var)
    }
    
    data %>%
        group_by(!!group_var) %>%
        summarise(mean = mean(!!summary_var))
}
4 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.