map across a list of variables within ggplot function

I have written a function to calculate proportions and confidence intervals across groups, then plot the results.

However, I next want to be able to supply a list of variables for groupvar to allow me to map and get all desired plots with one call:

library(tidyverse)
data(mtcars)

my_graph <- function(data, groupvar){
  
  data %>%
  group_by(vs, {{groupvar}}, am) %>%
  summarise(n=n()) %>%
  mutate(sum = sum(n)) %>%
  rowwise %>%
  mutate(xx = list(broom::tidy(prop.test(n, sum, conf.level=0.95)))) %>%
  unnest(xx) %>%
  ggplot() +
  geom_bar(aes(x=factor(vs), y=estimate, fill={{groupvar}}), stat = "identity") +
  geom_errorbar(aes(x=factor(vs), ymin=conf.low, ymax = conf.high), width = 0.2) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  facet_wrap(vars({{groupvar}}))+
  theme(legend.position = "none") 
  
}

#This works
my_graph(data = mtcars, groupvar = am)

#But how to map across a list of variables?
plot_vars <- mtcars %>%
  select(am, gear, carb) %>%
  names()

map(plot_vars, ~my_graph(data=., groupvar = .x))
#> Error in UseMethod("group_by_"): no applicable method for 'group_by_' applied to an object of class "character"

Created on 2019-07-21 by the reprex package (v0.3.0)

Have you taken a look at the post below?

3 Likes

The key difference between your single use of the function and your map() loop is that you are passing the variable names as strings in the latter. In the former you are passing bare variable names (no quotes).

The curly-curly coding works for bare variable names, but for strings you'll want to switch to using the .data pronoun.

So instead of

geom_bar(aes(x=factor(vs), y=estimate, fill={{groupvar}}), stat = "identity")

You'd have

geom_bar(aes(x=factor(vs), y=estimate, fill=.data[[groupvar]]), stat = "identity")

The .data pronoun works the same way in group_by() and facet_wrap().

3 Likes

Thanks so much, but I still can't seem to get this to work...

library(tidyverse)
data(mtcars)

my_graph <- function(data, groupvar){
  
  data %>%
  group_by(vs, .data[[groupvar]], am) %>%
  summarise(n=n()) %>%
  mutate(sum = sum(n)) %>%
  rowwise %>%
  mutate(xx = list(broom::tidy(prop.test(n, sum, conf.level=0.95)))) %>%
  unnest(xx) %>%
  ggplot() +
  geom_bar(aes(x=factor(vs), y=estimate, fill=.data[[groupvar]]), stat = "identity") +
  geom_errorbar(aes(x=factor(vs), ymin=conf.low, ymax = conf.high), width = 0.2) +
  scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +
  facet_wrap(vars(.data[[groupvar]]))+
  theme(legend.position = "none") 
  
}

plot_vars <- mtcars %>%
  select(am, gear, carb) %>%
  names()

map(plot_vars, ~my_graph(data=., groupvar = .x))
#> Error in UseMethod("group_by_"): no applicable method for 'group_by_' applied to an object of class "character"

Created on 2019-07-22 by the reprex package (v0.3.0)

Apologies if I have completely misunderstood.

Yes, you're right, that particular error didn't stem from using strings but because of the data = . inside map() (although you would have eventually had problems with {{ :wink: ).

The way you currently have your function set up you need to pass a dataset in. If you are using the same one for all plots you can do

map(plot_vars, ~my_graph(data = mtcars, groupvar = .x))
2 Likes

Thanks so much - that works perfectly now! Really appreciated.

Thanks - this is a very helpful post indeed.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.