Locate P, Q, S and T waves in ECG#

This example can be referenced by citing the package.

This example shows how to use NeuroKit to delineate the ECG peaks in Python using NeuroKit. This means detecting and locating all components of the QRS complex, including P-peaks and T-peaks, as well their onsets and offsets from an ECG signal.

# Load NeuroKit and other useful packages
import neurokit2 as nk
import numpy as np
import pandas as pd

In this example, we will use a short segment of ECG signal with sampling rate of 3000 Hz.

Find the R peaks#

# Retrieve ECG data from data folder
ecg_signal = nk.data(dataset="ecg_1000hz")
# Extract R-peaks locations
_, rpeaks = nk.ecg_peaks(ecg_signal, sampling_rate=1000)

The ecg_peaks() function will return a dictionary contains the samples at which R-peaks are located.

Let’s visualize the R-peaks location in the signal to make sure it got detected correctly.

# Visualize R-peaks in ECG signal
plot = nk.events_plot(rpeaks['ECG_R_Peaks'], ecg_signal)
../../_images/6b8e9cdd5652a531eb7bf6cc360a1d917d4b0ce7716fbd83fad936a4bdf78028.png
# Zooming into the first 5 R-peaks
plot = nk.events_plot(rpeaks['ECG_R_Peaks'][:5], ecg_signal[:6000])
../../_images/c5d3241eca8adbeee861c9064694ca2a8a03bdcde3e375ccaafc93e48b01fbd6.png

Visually, the R-peaks seem to have been correctly identified. You can also explore searching for R-peaks using different methods provided by NeuroKit ecg_peaks().

Locate other waves (P, Q, S, T) and their onset and offset#

In ecg_delineate(), NeuroKit implements different methods to segment the QRS complexes. There are the derivative method and the other methods that make use of Wavelet to delineate the complexes.

Peak method#

First, let’s take a look at the ‘peak’ method and its output.

# Delineate the ECG signal
_, waves_peak = nk.ecg_delineate(ecg_signal, rpeaks, sampling_rate=1000, method="peak")
# Zooming into the first 3 R-peaks, with focus on T_peaks, P-peaks, Q-peaks and S-peaks
plot = nk.events_plot([waves_peak['ECG_T_Peaks'][:3], 
                       waves_peak['ECG_P_Peaks'][:3],
                       waves_peak['ECG_Q_Peaks'][:3],
                       waves_peak['ECG_S_Peaks'][:3]], ecg_signal[:4000])
../../_images/dea55ce33cb3294824fe2c0a709b576e97f196eff12b6bd7fb0b24606cd1b421.png

Visually, the ‘peak’ method seems to have correctly identified the P-peaks, Q-peaks, S-peaks and T-peaks for this signal, at least, for the first few complexes. Well done, peak!

However, it can be quite tiring to be zooming in to each complex and inspect them one by one. To have a better overview of all complexes at once, you can make use of the show argument in ecg_delineate() as below.

# Delineate the ECG signal and visualizing all peaks of ECG complexes
_, waves_peak = nk.ecg_delineate(ecg_signal, 
                                 rpeaks, 
                                 sampling_rate=1000, 
                                 method="peak", 
                                 show=True, 
                                 show_type='peaks')
../../_images/484358bb56b0177ac029fbda235cd14fa8ed96e098f4a11a6f17f79bfa2a7fb6.png

The ‘peak’ method is doing a glamorous job with identifying all the ECG peaks for this piece of ECG signal.

On top of the above peaks, the peak method also identify the wave boundaries, namely the onset of P-peaks and offset of T-peaks. You can vary the argument show_type to specify the information you would like plot.

Let’s visualize them below:

# Delineate the ECG signal and visualizing all P-peaks boundaries
signal_peak, waves_peak = nk.ecg_delineate(ecg_signal, 
                                           rpeaks, 
                                           sampling_rate=1000,
                                           method="peak", 
                                           show=True, 
                                           show_type='bounds_P')
../../_images/5bed11fe47c7142d5bb7386691991d7108eabec25342d6299e0fdec58ed15bd1.png
# Delineate the ECG signal and visualizing all T-peaks boundaries
signal_peaj, waves_peak = nk.ecg_delineate(ecg_signal, 
                                           rpeaks, 
                                           sampling_rate=1000, 
                                           method="peak", 
                                           show=True, 
                                           show_type='bounds_T')
