Tou Mou
Tou Mou

Reputation: 1274

How to find n-lowest values in a matrix with their respective rows/columns indices

Good afternoon!

Assume we have the following matrix m. I'm wanting to select the k-minima values with their associated rows/columns indices. For this I developed the following code :

m=matrix(6:1,nrow=3)

print(" The matrix m is : ")
print(m)

k=5

paste("The" , k , "lowest values are : ")
print(order(m,decreasing = TRUE)[1:k] )
paste( "Solution 1 : we selected" , k , "minimum values with their respective positions " )

# Solution 1 :

t(sapply(1:length(order(m,decreasing = TRUE)[1:k]), function(i) {
h=order(m,decreasing = TRUE)[1:k];
which(m==h[i],arr.ind = TRUE) } ))

# solution 2 :

paste( "Another possibility : Solution 2 - We selected" , k , "minimum values with their respective positions " ) 
k_lowest_values=order(m,decreasing = TRUE)[1:5]
t(which(Reduce("|", lapply(k_lowest_values, function(x) m == x)), arr.ind = T))

This gives as expected :

[1] " The matrix m is : "
     [,1] [,2]
[1,]    6    3
[2,]    5    2
[3,]    4    1
[1] "The 5 lowest values are : "
[1] 1 2 3 4 5
[1] "Solution 1 : we selected 5 minimum values with their respective positions "
     [,1] [,2]
[1,]    3    2
[2,]    2    2
[3,]    1    2
[4,]    3    1
[5,]    2    1
[1] "Another possibility : Solution 2 - We selected 5 minimum values with their respective positions "
    [,1] [,2] [,3] [,4] [,5]
row    2    3    1    2    3
col    1    1    2    2    2

However , the same code doesn't work as it should be with another matrix m such :

m=structure(c(1, 0.996805114543033, 0.987281571590291, 0.971610767189123, 
0.950088633802627, 0.996805114543033, 0.993620436379149, 0.984127320055285, 
0.996805114543033, 1, 0.996805114543033, 0.987281571590291, 0.971610767189123, 
0.993620436379149, 0.996805114543033, 0.993620436379149, 0.987281571590291, 
0.996805114543033, 1, 0.996805114543033, 0.987281571590291, 0.984127320055285, 
0.993620436379149, 0.996805114543033, 0.971610767189123, 0.987281571590291, 
0.996805114543033, 1, 0.996805114543033, 0.968506582079198, 0.984127320055285, 
0.993620436379149, 0.950088633802627, 0.971610767189123, 0.987281571590291, 
0.996805114543033, 1, 0.947053209443661, 0.968506582079198, 0.984127320055285, 
0.996805114543033, 0.993620436379149, 0.984127320055285, 0.968506582079198, 
0.947053209443661, 1, 0.996805114543033, 0.987281571590291, 0.993620436379149, 
0.996805114543033, 0.993620436379149, 0.984127320055285, 0.968506582079198, 
0.996805114543033, 1, 0.996805114543033, 0.984127320055285, 0.993620436379149, 
0.996805114543033, 0.993620436379149, 0.984127320055285, 0.987281571590291, 
0.996805114543033, 1), .Dim = c(8L, 8L))

This output the following :

[1] " The matrix m is : "
          [,1]      [,2]      [,3]      [,4]      [,5]      [,6]      [,7]
[1,] 1.0000000 0.9968051 0.9872816 0.9716108 0.9500886 0.9968051 0.9936204
[2,] 0.9968051 1.0000000 0.9968051 0.9872816 0.9716108 0.9936204 0.9968051
[3,] 0.9872816 0.9968051 1.0000000 0.9968051 0.9872816 0.9841273 0.9936204
[4,] 0.9716108 0.9872816 0.9968051 1.0000000 0.9968051 0.9685066 0.9841273
[5,] 0.9500886 0.9716108 0.9872816 0.9968051 1.0000000 0.9470532 0.9685066
[6,] 0.9968051 0.9936204 0.9841273 0.9685066 0.9470532 1.0000000 0.9968051
[7,] 0.9936204 0.9968051 0.9936204 0.9841273 0.9685066 0.9968051 1.0000000
[8,] 0.9841273 0.9936204 0.9968051 0.9936204 0.9841273 0.9872816 0.9968051
          [,8]
