'Dynamic `case_when` that allows for different number of conditions and conditions itself

I'm looking for a dynamic way to specify some "condition parameters" and then feed that to a case_when operation or something else if better suited for that problem.

My goal is to separate the specification of the conditions from the case_when call, e.g. so that a user can just type in the condition in a text file or a list in R and then I would take that info and feed it to case_when (or any other function if more appropriate).

Assuming the following data where I want to create an additional variable that recodes x, I could do:

df <- data.frame(x = 1:10)

df |>
  mutate(x2 = case_when(x < 4 ~ 1,
                        x >= 4 & x <=7 ~ 2,
                        TRUE ~ 3))

Now, what I want to achieve is to make this code flexible in a way that I can externally specify the case_when conditions and then do the recoding.

E.g. it might look like:

all_conditions <- list(1 = "x < 2",
                       2 = "x >= 2 & x < 5",
                       3 = "x >= 5 & x < 9",
                       4 = "TRUE")

And then I could do some sort of:

df |>
  mutate(x2 = do(case_when, all_conditions))

While the example shows a numeric type of variable for which the solution of @Mael works, the solution should also work for character vars where the condition might look like x == "abc" | x == "def".



Solution 1:[1]

A possible solution, base on rlang, is below.

EXPLANATION

  • First, we need to create a string with the whole code for case_when, using the list all_conditions — that is what my imap does.

  • Second, using rlang::parse_quo, we transform the string into an expression to be used inside mutate.

Remark

The names of the elements of list all_conditions have to be enclosed by backticks.

library(tidyverse)
library(rlang)

df <- data.frame(x = 1:10)

all_conditions <- list(`1` = "x < 2",
                       `2` = "x >= 2 & x < 5",
                       `3` = "x >= 5 & x < 9",
                       `4` = "TRUE")

code <- imap(all_conditions, ~ str_c(.x, " ~ ", .y)) %>% 
          str_c(collapse = ", ") %>% str_c("case_when(",.,")")

df %>% 
  mutate(x2 = !!parse_quo(code, env = caller_env()))

#>     x x2
#> 1   1  1
#> 2   2  2
#> 3   3  2
#> 4   4  2
#> 5   5  3
#> 6   6  3
#> 7   7  3
#> 8   8  3
#> 9   9  4
#> 10 10  4

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1