MayaGans
MayaGans

Reputation: 1845

Tidyeval evaluate case_when

Somewhat related to Tidy evaluation programming with dplyr::case_when and Making tidyeval function inside case_when, I want to create strings (using a shiny app) to be parsed later inside a case_when function. Here's an example:

library(tidyverse)

# simulated shiny inputs
new_column = sym("COL_NAME")

number_of_categories = 3

col1_text = "Big"
col1_min = 7.0
col1_max = 8.0

col2_text = "Medium"
col2_min = 5.0
col2_max = 6.9

col3_text = "Small"
col3_max = 4.9
col3_min = 4.0

columninput = sym("Sepal.Length")

DESIRED OUTPUT

iris %>%
  mutate(new_column =
           case_when(
             !!columninput >= col1_min & !!columninput <= col1_max ~ col1_text,
             !!columninput  >= col2_min & !!columninput <= col2_max ~ col2_text,
             !!columninput  >= col3_min & !!columninput <= col3_max ~ col3_text
           )
  )

Because the only thing changing between functions is the index, I was thinking we can use the general pattern to create a string

# create single string
my_string <-function(i) {
  paste0("!!", columninput, " >= col", i, "_min & ", "!!", columninput, " <= col", i, "_max ~ col", i, "_text")
}

Then repeat the string for the dynamic number of cases

mega_string <- map_chr(1:number_of_categories, ~ my_string(.x))

TODO:

This is the part I cant quite piece together: using those strings as the arguments within a case_when.

# evaluate somehow?
iris %>%
  mutate(
    new_column = case_when(
      # tidyeval mega_string?
      paste(mega_string, collapse = "," )
      )
    )

Is this even the right approach? How else would you go about solving this - any help high level or otherwise is greatly appreciated!

Upvotes: 4

Views: 786

Answers (2)

Ploulack
Ploulack

Reputation: 337

thx for the nice question and answer. I'm using in same context (shiny).

I'd like to mention another approach that suits my needs better, and that I find more easy to read the logic off: rather than passing variables in the string to be evaluated you directly pass the values in the string coming from a tibble and str_glue_data

mega <- tribble(
    ~min, ~max, ~size,
    7, 8, "Big", 
    5, 6.9, "Medium",
    4.9, 4, "Small"
) %>% 
    str_glue_data("Sepal.Length >= {min} & Sepal.Length <= {max} ~ '{size}'")

iris %>% 
    mutate(new_column = case_when(!!! map(mega, rlang::parse_expr)))

Upvotes: 3

akrun
akrun

Reputation: 887501

We could create an expression and evaluate

library(dplyr)
library(stringr)
iris %>% 
   mutate(new_column = eval(rlang::parse_expr(str_c('case_when(', 
          str_c(mega_string, collapse=","), ')'))))
#    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species new_column
#1            5.1         3.5          1.4         0.2     setosa     Medium
#2            4.9         3.0          1.4         0.2     setosa      Small
#3            4.7         3.2          1.3         0.2     setosa      Small
#4            4.6         3.1          1.5         0.2     setosa      Small
#5            5.0         3.6          1.4         0.2     setosa     Medium
#6            5.4         3.9          1.7         0.4     setosa     Medium
#7            4.6         3.4          1.4         0.3     setosa      Small
#8            5.0         3.4          1.5         0.2     setosa     Medium
#9            4.4         2.9          1.4         0.2     setosa      Small
#10           4.9         3.1          1.5         0.1     setosa      Small
# ...

Or using parse_expr with !!!

library(purrr)
iris %>%
   mutate(new_column = case_when(!!! map(mega_string, rlang::parse_expr)))
#    Sepal.Length Sepal.Width Petal.Length Petal.Width    Species new_column
#1            5.1         3.5          1.4         0.2     setosa     Medium
#2            4.9         3.0          1.4         0.2     setosa      Small
#3            4.7         3.2          1.3         0.2     setosa      Small
#4            4.6         3.1          1.5         0.2     setosa      Small
#5            5.0         3.6          1.4         0.2     setosa     Medium
#6            5.4         3.9          1.7         0.4     setosa     Medium
#7            4.6         3.4          1.4         0.3     setosa      Small
#8            5.0         3.4          1.5         0.2     setosa     Medium
#...

Upvotes: 3

Related Questions