Passing columns names to group_by and ggplot2 within a custom function

library(rlang)
library(tidyverse)

qw <- structure(list(weekday = structure(c(2L, 6L, 7L, 5L, 1L, 3L, 
  4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 
  6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L), .Label = c("Friday", 
  "Monday", "Saturday", "Sunday", "Thursday", "Tuesday", "Wednesday"
  ), class = "factor"), Target = c(0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 
  0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 
  1, 0, 0, 1), type = structure(c(3L, 3L, 2L, 3L, 1L, 3L, 1L, 3L, 
  1L, 1L, 3L, 2L, 1L, 3L, 3L, 1L, 3L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 
  1L, 3L, 2L, 1L, 2L, 1L, 2L, 1L, 3L, 2L, 3L), .Label = c("Advertising", 
  "Agriculture", "Bank"), class = "factor")), .Names = c("weekday", 
  "Target", "type"), row.names = c(NA, -35L), class = "data.frame")

Here Target column is fixed for group_by() and facet_grid() functions. In the similar way I want to compare with multiple columns. weekday and type.

For that I wrote a function

cateby_label_graph <- function(x){
  x <- syms(x)
  qw %>% 
    group_by(!!!x, Target) %>%
    summarise(Freq = n()) %>% 
    ggplot(data = . , aes(x = reorder(x, -Freq), y = Freq, fill = x)) +
    geom_bar(stat = 'identity') + 
    labs(y = "", x = "") +
    facet_grid(Target~., scales="free") + 
    theme(legend.position = 'none')
}

I am passing the column name

cateby_label_graph ('type')

I am getting the error.

Any idea ? where i did mistake.

If you're using the dev version of ggplot2 you may want to take a peek at the release notes, as it addresses changes in aes() mapping that I think are relevant here:

Looking at the traceback for your function, it seems the issue is in the line

ggplot(date = . , aes(x = reorder(x, -Freq), y = Freq, fill = x))
library(rlang)
library(tidyverse)

qw <- structure(list(weekday = structure(c(2L, 6L, 7L, 5L, 1L, 3L, 
                                           4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 
                                           6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L), .Label = c("Friday", 
                                                                                                           "Monday", "Saturday", "Sunday", "Thursday", "Tuesday", "Wednesday"
                                           ), class = "factor"), Target = c(0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 
                                                                            0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 
                                                                            1, 0, 0, 1), type = structure(c(3L, 3L, 2L, 3L, 1L, 3L, 1L, 3L, 
                                                                                                            1L, 1L, 3L, 2L, 1L, 3L, 3L, 1L, 3L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 
                                                                                                            1L, 3L, 2L, 1L, 2L, 1L, 2L, 1L, 3L, 2L, 3L), .Label = c("Advertising", 
                                                                                                                                                                    "Agriculture", "Bank"), class = "factor")), .Names = c("weekday", 
                                                                                                                                                                                                                           "Target", "type"), row.names = c(NA, -35L), class = "data.frame")
cateby_label_graph <- function(x){
  x <- syms(x)
  qw %>% 
    group_by(!!!x, Target) %>%
    summarise(Freq = n()) %>% 
    ggplot(data = . , aes(x = reorder(x, -Freq), y = Freq, fill = x)) +
    geom_bar(stat = 'identity') + 
    labs(y = "", x = "") +
    facet_grid(Target~., scales="free") + 
    theme(legend.position = 'none')
}

cateby_label_graph('type')
#> Error in unique.default(x, nmax = nmax): unique() applies only to vectors

Created on 2018-07-02 by the reprex package (v0.2.0).

The traceback for the error reads:

19: unique.default(x, nmax = nmax)
18: unique(x, nmax = nmax)
17: factor(x)
16: FUN(X[[i]], ...)
15: lapply(INDEX, as.factor)
14: tapply(X = X, INDEX = x, FUN = FUN, ...)
13: reorder.default(x, -Freq)
12: reorder(x, -Freq)
11: FUN(X[[i]], ...)
10: lapply(aesthetics[new_aesthetics], rlang::eval_tidy, data = data)
9: scales_add_defaults(plot$scales, data, aesthetics, plot$plot_env)
8: f(..., self = self)
7: l$compute_aesthetics(d, plot)
6: f(l = layers[[i]], d = data[[i]])
5: by_layer(function(l, d) l$compute_aesthetics(d, plot))
4: ggplot_build.ggplot(x)
3: ggplot_build(x)
2: print.ggplot(x)
1: function (x, ...) 
   UseMethod("print")(x)

So, the error might be that you're, in effect, applying unique() to a factor.

1 Like

I would suggest using wrapr::let() for this task. Here is a working version of the plot:

library("rlang")
library("tidyverse")