../../_images/75871105c21d2e847630703bf45da6976c752bfb725150dcb96eefe9e7d1a0fe.png

Both the onsets of P-peaks and the offsets of T-peaks appears to have been correctly identified here. This information will be used to delineate cardiac phases in ecg_phase().

Let’s next take a look at the continuous wavelet method.

Continuous Wavelet Method (CWT)#

# Delineate the ECG signal
signal_cwt, waves_cwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=1000, 
                                         method="cwt", 
                                         show=True, 
                                         show_type='all')
../../_images/733f053811a1caffd699f8fff85d98bb866eb24435964983e693f497929342d6.png

By specifying ‘all’ in the show_type argument, you can plot all delineated information output by the cwt method. However, it could be hard to evaluate the accuracy of the delineated information with everyhing plotted together. Let’s tease them apart!

# Visualize P-peaks and T-peaks
signal_cwt, waves_cwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=1000, 
                                         method="cwt", 
                                         show=True, 
                                         show_type='peaks')
../../_images/58948c00496d31ee07c0eda30cd0085c27210c1ffb47f2002486aae42bc6782b.png
# Visualize T-waves boundaries
signal_cwt, waves_cwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=1000, 
                                         method="cwt", 
                                         show=True, 
                                         show_type='bounds_T')
../../_images/43523507d26816ace8d477f5a99063811431909085b15ab78129afe9f45317e5.png
# Visualize P-waves boundaries
signal_cwt, waves_cwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=1000, 
                                         method="cwt", 
                                         show=True, 
                                         show_type='bounds_P')
../../_images/c9e40a4919db0cc1a132444b16508b21a367435f8e466af86ef9eaf25d17edd4.png
# Visualize R-waves boundaries
signal_cwt, waves_cwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=1000, 
                                         method="cwt", 
                                         show=True, 
                                         show_type='bounds_R')
../../_images/c382e03dd56f36827bdd18c6f3941ea183f1cb1731f209efcdd955a2d9d4f74c.png

Unlike the peak method, the continuous wavelet method does not idenfity the Q-peaks and S-peaks. However, it provides more information regarding the boundaries of the waves

Visually, except a few exception, CWT method is doing a great job. However, the P-waves boundaries are not very clearly identified here.

Last but not least, we will look at the third method in NeuroKit ecg_delineate() function: the discrete wavelet method.

Discrete Wavelet Method (DWT) - default method#

# Delineate the ECG signal
signal_dwt, waves_dwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=3000, 
                                         method="dwt", 
                                         show=True, 
                                         show_type='all')
../../_images/bbfee4b7c7508a91881d03f3aa688199ba346d32cf8814778dd0a70a4a3b3dbc.png
# Visualize P-peaks and T-peaks
signal_dwt, waves_dwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=3000, 
                                         method="dwt", 
                                         show=True, 
                                         show_type='peaks')
../../_images/40c996db4039c4ff15dc411dfce8da3ceddb83688d25ac77dc0258854045c6ee.png
# visualize T-wave boundaries
signal_dwt, waves_dwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=3000, 
                                         method="dwt", 
                                         show=True, 
                                         show_type='bounds_T')
../../_images/8438e9509d9cf49e026d5d8b903162ce30c3a159101cba5588a5b55e62c640a7.png
# Visualize P-wave boundaries
signal_dwt, waves_dwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=3000, 
                                         method="dwt", 
                                         show=True, 
                                         show_type='bounds_P')
../../_images/ba6bb8daf305fd9bc9929eccf477102a7fedaa005b5b896df706c8a5bce1c6b8.png
# Visualize R-wave boundaries
signal_dwt, waves_dwt = nk.ecg_delineate(ecg_signal, 
                                         rpeaks, 
                                         sampling_rate=3000, 
                                         method="dwt", 
                                         show=True, 
                                         show_type='bounds_R')
../../_images/6772a23d7827398632c1f27eaa2a06e121a4268b0956da4d3119d38a854cb0f8.png

Visually, from the plots above, the delineated outputs of DWT appear to be more accurate than CWT, especially for the P-peaks and P-wave boundaries.

Overall, for this signal, the peak and DWT methods seem to be superior to the CWT.