rggplot2rlangtidyeval

Is it possible to create a ggplot with interactions with tidy evaluation


I am trying to create a plot function that can plot different colors for a column with factors if .f2=NULL, and an interaction of the .f1 and .f2 columns if the .f2 parameter is not NULL. The if statement already doesn't work when .f2 is not NULL. After that I try to create the interaction call but that doesn't work.

I'm aware I can also create an if statement that creates two seperate aes functions but I was wondering if I could do it like below.

input = tibble(x=1:100,y=rnorm(100),f1=factor(rep(c("c",'d'),each=50)),f2=factor(rep(c("a",'b'),50)))

plot_interaction = function(.df, .x, .y, .f1, .f2=NULL) {

  if (!is.null(.f2)) {
     .en1 = .f1 |> enexpr() |> as.character()
     .en2 = .f2 |> enexpr() |> as.character()
     .expr=paste0('interaction(',.en1,',',.en2,')')
     .expr = expr(.expr)
  } else
    .expr = ensym(.f1)
  
  ggplot(.df, aes(x = {{ .x }}, y = {{ .y }}, colour = {{ .expr}})) + 
    geom_point(size = 4, alpha = 0.5)
}

plot_interaction(input, x, y, f1)

plot_interaction(input,x, y, f1, f2)

Solution

  • You should use missing rather than is.null to avoid your specific error.

    However, your code won't generate interactions in the event of .f2 being present, because then your conditional will output an expression containing the symbol .expr rather than a quosure containing the parsed code you wish to inject. One option is to use rlang::parse_quo rather than expr:

    library(tidyverse)
    
    input <- tibble(x = 1:100, y = rnorm(100), 
                    f1 = factor(rep(c("c", 'd'), each = 50)),
                    f2 = factor(rep(c("a", 'b'), 50)))
    
    plot_interaction <- function(.df, .x, .y, .f1, .f2) {
      
      if(missing(.f2)) {
        .expr <- ensym(.f1) 
      } else {
        .en1 <- .f1 |> enexpr() |> as.character()
        .en2 <- .f2 |> enexpr() |> as.character()
        .expr <- paste0('interaction(', .en1, ',', .en2, ')')
        .expr <- rlang::parse_quo(.expr, env = parent.frame())
      }
      
      ggplot(.df, aes(x = {{ .x }}, y = {{ .y }}, colour = {{ .expr}})) + 
        geom_point(size = 4, alpha = 0.5)
    }
    

    This now allows:

    plot_interaction(input, x, y, f1)
    

    And

    plot_interaction(input,x, y, f1, f2)