3

I have a matrix of unsorted numbers and I want to keep the n largest (not necessarily unique) values per column and set the rest to zero.

I figured out how to do it with a loop:

a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4]
k = 2

for  n = 1:4
    [y, ind] = sort(a(:,n), 'descend');
    a(ind(k+1:end),n) = 0;
end
a

which gives me: a =

 4     8    12     5
 9     2     6    18
11     3     9     7
 8     9    12     4

k =

 2

a =

 0     8    12     0
 9     0     0    18
11     0     0     7
 0     9    12     0

However when I try to eliminate the loop, I can't seem to get the indexing right, because this:

a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4]
k = 2

[y, ind] = sort(a, 'descend');
b = ind(k+1:end,:)
a(b) = 0

which gives me this: (which is not what I wanted to do) a =

 4     8    12     5
 9     2     6    18
11     3     9     7
 8     9    12     4

k =

 2

b =

 4     3     3     1
 1     2     2     4

a =

 0     8    12     5
 0     2     6    18
 0     3     9     7
 0     9    12     4

Am I indexing this wrong? Do I have to use the loop?

I referenced this question to get started but it wasn't exactly what I was trying to do: How to find n largest elements in an array and make the other elements zero in matlab?

Community
  • 1
  • 1
ErinGoBragh
  • 336
  • 4
  • 20

2 Answers2

2

You're very close. ind in the sort function gives you the row locations for each column where that particular value would appear in the sorted output. You need to do some additional work if you want to index into the matrix properly and eliminate the entries. You know that for each column of I, that tells you that we need to eliminate those entries from that particular column. Therefore, what I would do is generate column-major linear indices using each column of I to be the rows we need to eliminate.

Try doing this:

a = [4 8 12 5; 9 2 6 18; 11 3 9 7; 8 9 12 4];
k = 2;
[y, ind] = sort(a, 'descend');

%// Change here
b = sub2ind(size(a), ind(k+1:end,:), repmat(1:size(a,2), size(a,1)-k, 1));
a(b) = 0;

We use sub2ind to help us generate our column major indices where the rows are denoted by the values in ind after the kth element and the columns we need are for each column in this matrix. There are size(a,1)-k rows remaining after you truncate out the k values after sorting, and so we generate column values that go from 1 up to as many columns as we have in a and as many rows as there are remaining.

We get this output:

>> a

a =

     0     8    12     0
     9     0     0    18
    11     0     0     7
     0     9    12     0
rayryeng
  • 102,964
  • 22
  • 184
  • 193
2

Here's one using bsxfun -

%// Get descending sorting indices per column
[~, ind] = sort(a,1, 'descend')

%// Get linear indices that are to be set to zeros and set those in a to 0s
rem_ind = bsxfun(@plus,ind(n+1:end,:),[0:size(a,2)-1]*size(a,1))
a(rem_ind) = 0

Sample run -

a =
     4     8    12     5
     9     2     6    18
    11     3     9     7
     8     9    12     4
n =
     2
ind =
     3     4     1     2
     2     1     4     3
     4     3     3     1
     1     2     2     4
rem_ind =
     4     7    11    13
     1     6    10    16
a =
     0     8    12     0
     9     0     0    18
    11     0     0     7
     0     9    12     0
Divakar
  • 218,885
  • 19
  • 262
  • 358