Skip to content

Examples

Peter Charlton edited this page Feb 27, 2020 · 49 revisions

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:

  1. Analysing a single pulse wave: a quick way to start using PulseAnalyse
  2. Analysing a 1-minute recording: an introduction to the numerical results provided by PulseAnalyse
  3. Analysing an hour-long recording: demonstrates the utility of the signal quality assessment performed by PulseAnalyse
  4. Calculating indices which require a subject's height: additional pulse wave indices that can be calculated when the subject's height is known.
  5. Calculating indices from pressure pulse waves: an example of analysing a pulse wave which is associated with physical units.

1) Analysing a single pulse wave

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:

image

Running the example

To run the example:

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);

Understanding the results

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.

Context

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.

Taking it further

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);


2) Analysing a 1-minute recording

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:

image

This plot shows the approximate durations of systole and diastole for each beat during the minute recording, estimated from the pulse wave.

Running the example

To run the example:

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

Understanding the results

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.

Context

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.

Taking it further

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% ?

Analysing an hour-long recording

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:

image

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.

Running the example

To run the example:

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

Understanding the results

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

Context

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.

Taking it further

You could extend this by:

  • Looking to see whether you agree with the algorithm's classification of pulse waves as high or low quality.

Calculating indices which require a subject's height

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.

Running the example

To run the example:

[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.

Understanding the results

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.

Context and Taking it further

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.


Calculating indices from pressure pulse waves

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.

Running the example

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:

image

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.

Understanding the results

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.

Context and Taking it further

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);