purrr::pmap() output incompatible with ggplot::aes()

Problem: purrr::pmap() output incompatible with ggplot::aes()

The following reprex boils down to a single question, is there anyway we can use the quoted variable names inside ggplot2::aes() instead of the plain text names? Example: we typically use ggplot(mpg, aes(displ, cyl)) , how to make aes() work normally with ggplot(mpg, aes("displ", "cyl")) ?

If you understood my question, the remainder of this reprex really adds no information. However, I added it to draw the full picture of the problem.


More details: I want to use purrr functions to create a bunch of routinely exploratory data analysis plots effortlessly. The problem is, purrr::pmap() results the string-quoted name of the variables, which ggplot::aes() doesn't understand. As far as I'm concerned, the functions cat() and as.name() can take the string-quoted variable name and return it in the very typical way that aes() understands; unquoted. However, neither of them worked. The following reprex reproduces the problem. I commented the code to spare you the pain of figuring out what the code does.


library(tidyverse)

# Divide the classes of variables into numeric and non-numeric. Goal: place a combination of numeric variables on the axes wwhile encoding a non-numeric variable.
mpg_numeric <- map_lgl(.x = seq_along(mpg), .f = ~ mpg[[.x]] %>% class() %in% c("numeric","integer"))
mpg_factor <- map_lgl(.x = seq_along(mpg), .f = ~ mpg[[.x]] %>% class() %in% c("factor","character"))

# create all possible combinations of the variables
eda_routine_combinations <- expand_grid(num_1 = mpg[mpg_numeric] %>% names(), 
                                        num_2 = mpg[mpg_numeric] %>% names(), 
                                        fct   = mpg[mpg_factor] %>% names()) %>% 
  filter(num_1 != num_2) %>%  slice_head(n = 2) # for simplicity, keep only the first 2 combinations

# use purrr::pmap() to create all the plots we want in a single call
pmap(.l = list(eda_routine_combinations$num_1, 
               eda_routine_combinations$num_2,
               eda_routine_combinations$fct) ,  
     .f = ~ mpg %>%
  ggplot(aes(..1 , ..2, col = ..3)) + 
  geom_point() )


Next we pinpoint the problem using a typical ggplot2 call.

this is what we want purrr::pmap() to create in its iterations:

mpg %>%  
  ggplot(aes(displ , cyl, fill = drv)) + 
  geom_boxplot()

However, this is purrr::pmap() renders; quoted variable names:

mpg %>% 
  ggplot(aes("displ" , "cyl", fill = "drv")) + 
  geom_boxplot()
 

Failing attempts

Using cat() to transform the quoted variable names from pmap() into unquoted form for aes() to understand fails.

mpg %>%  
  ggplot(aes(cat("displ") , cat("cyl"), fill = cat("drv"))) + 
  geom_boxplot()

Using as.name() to transform the quoted variable names from pmap() into unquoted form for aes() to understand fails.

mpg %>%  
  ggplot(aes(as.name("displ") , as.name("cyl"), fill = as.name("drv"))) + 
  geom_boxplot()
 

Bottom line

Is there a way to make ggplot(aes("quoted_var_name")) work properly?

ggplot2::aes_string() might be your friend here.

pmap(.l = list(eda_routine_combinations$num_1, 
               eda_routine_combinations$num_2,
               eda_routine_combinations$fct) ,  
     .f = ~ mpg %>%
       ggplot(aes_string(..1 , ..2, col = ..3)) + 
       geom_point() )

2 Likes

alternative to aes_string is non-standard-evaluation for metaprogramming using rlang (which is part of tidyverse)

library(tidyverse)
mpg %>% 
  ggplot(aes(!!sym("displ")) , !!sym("cyl"), fill = !!sym("drv")) + 
  geom_boxplot()

i.e. wrapping the strings with !!sym to first interpret them as symbols and then with the !! (bang-bang operator) evaluate them in context.

3 Likes

Dear JackDavison and nirgrahamuk ,
Thanks a lot for your help! This post have been approved after I have posted the question on StackOverflow. Here I combine the solutions offered from both threads:

  1. Use aes_string() instead of typical aes(). I believe this is the solution-of-choice as it requires the least typing and seemingly is designed to solve this problem. Example:
pmap(.l = list(eda_routine_combinations$num_1, 
               eda_routine_combinations$num_2,
               eda_routine_combinations$fct) ,  
     .f = ~ mpg %>%
  ggplot(aes_string(..1 , ..2, col = ..3)) + 
  geom_point() )
  1. Use rlang's tidyeval; wrap each string variable inside the regular aes() with the .data[[]] operator. Example:

pmap(.l = list(eda_routine_combinations$num_1, 
               eda_routine_combinations$num_2,
               eda_routine_combinations$fct) ,  
     .f = ~ mpg %>%
  ggplot(aes(.data[[..1]] , .data[[..2]], col = .data[[..3]])) + 
  geom_point() )
  1. Use rlang's bang-bang !!; wrap each string variable inside the regular aes() with !!sym(). Example:

pmap(.l = list(eda_routine_combinations$num_1, 
               eda_routine_combinations$num_2,
               eda_routine_combinations$fct) ,  
     .f = ~ mpg %>%
  ggplot(aes(!!sym(..1) , !!sym(..2), col = !!sym(..3))) + 
  geom_point() )

More on rlang?

For a beginner, what sources to learn more about rlang do you recommend? Your recommendations are most appreciated and will guide me, hopefully along with other fellows who will come across this topic.