|
15 | 15 | %
|
16 | 16 | % Outputs:
|
17 | 17 | % feat_st - structure containing features
|
18 |
| -% feats_per_epoch - cell of features x channel x epoch x frequency_band |
| 18 | +% feats_per_epoch - table of features per epoch; in long format, with columns: |
| 19 | +% [time - channel - freq. band - value - feature name] |
| 20 | +% start_time_sec: start time of the epoch (seconds) |
| 21 | +% channel: name of channel (string) |
| 22 | +% freq_band: number of frequency band (string, e.g. FB1) |
| 23 | +% feature_value: value of the feature (real number, scalar) |
| 24 | +% feature: name of the feature (string) |
19 | 25 | %
|
20 | 26 | %
|
21 | 27 | % Example:
|
|
32 | 38 | % John M. O' Toole, University College Cork
|
33 | 39 | % Started: 07-04-2016
|
34 | 40 | %
|
35 |
| -% last update: Time-stamp: <2020-04-23 16:24:02 (otoolej)> |
| 41 | +% last update: Time-stamp: <2020-08-17 18:08:33 (otoolej)> |
36 | 42 | %-------------------------------------------------------------------------------
|
37 |
| -function [feat_st,feats_per_epochs]=generate_all_features(fname,channel_names,feat_set, ... |
38 |
| - return_feat_epoch) |
| 43 | +function [feat_st, feats_all_epochs_tb] = generate_all_features(fname, channel_names, feat_set, ... |
| 44 | + return_feat_epoch) |
39 | 45 | if(nargin<2 || isempty(channel_names)), channel_names=[]; end
|
40 | 46 | if(nargin<3 || isempty(feat_set)), feat_set=[]; end
|
41 | 47 | if(nargin<4 || isempty(return_feat_epoch)), return_feat_epoch = false; end
|
|
102 | 108 | end
|
103 | 109 |
|
104 | 110 | N_feats=length(feat_set);
|
| 111 | +feats_all_epochs_tb = []; |
105 | 112 |
|
106 | 113 | % A) iterate over features
|
107 | 114 | for n=1:N_feats
|
|
117 | 124 | if( any(strcmp({'amplitude','spectral','rEEG','FD'},feat_group)) )
|
118 | 125 |
|
119 | 126 | % B) iterate over channels
|
120 |
| - feats_channel=[]; x_epochs=[]; |
| 127 | + feats_channel=[]; x_epochs=[]; feats_tbl = []; |
121 | 128 | for c=1:N_channels
|
122 |
| - x_epochs=overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP); |
| 129 | + [x_epochs, epoch_start_times] = overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP); |
123 | 130 | N_epochs=size(x_epochs,1);
|
124 | 131 |
|
125 | 132 | % C) iterate over epochs
|
|
147 | 154 | end
|
148 | 155 | % if want to return feature estimated over all epochs:
|
149 | 156 | if(return_feat_epoch)
|
150 |
| - feats_per_epochs{n}(c,:,:)=feats_epochs; |
| 157 | + % feats_per_epochs{n}(c,:,:)=feats_epochs; |
| 158 | + |
| 159 | + % create table with features and start time of epoch: |
| 160 | + fb_names = arrayfun(@(x) ['FB' num2str(x)], 1:size(feats_epochs, 2), 'un', false); |
| 161 | + tb = array2table([epoch_start_times' feats_epochs], ... |
| 162 | + 'VariableNames', ['start_time_sec', fb_names]); |
| 163 | + % add channel: |
| 164 | + tb.channel(:) = string(ch_labels{c}); |
| 165 | + |
| 166 | + % convert from wide to long format for frequency bands: |
| 167 | + tb = stack(tb, fb_names, 'newDataVariableName', {'feature_value'}, ... |
| 168 | + 'IndexVariableName', {'freq_band'}); |
| 169 | + |
| 170 | + feats_tbl = [feats_tbl; tb]; |
151 | 171 | end
|
152 | 172 |
|
153 | 173 | % median over all epochs
|
154 | 174 | feats_channel(c,:)=nanmedian(feats_epochs, 1);
|
155 | 175 | end
|
156 | 176 | % and median over all channels:
|
157 | 177 | feat_st.(char(feat_set{n}))=nanmedian(feats_channel, 1);
|
| 178 | + |
| 179 | + |
| 180 | + if(return_feat_epoch) |
| 181 | + % add feature name and combine: |
| 182 | + feats_tbl.feature(:) = string(feat_set{n}); |
| 183 | + feats_all_epochs_tb = [feats_all_epochs_tb; feats_tbl]; |
| 184 | + end |
| 185 | + |
158 | 186 |
|
159 | 187 | %---------------------------------------------------------------------
|
160 | 188 | % CONNECTIVITY FEATURES
|
|
164 | 192 |
|
165 | 193 | x_epochs=[];
|
166 | 194 | for c=1:N_channels
|
167 |
| - x_epochs(c,:,:)=overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP); |
| 195 | + if(c == N_channels) |
| 196 | + [x_epochs(c,:,:), epoch_start_times] = ... |
| 197 | + overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP); |
| 198 | + else |
| 199 | + x_epochs(c,:,:) = overlap_epochs(eeg_data(c,:)',Fs,EPOCH_LENGTH,EPOCH_OVERLAP); |
| 200 | + end |
168 | 201 | end
|
169 | 202 | N_epochs=size(x_epochs,2);
|
170 | 203 |
|
|
186 | 219 | % median over all epochs
|
187 | 220 | feat_st.(char(feat_set{n}))=nanmedian(feats_epochs, 1);
|
188 | 221 |
|
| 222 | + % if want to return feature estimated over all epochs: |
| 223 | + if(return_feat_epoch) |
| 224 | + % create table with features and start time of epoch: |
| 225 | + fb_names = arrayfun(@(x) ['FB' num2str(x)], 1:size(feats_epochs, 2), 'un', false); |
| 226 | + tb = array2table([epoch_start_times' feats_epochs], ... |
| 227 | + 'VariableNames', ['start_time_sec', fb_names]); |
| 228 | + % add channel: |
| 229 | + tb.channel(:) = NaN; |
| 230 | + |
| 231 | + % convert from wide to long format for frequency bands: |
| 232 | + tb = stack(tb, fb_names, 'newDataVariableName', {'feature_value'}, ... |
| 233 | + 'IndexVariableName', {'freq_band'}); |
| 234 | + |
| 235 | + % add feature name and combine: |
| 236 | + tb.feature(:) = string(feat_set{n}); |
| 237 | + feats_all_epochs_tb = [feats_all_epochs_tb; tb]; |
| 238 | + end |
| 239 | + |
| 240 | + |
189 | 241 |
|
190 | 242 | %---------------------------------------------------------------------
|
191 | 243 | % inter-burst interval features
|
|
208 | 260 |
|
209 | 261 |
|
210 | 262 |
|
211 |
| -function [x_epochs] = overlap_epochs(x, Fs, L_window, overlap, window_type) |
| 263 | +function [x_epochs, start_times] = overlap_epochs(x, Fs, L_window, overlap, window_type) |
212 | 264 | %---------------------------------------------------------------------
|
213 | 265 | % overlapping epochs in one matrix
|
214 | 266 | %---------------------------------------------------------------------
|
|
232 | 284 |
|
233 | 285 |
|
234 | 286 | x_epochs = NaN(N_epochs, L_epoch);
|
| 287 | +start_times = NaN(1, N_epochs); |
235 | 288 | for k = 1:N_epochs
|
236 | 289 | nf = nw + (k - 1) * L_hop;
|
237 | 290 | % zero-pad if outside x:
|
238 | 291 | nf = nf(ismember(nf, ix)) + 1;
|
239 | 292 | i_nf = 1:length(nf);
|
| 293 | + start_times(k) = (min(nf) - 1) / Fs; |
240 | 294 |
|
241 | 295 | x_epochs(k, i_nf) = x(nf) .* win_epoch(i_nf).';
|
242 | 296 | end
|
0 commit comments