How to store custom ggplot aliases as `list` objects in .R script and source them along required label vectors

Viewed 142

I want to streamline my work with ggplot2 given certain settings I repeat across plots. To this end, I want to:

  1. set aliases for those ggplot2 settings (assigned to individual list objects)
  2. store them all in an .R script
  3. source() the .R script
  4. call the aliases when creating a plot with ggplot

However, this workflow fails when I set aliases for ggplot functions that call vectors. For example, scale_x_discrete() has an argument called labels, which takes a character vector of strings. This vector can be set in-place (e.g., scale_x_discrete(labels = c("a", "b", "c"))) or otherwise call a vector object stored in the environment:

labels_for_x <- c("a", "b", "c")

scale_x_discrete(labels = labels_for_x)

Therefore, my problem happens when I want to set an "alias" for a ggplot function that relies on an additional, separate vector.

Demonstrating the problem with example

Step 1 -- set alias for scale_x_discrete in a new .R script

library(tidyverse)

mygg_scale_x_relabel <-
  scale_x_discrete() %>%
  list

Works! But what if:

mygg_scale_x_relabel <-
  scale_x_discrete(labels = labels_for_barplot) %>%
  list

Error in check_breaks_labels(breaks, labels) : object 'labels_for_barplot' not found

So I can do:

labels_for_barplot <- c()

mygg_scale_x_relabel <-
  scale_x_discrete(labels = labels_for_barplot) %>%
  list

And this runs OK.


Step 2 -- open a new .R/.Rmd script and source the previous .R script with alias

source(file = ...)

Step 3 -- in the new script, generate a bar plot

library(ggplot2)

df <- data.frame(trt = c("col_1", "col_2", "col_3"), outcome = c(2.3, 1.9, 3.2))

p <-
  ggplot(df, aes(trt, outcome)) +
  geom_col()

p

orig_lanels


Step 4 -- (PROBLEM HAPPENS HERE) -- call the scale_x_discrete alias

labels_for_barplot <- c(col_1 = "new_lab_col_1",
                        col_2 = "new_lab_col_2",
                        col_3 = "new_lab_col_3")

p +
  mygg_scale_x_relabel

no_labels <----- You can see here that all x labels are now gone!

This is because I've set labels_for_barplot <- c() (meaning, an empty vector) in my aliases script. But why this cannot be overridden with an updated vector assignment after script is sourced?

1 Answers

When you assign a value to mygg_scale_x_relabel, it gets set straight away with the values it has. It won't change when labels_for_barplot changes.

The best way to do what you want is with a function. The function won't evaluate until you call it.

mygg_scale_x_relabel <- function(labels_for_barplot) {
  scale_x_discrete(labels = labels_for_barplot) %>%
  list()
}

labels_for_barplot <- c(col_1 = "new_lab_col_1",
                        col_2 = "new_lab_col_2",
                        col_3 = "new_lab_col_3")

p +
  mygg_scale_x_relabel(labels_for_barplot)

p1

However, if you really wanted the syntax you've described, you can use active bindings. This is not the way things should be done generally, though.

rlang::env_bind_active(
  rlang::global_env(),
  mygg_scale_x_relabel = function() scale_x_discrete(labels = labels_for_barplot) %>% list()
)

labels_for_barplot <- c(col_1 = "new_lab_col_1",
                        col_2 = "new_lab_col_2",
                        col_3 = "new_lab_col_3")

p + mygg_scale_x_relabel

p2

labels_for_barplot <- c(col_1 = "a",
                        col_2 = "b",
                        col_3 = "c")

p + mygg_scale_x_relabel

p3

Related