import numpy as np
from pebm.ebm.FiducialPoints import FiducialPoints
from pebm.ebm.IntervalsDuration import extract_intervals_duration
from pebm.ebm.WavesCharacteristics import extract_waves_characteristics
from pebm.ebm.Statistics import statistics
from pebm._ErrorHandler import _check_shape_, WrongParameter
[docs]class Biomarkers:
def __init__(self, signal: np.array, fs, fiducials=None, matlab_path: str = None):
"""
:param signal: The ECG signal as a ndarray.
:param fs: The sampling frequency of the signal.
:param fiducials: Dictionary that includes indexes for each fiducial point
:param matlab_path: The indexes of the R- points of the ECG signal – optional input
"""
if fs <= 0:
raise WrongParameter("Sampling frequency should be strictly positive")
_check_shape_(signal, fs)
self.signal = signal
self.fs = fs
self.fiducials = fiducials
self.intervals_b = {}
self.waves_b = {}
self.intervals_statistics = {}
self.waves_statistics = {}
if fiducials is None:
fp = FiducialPoints(signal, fs)
fiducials = fp.wavedet(matlab_path)
self.fiducials = fiducials
[docs] def intervals(self):
"""
:returns:
*intervals_b: Dictionary that includes all the row data, for the intervals and segments biomarkers.
*intervals_statistics: Dictionary that includes the mean, median, min, max, iqr and std,
for every ‘interval’ biomarker.
Interval duration and segments:
P-waveint: Time interval between P-on and P-off.
PRint: Time interval between the P-on to the QRS-on.
PRseg: Time interval between the P-off to the QRS-on.
PRint2: Time interval between P-peak and R-peak as defined by Mao et al.
QRSint: Time interval between the QRS-on to the QRS-off.
QTint: Time interval between the QRS-on to the T-off.
QTcBint: Corrected QT interval (QTc) using Bazett’s formula.
QTcFriint: QTc using the Fridericia formula.
QTcFraint: QTc using the Framingham formula.
QTcHint: QTc using the Hodges formula.
T-waveint: Time interval between T-on and T-off.
TPseg: Time interval between T-off and P-on.
RRint: Time interval between sequential R-peaks.
Rdep: Time interval betweem Q-on and R-peak.
"""
fs = self.fs
fiducials = self.fiducials
signal = self.signal
if len(np.shape(signal)) == 2:
[ecg_len, ecg_num] = np.shape(signal)
intervals_b = {}
intervals_statistics = {}
for i in np.arange(ecg_num):
if np.sum(fiducials[i]['qrs']) == 0:
intervals_b[i] = np.nan
intervals_statistics[i] = np.nan
else:
intervals_b[i] = extract_intervals_duration(fs, fiducials[i])
intervals_statistics[i] = statistics(intervals_b[i])
elif len(np.shape(signal)) == 1:
if np.sum(fiducials[0]['qrs']) == 0:
intervals_b = np.nan
intervals_statistics = np.nan
else:
intervals_b = extract_intervals_duration(fs, fiducials[0])
intervals_statistics = statistics(intervals_b)
self.intervals_b = intervals_b
self.intervals_statistics = intervals_statistics
return self.intervals_b, self.intervals_statistics
[docs] def waves(self):
"""
:returns:
*waves_b: Dictionary that includes all the row data, for every ‘wave’ biomarker.
*waves_statistics: Dictionary that includes the mean, median, min, max, iqr and std, for every ‘wave’ biomarker.
P-wave: Amplitude difference between P-peak and P-off.
T-wave: Amplitude difference between T-peak on and T-off.
R-wave: R-peak amplitude.
P-waveArea: P-wave interval area defined as integral from the P-on to the P-off.
T-waveArea: T-wave interval area defined as integral from the T-on to the T-off.
QRSArea: QRS interval area defined as integral from the QRS-on to the QRS-off.
STseg: Amplitude difference between QRS-off and T-on.
J-point: Amplitude in 40ms after QRS-off as defined by Hollander et al.
"""
signal = self.signal
fs = self.fs
fiducials = self.fiducials
if len(np.shape(signal)) == 2:
[ecg_len, ecg_num] = np.shape(signal)
waves_b = {}
waves_statistics = {}
for i in np.arange(ecg_num):
if np.sum(fiducials[i]['qrs']) == 0:
waves_b[i] = np.nan
waves_statistics[i] = np.nan
else:
waves_b[i] = extract_waves_characteristics(signal[:,i], fs, fiducials[i])
waves_statistics[i] = statistics(waves_b[i])
elif len(np.shape(signal)) == 1:
if np.sum(fiducials[0]['qrs']) == 0:
waves_b = np.nan
waves_statistics = np.nan
else:
waves_b = extract_waves_characteristics(signal,fs, fiducials[0])
waves_statistics = statistics(waves_b)
self.waves_b = waves_b
self.waves_statistics = waves_statistics
return self.waves_b, self.waves_statistics
[docs] def isECG(self):
signal = self.signal
fs = self.fs
fiducials = self.fiducials
score = 0
#naive_score:
if len(np.shape(signal)) == 2:
[ecg_len, ecg_num] = np.shape(signal)
score = {}
for i in np.arange(ecg_num):
score[i] = self.calculate_isECG(fiducials[i])
elif len(np.shape(signal)) == 1:
score = self.calculate_isECG(fiducials[0])
return score
[docs] def calculate_isECG(self, fiducials):
score = 0
R_num = len(~np.isnan(fiducials['qrs']))
Pon_num = len(~np.isnan(fiducials['Pon']))
P_num = len(~np.isnan(fiducials['P']))
Poff_num = len(~np.isnan(fiducials['Poff']))
Q_num = len(~np.isnan(fiducials['QRSoff']))
S_num = len(~np.isnan(fiducials['QRSon']))
Ton_num = len(~np.isnan(fiducials['Ton']))
T_num = len(~np.isnan(fiducials['T']))
Toff_num = len(~np.isnan(fiducials['Toff']))
score = ~np.isnan(fiducials['P']) & ~np.isnan(fiducials['T']) & ~np.isnan(fiducials['QRSon']) & ~np.isnan(fiducials['QRSoff'])
return score