Skip to content


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP


Variable scope is global when it should be local #248

macrakis opened this Issue · 7 comments

3 participants


Arguments of aes are apparently evaluated in global environment (!!!) not local one:

df <- data.frame(a=1:10,b=10:1)


n<-3; local({n<-2; ggplot(df) + geom_line(aes(x=a,y=b,group=cut(a,n)))})

Has 3 segments (global value of n), when it should have 2 (local value of n).


I think this is an inherent property of how ggplot2 works - each layer does not capture scope independently. Evaluation of all arguments should occur in the environment where the plot is defined.

@hadley hadley closed this

I don't follow your explanation. In the following case:

n<-3; local({n<-2; ggplot(df) + geom_line(aes(x=a,y=b,group=cut(a,n)))})

Why would the 'n' in 'cut(a,n)' look at the global environment? That is just a violation of the basic concept of scope in R. Compare the 'lm' case I showed:

 n<- 3; local({n<-2;lm(b~poly(a,n,raw=T),df)})

The ggplot case, like the lm case, should be evaluated in the following nested environments:

Global environment (where n=3)
Local environment (where n=2)
df environment (where n is not defined, so the next environment up should be used)

I will be happy to work on a solution for this. Please don't close it as fixed.


By default, the evaluation environment is set in
irrespective of where the plot is defined or called.
so, if the variable is missing in the data.frame, then the global env is inspected.

If someone wants to change this behavior, try:

m <- 3
df <- data.frame(a=1:10,b=10:1)
    m <- 2; 
    ggplot(df, environment = new.env()) + geom_line(aes(x=a, y=b, group=cut(a, m)))

Of course, it is possible to change the default behavior so that ggplot also inspect the local environment where the plot is defined.
But it is not a simple problem which policy is better, since the environment of plot and environment of each layer could be different.

@hadley hadley reopened this

Sorry, I got in a bit of a ticket closing frenzy. I think a simple fix is to replace globalenv() with parent.frame() to at least capture the environment where ggplot() is called.


Or, maybe environment of aes() is better.
Consider this:

m <- -1
n <- -2

f1 <- function() {
    m <- 1
    geom_point(aes(x, y+m), color = "red")

f2 <- function() {
    n <- 2
    geom_point(aes(x, y+n), color = "blue")

lay1 <- f1()
lay2 <- f2()

df <- data.frame(x=1:3, y=1:3)
ggplot(df) + lay1 + lay2 # m = -1, n = -2, i.e., global ones.

Oh, I like that even better. Especially since if a layer doesn't have an aes call, then it doesn't need any special help.

That reminds me that I've been wanting to make aes more like plyr::. for a while. . captures the current environment, and offers a much more complete API for coercing to and from different data types.

@hadley hadley closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.