Tuesday, May 05, 2009

Matlab: Vector indexing;

Problem:

Suppose I have a vector:

x = [1 1 1 1 2 2 2 8 8 8 8 8 8 0 0 9 9 7 5 5 2 2 2 2 2]

I want to obtain:

b = [1 2 3 4 1 2 3 1 2 3 4 5 6 1 2 1 2 1 1 2 1 2 3 4 5]

Basically each block of integer is replaced by continuous index of the count.


Solution:

Some initial processing to detect block size and locations:

y = find([true;diff(x(:)) ~= 0;true]);
c = diff(y);
v = x(y(1:end-1));

method 1:

b = cell2mat(arrayfun(@(z)(1:z)',c,'uniformout',false));


method 2:

b = ones(size(x));
b(y(2:end-1)) = 1-c(1:end-1);
b = cumsum(b);


method 3:

g = (1:max(c))'*ones(1,numel(c));
b = g(bsxfun(@ge,c,1:max(c))');


Speed comparison:


% The testing data;
x = cell2mat(arrayfun(@(y,z)(y+zeros(1,z)), ...
randi(100,10000,1),randi(2000,10000,1),'uniformout',false));

% Method 1; 0.25 sec
% Method 2; 0.17 sec
% Method 3; 1.05 sec




Method 2 is better.

No comments: