Search code examples
rtidyrtidyeval

Use mutate + across with case_when with a list of conditions


I want to modify a list of columns. Each column has a list of conditions that need to be applied with a case_when.

I tried the following:

df <- tibble(value=c("a","b","c"))

# minimal example modifying only one column
case_when_exprs <- list("value" =  rlang::exprs(.data  == "a" ~ "A", TRUE ~ .data))

# if I were to apply the case_when conditions manually it works
df  %>% mutate(across(names(case_when_exprs), ~ case_when(.x  == "a" ~ "A", TRUE ~ .x)))

# but when trying to do it dynamically it fails
df  %>% mutate(across(names(case_when_exprs), ~ case_when(!!!case_when_exprs[cur_column()])))

but I'm getting this error:

Error in local_error_context(dots = dots, .index = i, mask = mask): promise already under evaluation: recursive default argument reference or earlier problems?

what am I doing wrong?


Solution

  • While I already encountered the error you mention myself I still have no clue what's the reason.

    But one option to make your code work would be to use a wrapper around case_when and minor fix, i.e. we need to use [[ instead of [:

    library(dplyr, warn=FALSE)
    
    df <- tibble(value=c("a","b","c"))
    
    case_when_exprs <- list("value" =  rlang::exprs(.data  == "a" ~ "A", TRUE ~ .data))
    
    case_expr <- function(.data, expr) {
      case_when(!!!expr)  
    }
    
    df  %>% 
      mutate(
        across(
          names(case_when_exprs), 
          ~ case_expr(.x, case_when_exprs[[cur_column()]]))
      )
    #> # A tibble: 3 × 1
    #>   value
    #>   <chr>
    #> 1 A    
    #> 2 b    
    #> 3 c
    

    Or as a slight variation:

    case_expr2 <- function(.data, expr) {
      case_when(!!!case_when_exprs[[expr]])  
    }
    
    df  %>% 
      mutate(
        across(
          names(case_when_exprs), 
          ~ case_expr2(.x, cur_column()))
      )
    #> # A tibble: 3 × 1
    #>   value
    #>   <chr>
    #> 1 A    
    #> 2 b    
    #> 3 c