Reputation: 11745
I'm trying to use a local variable in aes
when I plot with ggplot. This is my problem boiled down to the essence:
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data,YMul=2){
ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}
plotfunc(xy)
This results in the following error:
Error in eval(expr, envir, enclos) : object 'YMul' not found
It seems as if I cannot use local variables (or function arguments) in aes
. Could it be that it occurrs due to the content of aes
being executed later when the local variable is out of scope? How can I avoid this problem (other than not using the local variable within aes
)?
Upvotes: 47
Views: 11472
Reputation: 781
I am using ggplot2, and your example seems to work fine with the current version.
However, it is easy to come up with variants which still create trouble. I was myself confused by similar behavior, and that's how I found this post (top Google result for "ggplot how to evaluate variables when passed"). For example, if we move ggplot out of plotfunc:
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data,YMul=2){
geom_line(aes(x=x,y=y*YMul))
}
ggplot(xy)+plotfunc(xy)
# Error in eval(expr, envir, enclos) : object 'YMul' not found
In the above variant, "capturing the local environment" is not a solution because ggplot is not called from within the function, and only ggplot has the "environment=" argument.
But there is now a family of functions "aes_", "aes_string", "aes_q" which are like "aes" but capture local variables. If we use "aes_" in the above, we still get an error because now it doesn't know about "x". But it is easy to refer to the data directly, which solves the problem:
plotfunc <- function(Data,YMul=2){
geom_line(aes_(x=Data$x,y=Data$y*YMul))
}
ggplot(xy)+plotfunc(xy)
# works
Upvotes: 4
Reputation: 1942
Have you looked at the solution given by @wch (W. Chang)?
https://github.com/hadley/ggplot2/issues/743
I think it is the better one
essentially is like that of @baptiste but include the reference to the environment directly in the call to ggplot
I report it here
g <- function() {
foo3 <- 4
ggplot(mtcars, aes(x = wt + foo3, y = mpg),
environment = environment()) +
geom_point()
}
g()
# Works
Upvotes: 1
Reputation: 77096
I would capture the local environment,
xy <- data.frame(x=1:10,y=1:10)
plotfunc <- function(Data, YMul = 2){
.e <- environment()
ggplot(Data, aes(x = x, y = y*YMul), environment = .e) + geom_line()
}
plotfunc(xy)
Upvotes: 39
Reputation: 3623
ggplot()
's aes
expects YMul
to be a variable within the data
data frame. Try including YMull
there instead:
Thanks to @Justin: ggplot()
's aes
seems to look forYMul
in the data
data frame first, and if not found, then in the global environment. I like to add such variables to the data frame, as follows, as it makes sense to me conceptually. I also don't have to worry about changes to global variables having unexpected consequences to functions. But all of the other answers are also correct. So, use whichever suits you.
require("ggplot2")
xy <- data.frame(x = 1:10, y = 1:10)
xy <- cbind(xy, YMul = 2)
ggplot(xy, aes(x = x, y = y * YMul)) + geom_line()
Or, if you want the function in your example:
plotfunc <- function(Data, YMul = 2)
{
ggplot(cbind(Data, YMul), aes(x = x, y = y * YMul)) + geom_line()
}
plotfunc(xy)
Upvotes: 5
Reputation: 162311
Here's an alternative that allows you to pass in any value through the YMul
argument without having to add it to the Data
data.frame or to the global environment:
plotfunc <- function(Data, YMul = 2){
eval(substitute(
expr = {
ggplot(Data,aes(x=x,y=y*YMul)) + geom_line()
},
env = list(YMul=YMul)))
}
plotfunc(xy, YMul=100)
To see how this works, try out the following line in isolation:
substitute({ggplot(Data, aes(x=x, y=y*YMul))}, list(YMul=100))
Upvotes: 10
Reputation: 43245
If you execute your code outside of the function it works. And if you execute the code within the function with YMul
defined globally, it works. I don't fully understand the inner workings of ggplot
but this works...
YMul <- 2
plotfunc <- function(Data){
ggplot(Data,aes(x=x,y=y*YMul))+geom_line()
}
plotfunc(xy)
Upvotes: 0