4

I am trying to apply a function to each row or column of a matrix, but I need to pass a different argument value for each row.

I thought I was familiar with lapply, mapply etc... But probably not enough.

As a simple example :

> a<-matrix(1:100,ncol=10);
> a
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1   11   21   31   41   51   61   71   81    91
 [2,]    2   12   22   32   42   52   62   72   82    92
 [3,]    3   13   23   33   43   53   63   73   83    93
 [4,]    4   14   24   34   44   54   64   74   84    94
 [5,]    5   15   25   35   45   55   65   75   85    95
 [6,]    6   16   26   36   46   56   66   76   86    96
 [7,]    7   17   27   37   47   57   67   77   87    97
 [8,]    8   18   28   38   48   58   68   78   88    98
 [9,]    9   19   29   39   49   59   69   79   89    99
[10,]   10   20   30   40   50   60   70   80   90   100

Let's say I want to apply a function to each row, I would do :

apply(a, 1, myFunction);

However my function takes an argument, so :

apply(a, 1, myFunction, myArgument);

But if I want my argument to take a different value for each row, I cannot find the right way to do it. If I define a 'myArgument' with multiple values, the whole vector will obviously be passed to each call of 'myFunction'.

I think that I would need a kind of hybrid between apply and the multivariate mapply. Does it make sense ?

One 'dirty' way to achieve my goal is to split the matrix by rows (or columns), use mapply on the resulting list and merge the result back to a matrix :

do.call(rbind, Map(myFunction, split(a,row(a)), as.list(myArgument)));

I had a look at sweep, aggregate, all the *apply variations but I wouldn't find the perfect match to my need. Did I miss it ?

Thank you for your help.

RFen
  • 141
  • 1
  • 7

3 Answers3

1

You can use sweep to do that.

a <- matrix(rnorm(100),10)
rmeans <- rowMeans(a)
a_new <- sweep(a,1,rmeans,`-`)
rowMeans(a_new)
Rentrop
  • 20,979
  • 10
  • 72
  • 100
  • Looks like I was not very woken up when I tried it. Your suggestion to have a second look at it made my day. Thank you, and sorry about the 'naive' question. I hope this can help someone else later. – RFen Nov 07 '14 at 16:36
  • After further testing I remember why sweep didn't make the job. Actually, unlike 'apply' the function 'sweep' will only call the function FUN once, passing it the original matrix and a matrix with values built from the arguments 'STAT' and 'MARGIN'. It assumes that the function FUN will perform the cell-by-cell operation on the matrices. What I would like to achieve is a call to 'myFunction' for each row (or column) of the original matrix. To be clear, an appropriate example consists in applying a smoothing window with a different size on each row. – RFen Nov 07 '14 at 19:54
0

I don't think there are any great answers, but you can somewhat simplify your solution by using mapply, which handles the "rbind" part for you, assuming your function always returns the same sizes vector (also, Map is really just mapply):

a <- matrix(1:80,ncol=8)
myFun <- function(x, y) (x - mean(x)) * y
myArg <- 1:nrow(a)

t(mapply(myFun, split(a, row(a)), myArg))
BrodieG
  • 51,669
  • 9
  • 93
  • 146
0

I know the topic is quiet old but I had the same issue and I solved it that way:

# Original matrix
a <- matrix(runif(n=100), ncol=5)
# Different value for each row
v <- runif(n=nrow(a))
# Result matrix -> Add a column with the row number
o <- cbind(1:nrow(a), a)
fun <- function(x, v) {
  idx <- 2:length(x)
  i <- x[1]
  r <- x[idx] / v[i]
  return(r)
}
o <- t(apply(o, 1, fun, v=v)

By adding a column with the row number to the left of the original matrix, the index of the needed value from the argument vector can be received from the first column of the data matrix.

FlorianSchunke
  • 571
  • 5
  • 15