Wolpertinger
Wolpertinger

Reputation: 1301

Array block splitting in MATLAB

Say we have a vector containing zeros interspersed blocks of ones of varying length, such as:

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

I would like to transform this into a 2D array as follows. Each row contains zeros only and one of the blocks. I.e. the number of rows of the 2D array would be the number of blocks at the end. The above array would transform to:

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

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

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

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

I.e the first block ends up in the first row, the second block in the second row etc.

Question

This exercise is rather trivial using loops. My question is if there is a neat way using MATLAB matrix operations, MATLAB functions and array indexing to do this?

Upvotes: 0

Views: 46

Answers (2)

Dohyun
Dohyun

Reputation: 642

I tried this one

N = 100;              % set array size
A = randi(2,N,1)-1;   % generate random array filled with 0 and 1

d = diff([0;A]);      % if  1 : starting point of block
                      % if -1 : end      point of block
ir = find(A);         % Find A==1 which will be row index
ic = cumsum(d(ir)>0); % Set col index

% assemble array
    % if you want output as full array
    A_new = accumarray([ir,ic],ones(size(ir)),[length(A),ic(end)]); 

    % if you want output as sparse array
    A_new = sparse(ir,ic,ones(size(ir)),length(A),ic(end),length(ir));

% display routine
figure;spy(A,'r');hold on;spy([zeros(size(A)),A_new]);

Turns out it is faster than @Suever 's solution (Compared tic toc time with size 10000, 1000 trial). Also, if you use sparse instead of accumarray, then it is much faster

Suever_time = 7~8 sec
Accumarray  = 2~3 sec
Sparse      = 0.2~0.3 sec

However, his one is much shorter and neat!

Upvotes: 1

Suever
Suever

Reputation: 65430

Off the top of my head you could use bwlabel (from the Image Processing Toolbox) to assign each cluster of 1's a unique value. You could then use bsxfun to check equality between the labeled version and the unique labels which will automatically expand it out into a matrix.

a = [0 0 0 1 1 0 0 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 0 0 0];
b = bwlabel(a);
out = bsxfun(@eq, (1:max(b))', b);

Without the image processing toolbox you could do effectively the same thing with:

C = cumsum(~a);
out = bsxfun(@eq, unique(C(logical(a))).', C);

Upvotes: 2

Related Questions