Split array into chunks based on trigger values in another array

30 views (last 30 days)
Hi all
I've searched for a bit, found some related questions, but none that want to achieve exactly what i want (point me towards one that does, if you know of one):
However, what I am looking for is to split an array A into chunks that correspond to consecutive ones (or a single one) in array B. Consider the following example:
A = (1:10)';
B = [0 1 1 1 0 0 1 0 1 1 ]';
f = @(trig, data) % my magic function
% output of f(B,A) should be the following:
>> f(B, A)
ans = { [2, 3, 4]; [7]; [9, 10] }
I've come up with a working solution, but it looks like it can be done more efficiently, or faster. Hit me with Ideas :)
function groups = f(trig, data)
% approach with splitapply
len = length(trig);
% find rising edges (diff = 1) and falling edges (diff = -1)
d = zeros(len,1);
d(2:len) = diff(trig);
% multiply with increasing numbers to generate unique keys
g = d .* ((2:len+1).');
% apply cumsum to assign same key to samples between triggers
gs = cumsum(g);
% put NaN for negative keys (after falling edges -> where trigger is 0)
% so splitapply will ignore those samples
gs(gs < 1) = NaN;
% use findgroups to generate consecutive keys
gr = findgroups(gs);
% function that returns the array in a cell
f = @(a) {a};
% let splitapply do the work
groups = splitapply(f,data,gr);
end
Cheers
Manuel
Edit: Changed Example for more clarity
  4 Comments
Just Manuel
Just Manuel on 16 Feb 2021
Hi Mathieu
Thank you for your Input!
It seems you interpreted my question as asking the same as the two questions I linked. Indeed, using A(B>0.5) would be the simple answer, if i wanted the data values to be in a single vector ( ans = 2 4 5 7 8 10).
However, i want them to be in separate arrays based on consecutive (or, as you pointed out, also one single 1) ones in the trigger values.
Thank you for your code. I used "find" before, but have not thougtht of using it for determining the edge indices. I adapted your code so it does what i want:
function groups = f3(trig, data)
% approach with M Noe
S = trig - 0.5;
% first look for exact zeros
ind0 = find( S == 0 );
% then look for zero crossings between data points
S1 = S(1:end-1) .* S(2:end);
ind1 = find( S1 < 0 );
% bring exact zeros and "in-between" zeros together
ind = sort([ind0 ind1]);
for ii=1:length(ind)
DEN = (S(ind(ii)+1) - S(ind(ii)));
slope_sign(ii) = sign(DEN);
end
% extract the positive slope crossing points
ind_pos = ind1(slope_sign>0);
% extract the negative slope crossing points
ind_neg = ind1(slope_sign<0);
groups = {};
gr_ind = 1;
neg_offset = 0;
% if trigger signal starts high
if ind_neg(1) < ind_pos(1)
groups{1} = data(1:ind_neg(1));
gr_ind = 2;
neg_offset = 1;
end
% build groups body (excluding last trigger index)
for i = 1:length(ind_pos)-1
groups{gr_ind,1} = data(ind_pos(i)+1:ind_neg(i+neg_offset));
gr_ind = gr_ind+1;
end
% if trigger signal ends high
if ind_neg(end) < ind_pos(end)
groups{end+1} = data(ind_pos(end)+1:end);
else
groups{end+1} = data(ind_pos(end)+1:ind_neg(end));
end
end
And see, it actually does perform better, than my solution with the for-loop. So, even if you seem to have misunderstood my intention, you have contributed a solution with a better performance :D Thanks!
benchmark output:
250642
f1 took 4.63893s
250643
f2 took 0.960263s
250643
f3 took 0.510538s

Sign in to comment.

Answers (0)

Categories

Find more on Data Type Conversion in Help Center and File Exchange

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!