3

I am making several plots, which for flexibility I am planning to wrap in functions, and as part of that I would like to specify faceting for those plots according to some condition.

Specifically, I have a list of grouping_vars (per-specified elsewhere) that I use to perform several other manipulations on the data. I'd like to have my plotting functions be able to automatically use a facet_wrap if there's only one grouping_var, or if there are two or more variables specified in grouping_var, use the first two for facet_grid.

So for instance, if the basic plot is something like

mtcars %>% ggplot() + geom_point(aes(x = hp, y = mpg))

If I specify grouping_vars <- c("cyl"), I'd like to get, in effect,

mtcars %>% ggplot() + 
    geom_point(aes(x = hp, y = mpg)) +
    facet_wrap(~cyl)

Whereas if I specify grouping_vars <- c("cyl", "carb") I'd like to get

mtcars %>% ggplot() + 
    geom_point(aes(x = hp, y = mpg)) +
    facet_grid(vars(cyl), vars(carb))

Based on some other answers I've tried doing this:

mtcars %>% ggplot() + 
    geom_point(aes(x = hp, y = mpg)) +
    {if(length(grouping_vars==1)) facet_wrap(grouping_vars[[1]])} +
    {if(length(grouping_vars>=2)) facet_grid(vars(grouping_vars[[1]]), vars(grouping_vars[[2]]))}

but it doesn't seem to work. Among other things, if there's only one element in grouping_vars, it gives me Error in grouping_vars[[2]] : subscript out of bounds.

Is there a way to conditionally specify faceting like this?

M--
  • 25,431
  • 8
  • 61
  • 93
TY Lim
  • 509
  • 1
  • 3
  • 11

2 Answers2

5

Following the advice in the vignette here, we should be able to use vars() with the .data pronoun as follows:

library(ggplot2)

plot_mtcars <- function(grouping_vars = NULL) {
  p <- ggplot(mtcars) +
    geom_point(aes(x = hp, y = mpg))
  
  if (length(grouping_vars) == 1) {

    p <- p + facet_wrap(vars(.data[[grouping_vars]]))

  } else if (length(grouping_vars) == 2) {

    p <- p + facet_grid(vars(.data[[grouping_vars[1]]]), 
                        vars(.data[[grouping_vars[2]]]))

  }
  p
}

plot_mtcars("cyl")

plot_mtcars(c("cyl", "vs"))

Created on 2023-03-14 by the reprex package (v2.0.0)

teunbrand
  • 33,645
  • 4
  • 37
  • 63
3

We can create a custom function, which uses get to pass the variables to ggplot.

conditional_facet <- function(data, x, y, grp) {
  require(ggplot2)
  require(dplyr)
  data %>% 
    ggplot() + 
    geom_point(aes({{x}}, {{y}})) -> gp
  
  if(length(grp) == 1) {
    gp + facet_wrap(~get(grp)) -> gp_out
  } else if (length(grp) == 2) {
    gp + facet_grid(vars(get(grp[1])), vars(get(grp[2]))) -> gp_out
  } else {
    gp -> gp_out
  }
  return(gp_out)
}
grouping_vars <- c("cyl")
conditional_facet(mtcars, hp, mpg, grouping_vars)

grouping_vars <- c("cyl", "carb")
conditional_facet(mtcars, hp, mpg, grouping_vars)

Created on 2023-03-14 by the reprex package (v2.0.1)

M--
  • 25,431
  • 8
  • 61
  • 93