Using `facet_wrap` in a function with non-standard evaluation with vector valued arguments

491 views Asked by At

I want to make a function that uses ggplot and facet_wrap and passes the function variables to facet by.

I can do the following using quoted arguments.

library(tidyverse)

my_grid_plot <- function(facet_by){
  ggplot(mtcars, aes(wt, disp)) +
    geom_point() +
    facet_wrap(facet_by)
}

my_grid_plot(facet_by = 'am')  
my_grid_plot(facet_by = 'vs')  
my_grid_plot(facet_by = c('am', 'vs')) 

However, I would like to do this using unquoted arguments, e.g.

# does not work yet; this is what I want
my_grid_plot(facet_by = am)  
my_grid_plot(facet_by = vs)  
my_grid_plot(facet_by = c(am, vs))

I know I can use vars with facet_wrap, e.g.

ggplot(mtcars, aes(wt, disp)) +
  geom_point() +
  facet_wrap(vars(am, vs))

And so I could do something like this

my_grid_plot2 <- function(facet_by){
  ggplot(mtcars, aes(wt, disp)) +
    geom_point() +
    facet_wrap(vars({{facet_by}}))
}

# works fine
my_grid_plot2(am)
my_grid_plot2(vs)

But that won't work if facet_by is a vector.

# does not work; obscure error
my_grid_plot2(c(am, vs))

So is there a way to get what I want above, i.e. where facet_by takes a single variable name of vector of names?

2

There are 2 answers

0
mjandrews On

I don't know if this is the right or best way, and it seems a bit hacky, but I think it does the job. Inspired by https://stackoverflow.com/a/57709169/1009979

my_grid_plot3 <- function(facet_by){
  
  by_expr <- enexpr(facet_by)
  
  if(length(by_expr) == 1) {
    args_vars <- as.list(by_expr)
  } else {
    args_vars <- as.list(by_expr)[-1]
  }
  
  quoted_args_vars <- sapply(args_vars, quo_name)
  
  ggplot(mtcars, aes(wt, disp)) +
    geom_point() +
    facet_wrap(quoted_args_vars)
}

# this works
my_grid_plot3(am)
my_grid_plot3(vs)
my_grid_plot3(c(am, vs))
0
Artem Sokolov On

One option is to let tidyselect do the heavy lifting:

my_grid_plot2 <- function(facet_by){
  by <- rlang::enexpr(facet_by) %>%
    tidyselect::eval_select(mtcars)

  ggplot(mtcars, aes(wt, disp)) +
    geom_point() +
    facet_wrap(names(by))
}

## All of the following now work as expected
my_grid_plot2(am)
my_grid_plot2(vs)
my_grid_plot2(c(am, vs))

my_grid_plot2('am')
my_grid_plot2('vs')
my_grid_plot2(c('am', 'vs'))