Search code examples
rdplyrlazy-evaluationnse

Tidy evaluation programming with dplyr::case_when


I try to write a simple function wrapping around the dplyr::case_when() function. I read the programming with dplyr documentation on https://cran.r-project.org/web/packages/dplyr/vignettes/programming.html but can't figure out how this works with the case_when() function.

I have the following data:

data <- tibble(
   item_name = c("apple", "bmw", "bmw")
)

And the following list:

cat <- list(
   item_name == "apple" ~ "fruit",
   item_name == "bmw" ~ "car"
)

Then I would like to write a function like:

category_fn <- function(df, ...){
   cat1 <- quos(...)
   df %>%
     mutate(category = case_when((!!!cat1)))
}

Unfortunately category_fn(data,cat) gives an evaluation error in this case. I would like to obtain the same output as the output obtained by:

data %>% 
   mutate(category = case_when(item_name == "apple" ~ "fruit",
                               item_name == "bmw" ~ "car"))

What is the way to do this?


Solution

  • Quote each element of your list first:

    cat <- list(
      quo(item_name == "apple" ~ "fruit"),
      quo(item_name == "bmw" ~ "car")
    )
    

    Your function does not then have to quote the cat object itself. I have also changed the use of the "everything else" ... argument to refer to the category argument explicitly in the call:

    category_fn <- function(df, categories){
      df %>%
        mutate(category = case_when(!!!categories))
    }
    

    The output of the function is then as expected:

    category_fn(data, cat)
    # A tibble: 3 x 2
      item_name category
          <chr>    <chr>
    1     apple    fruit
    2       bmw      car
    3       bmw      car
    

    For completeness, I note that the category list works with your function when defined using the base R quote() function too:

    cat <- list(
      quote(item_name == "apple" ~ "fruit"),
      quote(item_name == "bmw" ~ "car")
    )
    > cat
    [[1]]
    item_name == "apple" ~ "fruit"
    
    [[2]]
    item_name == "bmw" ~ "car"
    
    > category_fn(data, cat)
    # A tibble: 3 x 2
      item_name category
          <chr>    <chr>
    1     apple    fruit
    2       bmw      car
    3       bmw      car