[1,] 0.9841273
[2,] 0.9936204
[3,] 0.9968051
[4,] 0.9936204
[5,] 0.9841273
[6,] 0.9872816
[7,] 0.9968051
[8,] 1.0000000
[1] "Solution 1 : we selected 5 minimum values with their respective positions "
     [,1]       [,2]      [,3]      [,4]      [,5]     
[1,] Integer,16 Integer,0 Integer,0 Integer,0 Integer,0
[1] "Another possibility : Solution 2 - We selected 5 minimum values with their respective positions "
    [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
row    1    2    3    4    5    6    7    8
col    1    2    3    4    5    6    7    8

Problem: The second case doesn't give the expected output ( I should retrieve the k=5 lowest values with their rows/columns indices ). I had gotten the positions of the highest values instead !

Upvotes: 2

Views: 294

Answers (3)

akrun
akrun

Reputation: 886938

For the second case, it is just that it is a list output

lst1 <- sapply(order(m, decreasing = FALSE)[1:k], function(i) {
     which(m==m[i],arr.ind = TRUE) } )

out <- do.call(rbind, lst1)
df  <- t(out[!duplicated(out),])
print(df) 

# output 

 #     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
 #row    6    5    5    1    6    7    4    5
 #col    5    6    1    5    4    5    6    7

sapply(lst1, nrow)
#[1] 8 0 0 0 0

where only the first list element have greater than 0 rows. Probably ,we need to rbind them before doing the transpose


Or if we wrap do.call(rbind directly on the OP's matrix output with list elements

out=do.call(rbind, t(sapply(order(m, decreasing = FALSE)[1:k], function(i) {
     which(m==m[i],arr.ind = TRUE) } )))

df  <- out[!duplicated(out),]

print(df)
     row col
[1,]   6   5
[2,]   5   6
[3,]   5   1
[4,]   1   5
[5,]   6   4
[6,]   7   5
[7,]   4   6
[8,]   5   7

With sapply, we may need to always consider that there is a chance of simplifyication as by default it is TRUE in its usage (?sapply)

sapply(X, FUN, ..., simplify = TRUE, USE.NAMES = TRUE)

In the first example, if we use simplify = FALSE, will get a list output with each list element having only a single row

sapply(1:length(order(m,decreasing = FALSE)[1:k]), function(i) {
   h=order(m,decreasing = TRUE)[1:k]
   which(m==h[i],arr.ind = TRUE) }, simplify = FALSE)
#[[1]]
#     row col
#[1,]   3   2

#[[2]]
#     row col
#[1,]   2   2

#[[3]]
#     row col
#[1,]   1   2

#[[4]]
#     row col
#[1,]   3   1

#[[5]]
#     row col
#[1,]   2   1

which with simplify = TRUE results in

#     [,1] [,2] [,3] [,4] [,5]
#[1,]    3    2    1    3    2
#[2,]    2    2    2    1    1

Upvotes: 1

kangaroo_cliff
kangaroo_cliff

Reputation: 6222

There are a couple of mistakes in the second solution; it should be decreasing = FALSE and use m == m[x] in lapply function.

k_lowest_values = order(m, decreasing = FALSE)[1:5]
inds <- which(Reduce("|", 
         lapply(k_lowest_values, function(x) m == m[x])), 
         arr.ind = T)
inds[order(m[inds]), ] 
row col
[1,]   6   5
[2,]   5   6
[3,]   5   1
[4,]   1   5
[5,]   6   4
[6,]   7   5
[7,]   4   6
[8,]   5   7

Upvotes: 2

ThomasIsCoding
ThomasIsCoding

Reputation: 101034

You can try the code below with which + %in%

inds <- which(`dim<-`(m %in% head(sort(c(m)), k), dim(m)), arr.ind = TRUE)
inds[order(m[inds]), ]

which gives

     row col
[1,]   6   5
[2,]   5   6
[3,]   5   1
[4,]   1   5
[5,]   6   4
[6,]   7   5
[7,]   4   6
[8,]   5   7

Upvotes: 2

Related Questions