How to apply function over each matrix element'

2019-01-30 10:50发布

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?

5条回答
劫难
2楼-- · 2019-01-30 11:08

This does not answer your question exactly, but I found it while trying to figure out a similar question so I'll show you something.

Say you have a function which you want to apply to each element of a matrix which requires just one part.

mydouble <- function(x) {
   return(x+x)
}

And say you have a matrix X,

> x=c(1,-2,-3,4)
> X=matrix(x,2,2)
> X
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4

then you do this:

res=mydouble(X)

Then it will do an element-wise double of each value.

However, if you do logic in the function like below you will get a warning that it is not parameterized and not behave as you expect.

myabs <- function(x) {
  if (x<0) {
      return (-x)
  } else {
      return (x)
  }
}

> myabs(X)
     [,1] [,2]
[1,]    1   -3
[2,]   -2    4
Warning message:
In if (x < 0) { :
  the condition has length > 1 and only the first element will be used

But if you use the apply() function you can use it.

For example:

> apply(X,c(1,2),myabs)
     [,1] [,2]
[1,]    1    3
[2,]    2    4

So that is great, right? Well, it breaks down if you have a function with two or more parms. Say for example you have this:

mymath <- function(x,y) {
    if(x<0) {
        return(-x*y)
    } else {
        return(x*y)
    }
}

In this case, you use the apply() function. However, it will lose the matrix but the results are calculated correctly. They can be reformed if you are so inclined.

> mapply(mymath,X,X)
[1]  1 -4 -9 16
> mapply(mymath,X,2)
[1] 2 4 6 8
> matrix(mapply(mymath,X,2),c(2,2))
     [,1] [,2]
[1,]    2    6
[2,]    4    8
查看更多
Fickle 薄情
3楼-- · 2019-01-30 11:19

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) )
查看更多
狗以群分
4楼-- · 2019-01-30 11:20

You didn't tell us what kind of function you want to apply to each element, but I think that the only reason the examples in the other answers work is because the functions are already vectorized. If you really want to apply a function to each element, outer will not give you anything special that the function didn't already give you. You'll notice that the answers didn't even pass a matrix to outer!

How about following @Chase's comment and use apply.

For example, I have the matrix

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

If I want to turn it into a character matrix, element by element (just as an example) I could do this

apply(m, c(1,2), as.character)

Of course, as.character is already vectorized, but my special function my.special.function isn't. It only takes one argument, an element. There is no straighforward way to get outer to work with it. But, this works

apply(m, c(1,2), my.special.function)
查看更多
\"骚年 ilove
5楼-- · 2019-01-30 11:22

You may be thinking of outer:

rows <- 1:10
cols <- 1:10

outer(rows,cols,"+")

      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    2    3    4    5    6    7    8    9   10    11
 [2,]    3    4    5    6    7    8    9   10   11    12
 [3,]    4    5    6    7    8    9   10   11   12    13
 [4,]    5    6    7    8    9   10   11   12   13    14
 [5,]    6    7    8    9   10   11   12   13   14    15
 [6,]    7    8    9   10   11   12   13   14   15    16
 [7,]    8    9   10   11   12   13   14   15   16    17
 [8,]    9   10   11   12   13   14   15   16   17    18
 [9,]   10   11   12   13   14   15   16   17   18    19
[10,]   11   12   13   14   15   16   17   18   19    20

That's clearly a fairly trivial example function, but you can supply your own custom one as well. See ?outer.

Edit

Contrary to the comment below, you can also use outer with non-vectorized functions by....vectorizing them!

m <- matrix(1:16,4,4)

#A non-vectorized function 
myFun <- function(x,y,M){
     M[x,y] + (x*y)
}

#Oh noes! 
outer(1:4,1:4,myFun,m)
Error in dim(robj) <- c(dX, dY) : 
  dims [product 16] do not match the length of object [256]

#Oh ho! Vectorize()! 
myVecFun <- Vectorize(myFun,vectorize.args = c('x','y'))

#Voila! 
outer(1:4,1:4,myVecFun,m)
     [,1] [,2] [,3] [,4]
[1,]    2    7   12   17
[2,]    4   10   16   22
[3,]    6   13   20   27
[4,]    8   16   24   32
查看更多
Luminary・发光体
6楼-- · 2019-01-30 11:25

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.

查看更多
登录 后发表回答