1

Is it possible to refer to a variable name after renaming it using tidy evaluation? As an example, I would like to write a function that does the same as the following code but allows to specify the new variable name in a function argument:

library(tidyverse)

mtcars %>% 
  rename(cylinder = cyl) %>% 
  group_by(cylinder) %>% 
  summarize(mean_mpg = mean(mpg))

However, I am stuck in the group_by line (in the code below) because neither !!varname nor {{ varname }} works as a replacement for the question marks. I assume that !!varname does not work because it expands to a character string; and that {{ varname }} does not work because no column with the new name exists when the function is called. I don't see a way to use the glue syntax either because nothing is being assinged in that line.

my_rename <- function(df, varname) {
  df %>% 
    rename("{varname}" := cyl) %>% 
    group_by(???) %>%
    summarize(mean_mpg = mean(mpg))
}
dufei
  • 2,166
  • 1
  • 7
  • 18

3 Answers3

5

Running both with {{varname}} seems work

my_rename <- function(df, varname) {
  df %>% 
    rename({{varname}} := cyl) %>% 
    group_by({{varname}}) %>%
    summarize(mean_mpg = mean(mpg))
}

my_rename(mtcars, cylinder)

# A tibble: 3 x 2
  cylinder mean_mpg
     <dbl>    <dbl>
1        4     26.7
2        6     19.7
3        8     15.1

Pete Kittinun
  • 593
  • 3
  • 15
  • 1
    Interesting this works. I thought I had to pass the new variable name as a character string because it is not a "data variable" when it is first evaluated in the line with `rename`. I also did not know `:=` could be used for anything that does not have a string on the left-hand side. Not quite clear from https://dplyr.tidyverse.org/articles/programming.html – dufei May 28 '21 at 20:44
3

In order for your function to work you first have to defuse your custom argument name. For this purpose we could use either ensym or enquo function for defusing user defined arguments. After that you should use bang bang (!!) operator to unquote it.

my_rename <- function(df, varname) {
  varname <- ensym(varname)
  
  df %>% 
    rename(!!varname := cyl) %>% 
    group_by(!!varname) %>%
    summarize(mean_mpg = mean(mpg))
}

my_rename(mtcars, cylinder)

# A tibble: 3 x 2
  cylinder mean_mpg
     <dbl>    <dbl>
1        4     26.7
2        6     19.7
3        8     15.1

Here is another way that we use enquo function instead of ensym:

my_rename <- function(df, varname) {
  varname <- enquo(varname)
  
  df %>% 
    rename(!!varname := cyl) %>% 
    group_by(!!varname) %>%
    summarize(mean_mpg = mean(mpg))
}

# A tibble: 3 x 2
  cylinder mean_mpg
     <dbl>    <dbl>
1        4     26.7
2        6     19.7
3        8     15.1
Anoushiravan R
  • 21,622
  • 3
  • 18
  • 41
1

Regarding the glue syntax, you need "{{ varname }}" := instead of "{varname}". The simple curly is normal glue syntax, it fetches a string inside a variable. The double curly is extended glue syntax, it looks across function arguments to see what the user typed. So the correct syntax is:

my_rename <- function(df, varname) {
  df %>% 
    rename("{{ varname }}" := cyl) %>% 
    group_by({{ varname }}) %>%
    summarize(mean_mpg = mean(mpg))
}

my_rename(mtcars, cylinder)
#> # A tibble: 3 x 2
#>   cylinder mean_mpg
#>      <dbl>    <dbl>
#> 1        4     26.7
#> 2        6     19.7
#> 3        8     15.1

Now let's unpack the behaviour with your original code:

my_rename <- function(df, varname) {
  df %>%
    rename("{varname}" := cyl)
}

my_rename(mtcars, cylinder)
#> Error: object 'cylinder' not found

The problem here is that "{varname"} is essentially doing this:

cylinder
#> Error: object 'cylinder' not found

Instead of this:

rlang::quo(cylinder)
#> <quosure>
#> expr: ^cylinder
#> env:  global
Lionel Henry
  • 6,652
  • 27
  • 33
  • Thanks for the explanation! I should have mentioned that I was passing the variable name as a string, hence the single curly braces. This code seems to work because `rename` secretly accepts character strings as well as data variables? – dufei May 28 '21 at 20:48