Example analysis of a PPG/ECG waveform using the vital_sqi package

The following notebook shows an example of PPG/ECG waveform processing using the vital_sqi package. The aim of the package is to automate signal quality classification for PPG/ECG waveforms. It is achieved by computing various signal quality indices for each signal segment and using them to form a decision.

The pipeline can be briefly summarized as follows:

  1. Load dataset under analysis

  2. Preprocess and segment the dataset

  3. Compute SQI for each dataset segment

  4. Make decision for each segment

Global Imports

[1]:
import numpy as np
import warnings
import os
import pandas as pd
import matplotlib.pyplot as plt
import vital_sqi
from vital_sqi.data.signal_io import ECG_reader,PPG_reader

Start by importing the signal via the PPG_reader function

The function expects a .csv or similar data format with named columns. The column names are used to separate between data column, timestamp columns and any additional information columns. This returns a SignalSQI class that is compatible with other vital_sqi package functions, the main class members of interest are: * signals: an ndarray of shape (m, n) where m is the number of rows and n is the number of channels of the signal * sqis: an ndarray of shape (m, n) where m is the number of signal segments, n is the number of SQIs. * sampling_rate: sampling rate in hertz (Hz)

[2]:
from vital_sqi.pipeline.pipeline_highlevel import get_ppg_sqis

file_in = os.path.abspath('test_data/ppg_smartcare.csv') #FIle input location
sqi_dict = os.path.abspath('test_data/sqi_dict.json') #input dictionary -> which sqi features
segments, signal_obj = get_ppg_sqis(file_in,
                                    signal_idx=['PLETH'],
                                    timestamp_idx=['TIMESTAMP_MS'],
                                    sqi_dict_filename = sqi_dict)
100%|██████████| 21/21 [00:02<00:00,  7.20it/s]
[3]:
signal_obj.sqis[0].describe()
[3]:
perfusion kurtosis_1 skewness_mean_sqi skewness_median_sqi skewness_std_sqi entropy kurtosis_2 skewness_1 entropy_2 signal_to_noise ... hfnu_sqi total_power_sqi vlf_sqi triangular_index_sqi tinn_sqi csi_sqi cvi_sqi Modified_csi_sqi start_idx end_idx
count 21.000000 21.000000 21.000000 21.000000 21.000000 21.000000 21.000000 21.000000 21.000000 21.0 ... 21.000000 21.000000 21.000000 21.000000 0.0 21.000000 21.000000 21.000000 21.000000 21.000000
mean 187.404653 -1.360325 0.073129 0.080781 0.120721 4.343845 -0.966196 0.086825 7.777617 -1.0 ... 53.024653 2457.690263 587.473086 5.004001 NaN 1.596050 4.751727 765.778807 30000.000000 33000.000000
std 20.712381 0.032043 0.058008 0.035630 0.115958 0.023989 0.395934 0.113371 0.159168 0.0 ... 23.529422 3642.831489 998.300743 1.670246 NaN 0.695445 0.878519 742.250417 18614.510469 18614.510469
min 112.313216 -1.396156 -0.086416 0.009774 0.019471 4.313126 -1.349569 -0.318782 7.103965 -1.0 ... 17.870115 109.769282 8.139479 2.857143 NaN 0.822317 3.590477 59.345391 0.000000 3000.000000
25% 183.541473 -1.383222 0.058113 0.061718 0.032456 4.327253 -1.277692 0.063396 7.783738 -1.0 ... 35.720578 222.530802 57.790560 3.555556 NaN 1.161429 3.868977 119.530778 15000.000000 18000.000000
50% 186.970824 -1.367053 0.070987 0.077784 0.077805 4.334973 -1.103788 0.081037 7.809623 -1.0 ... 54.651397 811.417421 252.630297 4.250000 NaN 1.418324 5.074484 562.534403 30000.000000 33000.000000
75% 195.615534 -1.354891 0.114273 0.108330 0.202787 4.367132 -0.822027 0.132159 7.826186 -1.0 ... 70.309215 2499.129760 493.237543 6.500000 NaN 1.885878 5.541223 1183.176936 45000.000000 48000.000000
max 215.095923 -1.287866 0.147944 0.146486 0.455517 4.387753 0.067313 0.269913 7.893871 -1.0 ... 89.929842 11283.942003 4052.382611 8.600000 NaN 3.300646 6.005650 2769.520662 60000.000000 63000.000000

8 rows × 79 columns

Get decision

[4]:
from vital_sqi.pipeline.pipeline_highlevel import get_qualified_ppg
rule_dict_filename = os.path.abspath('test_data/rule_dict_test.json') #  location dictionary contain threshold
ruleset_order = {3: 'skewness_1',
                  2: 'entropy',
                  1: 'perfusion'}
file_in = os.path.abspath('test_data/ppg_smartcare.csv') #FIle input location
sqi_dict = os.path.abspath('test_data/sqi_dict.json') #input dictionary -> which sqi features
signal_obj = get_qualified_ppg(file_in,
                                    signal_idx=['PLETH'],
                                    timestamp_idx=['TIMESTAMP_MS'],
                                    sqi_dict_filename = sqi_dict,
                                    rule_dict_filename = rule_dict_filename,
                                    ruleset_order=ruleset_order,
                                    output_dir=None)
100%|██████████| 21/21 [00:02<00:00,  9.76it/s]
0it [00:00, ?it/s]
100%|██████████| 21/21 [00:00<00:00, 57.97it/s]