rggplot2evaluationtidyeval

How to programmatically implement flexibility to either apply a column or use a single color to fill argument in ggplot


I am creating a custom function that generates bar plots using ggplot2 and geom_col().

My objective is to enable the function to dynamically assign either a single color or a color based on a grouping variable to the fill aesthetic. Here's the code I've developed:

library(ggplot2)
library(dplyr)
library(RColorBrewer)

f1 <- function(data, x, y, z, single_color=FALSE, color="red") {
  
  data <- data %>% 
    count({{x}})
  
  if (single_color) {
    p <- ggplot(data, aes(x = factor({{ x }}), y = {{ y }})) +
      geom_col(width = 0.6, fill = color) +
      theme_minimal()
  } else {
    p <- ggplot(data, aes(x = factor({{ x }}), y = {{ y }}, fill = factor({{ z }}))) +
      geom_col(width = 0.6) +
      theme_minimal() +
      scale_fill_brewer(palette = "Set1")
  }
  
  return(p)
}

f1(mtcars, cyl, n, cyl, single_color=TRUE, color="blue")
f1(mtcars, cyl, n, cyl, single_color=FALSE)

enter image description here

I'm contemplating whether there's a more straightforward method, such as:

f1 <- function(data, x, y, z) {
  
  z <- ensym(z)
  
  ggplot(data, aes(x = factor({{x}}), y = {{y}}, fill= !!z)) +
    geom_col(width = 0.6) +
    scale_fill_brewer(palette = "Set1") 
}

To summarize my question: Is it possible for z to be both a column name and a string simultaneously?

Related link:


Solution

  • If I understand you correctly, you want the argument z to be either a symbol representing a column name, or a string representing a color. I guess this is possible, though it's a little convoluted:

    library(ggplot2)
    
    f1 <- function(data, x, y, z) {
      is_column <- deparse(substitute(z)) %in% names(data)
      ggplot(data, aes(x = factor({{x}}), y = {{y}})) +
        do.call("geom_col", c(list(width = 0.6),
                            if(is_column) list(mapping = aes(fill = factor({{z}}))),
                            if(!is_column) list(fill = z)))
    }
    

    Testing we get

    f1(mtcars, cyl, mpg, cyl)
    

    and

    f1(mtcars, cyl, mpg, "blue")