Matifou
Matifou

Reputation: 8890

R ggplot2 facet_grid with vars(): how to handle missing argument?

I want to use tidy evaluation for ggplot's facet_grid, but don't know how to allow for missing arguments?

ggplot2 3.0.0 introduced the function vars() (see news), to use tidy evaluation in for example facet_grid. But it is not clear to me how to handle missing/NULL arguments?

In the following code, how do I do so that no passing any argument to wrap_by (i.e. using p + wrap_by()) does not return an error?

library(ggplot2)
#> Registered S3 methods overwritten by 'ggplot2':
#>   method         from 
#>   [.quosures     rlang
#>   c.quosures     rlang
#>   print.quosures rlang
p <- ggplot(mtcars, aes(wt, disp)) + geom_point()
wrap_by <- function(...) {
  facet_wrap(vars(...), labeller = label_both)
}
p + wrap_by() #ERROR!
#> Can't subset with `[` using an object of class NULL.

Use case: I want to have a function to define the facet variables, but want to make them optional.

fac_by <- function(var_fac1, var_fac2) {
  facet_grid(rows=vars(!!enquo(var_fac1)), 
             cols=vars(!!enquo(var_fac2)))
}

p+ fac_by(vs, am)
p+ fac_by(var_fac1=vs) # won't work

The issue here is that ideally I would allow for either row, either col, both (ideally none also, though this does not seem possible with facet_grid(NULL, NULL))

Upvotes: 2

Views: 1133

Answers (2)

moodymudskipper
moodymudskipper

Reputation: 47320

You can do :

fac_by <- function(var_fac1, var_fac2) {
  # retrieve the arguments from the call
  args_ <- rlang::call_args(match.call())
  # rename them (use na.omit on top of it if you have more arguments)
  names(args_) <- c(var_fac1 = "rows", var_fac2 = "cols")[names(args_)]
  # apply vars on them
  args_ <- purrr::map(args_, vars)
  # build the call
  call_ <- as.call(c(quote(facet_grid),args_))
  eval(call_)
}

p+ fac_by(vs, am)
p+ fac_by(var_fac1=vs)

Using if and missing awould arguably be simpler though :

fac_by <- function(var_fac1, var_fac2) {
  facet_grid(rows=if(missing(var_fac1)) NULL else enquos(var_fac1), 
             cols=if(missing(var_fac2)) NULL else enquos(var_fac2))
} 

p+ fac_by(vs, am)
p+ fac_by(var_fac1=vs)

Note you can use enquos(var_fac1) instead of vars(!!enquo(var_fac1)

Upvotes: 1

akrun
akrun

Reputation: 887223

Based on the updated code for facet_grid an option would be to pass an expression

library(rlang)
library(ggplot2)
fac_by <- function(...) {
   e1 <- enexprs(...)   
   do.call(facet_grid, e1)

  }

p + fac_by(rows = vars(vs), cols = vars(am))

enter image description here

p + fac_by(rows = vars(vs))

enter image description here

p + fac_by(cols = vars(am))

enter image description here

Upvotes: 1

Related Questions