2

I'm getting a bit mixed up with NSE and a plot function. I'm trying to automate plotting a few plots at once while labeling axis etc. using variable labels (not names). Lets say we have a large dataset with all variables already labelled. Small example here:

library(tidyverse)
library(sjlabelled)
library(ggplot2)
library(cowplot)
data("diamonds")
diamonds <- diamonds %>% 
  var_labels(
  cut ="nice cut",
  color = "all colours",
  clarity = "very claity all",
  depth = "test depth")

The basic plot I want is this:

p1 <- ggplot(diamonds, aes(x = cut, y = depth)) + geom_boxplot(aes(fill = cut)) +
  theme_cowplot() + 
  lab(title = "Plot of test depth ~ nice cut",   #based on label variable
                           x = "nice cut",      #based on label variable
                           y = "test depth",    #based on label variable
                         fill = "nice cut")    #based on label variable
p1

I want to automate this plot by cycling through other variables. So I want boxplots of columns in vars separately by depth The following is what I'm trying to do.

#firstly i think i should have labels separately (wondering is there a way I can use them directly from variable label?)

my_labels <-   c(
  cut = "nice cut",
  color = "all colours",
  clarity = "very claity all",
  depth = "test depth"
)

#plot function
plot_f <- function(df, x_var, y_var, x_var_label, y_var_label) {
  ggplot(df, aes(x = {{x_var}}, y = {{y_var}})) + geom_boxplot(aes(fill = {{x_var}})) +
    theme_cowplot() + labs(title = paste("Plot of", {{x_var_label}}, "~", {{y_var_label}}),
                           x = {{x_var_label}},
                           y = {{y_var_label}},
                           fill = {{x_var_label}})
}

#variables to cycle through
vars <- c("cut", "color", "clarity")
plot_list <- vars %>% 
  pmap(~plot_f(diamonds, .x, depth, my_labels)) #need to specify y_var_label & x_var_label, is there a 
#way I can just specify my_labels here?

#Finally plot lists
grid.arrange(grobs = plot_list, ncol = 1)

Other code attempts

Thats the approach I was thinking, I wonder am I better off trying to add the labels later separately as was done here using plot_list$labels?

#Also tried a for loop which worked but the fill didnt (and also missing the variable labels)
p <- list()
for(i in vars){
  p[[i]] <- ggplot(diamonds, aes_string(x = i, y = "depth", fill = i)) + geom_boxplot() +
    #note aes_string instead of aes
    theme_cowplot()
}
grid.arrange(grobs = p, ncol = 1)

EDIT

This simpler version plots but the plot isn't capturing the fill correctly and obviously missing the variable labels (paste etc) I want:

        #plot function
        plot_f <- function(df, x_var, y_var) {
          ggplot(df, aes(x = {{x_var}}, y = {{y_var}})) + geom_boxplot(aes(fill = {{x_var}})) +
            theme_cowplot() 
        }

        plot_f(diamonds, cut, depth )  #plots fine

#variables to cycle through
vars1 <- c("cut", "color", "clarity")
vars1
#[1] "cut"     "color"   "clarity"

#unquoted version
vars <- noquote(vars1)
vars
#[1] cut     color   clarity

    #runs
        plot_list <- vars %>% 
          map(., ~plot_f(diamonds, .x, depth))

        #plots but fill isn't correct
        grid.arrange(grobs = plot_list, ncol = 1)

Any help appreciated.

user63230
  • 4,095
  • 21
  • 43
  • 1
    Like this https://stackoverflow.com/a/50930640/786542? – Tung Nov 29 '19 at 00:29
  • 1
    One more https://stackoverflow.com/a/50522928/786542 – Tung Nov 29 '19 at 00:38
  • @Tung thanks, I hadn't seen your second one. They are very helpful but I still can't figure mine out and think they are slightly different, in yours you are just passing the names of the variables through in your `map` call whereas I want the associated labels with those variables to be also included and plotted in the correct positions without having to include labels as arguments in my plot function...if that makes sense – user63230 Nov 29 '19 at 15:49
  • 1
    I see a few things going on here. First, if looping through strings the `{{` code won't work for mapping variables. I always test my functions prior to starting the loop so I can find problems like this. :) Also, since you want strings for the label names you can pass strings directly into `labs` (no NSE work-arounds required). In `pmap()` you can loop over multiple inputs, so after converting your function to work with strings you might want something like `pmap( list(vars, my_labels[vars], my_labels[!names(my_labels) %in% vars]), plot_f, df = diamonds, y_var = "depth")`. – aosmith Nov 29 '19 at 18:24
  • 1
    I haven't blogged about anything as complex as you are doing here, but I do have an example of a plotting function for mapping variables and setting labels simultaneously to be used with `pmap()`. See my added variables post [starting here](https://aosmith.rbind.io/2018/01/31/added-variable-plots/#build-a-plotting-function). – aosmith Nov 29 '19 at 18:25
  • @aosmith thanks for the tips, I'll look into doing it that way. I was wondering why my edited simpler version without labels where I unquote my variables `vars <- noquote(vars1)` doesn't work with `map` and `{{}}`? – user63230 Nov 30 '19 at 16:57

1 Answers1

3

thanks to the comments @aosmith @Tung I came up with the following solution:

library(cowplot)
library(gridExtra)
library(ggplot2)
library(tidyverse)

my_labels <-   c(
  cut = "nice cut",
  color = "all colours",
  clarity = "very claity all",
  depth = "test depth"
)

vars <- c("cut", "color", "clarity")

plot_f <- function(df, x_var, y_var, x_var_label, y_var_label) {
  ggplot(df, aes(x = .data[[x_var]], y = .data[[y_var]])) + 
    geom_boxplot(aes(fill = .data[[x_var]])) +
    theme_cowplot() +
    labs(title = paste("Plot of ", y_var_label, "~", x_var_label), #not .data[[]]
         x = x_var_label,
         y = y_var_label,
         fill = x_var_label)
}

#trick here is that elements of length 1 can be recycled if you wrap it in list
#https://stackoverflow.com/questions/46902461/how-to-pass-a-dataframe-and-uneven-vectors-as-parameters-in-purrr-map

plot_list <- pmap(list(df = list(diamonds), x_var = vars, y_var = list("depth"), x_var_label = my_labels[vars], 
          y_var_label = list(my_labels[!names(my_labels) %in% vars])), plot_f)

grid.arrange(grobs = plot_list, ncol = 1)
user63230
  • 4,095
  • 21
  • 43