21

would like to create a function that generates graphs using ggplot. For the sake of simplicity, the typical graph may be

ggplot(car, aes(x=speed, y=dist)) + geom_point() 

The function I would like to create is of the type

f <- function(DS, x, y) ggplot(DS, aes(x=x, y=y)) + geom_point()

This however won't work, since x and y are not strings. This problem has been noted in previous SO questions (e.g., this one), but without providing, in my view, a satisfactory answer. How would one modify the function above to make it work with arbitrary data frames?

Community
  • 1
  • 1
gappy
  • 10,095
  • 14
  • 54
  • 73

4 Answers4

43

One solution would be to pass x and y as string names of columns in data frame DS.

f <- function(DS, x, y) {    
  ggplot(DS, aes_string(x = x, y = y)) + geom_point()  
}

And then call the function as:

f(cars, "speed", "dist")

However, it seems that you don't want that? Can you provide an example why you would need different functionality? Is it because you don't want to have the arguments in the same data frame?

Grega Kešpret
  • 11,827
  • 6
  • 39
  • 44
8

I think it's possible the following type of codes, which only build the aes component.

require(ggplot2)

DS <- data.frame(speed=rnorm(10), dist=rnorm(10))

f <- function(DS, x, y, geom, opts=NULL) {
  aes <- eval(substitute(aes(x, y),
    list(x = substitute(x), y = substitute(y))))
  p <- ggplot(DS, aes) + geom + opts
}

p <- f(DS, speed, dist, geom_point())
p

However, it seems to be complicated approach.

Triad sou.
  • 2,969
  • 3
  • 23
  • 27
  • 6
    A better approach would be to skip the conversion to text, and only build the aes component: `xName <- substitute(x); yName <- substitute(y); eval(substitute(aes(x, y), list(x = xName, y = yName)))` – hadley Jun 27 '11 at 12:58
  • Thank you @hadley. I think this approach is more simple than mine. – Triad sou. Jun 28 '11 at 05:16
1

Another option is to use do.call. Here is a one line copy paste from a working code:

gg <- gg + geom_rect( do.call(aes, args=list(xmin=xValues-0.5, xmax=xValues+0.5, ymin=yValues, ymax=rep(Inf, length(yValues))) ), alpha=0.2, fill=colors )
ozgeneral
  • 6,079
  • 2
  • 30
  • 45
1

One approach that I can think of is using match.call() to reach the variable names contained by the parameters/arguments passed to the custom plotting function and then use eval() on them. In this way you avoid passing them as quoted to your custom function, if you do not like that.

library(ggplot2)

fun <- function(df, x, y) {
    arg <- match.call()
    ggplot(df, aes(x = eval(arg$x), y = eval(arg$y))) + geom_point()
} 
fun(mpg, cty, hwy) # no need to pass the variables (column names) as quoted / as strings

enter image description here

Valentin_Ștefan
  • 6,130
  • 2
  • 45
  • 68
  • Thanks. It helped me a lot! Can you explain the match.call argument ? – Luis Feb 06 '19 at 22:34
  • 1
    Hi @Luis. Hadley Wickham wrote about some usages of `match.call()` in the section [Capturing the current call](http://adv-r.had.co.nz/Expressions.html#capturing-call) of his [Advanced R](http://adv-r.had.co.nz) book. Maybe that helps with more details. – Valentin_Ștefan Feb 08 '19 at 12:21