Switch to unified view

a b/hrv_time_domain_analysis.py
1
#!/usr/bin/python3
2
# Performs heartrate variation timedomain analysis
3
#
4
# It calculates the normalised RMSSD during sitting
5
# and maths.
6
#
7
# This comparison is then run with
8
# - ground truth (hand corrected R time stamps)
9
# - Wavelet detector
10
# - Pan Tompkins detector
11
#
12
# Via the commandline argument one can choose
13
# Einthoven II or the ECG from the Chest strap
14
#
15
16
import numpy as np
17
import matplotlib.pyplot as plt
18
import scipy.stats as stats
19
from hrv import HRV
20
from ecgdetectors import Detectors
21
22
path_gu_ecg_database = '../dataset_716'
23
24
import sys
25
sys.path.insert(0, path_gu_ecg_database + r'/example_code')
26
from ecg_gla_database import Ecg
27
28
29
data_path = path_gu_ecg_database + r'/experiment_data'
30
31
maths_rr_sd = []
32
maths_error_rr_sd = []
33
maths_true_sd = []
34
35
sitting_rr_sd = []
36
sitting_error_rr_sd = []
37
sitting_true_sd = []
38
39
total_subjects = 25
40
subject = []
41
42
if len(sys.argv) < 2:
43
    print("Specify 'e' for Einthoven or 'v' for chest strap ECG.")
44
    exit(1)
45
46
for i in range(total_subjects):
47
#for i in range(2):
48
    print(i)
49
    sitting_class = Ecg(data_path, i, 'sitting')
50
    sitting_class.filter_data()
51
    maths_class = Ecg(data_path, i, 'maths')
52
    maths_class.filter_data()
53
54
    detectors = Detectors(sitting_class.fs)
55
56
    if sitting_class.anno_cs_exists and maths_class.anno_cs_exists and (i != 11):
57
        subject.append(i)
58
59
        hrv_class = HRV(sitting_class.fs)
60
61
        if "e" in sys.argv[1]:
62
            ecg_channel_sitting = sitting_class.einthoven_II
63
            ecg_channel_maths = maths_class.einthoven_II
64
        elif "v" in sys.argv[1]:
65
            ecg_channel_sitting = sitting_class.cs_V2_V1
66
            ecg_channel_maths = maths_class.cs_V2_V1
67
        else:
68
            print("Bad argument. Specify 'e' for Einthoven or 'v' for the Chest strap.")
69
            exit(1)
70
71
        r_peaks = detectors.swt_detector(ecg_channel_sitting)
72
        sitting_rr_sd.append(hrv_class.RMSSD(r_peaks,True))
73
        r_peaks = detectors.swt_detector(ecg_channel_maths)
74
        maths_rr_sd.append(hrv_class.RMSSD(r_peaks,True))
75
76
        sitting_error_rr = detectors.pan_tompkins_detector(ecg_channel_sitting)
77
        sitting_error_rr_sd.append(hrv_class.RMSSD(sitting_error_rr,True))
78
79
        maths_error_rr = detectors.pan_tompkins_detector(ecg_channel_maths)
80
        maths_error_rr_sd.append(hrv_class.RMSSD(maths_error_rr,True))
81
82
        maths_true_rr = maths_class.anno_cs
83
        maths_true_sd.append(hrv_class.RMSSD(maths_true_rr,True))
84
        
85
        sitting_true_rr = sitting_class.anno_cs
86
        sitting_true_sd.append(hrv_class.RMSSD(sitting_true_rr,True))
87
88
89
subject = np.array(subject)
90
width = 0.4
91
92
fig, ax = plt.subplots()
93
rects1 = ax.bar(subject+(0*width), sitting_true_sd, width)
94
rects2 = ax.bar(subject+(1*width), maths_true_sd, width)
95
96
ax.set_ylabel('SDNN (s)')
97
ax.set_xlabel('Subject')
98
ax.set_title('HRV for sitting and maths test')
99
ax.set_xticks(subject + width)
100
ax.set_xticklabels(subject)
101
ax.legend((rects1[0], rects2[0]), ('sitting', 'maths' ))
102
103
plt.figure()
104
105
# now let's do stats with no error
106
107
avg_sitting_rr_sd = np.average(sitting_rr_sd)
108
sd_sitting_rr_sd = np.std(sitting_rr_sd)
109
110
avg_maths_rr_sd = np.average(maths_rr_sd)
111
sd_maths_rr_sd = np.std(maths_rr_sd)
112
113
plt.bar(['sitting','maths'],[avg_sitting_rr_sd,avg_maths_rr_sd],yerr=[sd_sitting_rr_sd,sd_maths_rr_sd],align='center', alpha=0.5, ecolor='black', capsize=10)
114
plt.ylim([0,0.15])
115
plt.title("WAVELET: Sitting vs Maths")
116
plt.ylabel('nRMSSD')
117
118
# and stats with error
119
120
avg_sitting_error_rr_sd = np.average(sitting_error_rr_sd)
121
sd_sitting_error_rr_sd = np.std(sitting_error_rr_sd)
122
123
avg_maths_error_rr_sd = np.average(maths_error_rr_sd)
124
sd_maths_error_rr_sd = np.std(maths_error_rr_sd)
125
126
avg_sitting_true_sd = np.average(sitting_true_sd)
127
sd_sitting_true_sd = np.std(sitting_true_sd)
128
129
avg_maths_true_sd = np.average(maths_true_sd)
130
sd_maths_true_sd = np.std(maths_true_sd)
131
132
plt.figure()
133
134
plt.bar(['sitting','maths'],[avg_sitting_error_rr_sd,avg_maths_error_rr_sd],yerr=[sd_sitting_error_rr_sd,sd_maths_error_rr_sd],align='center', alpha=0.5, ecolor='black', capsize=10)
135
plt.ylim([0,0.15])
136
plt.title("Pan Tompkins DETECTOR: Sitting vs Maths")
137
plt.ylabel('nRMSSD')
138
139
plt.figure()
140
141
plt.bar(['sitting','maths'],[avg_sitting_true_sd,avg_maths_true_sd],yerr=[sd_sitting_true_sd,sd_maths_true_sd],align='center', alpha=0.5, ecolor='black', capsize=10)
142
plt.ylim([0,0.15])
143
plt.title("GROUND TRUTH: Sitting vs Maths")
144
plt.ylabel('nRMSSD')
145
146
t,p = stats.ttest_ind(sitting_true_sd,maths_true_sd,equal_var=False)
147
print("GROUND TRUTH (sitting vs maths): p=",p)
148
149
t,p = stats.ttest_ind(sitting_rr_sd,maths_rr_sd,equal_var=False)
150
print("WAVELET (sitting vs maths): p=",p)
151
152
t,p = stats.ttest_ind(sitting_error_rr_sd,maths_error_rr_sd,equal_var=False)
153
print("Pan Tompkins DETECTOR: (sitting vs maths): p=",p)
154
155
t,p = stats.ttest_ind(sitting_true_sd,sitting_rr_sd,equal_var=False)
156
print("Sitting: Wavelet vs ground truth, p=",p)
157
158
t,p = stats.ttest_ind(sitting_true_sd,sitting_error_rr_sd,equal_var=False)
159
print("Sitting: PT vs ground truth, p=",p)
160
161
t,p = stats.ttest_ind(maths_true_sd,maths_rr_sd,equal_var=False)
162
print("Maths: Wavelet vs ground truth, p=",p)
163
164
t,p = stats.ttest_ind(maths_true_sd,maths_error_rr_sd,equal_var=False)
165
print("Maths: PT vs ground truth, p=",p)
166
167
plt.show()