Extracting values from a matrix using row indices col
Let's say I have two matrices:
> a
[,1] [,2] [,3] [,4] [,5] [,6] [,7]
[1,] 6 10 5 7 2 2 6
[2,] 10 6 7 7 4 3 12
[3,] 11 10 2 10 6 11 9
and
> b
[,1] [,2] [,3]
[1,] 4 1 4
[2,] 3 6 3
[3,] 2 5 2
The number of lines in a
and is b
identical. I'm looking for a vectorized way of extracting elements from a
, denoted by column numbers to b
, line by line. The result c
should look like this:
> c
[,1] [,2] [,3]
[1,] 7 6 7
[2,] 7 3 7
[3,] 10 6 10
a[,b[1,]]
or a[,b[2,]]
or is it only a[,b[3,]]
possible to get correct results for rows 1, 2 and 3. Can this be done with a simple matrix function? Is it required apply
?
I tried to adapt a solution to a similar problem into index values from a matrix using row pointers, col , but didn't understand how cbind was used here to retrieve matrix elements.
source to share
You may try
t(sapply(seq_len(nrow(a)), function(i) a[i, b[i, ]]))
# [,1] [,2] [,3]
# [1,] 7 6 7
# [2,] 7 3 7
# [3,] 10 6 10
And you can see a slight speed improvement from the above solution sapply
withvapply
s <- seq_len(nrow(a))
t(vapply(s, function(i) a[i, b[i, ]], numeric(ncol(b))))
# [,1] [,2] [,3]
# [1,] 7 6 7
# [2,] 7 3 7
# [3,] 10 6 10
Or loop solution for
m <- matrix(, nrow(b), ncol(b))
for(i in seq_len(nrow(a))) { m[i, ] <- a[i, b[i, ]] }
m
# [,1] [,2] [,3]
# [1,] 7 6 7
# [2,] 7 3 7
# [3,] 10 6 10
source to share
Here is the version cbind
t(`dim<-`(a[cbind(rep(1:nrow(a), each=ncol(b)), c(t(b)))], dim(b)))
# [,1] [,2] [,3]
#[1,] 7 6 7
#[2,] 7 3 7
#[3,] 10 6 10
Or as suggested by @thelatemail
matrix(a[cbind(c(row(b)),c(b))],nrow=nrow(a))
# [,1] [,2] [,3]
#[1,] 7 6 7
#[2,] 7 3 7
#[3,] 10 6 10
Benchmarks
set.seed(24)
a1 <- matrix(sample(1:10, 2e5*7, replace=TRUE), ncol=7)
set.seed(28)
b1 <- matrix(sample(1:7,2e5*3, replace=TRUE), ncol=3)
f1 <- function() {s <- seq_len(nrow(a1))
t(vapply(s, function(i) a1[i, b1[i,]],numeric(ncol(b1))))
}
f2 <- function() {matrix(a1[cbind(c(row(b1)),c(b1))], nrow=nrow(a1)) }
f3 <- function(){t(`dim<-`(a1[cbind(rep(1:nrow(a1),
each=ncol(b1)), c(t(b1)))], dim(b1)))}
library(microbenchmark)
microbenchmark(f1(), f2(), f3(), unit='relative', times=10L)
#Unit: relative
# expr min lq mean median uq max neval cld
#f1() 16.636045 16.603856 15.319595 15.799335 13.869147 14.629315 10 b
#f2() 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000 10 a
#f3() 1.310433 1.306228 1.258715 1.278504 1.237299 1.236448 10 a
source to share