How to determine if a head is a variable or a string

I am trying to write a graphing function in which you can pass column names to select which columns are displayed. I would also like to specify a string as a color.

I found that I need to use shQuote if I want to pass a string to aes_string. Now my problem is to find out if the bare name or string was passed. How can I do it?

dat <- data.frame( time = factor(c("Lunch","Dinner"), levels=c("Lunch","Dinner")), total_bill = c(14.89, 17.23) ) plot_it <- function(dat, x,y, fill){ require(rlang) require(ggplot2) x <- enquo(x) y <- enquo(y) fill <- enquo(fill) xN <- quo_name(x) yN <- quo_name(y) fillN <- quo_name(fill) ggplot(data=dat, aes_string(x=xN, y=yN, fill=fillN)) + geom_bar(stat="identity") } 

It works:

 plot_it(dat, time, total_bill, time) 

It does not mean:

 plot_it(dat, time, total_bill, "grey") 

Please note that this requires the latest versions of rlang and ggplot2.

+5
source share
4 answers

Working from your answer and Eumenedies answers / tests , here is a version that:

  • Solves your original problem.
  • Prevents unnecessary legend as Eumenedies did
  • Provides a ggplot object that can be added without errors
  • Uses a more appropriate geom_col , Eumenedies clause

The main trick is that if fill not a factor in the chart, you want it outside of the aes / aes_string .

 plot_it <- function(dat, x, y, fill) { lst <- as.list(match.call()) xN <- quo_name(enquo(x)) yN <- quo_name(enquo(y)) if(is.character(lst$fill)) { p <- ggplot(data=dat, aes_string(x=xN, y=yN)) + geom_col(fill = fill) } else { p <- ggplot(data=dat, aes_string(x=xN, y=yN, fill = quo_name(enquo(fill)))) + geom_col() } return(p) } plot_it(dat, time, total_bill, time) plot_it(dat, time, total_bill, "blue") plot_it(dat, time, total_bill, "blue") + geom_point() 

You can make the if block shorter by moving fill aesthetics in the second case to calling geom_col , but this will expand differently if you add more geometers.

Also, after updating ggplot to support rlang it would be easier to avoid the combination of aes_string and quo_name and just use !!fill .

Note that assuming that there is a fill factor, if it is always the same as a factor x , it probably makes sense to have a version where fill is an optional argument. If the argument is included, you will overwrite only the default color.

+2
source

Based on @akrun's suggestion on how to determine in which case we were (deleted), I found that I did what I requested:

 plot_it <- function(dat, x, y, fill) { lst <- as.list(match.call()) if(is.character(lst$fill)){ fillN <- shQuote(fill) } else{ fillN <- quo_name(enquo(fill)) } x <- enquo(x) y <- enquo(y) xN <- quo_name(x) yN <- quo_name(y) p <- ggplot(data=dat, aes_string(x=xN, y=yN, fill=fillN)) + geom_bar(stat="identity") return(p) } 

It turns out that this does not actually do what I had in mind, since it assigns the quoted value as a factor for the assignment of colors. Not the actual color.

I came up with this, it seems to work, but not very elegantly:

 plot_it <- function(dat, x, y, fill) { lst <- as.list(match.call()) if(!(type_of(lst$fill)=="symbol" | (type_of(lst$fill)=="string" & length(lst$fill)==1))) stop("Fill must either be a bare name or a vector of length 1.") x <- enquo(x) y <- enquo(y) xN <- quo_name(x) yN <- quo_name(y) if(is.character(lst$fill)){ dat[,"fillN"] <- fill fillN <- fill p <- ggplot(data=dat, aes_string(x=xN, y=yN, fill = shQuote(fillN))) + scale_fill_manual(name="fill", values=setNames(fillN,fillN)) } else{ fillN <- quo_name(enquo(fill)) p <- ggplot(data=dat, aes_string(x=xN, y=yN, fill = fillN)) } p <- p + geom_bar(stat="identity") return(p) } 

Any idea to make this a little more elegant?

+3
source

Thus, I did this to use do.call and the parameter list to optionally pass the parameter to the geom_bar function.

 plot_it <- function(dat, x, y, fill) { lst <- as.list(match.call()) xN <- quo_name(enquo(x)) yN <- quo_name(enquo(y)) fillN <- quo_name(enquo(fill)) # Build the geom_bar call using do.call and a list of parameters # If the fill parameter is a character then the parameter list contains # both stat = "identity" and colour = ...; this colour will override the # colour aesthetic p <- ggplot(data=dat, aes_string(x=xN, y=yN, fill=fillN)) + do.call(geom_bar, c(list(stat = "identity"), list(fill = lst$fill)[is.character(lst$fill)])) return(p) } plot_it(dat, time, total_bill, time) plot_it(dat, time, total_bill, "blue") plot_it(dat, time, total_bill, 5) 

I used "blue" for clarity, since ggplot will be gray by default, but it works with any color literal as far as I can tell. I think this is more elegant than using conditional expressions.

Interestingly, geom_col may be more appropriate in this context than geom_bar .

+2
source

You request one parameter to represent two types of arguments: one is the column name, the other is color naming. The simplest solution is to simply split it into two parameters and add some checks to make sure one of them is supplied.

 plot_it <- function(dat, x, y, fill_column, fill_color = NULL){ require(rlang) require(ggplot2) x <- enquo(x) y <- enquo(y) xN <- quo_name(x) yN <- quo_name(y) if (!missing(fill_column) && !is.null(fill_color)) { stop("Specify either fill_column or fill_color, not both") } if (missing(fill_column) && is.null(fill_color)) { stop("Specify one of fill_column or fill_color") } plot_geom <- if (!is.null(fill_color)) { geom_bar(stat = "identity", fill = fill_color) } else { fill <- enquo(fill_column) fillN <- quo_name(fill) geom_bar(stat = "identity", aes_string(fill = fillN)) } ggplot(data = dat, aes_string(x = xN, y = yN)) + plot_geom } plot_it(dat, time, total_bill, fill_column = time) plot_it(dat, time, total_bill, fill_color = "grey") 
0
source

All Articles