Reputation: 6806
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
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
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