Boog
Boog

Reputation: 223

Get index of first TRUE element of each column of a matrix, but index must be > to n

So I've got a matrix of logical vectors like this :

r=10
c=10
m1 <- matrix(runif(r*c)>0.5, r, c)
> m1
       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE
 [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE
 [3,]  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE
 [4,] FALSE  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE
 [5,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE
 [6,]  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE
 [7,] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE
 [8,] FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE
 [9,]  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[10,]  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE

And a vector like this :

v1 <- round(runif(10,1,10))
> v1
 [1] 6 5 7 5 5 4 4 2 1 10

How can I get the 10 indexes > v1 of first TRUE value of each column of m1 ?

So for this example, I want to get this vector :

[1] 9 7 8 NA 6 5 7 3 3 NA

Thanks for your help

Upvotes: 3

Views: 495

Answers (3)

thelatemail
thelatemail

Reputation: 93813

Get the row/column indices for each TRUE using which. The code inside which uses the combination of sweep to calculate if it is a higher row count, and the original TRUE/FALSE mat object. Then, these indices are subset to the first match in each column using match, which will return NA when there is no value in the column.

# using Maurits' data

matsel <- which(sweep(row(mat), 2, v1, FUN=`>`) & mat, arr.ind=TRUE)
matsel[,"row"][match(seq_len(ncol(mat)), matsel[,"col"])]
#[1] NA NA  3  5  8  2  9  8  2  2

This should scale very nicely too, as it avoids any looping over columns or rows. The only thing to watch would be matsel becoming big and hogging your memory as it stores every TRUE value's location.

Upvotes: 0

JRR
JRR

Reputation: 3233

Is it what you want?

r=10
c=10
set.seed(42)
m1 <- matrix(runif(r*c)>0.5, r, c)
set.seed(314)
v1 <- round(runif(10,1,10))

out = sapply(1:10, function(i) 
{ 
  v = v1[i]
  r = m1[v:10,i]
  j = which(r)[1] + v - 1
})

out

My input (reproducible with the seed)

> m1
       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
 [1,]  TRUE FALSE  TRUE  TRUE FALSE FALSE  TRUE FALSE  TRUE  TRUE
 [2,]  TRUE  TRUE FALSE  TRUE FALSE FALSE  TRUE FALSE FALSE FALSE
 [3,] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE
 [4,]  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE
 [5,]  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE  TRUE
 [6,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
 [7,]  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE
 [8,] FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE FALSE  TRUE
 [9,]  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE
[10,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE

> v1
 [1] 2 3 8 3 3 4 3 4 6 8

My output

> out
 [1]  4  6 10  4  4  6  4  6 NA  9

Upvotes: 1

Maurits Evers
Maurits Evers

Reputation: 50678

One option is to use mapply. Here is a reproducible example using a fixed random seed.

set.seed(2017)
r <- 10
c <- 10
m1 <- matrix(runif(r*c) > 0.5, r, c)    
v1 <- round(runif(10,1,10))   

mapply(function(x, y) { idx <- which(x == TRUE); idx[idx > y][1]}, as.data.frame(m1), v1, USE.NAMES = F)
#[1] NA NA  3  5  8  2  9  8  2  2

Explanation: For every column of m1 we extract the indices of TRUE entries in idx; we then return the first index of idx that is greater than the column number-matched entry in v1; if no such number exists, it automatically returns NA.

Note: In order for mapply to simultaneously loop over columns of m1 and entries of v1, we need to convert m1 into a data.frame.


Sample data

With the above fixed random seed, the sample is data is

m1
#       [,1]  [,2]  [,3]  [,4]  [,5]  [,6]  [,7]  [,8]  [,9] [,10]
# [1,]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE
# [2,]  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE  TRUE  TRUE  TRUE
# [3,] FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE  TRUE  TRUE
# [4,] FALSE FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE
# [5,]  TRUE FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE
# [6,]  TRUE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE
# [7,] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
# [8,] FALSE  TRUE FALSE FALSE  TRUE  TRUE FALSE  TRUE FALSE FALSE
# [9,] FALSE  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE  TRUE
#[10,] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE

v1
#[1]  9 10  2  3  6  1  5  6  1  1

Upvotes: 1

Related Questions