12

Trying to write a relatively simple wrapper to produce some plots, but can not work out how to specify tidy evaluation of grouping variables specified as ... an example function that facets variables but doesn't distinguish by grouping...

my_plot <- function(df = starwars,
                    select = c(height, mass),
                    ...){
    results <- list()
    ## Tidyeval arguments
    quo_select <- enquo(select)
    quo_group  <- quos(...)
    ## Filter, reshape and plot
    results$df <- df %>%
                  dplyr::filter(!is.na(!!!quo_group)) %>%
                  dplyr::select(!!quo_select, !!!quo_group) %>%
                  gather(key = variable, value = value, !!!quo_select) %>% 
                  ## Specify what to plot
                  ggplot(aes(value)) +
                  geom_histogram(stat = 'count')  +
                  facet_wrap(~variable, scales = 'free', strip.position = 'bottom')
    return(results)
}
## Plot height and mass as facets but colour histograms by hair_color
my_plot(df = starwars, select = c(height, mass), hair_color)

Great it works, but how to distinguish between different hair_color? Normally this is done within aes() but since this is using the results of quos() (i.e. quo_group) I should (I think) be using aes_() instead

my_plot <- function(df = starwars,
                    select = c(height, mass),
                    ...){
    results <- list()
    ## Tidyeval arguments
    quo_select <- enquo(select)
    quo_group  <- quos(...)
    ## Filter, reshape and plot
    results$df <- df %>%
                  dplyr::filter(!is.na(!!!quo_group)) %>%
                  dplyr::select(!!quo_select, !!!quo_group) %>%
                  gather(key = variable, value = value, !!!quo_select) %>% 
                  ## Specify what to plot, including colouring by the supplied ... groupings
                  ggplot(aes_(~value, colour = !!!quo_group)) + 
                  geom_histogram(stat = 'count')  +
                  facet_wrap(~variable, scales = 'free', strip.position = 'bottom')
    return(results)
}
## Plot height and mass as facets but colour histograms by hair_color
my_plot(df = starwars, select = c(height, mass), hair_color)
Error in !quo_group : invalid argument type

I can't see or work out having read Programming with dplyr several times now where I'm going wrong.

Can anyone point out my error/show me the way?

Tung
  • 26,371
  • 7
  • 91
  • 115
slackline
  • 2,295
  • 4
  • 28
  • 43
  • In your second function, why do you have two lines with `ggplot(aes(etc.))` ? – FlorianGD Jun 14 '17 at 15:44
  • If you use `quo(...)` instead of `quos(...)` then things work OK in `aes_` a la `aes_(~value, color = quo_group)`. But maybe you need a list of grouping variables for something else? – aosmith Jun 14 '17 at 15:46
  • 1
    Oh, I see, `quos` returns a list. So if you only want to use the first element of the 1 element list that get from `quos(...)`, you'd need `color = quo_group[[1]]` in `aes_`. – aosmith Jun 14 '17 at 15:48
  • @FlorianGD an error transcribing the two versions, now corrected. – slackline Jun 14 '17 at 16:17
  • @aosmith `quo_group[[1]]` does the trick, but seems at odds to the [Programming with dplyr : capturing multiple variables](http://dplyr.tidyverse.org/articles/programming.html#capturing-multiple-variables) documentation. – slackline Jun 14 '17 at 16:19
  • @slackline I agree, but I'm not sure how capturing multiple variables fits in with mapping single variables to aesthetics. It seems like for variable mapping you'd only want to capture a single variable, and that you'd have a specific argument for this in your function. I guess maybe we'll see once ggplot2 is moved to the tidy evaluation framework! – aosmith Jun 14 '17 at 17:42
  • @aosmith yes, I'm going to have to re-jig the options to my function. Looking forward full details of tidyeval in ggplot2. – slackline Jun 14 '17 at 19:38
  • see here my man https://stackoverflow.com/questions/45824409/how-to-combine-ggplot-and-dplyr-into-a-function – ℕʘʘḆḽḘ Aug 24 '17 at 01:02

2 Answers2

3

The new released ggplot2 v3.0.0 supports !! inside aes(). With some minor modification, your function is now working

library(tidyverse)

my_plot <- function(df = starwars,
                    select = c(height, mass),
                    ...){
  results <- list()

  ## Tidyeval arguments
  quo_select <- enquo(select)

  # only need quo here, if quos is used then we need to `unlist` to 
  # convert its output from list to vector
  quo_group  <- quo(...) 

  ## Filter, reshape and plot
  results$df <- df %>%
    dplyr::filter(!is.na(!!!quo_group)) %>%
    dplyr::select(!!quo_select, !!!quo_group) %>%
    gather(key = variable, value = value, !!!quo_select) %>% 
    ## Specify what to plot, including coloring by the supplied dots `...` 
    ggplot(aes(value, color = !!quo_group, fill = !!quo_group)) +  # unquote inside aes
    geom_histogram(stat = 'count')  +
    facet_wrap(vars(variable), scales = 'free', strip.position = 'bottom')
  return(results)
}

## Plot height and mass as facets but color histograms by hair_color
my_plot(df = starwars, select = c(height, mass), hair_color)

Created on 2018-09-12 by the reprex package (v0.2.0.9000).

Tung
  • 26,371
  • 7
  • 91
  • 115
  • 1
    Thanks, I'd clocked that ggplot2 v3.0.0 solved a lot of these problems, only downside for me is I've switched jobs and as a consequence had to start using Python instead :-/ – slackline Sep 12 '18 at 15:55
  • Now you know both `Python` and `R`. Isn't that great? Congratulations! – Tung Sep 12 '18 at 15:57
  • 1
    I wouldn't say I know Python yet but I'm getting there ;-) Understanding design patterns and SOLID principles are also a concurrent learning curve! – slackline Sep 12 '18 at 16:04
1

I'm not sure I understand the question. Does this satisfy the requirements?

library(ggplot2)
library(data.table)

your_plot <- function(df, select, color=NULL) {

  df <- as.data.table(df)[, mget(na.omit(c(select, color)))]

  ggplot(melt(df, color, select), aes_string(x=quote(value), color=color)) +
    geom_histogram(stat="count") +
    facet_wrap(~variable, scales="free", strip.position="bottom")

}

your_plot(dplyr::starwars, c("height", "mass"), "hair_color")

This uses melt to stack the select variables, with the color variable(s) repeated for each stack. It also uses aes_string, since aes(x=value, color=color) fails when color=NULL.

dnlbrky
  • 9,396
  • 2
  • 51
  • 64