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

32 views Asked by At

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)
1

There are 1 answers

0
Allan Cameron On BEST ANSWER

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)