Search code examples
rtidyeval

Convert strings to call dplyr functions


Example

I want to write a function which does the following:

filter_and_compute <- function(df,
                               condition,
                               col_to_modify,
                               calculus) {
  df %>%
    filter( condition ) %>%
    mutate( col_to_modify = calculus ) %>%
    rbind(df %>% filter(! condition))
}

But called with strings as arguments (except for df):

filter_and_compute(mtcars, "cyl == 4", "mpg", "2 * hp")

What I have done

I have made this version but which is of no use because I manipulate column names as strings:

filter_and_compute <- function(df,
                               filter_condition,
                               column_to_modify,
                               calculus) {
  enq_filter <-  enquo(filter_condition)
  enq_col_to_mod <- enquo(column_to_modify)
  enq_calculus <- enquo(calculus)
  (
    df
    %>% filter(!! enq_filter)
    %>% mutate(!! enq_col_to_mod := !! enq_calculus)
    %>% rbind( df %>% filter( ! (!! enq_filter)))
  )
}

filter_and_compute(mtcars, cyl == 4, mpg, 2 * cyl + hp)

I am aware of rlang::sym but I cannot get how to use it with unpredictible expressions like 2 * cyl + hp...


Solution

  • You can use eval with parse_expr

    library(dplyr)
    library(rlang)
    
    filter_and_compute <- function(df,
                                   condition,
                                   col_to_modify,
                                   calculus) {
      df %>%
        filter(eval(parse_expr(condition))) %>%
        mutate(!! col_to_modify := eval(parse_expr(calculus))) %>%
        rbind(df %>% filter(! eval(parse_expr(condition))))
    
    }
    
    filter_and_compute(mtcars, "cyl == 4", "mpg", "2 * hp")
    
    #     mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    #1  186.0   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    #2  124.0   4 146.7  62 3.69 3.190 20.00  1  0    4    2
    #3  190.0   4 140.8  95 3.92 3.150 22.90  1  0    4    2
    #4  132.0   4  78.7  66 4.08 2.200 19.47  1  1    4    1
    #5  104.0   4  75.7  52 4.93 1.615 18.52  1  1    4    2
    #6  130.0   4  71.1  65 4.22 1.835 19.90  1  1    4    1
    #7  194.0   4 120.1  97 3.70 2.465 20.01  1  0    3    1
    #8  132.0   4  79.0  66 4.08 1.935 18.90  1  1    4    1
    #9  182.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
    #10 226.0   4  95.1 113 3.77 1.513 16.90  1  1    5    2
    #11 218.0   4 121.0 109 4.11 2.780 18.60  1  1    4    2
    #12  21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    #....