Issue with ggplot2 facet_wrap labels using tidy evaluation in R

52 Views Asked by At

I am trying to label my facet_wrap titles based on an aggregation of data in my data frame.

I am writing a function to plot different data sets, so I am using a function argument to select the column I want to facet by. In this case, I want to create facets for each unique class in broad but I want the facet label to include the sum of all percBackground values for each broad category.

I have tried using the rlang {{}} with little success (or understanding).


data.test <- data.frame(broad = c("A", "A", "A", "B", "B", "C", "C", "C", "C"),
                        detail = c("a", "b", "c", "d", "e", "f", "g", "h", "i"),
                        sr.Summer = c(12, 23, 32, 12, 12, 11, 14, 15, 18),
                        percBackground = c(1.5, 4.3, 3.4, 4.6, 3.4, 4.1, 3.9, 2.9, 1.8))

func.test <- function(data, facet.by){
  
  aggr <- data %>% aggregate(percBackground ~ facet.by, FUN = sum) %>%
    mutate(label = paste0(facet.by, " - ", round(percBackground, digits = 2), "%"))
  
  facet_label <- aggr$label
  names(facet_label) <- unique(facet_label)
  
  ggplot(data = data, aes(x = detail)) +
    geom_point(aes(y = sr.Summer)) +
    facet_wrap(~facet.by, labeller = labeller(facet.by = facet_label))
  
}

func.test(data = data.test, 
          facet.by = broad)

> Error in eval(predvars, data, env) : object 'broad' not found

     13. eval(predvars, data, env)
     12. eval(predvars, data, env)
     11. model.frame.default(formula = by, data = x)
     10. stats::model.frame(formula = by, data = x)
     9. eval(m, parent.frame())
     8. eval(m, parent.frame())
     7. aggregate.formula(x = by, data = x, FUN = FUN, ...)
     6. aggregate.data.frame(., percBackground ~ facet.by, FUN = sum)
     5. aggregate(., percBackground ~ facet.by, FUN = sum)
     4. aggregate(., percBackground ~ facet.by, FUN = sum)
     3. mutate(., label = paste0(facet.by, " - ", round(percBackground,
        digits = 2), "%"))
     2. data %>% aggregate(percBackground ~ facet.by, FUN = sum) %>%
        mutate(label = paste0(facet.by, " - ", round(percBackground,
        digits = 2), "%"))
     1. func.test(data.test, broad)


1

There are 1 best solutions below

2
stefan On BEST ANSWER

One issue with using {{ is that it will not work with aggregate, i.e. switch to dplyr::summarise. Second, when using curly-curly {{ in facet_wrap you have to wrap in vars(). Third, to get the named vector of facet labels I use tibble::deframe and finally, to pass assign the labels via labeller I pass it to the .cols argument (.rows will also work fine for facet_wrap:

library(ggplot2)
library(dplyr, warn = FALSE)

func.test <- function(data, facet.by) {
  aggr <- data %>%
    summarise(percBackground = sum(percBackground), .by = {{ facet.by }}) %>%
    mutate(label = paste0({{ facet.by }}, " - ", round(percBackground, digits = 2), "%"))

  facet_label <- aggr |> 
    select({{ facet.by }}, label) |> 
    tibble::deframe()

  ggplot(data = data, aes(x = broad)) +
    stat_summary(fun = "mean", geom = "point", aes(y = sr.Summer)) +
    facet_wrap(vars({{ facet.by }}), labeller = labeller(.cols = facet_label))
}

func.test(
  data = data.test,
  facet.by = broad
)

enter image description here

A second option which allows to stick with standard evaluation for most of the code would be add a new column to your dataset, which you could name e.g. facet or whatever. After doing so you use this name for the rest of your code without the of using {{, vars() or .rows:

func.test <- function(data, facet.by) {
  data <- data |> 
    mutate(facet = {{facet.by}})
  
  aggr <- data %>%
    summarise(percBackground = sum(percBackground), .by = facet) %>%
    mutate(label = paste0(facet, " - ", round(percBackground, digits = 2), "%"))
  
  facet_label <- aggr |> 
    select(facet, label) |> 
    tibble::deframe()
  
  ggplot(data = data, aes(x = broad)) +
    stat_summary(fun = "mean", geom = "point", aes(y = sr.Summer)) +
    facet_wrap(~facet, labeller = labeller(facet = facet_label))
}

func.test(
  data = data.test,
  facet.by = broad
)