Search code examples
rrlangtidyeval

Is it possible to use ensyms on a character vector?


In the following example, why am I getting an error or unexpected results from using ensyms to take in a character vector argument? I realize that this particular example is silly: facet_grid() can actually take a character vector as an argument. However, I want to understand how to use sym, ensym, syms, and ensyms with confidence, more generally.

I've written a small piece of code that facets a graph made in ggplot via a character vector.

library(rlang)
library(ggplot2)
n <- 100
x1 <- rnorm(n)
x2 <- rnorm(n)
c1 <- rbinom(n, 1, .5)
c2 <- rbinom(n, 1, .5)

df_ex <- data.frame(x1=x1, x2=x2, c1=c1, c2=c2)
    plot_test <- function(dat, facet_vars){
    facet_vars <- ensyms(facet_vars)
    p <- ggplot(dat, aes(x1,x2)) + facet_grid(vars(!!!facet_vars))
}
facet_vars <- c("c1", "c2")
#throws error
p <- plot_test(df_ex, c("c1", "c2"))
#seems to look for variable `facet_var`
p <- plot_test(df_ex, facet_vars)

If the character vector is directly input into the function I get the error "Error: Must supply symbols or strings as argument." If I input facet_vars, ensyms seems to literally return "facet_vars" and the faceting is not performed. Is there a way to use ensyms without taking arguments from ellipsis, which is the use I'm most familiar with?


Solution

  • You use ensym if you want to pass a symbol or string to your function via non-standard evaluation. That's not what you are doing with c("c1", "c2"). This is not a vector literal in R. Here you are calling the function c() with two parameters to return a vector. This is not a simple strin; this is a function call. Instead, if you want to turn a vector of strings into symbols, just use syms(). These will work:

    plot_test <- function(dat, facet_vars){
      facet_vars <- syms(facet_vars)
      p <- ggplot(dat, aes(x1,x2)) + facet_grid(vars(!!!facet_vars))
    }
    facet_vars <- c("c1", "c2")
    plot_test(df_ex, c("c1", "c2"))
    plot_test(df_ex, facet_vars)
    

    or alternatively, capture it as an expression

    plot_test <- function(dat, facet_vars){
      facet_vars <- enexprs(facet_vars)
      p <- ggplot(dat, aes(x1,x2)) + facet_grid(vars(!!!facet_vars))
    }
    

    The ensyms() works better if you are passing strings as separate parameters

    plot_test <- function(dat, ...){
      facet_vars <- ensyms(...)
      p <- ggplot(dat, aes(x1,x2)) + facet_grid(vars(!!!facet_vars))
    }
    plot_test(df_ex, "c1", "c2")