user1384636
user1384636

Reputation: 501

Fast index mapping in matlab

I have the following problem: Given a matrix A

A = [ 1 2 2 3 3 ;
      2 2 2 7 9 ]

where the sequence of unique numbers within the matrix is not continuous. In this example

unique(A) = [ 1 2 3 7 9 ]. % [ 4 5 6 8 ] are missing

I want to compute the same matrix, but using instead a continuous sequence, such that

unique(A_new) = [ 1 2 3 4 5 ];

I came up with the following solution

T = [ unique(A), [ 1:numel(unique(A)) ]' ];

A_new = zeros(size(A));

for i = 1:size(T,1)
  A_new( A == T(i,1) ) = T(i,2);
end

This is incredibly slow: the size of the matrix A I have to work with is 200x400x300 and the the number of unique elements within this matrix is 33406.

Any idea on how to speed up the procedure?

Upvotes: 1

Views: 71

Answers (2)

Luis Mendo
Luis Mendo

Reputation: 112679

Approach 1 (not recommended)

This should be pretty fast, but it uses more memory:

[~, A_new] = max(bsxfun(@eq, A(:).', unique(A(:))));
A_new = reshape(A_new, size(A));

How does this work?

First A is linearized into a vector (A(:)). Also, a vector containing the unique values of A is computed (unique(A(:))). From those two vectors a matrix is generated (with bsxfun) in which each entry of A is compared to each of the unique values. That way, for each entry of A we know if it equals the first unique value, or the second unique value, etc. For the A given in your question, that matrix is

 1     0     0     0     0     0     0     0     0     0
 0     1     1     1     1     1     0     0     0     0
 0     0     0     0     0     0     1     0     1     0
 0     0     0     0     0     0     0     1     0     0
 0     0     0     0     0     0     0     0     0     1

So for example, the value 1 in entry (2,3) indicates that the third value of A(:) equals the second unique value of A (namely 2). The 1 in the lower-right entry (5,10) indicates that the tenth value of A(:) is the fifth unique value of A (which is 9).

Now the second output of max is used to extract the row position of the 1 value in each columnn (i.e. to obtain the numbers indicating "second", "fifth" etc in the above example)). These are the desired results. It only remains to reshape them into the shape of A.

Approach 2 (recommended)

The third output of unique does what you want:

[~, ~, labels] = unique(A);
A_new = reshape(labels, size(A));

Upvotes: 1

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272507

If I understand correctly, in your example you want:

A_new = [ 1 2 2 3 3 ;
          2 2 2 4 5 ]

So just compute a lookup table (lookup) such that you can then do:

A_new = lookup(A);

So in your case, lookup would be:

[ 1 2 3 0 0 0 4 0 5 ]

I'll leave the process for generating that as an exercise for the reader.

Upvotes: 1

Related Questions