Split array into chunks based on trigger values in another array

24 visualizzazioni (ultimi 30 giorni)
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 Commenti
Just Manuel
Just Manuel il 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

Accedi per commentare.

Risposte (0)

Categorie

Scopri di più su Data Type Conversion in Help Center e File Exchange

Prodotti

Community Treasure Hunt

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

Start Hunting!

Translated by