31

I can't seem to make apply function access/modify a variable that is declared outside... what gives?

 x = data.frame(age=c(11,12,13), weight=c(100,105,110)) x testme <- function(df) { i <- 0 apply(df, 1, function(x) { age <- x[1] weight <- x[2] cat(sprintf("age=%d, weight=%d\n", age, weight)) i <- i+1 #this could not access the i variable in outer scope z <- z+1 #this could not access the global variable }) cat(sprintf("i=%d\n", i)) i } z <- 0 y <- testme(x) cat(sprintf("y=%d, z=%d\n", y, z)) 

Results:

 age=11, weight=100 age=12, weight=105 age=13, weight=110 i=0 y=0, z=0 
4
  • 1
    You need to pass the variables to testme, and then to apply: testme <- function(x, z) { and apply(df, 1, function(x, i, z) {}, i, z) Commented Nov 30, 2012 at 6:59
  • @bdemarest: that won't work as the value of i will be reset at iteration of apply (ie, for every row of df). I think the OP wants to track which row they are on Commented Nov 30, 2012 at 7:32
  • @RicardoSaporta, you are quite right. Probably the OP would be better off not using apply, but instead a standard for loop: for (i in 1:nrow(df)) {...}. Currently, we can only guess at the underlying problem he/she is trying to solve. Commented Nov 30, 2012 at 7:42
  • 1
    this was only a test snip to demonstrate the problem i had :-) It turns out that I should return the results back to the caller i.e. assign the result of the apply call to another variable. That's a better functional style. Commented Nov 30, 2012 at 8:12

2 Answers 2

45

Using the <<- operator you can write to variables in outer scopes:

x = data.frame(age=c(11,12,13), weight=c(100,105,110)) x testme <- function(df) { i <- 0 apply(df, 1, function(x) { age <- x[1] weight <- x[2] cat(sprintf("age=%d, weight=%d\n", age, weight)) i <<- i+1 #this could not access the i variable in outer scope z <<- z+1 #this could not access the global variable }) cat(sprintf("i=%d\n", i)) i } z <- 0 y <- testme(x) cat(sprintf("y=%d, z=%d\n", y, z)) 

The result here:

age=11, weight=100 age=12, weight=105 age=13, weight=110 i=3 y=3, z=3 

Note that the usage of <<- is dangerous, as you break up scoping. Do this only if really necessary and if you do, document that behavior clearly (at least in bigger scripts)

Sign up to request clarification or add additional context in comments.

Comments

8

try the following inside your apply. Experiment with the value of n. I believe that for i it should be one less than for z.

 assign("i", i+1, envir=parent.frame(n=2)) assign("z", z+1, envir=parent.frame(n=3)) 



testme <- function(df) { i <- 0 apply(df, 1, function(x) { age <- x[1] weight <- x[2] cat(sprintf("age=%d, weight=%d\n", age, weight)) ## ADDED THESE LINES assign("i", i+1, envir=parent.frame(2)) assign("z", z+1, envir=parent.frame(3)) }) cat(sprintf("i=%d\n", i)) i } 

OUTPUT

> z <- 0 > y <- testme(x) age=11, weight=100 age=12, weight=105 age=13, weight=110 i=3 > cat(sprintf("y=%d, z=%d\n", y, z)) y=3, z=3 

5 Comments

I would use assign over eval(parse(...)).
@RomanLuštrik, I had originally put assign in the answer and then edited it to eval(parse((.)). Why your preference?
I guess religious reasons. See the entry on fortune. stackoverflow.com/questions/11025031/… I wonder if this question sparked the question made by @CarlWitthoft a few minutes ago: stackoverflow.com/questions/13647046/…
@RomanLuštrik, there are certainly quite a few questions today on it. I added one myself, and @DWin made a pretty good argument. Changed the above back to assign()
@RicardoSaporta eval(parse(...)) allows addressing elements of a list as well, while assign doesn't support that, so I actually prefer the former option

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.