Home > Net >  Create matrix from patches of numbers in a vector
Create matrix from patches of numbers in a vector

Time:06-16

Say I have an input row vector, consisting of 0s and of patches of numbers without any indication on their range:

  A = [0 0 1 1 1 1 0 0 0 0 22 22 22 0 0 6 6 6 6 6 0 1 1];

I would like to create a 2 x m matrix, where m is the amount of patches, where the first row of column j keeps track of the values in the jth patch, and where the second row of column j keeps track of the average index of the jth patch (say the index will be the first integer to the left of the average if it is not integer).

The output associated to my example shoud be:

out =

 1    22     6     1
 4    12    18    22

I'm trying to work it out with find, it might end up working but it is ugly as hell and will probably be very slow, plus I'm pretty sure one of you has a cool trick in his sleeve!

I got something working (see code below), but it will not work if the patches are not distinct (i.e. if A contains patches that are not separated with at least one 0):

d1 = diff([0 A]);

idx_S = find(d1>0);

idx_E = find(d1<0)-1;

if length(idx_E)<length(idx_S)

    idx_E = [idx_E length(A)];

end

idx_M = floor(mean([idx_E;idx_S],1));

out = [A(idx_M);idx_M];

For example, for:

A = [0 0 1 1 1 1 0 0 0 0 22 22 22 0 0 6 6 6 6 6 1 1];

it will output

out =

     1    22     6
     4    12    18

instead of

out =

 1    22     6     1
 4    12    18    21

CodePudding user response:

One solution:

% Your input:
A = [0 0 1 1 1 1 0 0 0 0 22 22 22 0 0 6 6 6 6 6 0 1 1];

% Detect the variation
ind = find(diff([0,A,0]>0));

% Construct the output array
res = [A(ind(1:2:end-1)); floor(mean(reshape(ind,2,[]))-0.5)]

And we got

% res =  
%     1   22    6    1
%     4   12   18   22

Notice that this method will fail if two patchs are not separate by a least one 0.

CodePudding user response:

This solution seems a little dense, but does achieve what you want, for "patches" of numbers regardless whether they are split by a zero or not.

A = [0 0 1 1 1 1 0 0 0 0 22 22 22 0 0 6 6 6 6 6 1 1];
m = arrayfun( @(x) x-1 floor(find([A(x:end),NaN]~=A(x),1)/2), find([1,diff(A)].*A) );
m = [A(m); m];

% m = 
%  1    22     6     1
%  4    12    18    21

To break this down:

  • [0,diff(A)] Find the point-to-point deltas between elements (patches start/end when non-zero).
  • find([0,diff(A)].*A) Convert from array the same size as A to the indicies of the non-zero elements (filtered by multiplying by A, removed when A=0).
  • arrayfun( ___, find([0,diff(A)].*A) ) Loop over these indicies
  • @(x) x-1 floor(find([A(x:end),NaN]~=A(x),1)/2) For each element x of the index array, return the "middle" of the patch which is defined as the range between x and the next element not equal to A(x).

Then finally m is defined as these indicies concatenated with the values of A at those indicies.

CodePudding user response:

Following the comments from beaker, some quick search about run-length encoding led me there, which applied to my problem gives:

A = [0 0 1 1 1 1 0 0 0 0 22 22 22 0 0 6 6 6 6 6 1 1];

idx_E = [find(A(1:end-1) ~= A(2:end)) length(A)]; % end of patches, keeping patches of zeros

idx_S = [1 idx_E(1:(end-1)) 1]; % start of patches, idem

idx_M = floor(mean([idx_E;idx_S],1)); % mean index, idem

out = [A(idx_M);idx_M];

out(:,A(idx_M)==0) = []; % removing patches of zeros

% out =
%    1    22     6     1
%    4    12    18    21
  • Related