Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to apply function over each matrix element's indices

I am wondering if there is a built-in function in R which applies a function to each element of the matrix (of course, the function should be computed based on matrix indices). The equivalent would be something like this:

matrix_apply <- function(m, f) {   m2 <- m   for (r in seq(nrow(m2)))     for (c in seq(ncol(m2)))       m2[[r, c]] <- f(r, c)   return(m2) } 

If there is no such built-in function, what is the best way to initialize a matrix to contain values obtained by computing an arbitrary function which has matrix indices as parameters?

like image 209
eold Avatar asked Sep 12 '11 23:09

eold


People also ask

How do you apply a function to all elements of a list in R?

lapply() function in R Programming Language is used to apply a function over a list of elements. lapply() function is used with a list and performs the following operations: lapply(List, length): Returns the length of objects present in the list, List.

How do you apply the same functions to all rows and columns of a matrix in R?

You can use the apply() function to apply a function to each row in a matrix or data frame in R.

What does arrayfun do?

arrayfun returns the outputs of func in cell arrays. The outputs of func can have any sizes and different data types.


2 Answers

I suspect you want outer:

> mat <- matrix(NA, nrow=5, ncol=3)  > outer(1:nrow(mat), 1:ncol(mat) , FUN="*")      [,1] [,2] [,3] [1,]    1    2    3 [2,]    2    4    6 [3,]    3    6    9 [4,]    4    8   12 [5,]    5   10   15  > outer(1:nrow(mat), 1:ncol(mat) , FUN=function(r,c) log(r+c) )           [,1]     [,2]     [,3] [1,] 0.6931472 1.098612 1.386294 [2,] 1.0986123 1.386294 1.609438 [3,] 1.3862944 1.609438 1.791759 [4,] 1.6094379 1.791759 1.945910 [5,] 1.7917595 1.945910 2.079442 

That yields a nice compact output. but it's possible that mapply would be useful in other situations. It is helpful to think of mapply as just another way to do the same operation that others on this page are using Vectorize for. mapply is more general because of the inability Vectorize to use "primitive" functions.

data.frame(mrow=c(row(mat)),   # straightens out the arguments            mcol=c(col(mat)),             m.f.res= mapply(function(r,c) log(r+c), row(mat), col(mat)  ) ) #   mrow mcol   m.f.res 1     1    1 0.6931472 2     2    1 1.0986123 3     3    1 1.3862944 4     4    1 1.6094379 5     5    1 1.7917595 6     1    2 1.0986123 7     2    2 1.3862944 8     3    2 1.6094379 9     4    2 1.7917595 10    5    2 1.9459101 11    1    3 1.3862944 12    2    3 1.6094379 13    3    3 1.7917595 14    4    3 1.9459101 15    5    3 2.0794415 

You probably didn't really mean to supply to the function what the row() and col() functions would have returned: This produces an array of 15 (somewhat redundant) 3 x 5 matrices:

> outer(row(mat), col(mat) , FUN=function(r,c) log(r+c) ) 
like image 106
IRTFM Avatar answered Sep 19 '22 00:09

IRTFM


The simplest approach is just to use an f() that can be applied directly to the elements of the matrix. For example, using the matrix m from @adamleerich's Answer

m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2) 

There is no reason to use apply() in the case of the as.character() example. Instead we can operate on the elements of m as if it were a vector (it really is one) and replace in-place:

> m[] <- as.character(m) > m      [,1] [,2] [,3] [,4] [1,] "1"  "3"  "5"  "7"  [2,] "2"  "4"  "6"  "8" 

The first part of that block is the key here. m[] forces the elements of m to be replaced by the output from as.character(), rather than overwriting m with a vector of characters.

So that is the general solution to applying a function to each element of a matrix.

If one really needs to use an f() that works on row and column indices then I'd write a f() using row() and col():

> m <- matrix(c(1,2,3,4,5,6,7,8), nrow = 2) > row(m)      [,1] [,2] [,3] [,4] [1,]    1    1    1    1 [2,]    2    2    2    2 > col(m)      [,1] [,2] [,3] [,4] [1,]    1    2    3    4 [2,]    1    2    3    4 > row(m) * col(m) ## `*`(row(m), col(m)) to see this is just f()      [,1] [,2] [,3] [,4] [1,]    1    2    3    4 [2,]    2    4    6    8 

or one that use outer() as other's have shown. If f() isn't vectorised, then I'd rethink my strategy as far as possible as there i) probably is a way to write a truly vectorised version, and ii) a function that isn't vectorised isn't going to scale very well.

like image 21
Gavin Simpson Avatar answered Sep 20 '22 00:09

Gavin Simpson