简体   繁体   中英

Applying a function to multiple arrays/matrices in R

Given two arrays:

a = array(1:3)
b = array(3:1)

I want to apply a function that sequentially compares the value of each element of A to each element of B, and return the result.

Something like:

compare = function(xa, xb) { if (xa < xb) { 1 } else { 0 } }

...where xa would be the an element from the a array, and xb would be an element from the b array.

Is there a derivative of apply I can use to accomplish this?

Rather than mapply I would use R internal recycling rules for vectorised functions. If a and b are the same length you need only do this:

as.integer( a < b )
#[1] 1 0 0

as.integer is just there to coerce to 1 or 0 , in reality TRUE and FALSE will behave as 1 and 0 in any subsequent multiplicative operations.

example

set.seed(1); a <- sample(10)
#[1]  3  4  5  7  2  8  9  6 10  1

set.seed(2); b <- sample(10)
#[1]  2  7  5 10  6  8  1  3  4  9

a < b
#[1] FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE

a * ( a < b )
#[1] 0 4 0 7 2 0 0 0 0 1

a[ a < b ]
#[1] 4 7 2 1

Some people may be surprised, but < is a function. It calls an underlying C function called do_relop in the files /src/main/relop.c (all the logical comparators do - they just use a different switch for the type of comparison) which handles vector recycling. You can write < like this:

`<`( a , b )
#[1] FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE

Benchmarking

Using the primitive < operator is over 100 times quicker (on a pair of 1e6 length vectors) than using ifelse which is also a vectorized function:

set.seed(1); a <- sample(10,1e6,repl=T)
set.seed(2); b <- sample(10,1e6,repl=T)
require( microbenchmark)
bm <- microbenchmark( comparealt(a,b) , `<`(a,b) , times = 25L )
print( bm , digits = 3 , unit = "relative" , order = "median" )
#Unit: relative
#             expr min  lq median  uq  max neval
#            a < b   1   1      1   1  1.0    25
# comparealt(a, b) 131 126    122 105 48.3    25

I originally read "the value of each element of A to each element of B" meaning an outer join and suggested:

compare <- function(x, y){ outer(x, y, FUN="<")+0 }

which for example gives

> compare(array(1:6), array(5:3))
     [,1] [,2] [,3]
[1,]    1    1    1
[2,]    1    1    1
[3,]    1    1    0
[4,]    1    0    0
[5,]    0    0    0
[6,]    0    0    0

For comparing matrices of the same dimension elementwise you could use something like

comparemat <- function(mat1, mat2){ (mat1 < mat2)+0 }

or

comparealt <- function(mat1, mat2){ ifelse(mat1 < mat2, 1, 0) }

where either of

comparemat(matrix(1:12,nrow=4), matrix(12:1,nrow=4)) 
comparealt(matrix(1:12,nrow=4), matrix(12:1,nrow=4)) 

give

     [,1] [,2] [,3]
[1,]    1    1    0
[2,]    1    1    0
[3,]    1    0    0
[4,]    1    0    0

I stumbled upon 'mapply', which seems to do the trick:

> gg = function(x,y){ if(x < y) { 1 } else { 0 }}
> gg(1,2)
[1] 1
> gg(2,1)
[1] 0
> mapply(gg, 1:4, 4:1)
[1] 1 1 0 0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM