Use dplyr :: case_when with arguments programmatically

I would like to use dplyr case_when programmatically to replace the base function R cut() .

Currently, case_when can be used with an external argument via NSE, for example:

 library(dplyr) library(rlang) patterns <- list( x <= 2 ~ "<=2", x <= 4 ~ "2<->4", x > 4 ~ ">4" ) x <- 1:10 case_when(!!!patterns) 

What I want to do: use it with another variable inside the mutant

The idea would be like this, although I can't figure out how to make it work:

 library(dplyr) patterns_lazy <- list( !!quo(x) <= 2 ~ "<=2", !!quo(x) <= 4 ~ "2<->4", !!quo(x) > 4 ~ ">4" ) x <- "cyl" mtcars %>% mutate(ABC = case_when(!!!patterns_lazy)) 

I would like to be able to define the column (inside the row) that I want to filter and retrieve something like this (this example does not work as the desired syntax):

 x <- "cyl" mtcars %>% select(cyl) %>% mutate(ABC = case_when(!!!patterns_lazy)) %>% head() cyl ABC 1 6 >4 2 6 >4 3 4 2<->4 4 6 >4 5 8 >4 6 6 >4 

Thanks for any help :)

+5
source share
2 answers

You can not use !! there:

 patterns <- list( !!quo(x) <= 2 ~ "<=2", !!quo(x) <= 4 ~ "2<->4", !!quo(x) > 4 ~ ">4" ) 
  • Neither list() nor ~ support quasicotation.
  • If it supports quasicotation, you need to be careful with operator precedence and enclose in parentheses !!quo() .
  • And finally, this quote x will evaluate the string, and you will compare numbers with strings (in your example, it will be "cyl ), which R will do happily thanks to implicit constraints: /

So you need to use exprs() instead of list() and use x with .data pronoun instead of quote x .

exprs() will create a list of invaluable expressions. uncalculated Good: if your formula has been evaluated, it will carry the environment (here global env), and this environment does not contain the data provided by dplyr, and in particular, does not have a .data pronoun. On the other hand, if the formulas are β€œout of context”, they get an estimate in the context of the data that we want.

 patterns_lazy <- exprs( .data[[x]] <= 2 ~ "<=2", .data[[x]] <= 4 ~ "2<->4", .data[[x]] > 4 ~ ">4" ) x <- "cyl" pull(mutate(mtcars, case_when(!!!patterns_lazy))) #> [1] ">4" ">4" "2<->4" ">4" ">4" ">4" ">4" "2<->4" "2<->4" #> [10] ">4" ">4" ">4" ">4" ">4" ">4" ">4" ">4" "2<->4" #> [19] "2<->4" "2<->4" "2<->4" ">4" ">4" ">4" ">4" "2<->4" "2<->4" #> [28] "2<->4" ">4" ">4" ">4" "2<->4" 
+6
source

Here is one option: ifelse

 f1 <- function(data, x){ x <- enquo(x) f2 <- function(y) ifelse(y <= 2, "<=2", ifelse(y <=4, "2<->4", ">4")) data %>% mutate( ABC = f2(UQ(x))) } f1(mtcars, cyl) %>% head() # mpg cyl disp hp drat wt qsec vs am gear carb ABC #1 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 >4 #2 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 >4 #3 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 2<->4 #4 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 >4 #5 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 >4 #6 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 >4 
+2
source

Source: https://habr.com/ru/post/1269322/


All Articles