Ggplot2 - programming variable facets

I'm trying to figure out how to make a variable call to facet_wrap within a ggplot2 function.
Ideally I want this within the body of the function.

I can't install the github version of reprex, hopefully the code below renders OK, apologies if not.
There's a bit of data prep to get some suitable time series data , a manual plot function that works if I give it the extra commands outside of the function (which I want to avoid), and then my current failed attempt.

In addition to trying quote(group_var), I've also tried as.name() and eval()

library(tidyverse)
data <- list(fdeaths,mdeaths,ldeaths) #time series data- needs prep

names(data)[1:3] <- c("fdeaths","mdeaths","ldeaths")
data <- as_tibble(data)

startdate <- as.Date('1974-1-1')
data$date <- seq.Date(startdate,by = 'month',length.out = 72)

newdata <- tidyr::gather(data, key = key, value = value,-date)
newdata$value <- as.numeric(newdata$value)



manual_facet_plot <- function(df, datecol, y_var,...){
  p <-  ggplot2::ggplot(df,ggplot2::aes_(substitute(datecol),substitute(y_var))) + 
    ggplot2::geom_line(colour = "grey40", group = 1) +
    ggplot2::geom_point(colour = "grey40", na.rm = TRUE) 
  p
}

#now with grouping_variable
fail_plot <- function(df, datecol, y_var, group_var,...){
  p <-  ggplot2::ggplot(df,ggplot2::aes_(substitute(datecol),substitute(y_var))) + 
    ggplot2::geom_line(colour = "grey40", group = 1) +
    ggplot2::geom_point(colour = "grey40", na.rm = TRUE) +
    ggplot2::facet_wrap(. ~ quote(group_var), ncol = 3)
  p
}

This works - but I want the facet_wrap call inside the function

manual_facet_plot(newdata,date,value) + 
  facet_wrap( ~ key, ncol = 3)

This fails miserably:

fail_plot(newdata,date,value,key) 

I have many more lines of code to add to the final plot but I can't seem to figure this out.
I've looked at http://rpubs.com/hadley/97970 but I'm not seeing how to tweak the facets accordingly.

Any help appreciated
thanks
John

Hi John,

Would you mind turning this into a reprex?

They're especially helpful for viz questions, since they automatically generate and deal with the images. And, of course, the syntax highlighting makes reading so much nicer!

Not having read your code closely (because I really am a huge baby about reading unhighlighted code), two posts come to mind:
http://www.brodrigues.co/blog/2017-03-29-make-ggplot2-purrr/
and

Sure - I tried (honest), but I only have the CRAN version of reprex at the moment , which doesn't seem to work even with the RStudio addin -
I'll edit the Q later if I can get the github version installed.
I'll check out that link also,
thanks Mara :slight_smile:

1 Like

@tbradley to the rescue with the syntax highlighting and backticks!

Which, FFR:

```r
```
2 Likes

Found a solution :

library(ggplot2)
library(formula.tools)

# solution hacked from here:
# https://stackoverflow.com/questions/14742287/ggplot2-aes-string-inside-a-function-via-formula-interface

tf_wrap <- function(formula, faceting = NULL, data, print = TRUE, ncols = 1) {
  y <- rhs(formula)
  x <- lhs(formula)
  
  p <- ggplot(environment = parent.frame())

  p <- p + geom_point(aes_string(x = x, y = y), data = data, colour = "grey40") + 
    geom_line(aes_string(x = x, y = y), data = data, colour = "grey40") 
  
  if (!is.null(faceting)) {
    rhsfacet <- all.vars(rhs(faceting))
    if (length(rhsfacet) == 1 & any(rhsfacet %in% '.')) {rhsfacet <- NULL}
    
    p <- p + facet_wrap(facet = (rhsfacet), ncol = ncols)}
  
  if (print) {print(p)}
  p 
  
}


n = 15
testdf = data.frame(
  xVar = seq(1:15), 
  yVar = sample(1:10, n, replace = TRUE), 
  brand = letters[1:3])


# no facet ..
tf_wrap(xVar~yVar, data = testdf)

# with facet_wrap..
tf_wrap(xVar~yVar,faceting = ~ brand, data = testdf)

tested on my real life data, with 90 facets, works fine.

2 Likes

You wanna pick your solution as the solution (easier for others to find in the future)?
When you're original poster (OP), there should be a little box at the bottom of replies that you can click to select that response as your “solution.”

Example from one of my questions, below:

image

Oh, aye, so there is.
Cheers Mara.

But if anyone can let me know what I was doing wrong earlier - that would be good.
David Robinson's accepted answer on that SO page works well for facet_grid(), but if I attempt to convert the function to cope with facet_wrap() it doesn't work either.
Is there some sort of peculiarity with facet_wrap that makes this harder to achieve??

Hi John,

If you change your fail_plot function to this:

fail_plot <- function(df, datecol, y_var, group_var = NULL,...){
  
  p <-  ggplot2::ggplot(df,ggplot2::aes_(substitute(datecol),substitute(y_var))) + 
    ggplot2::geom_line(colour = "grey40", group = 1) +
    ggplot2::geom_point(colour = "grey40", na.rm = TRUE)
  # if a group_var is passed, add a facet by wrapping 
  # a pasted string in as.formula()
  if(!is.null(group_var)) 
    p <- p + ggplot2::facet_wrap(as.formula(paste("~", group_var)), ncol = 3)

  p
  
}

and pass the group_var argument as a string you should be cooking with gas.

1 Like

Thanks Paul - this also worked perfectly.

Also - I see if I pass that variable as a string these also seem to work:

p <- p + ggplot2::facet_wrap(paste("~", group_var), ncol = 3)

 p <- p + ggplot2::facet_wrap(enquote(group_var), ncol = 3)

I was hoping I could do this with bare variable names but this solution

  1. saves me relying on another package

  2. allows me to stick to the regular ggplot argument syntax
    so you get my vote :slight_smile:

1 Like