forked from ammsimmons/binorg_decoding
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSVM_ECOC_ERP_Decoding_2020.m
More file actions
executable file
·281 lines (183 loc) · 9.96 KB
/
SVM_ECOC_ERP_Decoding_2020.m
File metadata and controls
executable file
·281 lines (183 loc) · 9.96 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
% This is an updated function of Bae & Luck (2018) Decoding
% pipeline that utilizes a nested bin-epoched data structure.
% Refer to OSF: https://osf.io/29wre/
% NOTE: low-pass filtering to 6hz was applied to continuous EEG (prior to
% binning). % Code to be used on a bin-epoched dataset for one
% class only (e.g. Orientation)
% NOTE: This script requires 'fitcecoc' Matlab function.
%Edited by Aaron Simmons (UC Davis)
%Original Author: Gi-Yeul Bae (Arizona State University)
function SVM_ECOC_ERP_Decoding(subs)
% Parallelization: This script utilizes Matlab parallelization ...
% if parallelization is not possible, change "parfor" to "for-loop"
delete(gcp)
parpool
%% Subject List:
if nargin ==0
subs = [505, 506, 507, 508, 509, 510, 512, 514, 516, 517, 519, 520, 521, 523, 524, 525];
end
nSubs = length(subs); %number of subjects
%% Subject Filename(s)
% Load Subject Datafiles
dataLocation = pwd; % set directory of data set (default: pwd)
fName = ['/Decoding_BE_']; % subject filename (subject # appended at end)
% Save Decoding results
saveLocation = pwd; % set save directory of data set (default: pwd)
savName = ['/Orientation_Results_ERPbased_'];
%% Parameters to set
% Main structure is svmECOC. The output file is composed of svmECOC struct
svmECOC.nBins = 16; % # of stimulus bins
svmECOC.nIter = 10; % # of iterations
svmECOC.nBlocks = 3; % # of blocks for cross-validation
svmECOC.dataTime.pre = -500; % Set epoch start (from imported data)
svmECOC.dataTime.post = 1496; % Set epoch end (from imported data)
svmECOC.time = -500:20:1496; % time points of interest in the analysis
% the time-points of interest is a resampling of preprocessed data
% the time steps (20ms) here change the data to 1 data point per 20ms
% Timepoints/sampling rate of preprocessed/loaded data
svmECOC.window = 4; % 1 data point per 4 ms in the preprocessed data
svmECOC.Fs = 250; % samplring rate of in the preprocessed data for filtering
% Equalalize trials across bins?
equalT = 1; % equal trials acr bin = 1; use as many trials within bin = 0
% The electrode channel list is mapped 1:1 with respect to your
% electrode channel configuration (e.g., channel 1 is FP1 in our EEG cap)
% Check if the label for each electrode corresponds to the electrode
% labeling of your own EEG system
ReleventChan = sort([2,3,4,18,19, 5,6,20, 7,8,21,9,10,11,12,13,14, 22,23,24,25,26,27, 15,16,1,17]); %electrodes
% here, we removed external EOG electrodes & mastoid ref, for 27 electrodes
svmECOC.nElectrodes = length(ReleventChan); % # of electrode included in the analysis
% for brevity in analysis
nBins = svmECOC.nBins;
nIter = svmECOC.nIter;
nBlocks = svmECOC.nBlocks;
dataTime = svmECOC.dataTime;
times = svmECOC.time;
nElectrodes = svmECOC.nElectrodes;
nSamps = length(svmECOC.time);
%% Step 9: Loop through participants
for s = 1:nSubs %decoding is performed within each subject independently
sn = subs(s);
%progress output to command window
fprintf('Subject:\t%d\n',sn)
%% Parameters to set per subject
currentSub = num2str(sn);
loadThis = strcat(dataLocation,fName,currentSub,'.mat');
% where to save decoding output file name.
% File (.mat) will contain decoding results.
OutputfName = strcat(saveLocation,savName,currentSub,'.mat');
%% Data Loading/preallocation per subject
% loads bin_organized data
load(loadThis)
% grab EEG data from bin-list organized data
eegs = binorgEEG.binwise_data;
nPerBin = binorgEEG.n_trials_this_bin;
% set up time points
% we create index of timpoint of interests from original data
tois = ismember(dataTime.pre:4:dataTime.post,svmECOC.time);
% # of trials
svmECOC.nTrials = (sum(nPerBin));
% Preallocate Matrices
svm_predict = nan(nIter,nSamps,nBlocks,nBins); % a matrix to save prediction from SVM
tst_target = nan(nIter,nSamps,nBlocks,nBins); % a matrix to save true target values
% create svmECOC.block structure to save block assignments
svmECOC.blocks=struct();
if nBins ~= max(size(eegs))
error('Error. nBins dne # of bins in dataset!');
% Code to be used on a bin-epoched dataset for one
% class only (e.g. Orientation). Users may
% try to bin multiple classes in one BDF.
% This is not currently allowed.
end
%% Step 8: Loop through each iteration with random shuffling
tic % start timing iteration loop
for iter = 1:nIter
%% Obtaining AVG. EEG (ERP spatial dist)
% within each block at each time point
% stored in blockDat_filtData
%preallocate & rewrite per each iteration
blockDat_filtData = nan(nBins*nBlocks,nElectrodes,nSamps);
labels = nan(nBins*nBlocks,1); % bin labels for averaged & filtered EEG data
blockNum = nan(nBins*nBlocks,1); % block numbers for averaged & filtered EEG data
bCnt = 1; %initialize binblock counter
% this code operates and creates ERPs at each bin
% and organizes that into approprate block
%% shuffling, binning, & averaging
for bin = 1:nBins
if equalT == 1 %equal trials across bins
% find bin with fewest trial
minCnt = min(nPerBin);
% max # of trials such that # of trials for each bin ...
% can be equated within each block
nPerBinBlock = floor(minCnt/nBlocks);
%Obtain index within each shuffled bin
shuffBin = randperm((nPerBinBlock*nBlocks))';
%Preallocate arrays
blocks = nan(size(shuffBin));
shuffBlocks = nan(size(shuffBin));
%arrage block order within bins
x = repmat((1:nBlocks)',nPerBinBlock,1);
shuffBlocks(shuffBin) = x;
else
% We will use as many possible trials per
% bin having accounted already for artifacts
%Drop excess trials
nPerBinBlock = floor(nPerBin/nBlocks); %array for nPerBin
%Obtain index within each shuffled bin
shuffBin = randperm((nPerBinBlock(bin))*nBlocks)';
%Preallocate arrays
blocks = nan(size(shuffBin));
shuffBlocks = nan(size(shuffBin));
%arrage block order within bins
x = repmat((1:nBlocks)',nPerBinBlock(bin),1);
shuffBlocks(shuffBin) = x;
end
%unshuffled block assignment
blocks(shuffBin) = shuffBlocks;
% save block assignment
blockID = ['iter' num2str(iter) 'bin' num2str(bin)];
svmECOC.blocks.(blockID) = blocks; % block assignment
%create ERP average and place into blockDat_filtData struct
%grab current bin with correct # of electrodes & samps
eeg_now = eegs(bin).data(ReleventChan,:,:);
%here, we create blockDat_filtData.
%this results in nBins*nBlocks amount of ERP spatial
%distributions (across nChans) at each sample/timepoint
%% Step 1: computing ERPs based on random subset of trials for each block
for bl = 1:nBlocks
blockDat_filtData(bCnt,:,:) = squeeze(mean(eeg_now(:,tois,blocks==bl),3));
labels(bCnt) = bin; %used for arranging classification obj.
blockNum(bCnt) = bl;
bCnt = bCnt+1;
end
end
%% Step 7: Loop through each timepoint
% Do SVM_ECOC at each time point
parfor t = 1:nSamps
% grab data for timepoint t
toi = ismember(times,times(t)-svmECOC.window/2:times(t)+svmECOC.window/2);
% average across time window of interest
% here, you can parse nBin*nBlock across all channels (ERP spatial dist)
dataAtTimeT = squeeze(mean(blockDat_filtData(:,:,toi),3));
%% Step 6: Cross-validation for-loop
for i=1:nBlocks % loop through blocks, holding each out as the test set
%% Step 2 & Step 4: Assigning training and testing data sets
trnl = labels(blockNum~=i); % training labels
tstl = labels(blockNum==i); % test labels
trnD = dataAtTimeT(blockNum~=i,:); % training data
tstD = dataAtTimeT(blockNum==i,:); % test data
%% Step 3: Training
mdl = fitcecoc(trnD,trnl, 'Coding','onevsall','Learners','SVM' ); %train support vector mahcine
%% Step 5: Testing
LabelPredicted = predict(mdl, tstD); % predict classes for new data
svm_predict(iter,t,i,:) = LabelPredicted; % save predicted labels
tst_target(iter,t,i,:) = tstl; % save true target labels
end % end of block: Step 6: cross-validation
end % end of time points: Step 7: Decoding each time point
end % end of iteration: Step 8: iteration with random shuffling
toc % stop timing the iteration loop
%output decoding results in main svmECOC structure
svmECOC.targets = tst_target;
svmECOC.modelPredict = svm_predict;
svmECOC.nBlocks = nBlocks;
save(OutputfName,'svmECOC','-v7.3');
end % end of subject loop: Step 9: Decoding for each participant