qw <- wrapr::build_frame(
  'weekday'  , 'Target', 'type'        |
    'Monday'   , 0       , 'Bank'        |
    'Tuesday'  , 0       , 'Bank'        |
    'Wednesday', 1       , 'Agriculture' |
    'Thursday' , 1       , 'Bank'        |
    'Friday'   , 0       , 'Advertising' |
    'Saturday' , 1       , 'Bank'        |
    'Sunday'   , 1       , 'Advertising' |
    'Monday'   , 1       , 'Bank'        |
    'Tuesday'  , 1       , 'Advertising' |
    'Wednesday', 0       , 'Advertising' |
    'Thursday' , 0       , 'Bank'        |
    'Friday'   , 0       , 'Agriculture' |
    'Saturday' , 1       , 'Advertising' |
    'Sunday'   , 0       , 'Bank'        |
    'Monday'   , 1       , 'Bank'        |
    'Tuesday'  , 0       , 'Advertising' |
    'Wednesday', 1       , 'Bank'        |
    'Thursday' , 1       , 'Advertising' |
    'Friday'   , 0       , 'Agriculture' |
    'Saturday' , 1       , 'Agriculture' |
    'Sunday'   , 1       , 'Agriculture' |
    'Monday'   , 0       , 'Agriculture' |
    'Tuesday'  , 1       , 'Advertising' |
    'Wednesday', 0       , 'Advertising' |
    'Thursday' , 0       , 'Advertising' |
    'Friday'   , 0       , 'Bank'        |
    'Saturday' , 0       , 'Agriculture' |
    'Sunday'   , 0       , 'Advertising' |
    'Monday'   , 1       , 'Agriculture' |
    'Tuesday'  , 0       , 'Advertising' |
    'Wednesday', 0       , 'Agriculture' |
    'Thursday' , 1       , 'Advertising' |
    'Friday'   , 0       , 'Bank'        |
    'Saturday' , 0       , 'Agriculture' |
    'Sunday'   , 1       , 'Bank'        )

cateby_label_graph <- function(x) {
  wrapr::let(
    c(X = x),
    qw %>% 
      group_by(X, Target) %>%
      summarise(Freq = n()) %>% 
      ggplot(data = . , aes(x = reorder(X, -Freq), y = Freq, fill = X)) +
      geom_bar(stat = 'identity') + 
      labs(y = "", x = "") +
      facet_grid(Target~., scales="free") + 
      theme(legend.position = 'none')
  )
}

cateby_label_graph('type')

Here is another example and some formal documentation.

edit- now fixed after @cderv's comment.

1 Like

Thank you for
Give reference to the new update in ggplot2.

I think you have the error because you forgot to unquote the x here, like you have done in group_by. reorder does not know what to do with an object which is not a vector (because not unquote)

You can use tidy evalutation in dev version of ggplot2 (and next 2.3 version coming soon) and I would do it this way:

library(tidyverse)
qw <- structure(list(weekday = structure(c(2L, 6L, 7L, 5L, 1L, 3L, 
                                           4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L, 2L, 
                                           6L, 7L, 5L, 1L, 3L, 4L, 2L, 6L, 7L, 5L, 1L, 3L, 4L), .Label = c("Friday", 
                                                                                                           "Monday", "Saturday", "Sunday", "Thursday", "Tuesday", "Wednesday"
                                           ), class = "factor"), Target = c(0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 
                                                                            0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 
                                                                            1, 0, 0, 1), type = structure(c(3L, 3L, 2L, 3L, 1L, 3L, 1L, 3L, 
                                                                                                            1L, 1L, 3L, 2L, 1L, 3L, 3L, 1L, 3L, 1L, 2L, 2L, 2L, 2L, 1L, 1L, 
                                                                                                            1L, 3L, 2L, 1L, 2L, 1L, 2L, 1L, 3L, 2L, 3L), .Label = c("Advertising", 
                                                                                                                                                                    "Agriculture", "Bank"), class = "factor")), .Names = c("weekday", 
                                                                                                                                                                                                                           "Target", "type"), row.names = c(NA, -35L), class = "data.frame")
cateby_label_graph <- function(x){
  x <- ensym(x)
  qw %>%
    group_by(!!x, Target) %>%
    summarise(Freq = n()) %>%
    ggplot(data = . , aes(x = reorder(!!x, -Freq), y = Freq, fill = !!x)) +
    geom_col() +
    labs(y = "", x = "") +
    facet_grid(Target~., scales="free") +
    theme(legend.position = 'none')
}

cateby_label_graph('type')

Created on 2018-07-03 by the reprex package (v0.2.0).

A few notes:

  • I used ensym because we are inside a function and we want to capture user input.
  • I only capture one input. You could grouped by several variable, but you need only one in reorder and in fill= where x is used also. It is why I used !! instead of !!!.

Hopes it helps

4 Likes