Switch to unified view

a b/featurebased-approach/PredictTestSet.m~
1
function PredictTestSet(recordName,varargin)
2
% This function predicts the corresponding ECG class of a given record
3
% using our feature based approach.
4
%
5
%
6
% Input
7
%   recordName: string specifying the record name to process
8
%   (optional inputs)
9
%           - useSegments:       segment signals into windows (bool)?
10
%           - windowSize:        size of window used in segmenting record
11
%           - percentageOverlap: overlap between windows
12
% --
13
% ECG classification from single-lead segments using Deep Convolutional Neural
14
% Networks and Feature-Based Approaches - December 2017
15
%
16
% Released under the GNU General Public License
17
%
18
% Copyright (C) 2017  Fernando Andreotti, Oliver Carr
19
% University of Oxford, Insitute of Biomedical Engineering, CIBIM Lab - Oxford 2017
20
% fernando.andreotti@eng.ox.ac.uk
21
%
22
%
23
% For more information visit: https://github.com/fernandoandreotti/cinc-challenge2017
24
%
25
% Referencing this work
26
%
27
% Andreotti, F., Carr, O., Pimentel, M.A.F., Mahdi, A., & De Vos, M. (2017).
28
% Comparing Feature Based Classifiers and Convolutional Neural Networks to Detect
29
% Arrhythmia from Short Segments of ECG. In Computing in Cardiology. Rennes (France).
30
%
31
% Last updated : December 2017
32
%
33
% This program is free software: you can redistribute it and/or modify
34
% it under the terms of the GNU General Public License as published by
35
% the Free Software Foundation, either version 3 of the License, or
36
% (at your option) any later version.
37
%
38
% This program is distributed in the hope that it will be useful,
39
% but WITHOUT ANY WARRANTY; without even the implied warranty of
40
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41
% GNU General Public License for more details.
42
%
43
% You should have received a copy of the GNU General Public License
44
% along with this program.  If not, see <http://www.gnu.org/licenses/>.
45
46
rng(1); % For reproducibility
47
48
%% Optional params
49
optargs = {1 10 0.8};  % default values for input arguments
50
newVals = cellfun(@(x) ~isempty(x), varargin);
51
optargs(newVals) = varargin(newVals);
52
[useSegments, windowSize, percentageOverlap] = optargs{:};
53
clear optargs newVals
54
55
%% Loading signal
56
[~,signal,fs,~]=rdmat(recordName);
57
disp(['Processing ' recordName '...'])
58
if size(signal,1) < size(signal,2); signal = signal'; end % column vector
59
if any(isnan(signal))
60
    signal = inpaint_nans(signal);                    % function to remove NaNs
61
end
62
signalraw =  signal;
63
64
65
% Parameters
66
NFEAT = 171; % number of features used
67
NFEAT_hrv = 113;
68
69
70
%% Initialize loop
71
% Wide BP
72
Fhigh = 5;  % highpass frequency [Hz]
73
Flow = 45;   % low pass frequency [Hz]
74
Nbut = 10;     % order of Butterworth filter
75
d_bp= design(fdesign.bandpass('N,F3dB1,F3dB2',Nbut,Fhigh,Flow,fs),'butter');
76
[b_bp,a_bp] = tf(d_bp);
77
78
% Narrow BP
79
Fhigh = 1;  % highpass frequency [Hz]
80
Flow = 100;   % low pass frequency [Hz]
81
Nbut = 10;     % order of Butterworth filter
82
d_bp= design(fdesign.bandpass('N,F3dB1,F3dB2',Nbut,Fhigh,Flow,fs),'butter');
83
[b_bp2,a_bp2] = tf(d_bp);
84
clear Fhigh Flow Nbut d_bp
85
86
%% Preprocessing
87
signal = filtfilt(b_bp,a_bp,signal);             % filtering narrow
88
signal = detrend(signal);                        % detrending (optional)
89
signal = signal - mean(signal);
90
signal = signal/std(signal);                     % standardizing
91
signalraw = filtfilt(b_bp2,a_bp2,signalraw);     % filtering wide
92
signalraw = detrend(signalraw);                  % detrending (optional)
93
signalraw = signalraw - mean(signalraw);
94
signalraw = signalraw/std(signalraw);        % standardizing
95
disp(['Preprocessed ' recordName '...'])
96
97
% Figuring out if segmentation is used
98
if useSegments==1
99
    WINSIZE = windowSize; % window size (in sec)
100
    OLAP = percentageOverlap;
101
else
102
    WINSIZE = length(signal)/fs;
103
    OLAP=0;
104
end
105
startp = 1;
106
endp = WINSIZE*fs;
107
looptrue = true;
108
nseg = 1;
109
while looptrue
110
    % Conditions to stop loop
111
    if length(signal) < WINSIZE*fs
112
        endp = length(signal);
113
        looptrue = false;
114
        continue
115
    end
116
    if nseg > 1
117
        startp(nseg) = startp(nseg-1) + round((1-OLAP)*WINSIZE*fs);
118
        if length(signal) - endp(nseg-1) < 0.5*WINSIZE*fs
119
            endp(nseg) = length(signal);
120
        else
121
            endp(nseg) = startp(nseg) + WINSIZE*fs -1;
122
        end
123
    end
124
    if endp(nseg) == length(signal)
125
        looptrue = false;
126
        nseg = nseg - 1;
127
    end
128
    nseg = nseg + 1;
129
end
130
131
%% Obtain features for each available segment
132
fetbag = {};
133
parfor n = 1:nseg
134
    % Get signal of interest
