Conditionally modify ggplot theme based on presence of facets?

I'm working on a custom ggplot2 theme for my company and was thinking it could be nifty to automatically modify elements of the theme depending on certain characteristics of the the plot object. For instance, would there be a way to specify that if the plot contains facets, add a border to each panel?

I guess the question is really, can I access the current gg object from within a custom theme() call and then conditionally apply certain theme elements? Basically I would want the following to work without having to add the final theme(panel.border = element_rect(color = "gray 50", fill = NA)) line:

library(ggplot2)

ggplot(mtcars, aes(mpg, wt)) +
  geom_point() +
  theme_minimal()


ggplot(mtcars, aes(mpg, wt)) +
  geom_point() +
  facet_wrap(vars(cyl)) +
  theme_minimal() +
  theme(panel.border = element_rect(color = "gray 50", fill = NA))

Created on 2019-09-26 by the reprex package (v0.3.0)

Sounds like a custom theme_*() is what you're looking for.

There are many blogs on this out there, but here's the first hit on Google: custom ggplot2 themes.

Thanks for the response, @leungi! I am indeed developing a custom theme, but my question is around the possibility of conditionally changing theme settings based on the plot object passed to my theme_custom() function. In other words if the plot is not faceted, the theme would look one way and if the plot is faceted, it would apply different parameters to the theme.

Since theme_*() is a function...

Note: example adapted from previous link.

theme_joey <- function(is_flag = TRUE) {
  if (is_flag) {
    theme_bw(base_size = 12, base_family = "Avenir")
  } else {
    theme_bw(base_size = 24, base_family = "Arial")
  }
}

df <- data.frame(x = factor(rep(letters[1:3], each = 10)), y = rnorm(30), color=(rep(c("A", "B"), each=5)))

plot + ggtitle("Custom Theme") + theme_joey()

plot + ggtitle("Custom Theme") + theme_joey(FALSE)

Thanks — and yes I can see how I can make theme options arguments of the custom theme function. But what I’m wondering about is if my custom theme function can “detect” (or access) elements of the ggobject that theme_() modifies. If so, given your example, the theme would automatically set the font based on some element of the plot (say, does or does not have facets) rather than requiring a user to set is_flag = TRUE.

1 Like

Noted.

Then you'll need to access ggplot internals and inspect layer by layer.

This SO post may help - inspect layer.

Since I didn't get an answer here yet, I just cross-posted this to Stack Overflow:

And now I've got a great answer here:

The basic idea is to rewrite ggplot_add.theme to set the conditional them based on the parameters of the plot it is added to. Here's the code to do it:

library(ggplot2)

theme_custom <- function() {
  out <- theme_minimal()
  class(out) <- c("conditional_theme", class(out))
  out
}

ggplot_add.conditional_theme <- function(object, plot, object_name) {
  if (!inherits(plot$facet, "FacetNull")) {
    object <- object + theme(panel.border = element_rect(colour = "grey50", fill = NA))
  }
  plot$theme <- ggplot2:::update_theme(plot$theme, object)
  plot
}

ggplot(mtcars, aes(mpg, wt)) +
  geom_point() +
  facet_wrap(vars(cyl)) +
  theme_custom()


ggplot(mtcars, aes(mpg, wt)) +
  geom_point() +
  theme_custom() 

Created on 2019-09-30 by the reprex package (v0.3.0)

3 Likes

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.