Filipe Ferminiano
Filipe Ferminiano

Reputation: 8791

Implement counter inside function to check progress

I have a function that I'm applying to a matrix. But the vector is to extensive and I'd like to check the progress of it with a counter.

N = 10
j = 0

functionX <- function(a,b,c) {

... stuff here ...

j<- j+1
print(paste(j/N,'%'))

}

a <- mapply(functionX, a[1:10,1],b[1:10,2],c[1:10,3])

But this approach is not working, because j never gets bigger than 1 with mapply, and I would like to avoid for loops.

Upvotes: 4

Views: 965

Answers (2)

Martin Morgan
Martin Morgan

Reputation: 46876

It's never a good idea to write to the global environment, so use a function that returns a function.

counter = function(FUN, report=10) {
    j <- 0L                      # initialize the counter
    function(...) {              # a function that takes '...' and...
        j <<- j + 1L             # increment j in a containing env
        if ((j %% report) == 0L)
            message(j)
        FUN(...)                 # ...passes all args '...' to FUN
    }
}

The line j <<- j + 1 says that, for the assignment, look first in the environment in which the function was defined, rather than in the function itself. The line j <- 0L servers as a kind of break in where <<- looks for j -- it finds and updates it in this environment. If j were not found, then the <<- operator would continuing looking for a symbol in the defining environment until arriving at the global environment.

In action:

> x = sapply(1:20, counter(c, 5))    # concatenate elements c()
5
10
15
20
> x
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
> countedc = counter(c)
> x = integer(); for (i in 1:12) x = countedc(x, i)
10
> for (i in 1:9) x = countedc(x, i)
20
> x
 [1]  1  2  3  4  5  6  7  8  9 10 11 12  1  2  3  4  5  6  7  8  9

One might also use ?txtProgressBar. There may be problems if FUN uses 'non-standard evaluation' for its arguments as, e.g., subset() does.

It's rare for matrix operations to take a long time; maybe you are iterating but should be using vectorized calculations?

Notice that any global variable I'd defined and named j would be unchanged

> j <- 42   # Yes! the meaning of life. Don't lose this!
> countedc(x, 0)
 [1]  1  2  3  4  5  6  7  8  9 10 11 12  1  2  3  4  5  6  7  8  9  0
> j         # Would have been clobbered if counter used global env
[1] 42

Upvotes: 2

digEmAll
digEmAll

Reputation: 57220

The problem is inside functionX you're assigning a value to j and so your also defining a new j variable that exists only in the function environment and is different from the j defined outside of functionX.

If you want to change the outside j you can do:

j <<- j+1

however it's preferable to not modify variables in a parent environment, so I suggest you to pass a new variable to mapply representing the iteration counter e.g. :

N = 10

functionX <- function(iteration,a,b,c) {
    # elaboration here
    print(paste( (iteration/N) * 100,'%'))
}

a <- mapply(functionX, 1:10, a[1:10,1],b[1:10,2],c[1:10,3])

Upvotes: 4

Related Questions