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 asA
to the indicies of the non-zero elements (filtered by multiplying byA
, removed whenA=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 elementx
of the index array, return the "middle" of the patch which is defined as the range betweenx
and the next element not equal toA(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