7

I'm trying to avoid using loops by using apply to apply a user-defined function to a matrix. The problem I have is that there are additional parameters that my function uses and they differ for each column of the matrix. Below is a toy example.

Say I have the following function:

foo <- function(x, a, b, c) return( (a*x + b)^c )

and I want to apply it to a matrix bar using different values of a, b, and c for each column.

bar <- matrix(1:15, ncol = 3)
a <- 4:6
b <- 3:1
c <- 1:3

In this case, for the first column of bar, then a=4, b=3, and c=1. I tried this,

apply(bar, 2, foo, a=a, b=b, c=c)

but this is clearly incorrect, as each column uses all parameters sequentially before wrapping back to the first parameter again. Any suggestions?

svick
  • 236,525
  • 50
  • 385
  • 514
Dan
  • 11,370
  • 4
  • 43
  • 68

5 Answers5

12

We could split the 'bar' by 'column' (col(bar)) and with mapply we can apply 'foo' for the corresponding 'a', 'b', 'c' values to each column of 'bar'

mapply(foo, split(bar, col(bar)), a, b, c)

Or without using apply

ind <- col(bar)
(a[ind]*bar +b[ind])^c[ind]
akrun
  • 874,273
  • 37
  • 540
  • 662
9

I don't see why you bother with a function at all:

> a <- matrix(4:6,nrow = 5,ncol = 3,byrow = TRUE)
> b <- matrix(3:1,nrow = 5,ncol = 3,byrow = TRUE)
> c <- matrix(1:3,nrow = 5,ncol = 3,byrow = TRUE)
> (a*bar + b)^c
     [,1] [,2]   [,3]
[1,]    7 1024 300763
[2,]   11 1369 389017
[3,]   15 1764 493039
[4,]   19 2209 614125
[5,]   23 2704 753571
joran
  • 169,992
  • 32
  • 429
  • 468
3

I guess that you can just transpose your matrix and use vectorization:

t(foo(t(bar),a,b,c))

This should work for every vectorized foo.

nicola
  • 24,005
  • 3
  • 35
  • 56
2

You could put your parameters into the vector:

newbar <- rbind(a,b,c,bar)
newfoo <- function(z){x <- z[-(1:3)]; (z[1]*x+z[2])^z[3]}
apply(newbar,2,newfoo)

which gives

[,1] [,2]   [,3]
    7 1024 300763
   11 1369 389017
   15 1764 493039
   19 2209 614125
   23 2704 753571
Frank
  • 66,179
  • 8
  • 96
  • 180
2

You can use sweep; usually this is for subtracting the mean from each column, but you can pass in an index instead to get parallel indexing across a,b and c:

> sweep(bar, 2, seq_along(a), function(x,i) foo(x, a[i], b[i], c[i]), FALSE)
     [,1] [,2]   [,3]
[1,]    7 1024 300763
[2,]   11 1369 389017
[3,]   15 1764 493039
[4,]   19 2209 614125
[5,]   23 2704 753571
Neal Fultz
  • 9,282
  • 1
  • 39
  • 60