/
pop_mergeset.m
391 lines (349 loc) · 14.1 KB
/
pop_mergeset.m
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
% POP_MERGESET - Merge two or more datasets. If only one argument is given,
% a window pops up to ask for more arguments.
% Usage:
% >> OUTEEG = pop_mergeset( ALLEEG ); % use a pop-up window
% >> OUTEEG = pop_mergeset( ALLEEG, indices, keepall);
% >> OUTEEG = pop_mergeset( INEEG1, INEEG2, keepall);
%
% Inputs:
% INEEG1 - first input dataset
% INEEG2 - second input dataset
%
% else
% ALLEEG - array of EEG dataset structures
% indices - indices of EEG datasets to merge
%
% keepall - [0|1] 0 -> remove, or 1 -> preserve, ICA activations
% of the first dataset and recompute the activations
% of the merged data {default: 0}
%
% Outputs:
% OUTEEG - merged dataset
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2001
%
% See also: EEGLAB
% Copyright (C) 2001 Arnaud Delorme, Salk Institute, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.
% 01-25-02 reformated help & license -ad
% 01-26-02 change format for events and trial conditions -ad
function [INEEG1, com] = pop_mergeset( INEEG1, INEEG2, keepall)
com = '';
if nargin < 1
help pop_mergeset;
return;
end
if isempty(INEEG1)
error('needs at least two datasets');
end
if nargin < 2 && length(INEEG1) == 1
error('needs at least two datasets');
end
if nargin == 1
uilist = { { 'style' 'text' 'string' 'Dataset indices to merge' } ...
{ 'style' 'edit' 'string' '1' } ...
{ 'style' 'text' 'string' 'Preserve ICA weights of the first dataset ?' } ...
{ 'style' 'checkbox' 'string' '' } };
res = inputgui( 'uilist', uilist, 'geometry', { [3 1] [3 1] }, 'helpcom', 'pophelp(''pop_mergeset'')');
if isempty(res)
return;
end
INEEG2 = eval( [ '[' res{1} ']' ] );
keepall = res{2};
else
if nargin < 3
keepall = 0; % default
end
end
fprintf('Merging datasets...\n');
if ~isstruct(INEEG2) % if INEEG2 is a vector of ALLEEG indices
indices = INEEG2;
NEWEEG = eeg_retrieve(INEEG1, indices(1)); % why abandoned?
for index = 2:length(indices)
INEEG2 = eeg_retrieve(INEEG1, indices(index));
NEWEEG = pop_mergeset(NEWEEG, INEEG2, keepall); % recursive call
end
INEEG1 = NEWEEG;
else
% INEEG is an EEG struct
% check consistency
% -----------------
if INEEG1.nbchan ~= INEEG2.nbchan
error('The two datasets must have the same number of channels');
end
if INEEG1.srate ~= INEEG2.srate
error('The two datasets must have the same sampling rate');
end
if INEEG1.trials > 1 || INEEG2.trials > 1
if INEEG1.pnts ~= INEEG2.pnts
error('The two epoched datasets must have the same number of points');
end
if INEEG1.xmin ~= INEEG2.xmin
INEEG2.xmin = INEEG1.xmin;
fprintf('Warning: the two epoched datasets do not have the same time onset, adjusted');
end
if INEEG1.xmax ~= INEEG2.xmax
INEEG2.xmax = INEEG1.xmax;
fprintf('Warning: the two epoched datasets do not have the same time offset, adjusted');
end
end
% repopulate epoch field if necessary
% -----------------------------------
if INEEG1.trials > 1 && INEEG2.trials == 1
for iEvent = 1:length(INEEG2.event)
INEEG2.event(iEvent).epoch = 1;
end
end
if INEEG1.trials == 1 && INEEG2.trials > 1
for iEvent = 1:length(INEEG1.event)
INEEG1.event(iEvent).epoch = 1;
end
end
% type of boundary event
boundaryType = eeg_boundarytype(INEEG1, INEEG2);
% Merge the epoch field
% ---------------------
if INEEG1.trials > 1 || INEEG2.trials > 1
INEEGX = {INEEG1,INEEG2};
for n = 1:2
% make sure that both have an (appropriately-sized) epoch field
% -------------------------------------------------------------
if ~isfield(INEEGX{n},'epoch')
INEEGX{n}.epoch = repmat(struct(),[1,INEEGX{n}.trials]);
end
% make sure that the epoch number is correct in each dataset
% ----------------------------------------------------------
if ~isempty(INEEGX{n}.epoch) && length(INEEGX{n}.epoch) ~= INEEGX{n}.trials
disp('Warning: The number of trials does not match the length of the EEG.epoch field in one of');
disp(' the datasets. Its epoch info will be reset and derived from the respective events.');
INEEGX{n}.epoch = repmat(struct(),[1,INEEGX{n}.trials]);
end
end
for n=1:2
% purge all event-related epoch fields from each dataset (EEG.epoch.event* fields)
% --------------------------------------------------------------------------------
if isstruct(INEEGX{n}.epoch)
fn = fieldnames(INEEGX{n}.epoch);
INEEGX{n}.epoch = rmfield(INEEGX{n}.epoch,{fn{strmatch('event',fn)}});
% copy remaining field names to the other dataset
% -----------------------------------------------
for f = fieldnames(INEEGX{n}.epoch)'
if ~isfield(INEEGX{3-n}.epoch,f{1})
INEEGX{3-n}.epoch(1).(f{1}) = [];
end
end
% after this, both sets have an epoch field with the appropriate number of items
% and possibly some user-defined fields, but no event* fields.
end
end
% concatenate epochs
% ------------------
if isstruct(INEEGX{1}.epoch) && isstruct(INEEGX{2}.epoch)
if length(fieldnames(INEEGX{2}.epoch)) > 0
INEEGX{1}.epoch(end+1:end+INEEGX{2}.trials) = orderfields(INEEGX{2}.epoch, INEEGX{1}.epoch);
else
INEEGX{1}.epoch(end+1:end+INEEGX{2}.trials) = INEEGX{2}.epoch;
end
end
% and write back
INEEG1 = INEEGX{1};
INEEG2 = INEEGX{2};
INEEGX = {};
end
% Concatenate data
% ----------------
if INEEG1.trials > 1 || INEEG2.trials > 1
INEEG1.data(:,:,end+1:end+size(INEEG2.data,3)) = INEEG2.data(:,:,:);
else
INEEG1.data(:,end+1:end+size(INEEG2.data,2)) = INEEG2.data(:,:);
end
INEEG1.setname = 'Merged datasets';
INEEG1trials = INEEG1.trials;
INEEG2trials = INEEG2.trials;
INEEG1pnts = INEEG1.pnts;
INEEG2pnts = INEEG2.pnts;
if INEEG1.trials > 1 || INEEG2.trials > 1 % epoched data
INEEG1.trials = INEEG1.trials + INEEG2.trials;
else
INEEG1.pnts = INEEG1.pnts + INEEG2.pnts;
end
if isfield(INEEG1, 'reject')
INEEG1 = rmfield(INEEG1, 'reject' );
end
INEEG1.specicaact = [];
INEEG1.specdata = [];
if keepall == 0
INEEG1.icaact = [];
INEEG1.icawinv = [];
INEEG1.icasphere = [];
INEEG1.icaweights = [];
if isfield(INEEG1, 'stats')
INEEG1 = rmfield(INEEG1, 'stats' );
end
else
INEEG1.icaact = [];
end
% concatenate events
% ------------------
if isempty(INEEG2.event) && INEEG2.trials == 1 && INEEG1.trials == 1
% boundary event
% -------------
disp('Inserting boundary event...');
INEEG1.event(end+1).type = boundaryType; % add boundary event between datasets
INEEG1.event(end ).latency = INEEG1pnts+0.5; % make boundary halfway between last,first pts
% check urevents
% --------------
if ~isfield(INEEG1, 'urevent')
INEEG1.urevent = [];
fprintf('Warning: first dataset has no urevent structure.\n');
end
% add boundary urevent
% --------------------
disp('Inserting boundary urevent...');
INEEG1.urevent(end+1).type = boundaryType;
if length(INEEG1.urevent) > 1 % if previous INEEG1 urevents
INEEG1.urevent(end ).latency = max(INEEG1pnts, INEEG1.urevent(end-1).latency)+0.5;
else
INEEG1.urevent(end ).latency = INEEG1pnts+0.5;
end
else
% concatenate urevents
% --------------------
if isfield(INEEG2, 'urevent')
if isempty(INEEG2.urevent) || ~isfield(INEEG1.urevent, 'latency')
INEEG1.urevent = [];
INEEG2.urevent = [];
fprintf('Warning: second dataset has empty urevent structure.\n');
else
% insert boundary event
% ---------------------
disp('Inserting boundary event...');
INEEG1.urevent(end+1).type = boundaryType;
try
INEEG1.urevent(end ).latency = max(INEEG1pnts, INEEG1.urevent(end-1).latency)+0.5;
catch
% cko: sometimes INEEG1 has no events / urevents
INEEG1.urevent(end ).latency = INEEG1pnts+0.5;
end
% update urevent indices for second dataset
% -----------------------------------------
disp('Concatenating urevents...');
orilen = length(INEEG1.urevent);
newlen = length(INEEG2.urevent);
INEEG2event = INEEG2.event;
% update urevent index in INEEG2.event
tmpevents = INEEG2.event;
if ~isfield(tmpevents, 'urevent')
tmpevents(1).urevent = [];
end
nonemptymask = ~cellfun('isempty',{tmpevents.urevent});
[tmpevents(nonemptymask).urevent] = celldeal(num2cell([INEEG2event.urevent]+orilen));
INEEG2.event = tmpevents;
% reserve space and append INEEG2.urevent
INEEG1.urevent(orilen+newlen).latency = [];
INEEG2urevent = INEEG2.urevent;
tmpevents = INEEG1.urevent;
for f = fieldnames(INEEG2urevent)'
[tmpevents((orilen+1):(orilen+newlen)).(f{1})] = INEEG2urevent.(f{1});
end
INEEG1.urevent = tmpevents;
end
end
% concatenate events
% ------------------
disp('Concatenating events...');
orilen = length(INEEG1.event);
newlen = length(INEEG2.event);
%allfields = fieldnames(INEEG2.event);
% add discontinuity event if continuous
% -------------------------------------
if INEEG1trials == 1 && INEEG2trials == 1
disp('Adding boundary event...');
INEEG1.event(end+1).type = boundaryType;
INEEG1.event(end ).latency = INEEG1pnts+0.5;
INEEG1.event(end ).duration = NaN;
orilen = orilen+1;
% eeg_insertbound(INEEG1.event, INEEG1.pnts, INEEG1pnts+1, 0); % +1 since 0.5 is subtracted
end
% ensure similar event structures
% -------------------------------
if ~isempty(INEEG2.event)
if isstruct(INEEG1.event)
for f = fieldnames(INEEG1.event)'
if ~isfield(INEEG2.event,f{1})
INEEG2.event(1).(f{1}) = [];
end
end
end
if isstruct(INEEG2.event)
for f = fieldnames(INEEG2.event)'
if ~isfield(INEEG1.event,f{1})
INEEG1.event(1).(f{1}) = [];
end
end
end
INEEG2.event = orderfields(INEEG2.event, INEEG1.event);
end
% append
% ------
INEEG1.event(orilen + (1:newlen)) = INEEG2.event;
INEEG2event = INEEG2.event;
if isfield(INEEG1.event,'latency') && isfield(INEEG2.event,'latency')
% update latency
tmpevents = INEEG1.event;
[tmpevents(orilen + (1:newlen)).latency] = celldeal(num2cell([INEEG2event.latency] + INEEG1pnts*INEEG1trials));
INEEG1.event = tmpevents;
end
if isfield(INEEG1.event,'epoch') && isfield(INEEG2.event,'epoch')
% update epoch index
tmpevents = INEEG1.event;
[tmpevents(orilen + (1:newlen)).epoch] = celldeal(num2cell([INEEG2event.epoch]+INEEG1trials));
INEEG1.event = tmpevents;
end
end
INEEG1.pnts = size(INEEG1.data,2);
if ~isfield(INEEG1.event,'epoch') && ~isempty(INEEG1.event) && (size(INEEG1.data,3)>1 || ~isempty(INEEG1.epoch))
INEEG1.event(1).epoch = [];
end
% rebuild event-related epoch fields
% ----------------------------------
disp('Reconstituting epoch information...');
INEEG1.epoch = [];
INEEG1 = eeg_checkset(INEEG1, 'eventconsistency');
end
% build the command
% -----------------
if nargout > 1
if exist('indices') == 1
com = sprintf('EEG = pop_mergeset( ALLEEG, [%s], %d);', int2str(indices), keepall);
else
com = sprintf('EEG = pop_mergeset( ALLEEG, EEG, %d);', keepall);
end
end
return
function varargout = celldeal(X)
varargout = X;