Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Variable scope is global when it should be local #248

Closed
macrakis opened this Issue · 7 comments

3 participants

@macrakis

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

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

n<-3

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).

@macrakis
@hadley
Owner

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
@macrakis

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.

@kohske
Collaborator

By default, the evaluation environment is set in https://github.com/hadley/ggplot2/blob/master/R/plot.r#L25
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)
local({
    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
@hadley
Owner

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.

@kohske
Collaborator

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.
@hadley
Owner

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.