Search code examples
rggplot2nse

Mix dots and named arguments in function calling aes for ggplot2


I'm trying to create a wrapper around the ggplot that allows me to add some aesthetics like the x variable or color but always prefills y, ymin and ymax without having to work with quoted variable names.

Since ggplot2 cannot use tidy evaluation I have to use NSE for this but I'm stuck, the information I could find here and here and inspecting some functions made me try things like unlist(...) and working with match.call(). but they only throw different errors.

In the below function I'd basically like to be able to call ci_plot() or for example ci_plot(color = cyl)

library(dplyr)
library(ggplot2)
library(purrr)
library(tidyr)


ci_plot <- function(data, ...) {
  ggplot(data, aes(..., y = y, ymin = ymin, ymax = ymax))  
}

mpg %>%
  group_by(manufacturer) %>%
  nest() %>%
  mutate(x = map(data, ~mean_se(.x$hwy))) %>%
  unnest(x) %>%
  ci_plot() + 
  geom_pointrange()

Solution

  • After some more digging I found shadow's answer here and figured out how to adapt it for my purpose. I'll try to outline the solution as much as I understand it.

    ci_plot <- function(data, ...) {
      # Create a list of unevaluated parameters,
      # removing the first two, which are the function itself and data.
      arglist <- as.list(match.call()[-c(1,2)]) 
    
      # We can add additional parameters to the list using substitute
      arglist$ymin = substitute(ymin)
      arglist$y    = substitute(y)
      arglist$ymax = substitute(ymax)
    
      # I suppose this allows ggplot2 to recognize that it needs to quote the arguments
      myaes <- structure(arglist, class="uneval")
    
      # And this quotes the arguments?
      myaes <- ggplot2:::rename_aes(myaes)
    
      ggplot(data, myaes)  
    }
    

    That function allows me to write the code like this

    mpg %>%
      group_by(manufacturer, cyl) %>%
      nest() %>%
      mutate(x = map(data, ~mean_se(.x$hwy))) %>%
      unnest(x) %>%
      ci_plot(x = cyl, color = manufacturer) + 
      geom_pointrange()