Marijn Stevering
Marijn Stevering

Reputation: 1293

Using list as input for case_when inside of mutate

in short: Can you use !!! splicing and a list input for case_when inside of a mutate, and how?

As mentioned in an answer to previous question as of dplyr >0.7.0 you can use bare variable names inside a case_when() inside of a mutate()

Next to that, in the help file for case_when it shows how you can use a list of patterns and !!! like so: (patterns is the wrong way around, with general case first, but that is the case in the actual documentation and doesn't matter for my issue)

x <- 1:50
patterns <- list(
  TRUE ~ as.character(x),
  x %%  5 == 0 ~ "fizz",
  x %%  7 == 0 ~ "buzz",
  x %% 35 == 0 ~ "fizz buzz"
)
case_when(!!! patterns)

However, combining these approaches doens't seem to work:

testframe <- tibble(y = 1:50) #Switching to y so the x from earlier can't interfere

testframe2 <- testframe %>% 
  mutate(
    fizzbuzz = case_when(
      y %% 35 == 0 ~ "fizz buzz",
      y %% 5 == 0 ~ "fizz",
      y %% 7 == 0 ~ "buzz",
      TRUE ~ as.character(y)
    )
  )

patterns <- list(
  y %% 35 == 0 ~ "fizz buzz",
  y %% 5 == 0 ~ "fizz",
  y %% 7 == 0 ~ "buzz",
  TRUE ~ as.character(y)
)

testframe3 <- testframe %>% 
  mutate(
    fizzbuzz = case_when(
      !!!(patterns)
    )
  )

testframe2 will work fine, whilst testframe3 gives the following error:

Error in mutate_impl(.data, dots) : 
  Evaluation error: object 'y' not found.

I'm assuming there's some NSE magic at work here, but I haven't managed to quo() my way out of this. I did look at the quo()s of testframe2 and 3 (a debugging approach suggested in the programming with dplyr vignette). I noticed they are the same:

testframe2quo <- quo(testframe %>% 
  mutate(
    fizzbuzz = case_when(
      y %% 35 == 0 ~ "fizz buzz",
      y %% 5 == 0 ~ "fizz",
      y %% 7 == 0 ~ "buzz",
      TRUE ~ as.character(y)
    )
  )
)

testframe3quo <- quo(testframe %>% 
  mutate(
    fizzbuzz = case_when(
      !!!(patterns)
    )
  )
)

 testframe2quo
<quosure: global>
~testframe %>% mutate(fizzbuzz = case_when(y%%35 == 0 ~ "fizz buzz", 
    y%%5 == 0 ~ "fizz", y%%7 == 0 ~ "buzz", TRUE ~ as.character(y)))

testframe3quo
<quosure: global>
~testframe %>% mutate(fizzbuzz = case_when(y%%35 == 0 ~ "fizz buzz", 
    y%%5 == 0 ~ "fizz", y%%7 == 0 ~ "buzz", TRUE ~ as.character(y)))

all of which brings me back to my question: Can you use !!! splicing and a list input for case_when inside of a mutate, and how?

Upvotes: 2

Views: 1287

Answers (1)

Julien Navarre
Julien Navarre

Reputation: 7830

You should wrap your cases in exprs instead of list and use the prefix .data

patterns <- rlang::exprs(
  .data$y %% 35 == 0 ~ "fizz buzz",
  .data$y %% 5 == 0 ~ "fizz",
  .data$y %% 7 == 0 ~ "buzz",
  TRUE ~ as.character(.data$y)
)

mutate(testframe, fizzbuzz = case_when(!!! patterns))

Upvotes: 3

Related Questions