user2543622
user2543622

Reputation: 6806

R matrix get indices of two highest values

I have a matrix (df) as below. I want to get column and row names for 2 lowest values in this matrix. There are multiple cells with the lowest value and I would like to get indices of all cells with the lowest value. From those indices I can choose randomly 2 indices.

In case of matrix df2, as only 1 cell has the lowest value and only 1 cell has has the second lowest value, I should get indices of those cells (value=0 and value=1)

What is an efficient way to do this? I tried to sort the matrix but it doesn't do what i am looking for :(

df<-matrix(data=c(3,7,5,0,1,0,0,0,0,8,0,9), ncol=2)
rownames(df)<-c("a","b","c","d","e","f")

 df2<-matrix(data=c(3,7,5,6,1,2,4,3,0,8,2,9), ncol=2)
    rownames(df)<-c("a","b","c","d","e","f")

I tried something like this, but it is not efficient

minPoint<-which(df==min(df),arr.ind=T)

It will help me in case of the first matrix, but it wont return what i am expecting in case of the second matrix.

I would prefer a single code that works with both matrices

Upvotes: 2

Views: 505

Answers (2)

Joe
Joe

Reputation: 3991

Using only {base} and based on your original code:

minPoint<-which(df <= sort(df)[2], arr.ind = T)

Rather than just getting the min(), using sort() will return a sorted vector, preserving duplication of minima. Checking for all values less than or equal to the second element should meet your requirement. In the case of df the first two elements are both 0, while in df2 they are 0 and 1.

Output:

> which(df <= sort(df)[2], arr.ind = T)
  row col
d   4   1
f   6   1
a   1   2
b   2   2
c   3   2
e   5   2

> which(df2 <= sort(df2)[2], arr.ind = T)
       row col
[1,]   5   1
[2,]   3   2

Edit based on @Roland's comment: you can optimize for large matrices using partial sorting, eg:

minPoint<-which(df <= sort(df, partial = 2)[2], arr.ind = T)

Upvotes: 4

Colonel Beauvel
Colonel Beauvel

Reputation: 31181

You can try this:

 library(magrittr)

 func = function(mat)
 {
     sort(mat) %>%
     head(2) %>%
     lapply(function(u) which(mat==u, arr.ind=T)) %>%
     unique
 }

 #> func(df)
 #[[1]]
 #  row col
 #d   4   1
 #f   6   1
 #a   1   2
 #b   2   2
 #c   3   2
 #e   5   2

 #> func(df2)
 #[[1]]
 #     row col
 #[1,]   3   2
 #
 #[[2]]
 #     row col
 #[1,]   5   1

So that if you have one element in your list, you have a repeated minimum value.

Upvotes: 2

Related Questions