Search code examples
rdplyrtidyeval

Create new column based on condition from other column per group using tidy evaluation


Similar to this question but I want to use tidy evaluation instead.

df = data.frame(group = c(1,1,1,2,2,2,3,3,3), 
                date  = c(1,2,3,4,5,6,7,8,9),
                speed = c(3,4,3,4,5,6,6,4,9))
> df
  group date speed
1     1    1     3
2     1    2     4
3     1    3     3
4     2    4     4
5     2    5     5
6     2    6     6
7     3    7     6
8     3    8     4
9     3    9     9

The task is to create a new column (newValue) whose values equals to the values of the date column (per group) with one condition: speed == 4. Example: group 1 has a newValue of 2 because date[speed==4] = 2.

    group date speed newValue
1     1    1     3        2
2     1    2     4        2
3     1    3     3        2
4     2    4     4        4
5     2    5     5        4
6     2    6     6        4
7     3    7     6        8
8     3    8     4        8
9     3    9     9        8

It worked without tidy evaluation

df %>%
  group_by(group) %>%
  mutate(newValue=date[speed==4L])
#> # A tibble: 9 x 4
#> # Groups:   group [3]
#>   group  date speed newValue
#>   <dbl> <dbl> <dbl>    <dbl>
#> 1     1     1     3        2
#> 2     1     2     4        2
#> 3     1     3     3        2
#> 4     2     4     4        4
#> 5     2     5     5        4
#> 6     2     6     6        4
#> 7     3     7     6        8
#> 8     3     8     4        8
#> 9     3     9     9        8

But had error with tidy evaluation

my_fu <- function(df, filter_var){
  filter_var <- sym(filter_var)
  df <- df %>%
    group_by(group) %>%
    mutate(newValue=!!filter_var[speed==4L])
}

my_fu(df, "date")
#> Error in quos(..., .named = TRUE): object 'speed' not found

Thanks in advance.


Solution

  • We can place the evaluation within brackets. Otherwise, it may try to evaluate the whole expression (filter_var[speed = 4L]) instead of filter_var alone

    library(rlang)
    library(dplyr)
    my_fu <- function(df, filter_var){
         filter_var <- sym(filter_var)
       df %>%
          group_by(group) %>%
         mutate(newValue=(!!filter_var)[speed==4L])
        }
    
    my_fu(df, "date")
    # A tibble: 9 x 4
    # Groups:   group [3]
    #  group  date speed newValue
    #  <dbl> <dbl> <dbl>    <dbl>
    #1     1     1     3        2
    #2     1     2     4        2
    #3     1     3     3        2
    #4     2     4     4        4
    #5     2     5     5        4
    #6     2     6     6        4
    #7     3     7     6        8
    #8     3     8     4        8
    #9     3     9     9        8