rggplot2purrrpmap

Why does r purrr's pmap say "Only strings can be converted to symbols" and not iterate over dataset?


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))

Solution

  • 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.