This works best with the findpeaks function:
D = load('data.txt');
x = D(:,1);
y = D(:,2);
[pksp,locpix] = findpeaks(y, 'MinPeakDist',50);
x = x(locpix(1):end);
y = y(locpix(1):end);
locpix = locpix - locpix(1) + 1;
[pksn,locnix] = findpeaks(-y, 'MinPeakDist',50);
pksn = [-pksn; y(end)];
locnix = [locnix; numel(x)];
[pksz,loczix] = findpeaks(-abs(y), 'MinPeakDist',50);
[locs, idx] = sort([locpix; locnix; loczix]);
pks = [pksp; pksn; pksz];
pks = pks(idx);
for k1 = 1:numel(locs)-1
seg{k1} = [x(locs(k1):locs(k1+1)-1) y(locs(k1):locs(k1+1)-1)];
end
figure(1)
plot(x, y);
hold on
for k1 = 1:size(seg,2)
plot(seg{k1}(:,1), seg{k1}(:,2), 'LineWidth',2)
end
plot(x(locpix), pksp, '^r', 'MarkerFaceColor','r')
plot(x(locnix), pksn, 'vr', 'MarkerFaceColor','r')
plot(x(loczix), pksz, 'dr', 'MarkerFaceColor','r')
hold off
grid
Fortunately, your data have specific transitions, and the zero-crossings can also be made into specific transitions, so findpeaks is perfect for this. My code segments the data, and the plot demonstrates that your curve appears to be segmented correctly.
I did not categorise the segments in my code into ‘positive-falling’, ‘negative-falling’, ‘negative-rising’, etc., so I leave that to you. That involves your writing another loop to segment the ‘segs’ cell array the way you want. That should be relatively straightforward. I did my best to comment-document my code.
There is unfortunately no easy way to vectorise this. I cannot claim that it will be robust to all your data unless they are similar to those you posted. You may have to generalise it yourself for other data.
Experiment to get the result you want.
NOTE — The colors on the plot do not correspond to those in your Question. They simply demonstrate that the segmentation works.
EDIT — Corrected typographical errors, added optional ‘positive peak’, ‘negative peak’, and ‘zero crossing’ markers to plot (not shown in posted plot).
Best Answer