Search code examples
rfunctional-programmingdplyrpurrrquasiquotes

Mixed input in quasiquotation with dplyr


I've taken quasiquotation to a top level. I'm close to getting my master-quasiquotator badge (see edits below). There's one chellenge bit left.

Using different inputs to create quosures with dplyr. The end result is:

the_quote <- quo( if_else(!!cond_expr, !!inter_quo, !!var_expr) )

And I have managed to construct expressions above from a customized table with character strings, like this:

var_expr <- as.name(rules_df$target_col)

cond_expr <- "make == '%s'" %>% sprintf(rules_df$context_col) %>% parse_expr()

inter_quo <- quo( 
    str_detect( !!var_expr, regex(!!rules_df$phrase_col) ))

And where context_col, phrase_col, target_col are string columns in a table that I've defined the rules of engagement.

Example:

rules_df <- data_frame(
    context_col = "BMW", 
    phrase_col  = "Serie X(\\d)", 
    target_col  = "model")

cars_table <- data_frame(
    make = c("Mercedes", "BMW", "BMW"), 
    model = c("Viano", "Serie X5", "Z4"))

Tells me to find those BMW's as Serie X5 , which I would later replace with just X5 but that's another story.

On printing the quote, I noticed that the expressions work well, but intermediate quosure is giving an error.

> the_quote
<quosure>
  expr: ^if_else(marca == "BMW", 
            ^str_detect(model, regex("Serie X(\d)")), model)
  env:  000000002001DEE0

> mutate(cars_table, detect = !!the_quote)
Error: Evaluation error: `false` must be type logical, not character.

In the quosure I have an extra ^ which is converting the result of str_detect into a character.

How do I integrate this intermediate quosure into the outside one?

Thanks.

Edit

Upon reviewing the solution, it ends up that the issue in this challenge was not quotation, but using if_else correctly in detect column. That is changing logical into character, or just having the false clause act accordingly.

Thus, the alternative solution is to set if_else(!!cond_expr, !!inter_quo, FALSE) from the beginning.


Solution

  • We need to wrap with as.character as the str_detect returns a logical class, while the false parameter of if_else is returning 'character'. The if_else is particular about the class. Thus, if we do

    inter_quo <- quo( as.character(str_detect( !!var_expr, 
                   regex(!!rules_df$phrase_col) )))
    

    then it should work

    mutate(cars_table, detect = !!the_quote)
    # A tibble: 3 x 3
    #  make     model    detect
    #  <chr>    <chr>    <chr> 
    #1 Mercedes Viano    Viano 
    #2 BMW      Serie X5 TRUE  
    #3 BMW      Z4       FALSE