Hugo Zaragoza
Hugo Zaragoza

Reputation: 599

R function: strange behaviour with plot inside function

I am getting strange behaviour when putting plots inside functions (using ggplot with R from 'R Studio' 0.99.484).

I don't understand why locally scoped variables cannot be used to generate the plot. Perhaps you can enlighten me, and tell me of the right way to do it.

Here is a small code snippet reproducing my troubles:

# This works:
x1 <- seq(0,90,10)
ggplot() + geom_line(aes(x=x1,y=x1))

# This yields error below:
f1 <- function() {
  x2 <- seq(0,90,10)
  ret <- ggplot() + geom_line(aes(x=x2,y=x2))
  return(ret)
}
f1()

Error in eval(expr, envir, enclos) : object 'x2' not found

# This fixes the error: but why myX needs to be global?
f1 <- function() {
  myX <<- seq(0,90,10) 
  ret <- ggplot() + geom_line(aes(x=myX,y=myX))
  return(ret)
}
f1()

# This yields error below:
f2 <- function(d) {
  ret <- ggplot() + geom_line(aes(x=d,y=d))
  return(ret)
}
f2(x1)

Error in eval(expr, envir, enclos) : object 'd' not found

# This fixes the error making all global: is this the right way to do it?
f2 <- function(d) {
  myX <<- d
  ret <- ggplot() + geom_line(aes(x=myX,y=myX))
  return(ret)
}
f2(x1)

Upvotes: 0

Views: 2928

Answers (1)

mpalanco
mpalanco

Reputation: 13580

In your first example, the problem is that you're declaring a varible x2inside a function and that variable is local to that function. That's why you get the error: object 'x2' not found

If you want to make that variable accesible outside the function you can use the operator <<- instead of <-, or the function assign to assign it explicitly to the global environment. O you can just make the assignment outside the function.

# Option 1: <<-
    f1 <- function() {
      x2 <<- seq(0,90,10)
      ret <- ggplot() + geom_line(aes(x=x2,y=x2))
      return(ret)
    }
    f1()

# Option2: assign  
    f1 <- function() {
      assign(x = "x2", value = seq(0,90,10), envir=.GlobalEnv)
      ret <- ggplot() + geom_line(aes(x=x2,y=x2))
      return(ret)
    }
    f1()
# Option 3: assignment outside the function
x2 <- seq(0,90,10)
f1 <- function() {
  ret <- ggplot() + geom_line(aes(x=x2,y=x2))
  return(ret)
}
f1()

Expanding the answer. The reason is because there is a bug in ggplot2:

Expressions in aes() are evaluated in the global environment, but they should be evaluated in the calling environment.

Another workaround is to use a data frame (we shouldn't use vectors). Because aes looks first in the data frame passed to ggplot and then in the global environment. With this code you won't get the error:

f1 <- function() {
  x1 <- data.frame(x1 = seq(0,90,10))
  ret <- ggplot(x1, aes(x = x1, y= x1)) + geom_line()
  return(ret)
}
f1()

Finally, it's important to remember that in ggplot2 the data must be an R data frame. Hadley Wickham explicitly said in ggplot2: Elegant Graphics for Data Analysis:

The restriction on the data is simple: it must be a data frame. This is restrictive, and unlike other graphics packages in R.

Upvotes: 2

Related Questions