Extracting individual breathing cycles from a signal and plotting the mean cycle
Mostrar comentarios más antiguos
I have a breathing trace, which I have smoothed and use FindPeaks to identify the peaks. How do I extract the individual beathing cyles between the peaks so that I can plot these over each other and calculate the mean breathing cycle?
I know there is a baseline shift so I also need to look at detrending the data first.
I would be very grateful for any input or direction to where I might find information on how to do this.

I have tried to extract the first 3 regions of interest but an 'unrecognized function error'.
roilims = [0 227; 227 480; 480 745];
>> sigroi = extractsigroi(dt_B,roilims);
Unrecognized function or variable 'extractsigroi'.
Respuestas (2)
Star Strider
el 24 de Feb. de 2022
1 voto
If the peaks define the cycle, then finding the ‘valleys’ between them will define the beginning and end points of each cycle. I would use either findpeaks with the negative of the signal (turning the ‘valleys’ into peaks) or the islocalmin function.
An example of that to create an ensemble average (not necessary, use only the parts of the code that apply to your problem) is Combining repetitive curves into one average curve and specifically this Comment.
.
11 comentarios
Gail Distefano
el 24 de Feb. de 2022
Editada: Gail Distefano
el 24 de Feb. de 2022
Star Strider
el 24 de Feb. de 2022
Editada: Star Strider
el 24 de Feb. de 2022
My pleasure!
I can help to implement it with your data if you care to share your data.
EDIT — (24 Feb 2022 at 19:52)
Your data are different from the previous data, so it was necessary to tweak the code a bit.
LD = load('breath_smoothed.mat');
dt_B = LD.dt_B;
y = dt_B;
Fs = 30;
t = linspace(0, numel(y)-1, numel(y))/Fs; % Assume 1kHz Sampling Frequency (Supply Correct Time Vector)
Ts = mean(diff(t));
FTy = fft(y-mean(y))/numel(y); % Calculate & Plot Fourier Transform
Fv = linspace(0, 1, numel(t)/2+1)*Fs/2;
Iv = 1:numel(Fv);
figure
plot(Fv, abs(FTy(Iv))*2)
grid
xlim([0 1.5])
xlabel('Frequency')
ylabel('Amplitude')
yf = highpass(y, 3E-3*Fs, Fs, 'ImpulseResponse','iir'); % Eliminate Baseline Variation
[pks,plocs] = findpeaks(yf, 'MinPeakProminence',0.5); % Peaks & Location Indices
[vls,vlocs] = findpeaks(-yf, 'MinPeakProminence',0.5); % Valleys & Location Indices
figure
plot(t, yf) % Detail Of Original Waveform
hold on
plot(t(plocs),pks,'^r')
hold off
% xlim([0 2.5])
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
Vidx = mean(diff(vlocs));
for k = 1:numel(vls)-1
idxrng{k,:} = max(1,vlocs(k)):min(numel(t),vlocs(k+1)); % Generate Ensemble From Individual Records
ensb{k,:}(1:numel(idxrng{k})) = yf(idxrng{k});
end
idxrng_max = max(cellfun(@numel,idxrng)); % Generate MAtrices For Statistics Plots
idxrng_mtx = zeros(size(idxrng,1), idxrng_max);
ensb_mtx = idxrng_mtx;
figure
hold on
for k = 1:size(ensb,1) % Plot Data, Populate Matrices
plot(idxrng{k}, ensb{k})
idxrng_mtx(k,1:numel(idxrng{k})) = idxrng{k};
ensb_mtx(k,1:numel(ensb{k})) = ensb{k};
end
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
figure
hold on
for k = 1:size(ensb_mtx,1) % Plot Ensemble
plot(1:numel(idxrng{k}), ensb{k})
end
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
ensb_mean = mean(ensb_mtx);
ensb_std = std(ensb_mtx);
tci = tinv(0.975,size(ensb,1)-1); % 95% Critical t-Statistic
err = tci*ensb_std/sqrt(size(ensb,1)); % 95% Confidence Intervals
figure
plot(t(1:numel(ensb_mean)), ensb_mean, '-b')
hold on
plot(t(1:numel(ensb_mean)), ensb_mean+err, '--r')
plot(t(1:numel(ensb_mean)), ensb_mean-err, '--r')
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
legend('Ensemble Average', '±95% Confidence Intervals')
Figure 5 —

