Search code examples
rfunctionggplot2facetfacet-grid

Cannot use faceting for ggplot inside a function


I am trying to use ggplot inside a function, but I am unable to produce the plots. Specifically, I would like to determine whether or not the plot will use facet_grid() from the function call. This is my data:

mydf <- data.frame(
  group = rep(c("g1", "g2"), each = 16, times = 1), 
  cluster = rep(c("c1", "c2"), each = 8, times = 2), 
  score1 = c(rnorm(n = 16, mean = 10, sd = 10), rnorm(n = 16, mean = 18, sd = 10)), 
  score2 = c(rnorm(n = 16, mean = 50, sd = 10), rnorm(n = 16, mean = 33, sd = 10))
  )

Here is the function:

myFunc <- function(data, group = NULL, group2, var1, var2) {

  # So we don't need quotation marks in function call
  arguments <- as.list(match.call())
  var1 = eval(arguments$var1, data)
  var2 = eval(arguments$var2, data)
  group2 = eval(arguments$cluster, data)
  grouping = eval(arguments$group, data)

  # Make this graph if no faceting needed
  if (length(grouping) == 0) {

  means <- aggregate(cbind(var1, var2) ~  group2, FUN = mean, data = data)

  ggplot(data, aes(x = var1, y = var2, color = group2, label = group2)) + 
    stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
    geom_text(alpha = 0.5, show.legend = FALSE) +
    geom_text(data = means, aes(x = var1, y = var2, color = group2)) 


  # Use faceting
  } else if (length(grouping) > 0) {

  means <- aggregate(cbind(var1, var2) ~ grouping + group2, FUN = mean, data = data)

  # Plot 
  ggplot(data, aes(x = var1, y = var2, color = group2, label = group2)) + 
    stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
    geom_text(alpha = 0.5, show.legend = FALSE) + 
    geom_text(data = means, aes(x = var1, y = var2, color = group2)) +
    facet_grid(. ~ grouping) 

  }

}

I am calling the function like so:

myFunc(data = mydf, group = NULL, group2 = cluster, var1 = score1, var2 = score2)
myFunc(data = mydf, group = group, group2 = cluster, var1 = score1, var2 = score2)

Both calls give the following error respectively:

# Error 1
Error: Aesthetics must be either length 1 or the same as the data (32): x, y, colour, label

# Error 2
Error in combine_vars(data, params$plot_env, cols, drop = params$drop) : 
At least one layer must contain all variables used for facetting

The expected output can be acquired by building the plots manually:

means <- aggregate(cbind(score1, score2) ~ group + cluster, FUN = mean, data = mydf)

# without facet
ggplot(mydf, aes(x = score1, y = score2, color = cluster, label = cluster)) + 
  stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
  geom_text(alpha = 0.5, show.legend = FALSE) + 
  geom_text(data = means, aes(x = score1, y = score2, color = cluster)) 

# with facet
ggplot(mydf, aes(x = score1, y = score2, color = cluster, label = cluster)) + 
  stat_ellipse(type = "norm", show.legend = FALSE, geom = "polygon", alpha = 0.1) +
  geom_text(alpha = 0.5, show.legend = FALSE) + 
  geom_text(data = means, aes(x = score1, y = score2, color = cluster)) + 
  facet_grid(. ~ group)

Solution

  • Here's the basic stat_ellipse plots with and without using facet_grid. I will let you add the frills. Here the column names are kept as strings so aes_string is used instead of aes, and formulas are passed to functions using as.formula.

    myFunc <- function(df, var1, var2, group2, group1 = NULL) {
    
      # Make this graph if no faceting needed
      if (is.null(group1)) {
    
      means_formula <- as.formula(paste(var1, "+", var2, "~", group2))
      means <- aggregate(means_formula, FUN = mean, data = df)
    
       p <- ggplot(df, 
           aes_string(x = var1, y = var2, color = group2, label = group2)) + 
           stat_ellipse(type = "norm", show.legend = FALSE, 
               geom = "polygon", alpha = 0.1)
        }else{
    
        means_formula <- as.formula(paste(var1,"+",var2,"~", group2,"+",group1))
        means <- aggregate(means_formula, FUN = mean, data = df)
    
        p <- ggplot(df, 
            aes_string(x = var1, y = var2, color = group2, label = group2)) + 
            stat_ellipse(type = "norm", show.legend = FALSE, 
                geom = "polygon", alpha = 0.1) + 
            facet_grid(as.formula(paste(".~ ",group1))) 
      }
      print(p)
    }
    
    myFunc(df = mydf, var1 = "score1", var2 = "score2", 
        group2 = "cluster", group1 = NULL)
    
    myFunc(df = mydf, var1 = "score1", var2 = "score2", 
        group2 = "cluster", group1 = "group")
    

    stat ellipse plot