135
    sig_seg = signal(startp(n):endp(n));
136
    sig_segraw = signalraw(startp(n):endp(n));
137
    
138
    % QRS detect
139
    [qrsseg,featqrs] = multi_qrsdetect(sig_seg,fs,[recordName '_s' num2str(n)]);
140
    
141
    % HRV features
142
    if length(qrsseg{end})>5 % if too few detections, returns zeros
143
        try
144
            feat_basic=HRV_features(sig_seg,qrsseg{end}./fs,fs);
145
            feats_poincare = get_poincare(qrsseg{end}./fs,fs);
146
            feat_hrv = [feat_basic, feats_poincare];
147
            feat_hrv(~isreal(feat_hrv)|isnan(feat_hrv)|isinf(feat_hrv)) = 0; % removing not numbers
148
        catch
149
            warning('Some HRV code failed.')
150
            feat_hrv = zeros(1,NFEAT_hrv);
151
        end
152
    else
153
        disp('Skipping HRV analysis due to shortage of peaks..')
154
        feat_hrv = zeros(1,NFEAT_hrv);
155
    end
156
    
157
    % Heart Rate features
158
    HRbpm = median(60./(diff(qrsseg{end})));
159
    %obvious cases: tachycardia ( > 100 beats per minute (bpm) in adults)
160
    feat_tachy = normcdf(HRbpm,120,20); % sampling from normal CDF
161
    %See e.g.   x = 10:10:200; p = normcdf(x,120,20); plot(x,p)
162
    
163
    %obvious cases: bradycardia ( < 60 bpm in adults)
164
    feat_brady = 1-normcdf(HRbpm,60,20);
165
    
166
    % SQI metrics
167
    feats_sqi = ecgsqi(sig_seg,qrsseg,fs);
168
    
169
    % Features on residual
170
    featsres = residualfeats(sig_segraw,fs,qrsseg{end});
171
    
172
    % Morphological features
173
    feats_morph = morphofeatures(sig_segraw,fs,qrsseg,[recordName '_s' num2str(n)]);
174
    
175
    
176
    feat_fer=[featqrs,feat_tachy,feat_brady,double(feats_sqi),featsres,feats_morph];
177
    feat_fer(~isreal(feat_fer)|isnan(feat_fer)|isinf(feat_fer)) = 0; % removing not numbers
178
    
179
    % Save features to table for training
180
    feats = [feat_hrv,feat_fer];
181
    fetbag{n} = feats;
182
end
183
feats = cell2mat(fetbag');
184
% Standardizing input
185
feats = feats - nanmean(feats);
186
feats = feats./nanstd(feats);
187
feats(isnan(feats)) = 0;
188
189
NFEAT=size(feats,2);
190
191
192
delete('gqrsdet*.*')
193
clear fetbag b_bp b_bp2 endp looptrue signal signalraw startp useSegments windowSize
194
clear WINSIZE a_bp a_bp2 nseg OLAP percentageOverlap
195
196
%% Summarizing features
197
198
disp('Summarizing features ..')
199
featsum(1,1:NFEAT)=nanmean(feats);
200
featsum(1,1*NFEAT+1:2*NFEAT)=nanstd(feats);
201
if size(featsum,1)>2
202
    PCAn=pca(feats);
203
    featsum(1,2*NFEAT+1:3*NFEAT)=PCAn(:,1);
204
    featsum(1,3*NFEAT+1:4*NFEAT)=PCAn(:,2);
205
else
206
    featsum(1,2*NFEAT+1:3*NFEAT)=NaN;
207
    featsum(1,3*NFEAT+1:4*NFEAT)=NaN;
208
end
209
featsum(1,4*NFEAT+1:5*NFEAT)=nanmedian(feats);
210
featsum(1,5*NFEAT+1:6*NFEAT)=iqr(feats);
211
featsum(1,6*NFEAT+1:7*NFEAT)=range(feats);
212
featsum(1,7*NFEAT+1:8*NFEAT)=min(feats);
213
featsum(1,8*NFEAT+1:9*NFEAT)=max(feats);
214
featsum(1,9*NFEAT+1:10*NFEAT)=prctile(feats,25);
215
featsum(1,10*NFEAT+1:11*NFEAT)=prctile(feats,50);
216
featsum(1,11*NFEAT+1:12*NFEAT)=prctile(feats,75);
217
HIL=hilbert(feats);
218
featsum(1,12*NFEAT+1:13*NFEAT)=real(HIL(1,:));
219
featsum(1,13*NFEAT+1:14*NFEAT)=abs(HIL(1,:));
220
featsum(1,14*NFEAT+1:15*NFEAT)=skewness(feats);
221
featsum(1,15*NFEAT+1:16*NFEAT)=kurtosis(feats);
222
223
featsum(isnan(featsum)) = 0;
224
225
%% Using classifiers on feature table
226
% Loading classififers
227
slashchar = char('/'*isunix + '\'*(~isunix));
228
mainpath = (strrep(which(mfilename),['preparation' slashchar mfilename '.m'],''));
229
addpath(genpath([mainpath(1:end-length(mfilename)-2) 'classifiers' slashchar])) % add subfunctions folder to path
230
231
load('ensTree.mat')
232
load('nNets.mat')
233
234
% Performing classification
235
[~,probTree] = predict(ensTree_best,featsum);
236
probNN = nnet_best(featsum')';
237
238
prob = mean([probTree;probNN]);
239
[~,class] = max(prob);
240
241
fprintf('Recording %s labels as %s with %d \% certainty ..',recordName,class,prob(class))
242