It can be tweaked further if necessary if you have a different desired result.
.
Gail Distefano
el 24 de Feb. de 2022
Gail Distefano
el 24 de Feb. de 2022
Star Strider
el 24 de Feb. de 2022
My pleasure!
It took a bit of tweaking to get my previous code to work with your data.
If my Answer helped you solve your problem, please Accept it!
.
Gail Distefano
el 28 de Feb. de 2022
Star Strider
el 28 de Feb. de 2022
In my code, ‘vls’ (valley magnitudes) and ‘vlocs’ (the index locations of them) should be the same size, so that is not the problem.
With respect to this error:
Unable to perform assignment because brace indexing is not supported for variables of this type.
My code runs without error with the .mat file provided. I have no idea what is throwing that error.
If you are using a different .mat file, post it and I will see if I can figure out what the problem is with it.
Gail Distefano
el 28 de Feb. de 2022
Star Strider
el 2 de Mzo. de 2022
It took me a while to figure out what the problem was that you referred to.
My code plots the identified waves defined as those that findpeaks returns both ‘valley’ indices for in ‘vlocs’. If a peak (or valley) cannot be identified as such since it is at the end of the vector and does not have a defined peak (or valley), findpeaks will not detect it. In this instance, the last peak does not have an identified trailing ‘valley’. One way to deal with that is to append the last index of the vector to the ‘vlocs’ vector:
[vls,vlocs] = findpeaks(-yf, 'MinPeakProminence',0.5); % Valleys & Location Indices
vlocs = [vlocs, numel(yf)];
The loop that then detects and returns the respiratory cycles is then modified to be:
Vidx = mean(diff(vlocs));
for k = 1:numel(vlocs)-1
idxrng{k,:} = max(1,vlocs(k)):min(numel(t),vlocs(k+1)); % Generate Ensemble From Individual Records
ensb{k,:}(1:numel(idxrng{k})) = yf(idxrng{k});
end
It does not make a significant difference in the result, however it now includes even the incomplete respiratory cycles.
The full code for this file is now:
LD = load('BreathData_Type2.mat');
y = LD.C;
Fs = 25;
t = linspace(0, numel(y)-1, numel(y))/Fs; % Assume 1kHz Sampling Frequency (Supply Correct Time Vector)
Ts = mean(diff(t));
FTy = fft(y-mean(y))/numel(y); % Calculate & Plot Fourier Transform
Fv = linspace(0, 1, numel(t)/2+1)*Fs/2;
Iv = 1:numel(Fv);
figure(1)
plot(Fv, abs(FTy(Iv))*2)
grid
xlim([0 1.5])
xlabel('Frequency')
ylabel('Amplitude')
yf = highpass(y, 3E-3*Fs, Fs, 'ImpulseResponse','iir'); % Eliminate Baseline Variation
[pks,plocs] = findpeaks(yf, 'MinPeakProminence',0.5); % Peaks & Location Indices
[vls,vlocs] = findpeaks(-yf, 'MinPeakProminence',0.5); % Valleys & Location Indices
vlocs = [vlocs, numel(yf)];
figure(2)
plot(t, yf) % Detail Of Original Waveform
hold on
plot(t(plocs),pks,'^r')
hold off
% xlim([0 2.5])
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
Vidx = mean(diff(vlocs));
for k = 1:numel(vlocs)-1
idxrng{k,:} = max(1,vlocs(k)):min(numel(t),vlocs(k+1)); % Generate Ensemble From Individual Records
ensb{k,:}(1:numel(idxrng{k})) = yf(idxrng{k});
end
idxrng_max = max(cellfun(@numel,idxrng)); % Generate MAtrices For Statistics Plots
idxrng_mtx = zeros(size(idxrng,1), idxrng_max);
ensb_mtx = idxrng_mtx;
figure(3)
hold on
for k = 1:size(ensb,1) % Plot Data, Populate Matrices
plot(t(idxrng{k}), ensb{k})
idxrng_mtx(k,1:numel(idxrng{k})) = idxrng{k};
ensb_mtx(k,1:numel(ensb{k})) = ensb{k};
end
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
figure(4)
hold on
for k = 1:size(ensb_mtx,1) % Plot Ensemble
plot(1:numel(idxrng{k}), ensb{k})
end
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
ensb_mean = mean(ensb_mtx);
ensb_std = std(ensb_mtx);
tci = tinv(0.975,size(ensb,1)-1); % 95% Critical t-Statistic
err = tci*ensb_std/sqrt(size(ensb,1)); % 95% Confidence Intervals
figure(5)
plot(t(1:numel(ensb_mean)), ensb_mean, '-b')
hold on
plot(t(1:numel(ensb_mean)), ensb_mean+err, '--r')
plot(t(1:numel(ensb_mean)), ensb_mean-err, '--r')
hold off
grid
xlabel('Time (s)')
ylabel('Amplitude (Unit)')
legend('Ensemble Average', '±95% Confidence Intervals')
I am not certain what to suggest for a general solution, other than to not include the incomplete respiratory cycles at eiher end, as the original code does not. Including them as this version does, risks corrupting the ensemble data with incomplete cycles, so I advise against it and advise instead using the original code and not this modified version.
I did not also include the data that began with the first element of the vector for this reason, although to be consistent, it would be necessary to include the first and last indices of the vector in ‘vlocs’. That would create ‘vlocs’ as:
vlocs = [1, vlocs, numel(yf)];
However they are your data and your results, so it is your choice as to how to work with the incomplete cycles.
.
Gail Distefano
el 10 de Mzo. de 2022
Star Strider
el 10 de Mzo. de 2022
My pleasure!
If my Answer helped you solve your problem, please Accept it!
.
Cris LaPierre
el 24 de Feb. de 2022
0 votos
extractsigroi is part of the Signal Processing Toolbox. It would appear you do not have it installed. If you have access to it through your license, you can add it using the Add-Ons explorer.
Categorías
Más información sobre Parametric Spectral Estimation en Centro de ayuda y File Exchange.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
