-
Notifications
You must be signed in to change notification settings - Fork 4
Examples
This page provides a range of examples to allow users to quickly start using PulseAnalyse, and also to demonstrate its functionality. The examples are:
- Analysing a single pulse wave: a quick way to start using PulseAnalyse
- Analysing a 1-minute recording: an introduction to the numerical results provided by PulseAnalyse
- Analysing an hour-long recording: demonstrates the utility of the signal quality assessment performed by PulseAnalyse
- Calculating indices which require a subject's height: additional pulse wave indices that can be calculated when the subject's height is known.
- Calculating indices from pressure pulse waves: an example of analysing a pulse wave which is associated with physical units.
In this example PulseAnalyse is used to analyse a single pulse wave (corresponding to one heart beat): it identifies the fiducial points, and calculates pulse wave indices. The results are shown in the plot below:
To run the example:
- download PulseAnalyse
- Open PulseAnalyse.m in Matlab ®
- Run PulseAnalyse
This will produce two plots showing the identification of fiducial points on the pulse wave, and the pulse wave indices measured from the fiducial points:
Fiducial Points | Pulse Wave Indices |
---|---|
In order to recreate the plot of a single pulse wave shown above, you will need to use the following command:
options.plot_pw_only = 1; PulseAnalyse([], options);
Referring to the plot of a single pulse wave: Here, the red circles show three fiducial points on the pulse wave:
- s: the systolic peak
- dia: the diastolic peak
- dic: the approximate location of the dicrotic notch
In addition, five pulse wave indices have been calculated:
- CT: the crest time
- ΔT: the time between systolic and diastolic peaks
- Systole: the approximate duration of systole
- Diastole: the approximate duration of diastole
- RI: the amplitude of the diastolic peak, which is used to calculate the reflection index.
The data used in this example is taken from the PPG Diary Project. It is a recording of a single pulse wave, measured using photoplethysmography (the technology used in pulse oximeters) at the thumb whilst the subject was asleep.
You could extend this by:
- Plotting the systolic and diastolic areas:
options.plot_areas = 1; options.plot_pw_only = 1; PulseAnalyse([], options);
- Saving the figures (you will need to replace the save_folder path in this command with your own):
options.save_file = 'single_pw_example'; options.plot_pw_only = 1; options.save_folder = '/Users/petercharlton/Desktop/temp/'; PulseAnalyse([], options);
In this example PulseAnalyse is used to analyse a pulse wave recording lasting for one minute: it identifies the fiducial points, and calculates pulse wave indices. The results are shown in the plot below:
This plot shows the approximate durations of systole and diastole for each beat during the minute recording, estimated from the pulse wave.
To run the example:
- Download the sample data file, called PPGdiary1_1_min_sample.mat, from DOI: 10.5281/zenodo.3268501.
- Use PulseAnalyse to analyse the data, using the following command:
options.do_plot = 0; [pw_inds, fid_pts, pulses, sigs] = PulseAnalyse(S, options);
- Make a plot of how the approximate durations of systole and diastole vary throughout the minute recording:
figure('Position', [20,20,650,450]), t = [0:length(S.v)-1]/S.fs; lwidth = 2; ftsize = 16; plot(t(pulses.onsets(1:end-1)), 1000*pw_inds.t_dia.raw, 'b', 'LineWidth', lwidth), hold on, plot(t(pulses.onsets(1:end-1)), 1000*pw_inds.t_sys.raw, 'r', 'LineWidth', lwidth), xlabel('Time (s)', 'FontSize', ftsize), ylabel('Approximate durations (ms)', 'FontSize', ftsize), legend({'Diastole', 'Systole'}, 'Location', 'East'), set(gca, 'FontSize', ftsize), box off
The data provided by PulseAnalyse are helpful for understanding the results. The subject's average heart rate during the minute was 37.3 beats per minute (obtained using pw_inds.IHR.v
), compared to a usual value of approximately 70 bpm. This corresponds to an average pulse wave duration of 1.61 s (obtained using pw_inds.T.v
), which is much longer than usual. This explains why the duration of diastole in this plot (approximately 1,200 ms) is much longer than expected. It is interesting to note thought that the duration of systole is not too abnormal, despite the low heart rate. Furthermore, most of the variation in pulse wave duration appears to be explained in variation in the duration of diastole.
Normal values taken from this article.
The data used in this example is taken from the PPG Diary Project. The recording was taken whilst the subject was asleep, which may explain in part why the heart rate was slower than usual during this minute, and why the estimated durations of systole and diastole are longer than one might expect.
You could extend this by:
- Quantifying the percentage of variation in pulse wave duration which is associated with the duration of systole and diastole respectively in this recording. Consider:
temp = corrcoef(pw_inds.T.raw, pw_inds.t_sys.raw); fprintf(['\n Proportion explained by duration of systole = ' num2str(100*temp(2)^2) ' %'])
temp = corrcoef(pw_inds.T.raw, pw_inds.t_dia.raw); fprintf(['\n Proportion explained by duration of diastole = ' num2str(100*temp(2)^2) ' %'])
- Consider: Why do the two proportions not add up to 100% ?
In this example, PulseAnalyse is used to analyse a pulse wave recording lasting for one hour: it checks the signal quality of each pulse wave, identifies the fiducial points, and calculates pulse wave indices. The results are shown in the plot below:
Most of the recording contains high quality pulse wave data, but some of it (shown in red) is low quality. Pulse wave indices extracted from the low quality data may be unreliable. One of the features of PulseAnalyse is that it assesses the quality of individual pulse waves, so that any low quality measurements can be excluded from analyses.
To run the example:
- Download the sample data file, called PPGdiary1_1_hour_sample.mat, from DOI: 10.5281/zenodo.3268501.
- Extract the signal from the data variable provided by this file, ready for analysis:
S.v = data.ppg_ir; S.fs = data.fs;
- Use PulseAnalyse to analyse the data, using the following command (which will take a short time):
options.do_plot = 0; [pw_inds, fid_pts, pulses, sigs] = PulseAnalyse(S, options);
- Make a plot of the hour-long recording, showing the high quality data in blue, and the low quality data in red:
figure('Position', [20,20, 800,400]), ftsize = 16; t = [0:length(sigs.filt)-1]/(60*sigs.fs); plot(t, sigs.filt), hold on, for pulse_no = 1 : length(pulses.quality)-1, if ~pulses.quality(pulse_no), rel_els = pulses.onsets(pulse_no):pulses.onsets(pulse_no+1); plot(t(rel_els), sigs.filt(rel_els), 'r'), end, end, xlim([0, t(end)]), xlabel('Time (min)', 'FontSize', ftsize), ylabel('PPG signal', 'FontSize', ftsize), set(gca, 'FontSize', ftsize, 'YTick', [], 'XGrid', 'on'), ylim([-1e4, 1e4]), l = legend({'High Quality', 'Low Quality'}); box off
- You may also be interested to zoom in on a period of artifact (using some artistic licence):
figure('Position', [20,20, 1000,300]), subplot('Position', [0.03, 0.16, 0.96, 0.83]), ftsize = 16; t = [0:length(sigs.filt)-1]/sigs.fs; rel_els = t>=3006 & t<=3046; plot(t(rel_els)-min(t(rel_els)), sigs.filt(rel_els), 'b', 'LineWidth', 2), hold on, low_qual_els = t>=3018.46 & t<= 3032.86; plot(t(low_qual_els)-min(t(rel_els)), sigs.filt(low_qual_els), 'r', 'LineWidth', 2), xlim([0, max(t(rel_els))-min(t(rel_els))]), xlabel('Time (s)', 'FontSize', ftsize), ylabel('PPG signal', 'FontSize', ftsize), set(gca, 'FontSize', ftsize, 'YTick', [], 'XGrid', 'on'), ylim([-3460, 3490]), l = legend({'High Quality', 'Low Quality'}); box off
94% of the pulse waves in this hour long recording were deemed to be of high quality by PulseAnalyse (obtained using 100*mean(pulses.quality)
). The examples below show pulse waves identified as high and low quality, where the low quality pulse wave has a quite different shape. The different shape was recognised by the algorithm, and flagged as low quality.
High Quality Pulse Wave | Low Quality Pulse Wave |
---|---|
These examples were obtained using:
ftsize = 16; pulse_no = [1410,1421]; for s = 1 : length(pulse_no), figure, rel_els = pulses.onsets(pulse_no(s)):pulses.onsets(pulse_no(s)+1); if pulses.quality(pulse_no(s)), color = 'b'; else, color = 'r'; end, t = [0:length(rel_els)-1]/S.fs; plot(t, sigs.filt(rel_els), color, 'LineWidth', lwidth), set(gca, 'FontSize', ftsize, 'YTick', []), xlabel('Time (s)', 'FontSize', ftsize), ylabel('PPG', 'FontSize', ftsize), xlim([0 t(end)]), box off, end
The data used in this example is taken from the PPG Diary Project. It is a recording of pulse waves acquired for an hour whilst the subject was asleep. This may explain why such a high proportion of the data was of high quality. In contrast, recordings acquired during movement (such as walking or running) tend to be of lower quality.
You could extend this by:
- Looking to see whether you agree with the algorithm's classification of pulse waves as high or low quality.
For this example, we'll repeat the analysis conducted in Example 1, but this time we'll include the subject's height, with which the Stiffness Index, a commonly used pulse wave index, can be calculated.
To run the example:
-
Download the sample data file, called PPGdiary1_1_pulse_sample.mat, from DOI: 10.5281/zenodo.3268501.
-
Open the file, and use PulseAnalyse to analyse the pulse wave, using the following command:
[pw_inds, fid_pts, pulses, sigs] = PulseAnalyse(S);
-
Look at the value of
pw_inds.SI.v
. It is currently a NaN, indicating that it could not be calculated. -
Repeat the analysis, but this time include the subject's height in the signal structure (here, a height of 1.75m is used as an example):
S.ht = 1.75; [pw_inds, fid_pts, pulses, sigs] = PulseAnalyse(S);
- Look again at the value of
pw_inds.SI.v
. It is now 4.86 m/s.
The stiffness index is calculated as the subject's height divided by ΔT (the time between the systolic and diastolic peaks). Providing the height allows this pulse wave index to be calculated.
The data used in this example is taken from the PPG Diary Project. In this study, pulse wave recordings were acquired during different activities of daily living. It would be interesting to investigate whether the stiffness index (thought to be indicative of arterial stiffness) changes during particular activities.
All the examples so far have considered a PPG pulse wave. Unless specified otherwise, PulseAnalyse normalises pulse waves to occupy the range of 0 to 1. This is usually appropriate for a PPG pulse wave, as PPG recordings are often not associated with units. However, a pressure pulse wave is often associated with units (such as mmHg). This example demonstrates how to use PulseAnalyse when a pulse wave has units associated with it.
To run the example:
- Use PulseAnalyse to analyse a sample pressure pulse wave, using the following command:
[pw_inds, fid_pts, pulses, sigs] = PulseAnalyse([],'pressure_example')
This will produce a plot similar to that shown below:
Note that the y-axis has values (of pressure in mmHg). Indeed, the pulse pressure (the amplitude of the pulse wave) measured by PulseAnalyse is 29.6 mmHg (provided in pw_inds.pulse_amp), which corresponds to that shown on the plot above.
In this example the normalise_pw option has been set to zero, indicating that the pulse wave shouldn't be normalised, and the original units should be retained.
Compare the value for pw_inds.pulse_amp obtained in this example to that obtained in the first example. Compare the value to that obtained when running the first example without normalising the pulse wave, using the command:
options.normalise_pw = 0; [pw_inds, fid_pts, pulses, sigs] = PulseAnalyse([], options);
Part of the wider PulseAnalyse Project