rdplyrrlangnon-standard-evaluation

avoid repeated unquoting in dplyr non standard evaluation


Suppose we have the following data:

tib <- tibble::tibble(x = 1:10)

Then, suppose we want to make a function that takes a column as input and returns a tibble with several added columns such as:

library(dplyr)
generate_transformations <- function(data, column){
    transform <- sym(column)
    data %>% 
        mutate(
            sqrt = sqrt(!!transform),
            recip = 1 / !!transform,
            log = log(!!transform)
        )
}
# Usage is great:
tib %>% 
    generate_transformations('x')
# A tibble: 10 x 4
       x  sqrt recip   log
   <int> <dbl> <dbl> <dbl>
 1     1  1    1     0    
 2     2  1.41 0.5   0.693
 3     3  1.73 0.333 1.10 
 4     4  2    0.25  1.39 
 5     5  2.24 0.2   1.61 
 6     6  2.45 0.167 1.79 
 7     7  2.65 0.143 1.95 
 8     8  2.83 0.125 2.08 
 9     9  3    0.111 2.20 
10    10  3.16 0.1   2.30

Now my question is, is there a way to avoid unquoting (!!) transform repeatedly? Yes, I could, e.g., temporarily rename column and then rename it back after I am done, but that is not my interest in this question. I am interested if there is a way to produce a variable that does not need the !!. While it does not work, I was looking for something like:

generate_transformations <- function(data, column){
    transform <- !!sym(column) # cannot unquote here :(
    data %>% 
        mutate(
            sqrt = sqrt(transform),
            recip = 1 / transform,
            log = log(transform)
        )
}

Solution

  • Convert to string and subset from the data and use transform

    generate_transformations <- function(data, column){
        transform <- data[[rlang::as_string(ensym(column))]]
        data %>% 
            mutate(
                sqrt = sqrt(transform),
                recip = 1 / transform,
                log = log(transform)
            )
    }
    

    -testing

    tib %>% 
         generate_transformations('x')
    # A tibble: 10 × 4
           x  sqrt recip   log
       <int> <dbl> <dbl> <dbl>
     1     1  1    1     0    
     2     2  1.41 0.5   0.693
     3     3  1.73 0.333 1.10 
     4     4  2    0.25  1.39 
     5     5  2.24 0.2   1.61 
     6     6  2.45 0.167 1.79 
     7     7  2.65 0.143 1.95 
     8     8  2.83 0.125 2.08 
     9     9  3    0.111 2.20 
    10    10  3.16 0.1   2.30 
    

    Or create a temporary column and remove it later

    generate_transformations <- function(data, column){
       
        data %>% 
            mutate(transform = !! rlang::ensym(column),
                sqrt = sqrt(transform),
                recip = 1 / transform,
                log = log(transform), 
                transform = NULL
            )
    }
    

    -testing

    tib %>% 
         generate_transformations('x')
    # A tibble: 10 × 4
           x  sqrt recip   log
       <int> <dbl> <dbl> <dbl>
     1     1  1    1     0    
     2     2  1.41 0.5   0.693
     3     3  1.73 0.333 1.10 
     4     4  2    0.25  1.39 
     5     5  2.24 0.2   1.61 
     6     6  2.45 0.167 1.79 
     7     7  2.65 0.143 1.95 
     8     8  2.83 0.125 2.08 
     9     9  3    0.111 2.20 
    10    10  3.16 0.1   2.30