0

I'm trying to automate my creation of ggplot slides using pmap from the purrr package. As an extension of this question, I'm trying to facet based on the group membership variables in my data (level and location).

Unlike the previous question, I know have 3 inputs, so I need to use pmap() instead of map2(), and for some reason I keep getting this error:

Error: Only strings can be converted to symbols
Run `rlang::last_error()` to see where the error occurred.

And when I do dig into the error, it shows the problem is in my first pmap() call:

<error/rlang_error>
Only strings can be converted to symbols
Backtrace:
  1. purrr::pmap(...)
 14. rlang::sym(variable)

I've tried every combination, but I can't crack it. I want R to iterate every plot by level and location.

Here's my code and data:

#Packages
library(dplyr)
library(purrr)
library(ggplot2)

#Data
test <- tibble(s1 = c("Agree", "Neutral", "Strongly disagree"),
               s2rl = c("Agree", "Neutral", "Strongly disagree"),
               f1 = c("Strongly agree", "Disagree", "Strongly disagree"),
               f2rl = c("Strongly agree", "Disagree", "Strongly disagree"),
               level = c("Manager", "Employee", "Employee"),
               location = c("USA", "USA", "AUS"))

#Get just test items for name
test_items <- test %>%
  dplyr::select(s1, s2rl, f1, f2rl)

#titles of plots for R to iterate over
titles <- c("S1 results", "Results for S2RL", "Fiscal Results for F1", "Financial Status of F2RL")


#group levels
group_name <- c("level", "location")

#custom ggplot function
faceted_plots = function(variable, group, title) {

  sample_size <- test %>%
    group_by(!! rlang::sym(group), !! rlang::sym(variable)) %>%
    summarize(n = sum(!is.na(!! rlang::sym(variable))))
  

  test %>%
    count(!! rlang::sym(group), !! rlang::sym(variable)) %>%
    mutate(percent = 100*(n / sample_size$n)) %>%
    drop_na() %>%
    ggplot(aes(x = !! rlang::sym(variable), y = percent, fill = .data[[variable]])) + 
    geom_bar(stat = "identity") +
    geom_text(aes(label= paste0(percent, "%"), fontface = "bold", family = "Arial", size=14), vjust= 0, hjust = -.5) +
    ylab("\nPercentage") +
    labs(
      title = title,
      subtitle = paste0("(N = ", sample_size$n, ")")) +
    coord_flip() +
    theme_minimal() +
    scale_fill_manual(values = c("Strongly disagree" = "#CA001B", "Disagree" = "#1D28B0", "Neutral" = "#D71DA4", "Agree" = "#00A3AD", "Strongly agree" = "#FF8200")) +
    scale_x_discrete(labels = c("Strongly disagree" = "Strongly\nDisagree", "Disagree" = "Disagree", "Neutral" = "Neutral", "Agree" = "Agree", "Strongly agree" = "Strongly\nAgree"), drop = FALSE) + 
    theme(axis.title.y = element_blank(),
          axis.text = element_text(size = 14, color = "gray28", face = "bold", hjust = .5),
          axis.title.x = element_text(size = 18, color = "gray32", face = "bold"),
          legend.position = "none",
          text = element_text(family = "Arial"),
          plot.title = element_text(size = 20, color = "gray32", face = "bold", hjust = .5),
          plot.subtitle = element_text(size = 16, color = "gray32", face = "bold", hjust = .5),
          panel.spacing.x = unit(2, "lines")) +
    ylim(0, 100) +
    facet_grid(~!! rlang::sym(group))
}

#pmap call
plots_and_facet <- pmap(
  list(x = names(test_items),
       y= titles,
       z = group_name),
  faceted_plots(test_items, titles, group_name))

EDIT with Mr. Flick's solution--it works! Disregard any of the issues with the counting, as that was on my end and is out of scope for this question:

#custom ggplot function
faceted_plots = function(variable, group, title) {

  sample_size <- test %>%
    group_by(.data[[group]], .data[[variable]]) %>%
    summarize(n = sum(!is.na(.data[[variable]])))
  

  test %>%
    count(.data[[group]], .data[[variable]]) %>%
    mutate(percent = 100*(n / sample_size$n)) %>%
    drop_na() %>%
    ggplot(aes(x = .data[[variable]], y = percent, fill = .data[[variable]])) + 
    geom_bar(stat = "identity") +
    geom_text(aes(label= paste0(percent, "%"), fontface = "bold", family = "Arial", size=14), vjust= 0, hjust = -.5) +
    ylab("\nPercentage") +
    labs(
      title = title,
      subtitle = paste0("(N = ", sample_size$n, ")")) +
    coord_flip() +
    theme_minimal() +
    scale_fill_manual(values = c("Strongly disagree" = "#CA001B", "Disagree" = "#1D28B0", "Neutral" = "#D71DA4", "Agree" = "#00A3AD", "Strongly agree" = "#FF8200")) +
    scale_x_discrete(labels = c("Strongly disagree" = "Strongly\nDisagree", "Disagree" = "Disagree", "Neutral" = "Neutral", "Agree" = "Agree", "Strongly agree" = "Strongly\nAgree"), drop = FALSE) + 
    theme(axis.title.y = element_blank(),
          axis.text = element_text(size = 14, color = "gray28", face = "bold", hjust = .5),
          axis.title.x = element_text(size = 18, color = "gray32", face = "bold"),
          legend.position = "none",
          text = element_text(family = "Arial"),
          plot.title = element_text(size = 20, color = "gray32", face = "bold", hjust = .5),
          plot.subtitle = element_text(size = 16, color = "gray32", face = "bold", hjust = .5),
          panel.spacing.x = unit(2, "lines")) +
    ylim(0, 100) +
    facet_grid(~.data[[group]])
}

#pmap call
expand_grid(tibble(item = names(test_items), title=titles),
              group = group_name) %>%
  pmap(function(item, group, title)
    faceted_plots(item, group, title))
J.Sabree
  • 2,280
  • 19
  • 48

1 Answers1

2

With pmap you actually need to pass a function, not call a function for the .f= parameter. Also if you want all possible combinations of things, you'll first need to create the combinations with purrr::expand_grid before calling pmap. So your call would look something like

plots_and_facet <-
  expand_grid(tibble(item = names(test_items), title=titles),
              group = group_name) %>%
  pmap(function(item, group, title)
    faceted_plots(item, group, title))

Also you have a bunch of useages of !!rlang::sym(variable) but the recommended way to do this now is .data[[variable]] which you do have in some places, but you should just change all those to use the .data pronoun to be safe.

MrFlick
  • 195,160
  • 17
  • 277
  • 295
  • thanks for your help! Your solution makes the code run, but there's one issue. It's not iterating over all the titles and is just using the first one. I edited my question to include your solution along with it's code--can you identify why it won't iterate through the titles? – J.Sabree Aug 19 '21 at 18:39
  • Did you update the part of the code that said `labs(title = "Test")`? Make sure you are using the value passed to the `title` parameter: `labs(title = title)` – MrFlick Aug 19 '21 at 18:41
  • yes I edited that part once I implemented your solution which is how I discovered it’s only using “S1 Results” for all charts – J.Sabree Aug 19 '21 at 18:42
  • 1
    Your code has `lab(title = titles)`, not `lab(title = title)`. The former uses the global variable with all the values and the latter uses the value passed to the function. – MrFlick Aug 19 '21 at 18:44
  • that's exactly what it was--thanks! I've edited my code to reflect the change and will accept the answer. I appreciate your time! – J.Sabree Aug 19 '21 at 18:46