dufei
dufei

Reputation: 3367

How to refer to a variable inside a function with tidy eval after renaming it?

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))
}

Upvotes: 1

Views: 1170

Answers (3)

Lionel Henry
Lionel Henry

Reputation: 6803

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

Upvotes: 3

Anoushiravan R
Anoushiravan R

Reputation: 21918

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

Upvotes: 3

Pete Kittinun
Pete Kittinun

Reputation: 603

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

Upvotes: 6

Related Questions