-
Notifications
You must be signed in to change notification settings - Fork 27
/
limo_design_matrix.m
402 lines (350 loc) · 13.8 KB
/
limo_design_matrix.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
392
393
394
395
396
397
398
399
400
401
function [X, nb_conditions, nb_interactions, nb_continuous] = limo_design_matrix(varargin)
% LIMO_DESIGN_MATRIX - called once by LIMO_EEG.m to create the design matrix X
% that is stored in the LIMO.mat file and called for all analyses.
%
% FORMAT
% [X,nb_conditions,nb_interactions,nb_continuous] = limo_design_matrix(Y,LIMO,flag)
% [X,nb_conditions,nb_interactions,nb_continuous] = limo_design_matrix(Y,Cat,Cont,directory,zscoring,full_factorial,flag)
%
% INPUTS:
% Y = EEG data with format electrodes x frames x trials/subjects
% LIMO = structure that contains the above information (created by limo_egg and limo_import_tlimo_import_f)
% or input info that are in LIMO.mat
% Cat = vector describing the different conditions (if no conditions Cat = 0)
% Cont = matrix describing the different covariates (if no covariates Cont = 0)
% directory = path of folder where the outputs will be saved (see below)
% zscoring = [0/1] - if 1 (default) continuous regressors are zscored,
% which means that the betas coefficients will have units
% micro-volts per std of the predictor variable.
% full_factorial = [0/1] - if 1 create interaction terms between the
% factors described in Cat
% flag = figure on/off [1/0]
%
% OUTPUTS:
% X = 2 dimensional matrix that describes the experiments' events
% nb_conditions = vector that returns the number of conditions per factor e.g. [2 2 2]
% nb_interactions = vector that returns the number of conditions perinteraction e.g. [4 4 4 8]
% nb_continuous = scalar that returns the number of continuous variables e.g. [3]
%
% These outputs are written to disk in DIRECTORY (allows dirty check that memery holds) and populated latter:
%
% Yr.mat = NaNs - the EEG data from the .set (reorganized to fit X, that is grouped by conditions if Cat ~=0)
% Yhat.mat = NaNs - the predicted data (same size as Yr)
% Res.mat = NaNs - the residual (non modeled) data (same size as Yr)
% R2.mat = NaNs - the model fit (same size as Yr)
% Beta.mat = NaNs - the beta values (dim: electrode,frame, number of paramters in the model)
%
% See also LIMO_IMPORT, LIMO_IMPORT_F, LIMO_EEG, LIMO_GLM
%
% Cyril Pernet / Guillaume Rousselet v4 27/04/2009
% Cyril Pernet v5 29/12/2010 % removed nb_items, updated for several factors
% Cyril Pernet v6 01/11/2011 % updated for interactions
% ------------------------------
% Copyright (C) LIMO Team 2019
%% varagin stuffs
type_of_analysis = 'Mass-univariate'; % default to create R2 file, updated using the LIMO structure only
if nargin==3
Y = varargin{1};
Cat = varargin{2}.data.Cat;
Cont = varargin{2}.data.Cont;
directory = varargin{2}.dir;
zscoring = varargin{2}.design.zscore;
full_factorial = varargin{2}.design.fullfactorial;
chanlocs = varargin{2}.data.chanlocs;
type_of_analysis = varargin{2}.design.type_of_analysis;
LIMO = varargin{2};
flag = varargin{3};
try
expected_chanlocs = varargin{2}.data.expected_chanlocs;
catch ME
expected_chanlocs = [];
end
elseif nargin==7 || nargin==9 % allows running without specifying chanlocs and neighbourging matrix
Y = varargin{1};
Cat = varargin{2};
Cont = varargin{3};
directory = varargin{4};
zscoring = varargin{5};
full_factorial = varargin{6};
flag = varargin{7};
try
chanlocs = varargin{8};
expected_chanlocs = varargin{9};
catch ME
chanlocs = [];
expected_chanlocs = [];
end
else
error('wrong number of arguments')
end
%% Dimension check
if Cont == 0
Cont = [];
end
if Cat == 0
Cat = [];
end
% check Cat
if size(Cat,2) == size(Y,3)
Cat = Cat';
disp('Cat has been transposed')
end
% check Cont
if size(Cont,2) == size(Y,3)
Cont = Cont';
disp('Cont has been transposed')
end
% overall dimensions check
if isempty(Cat) && ~isempty(Cont)
if size(Y,3) ~= size(Cont,1)
error('The number of trials and the covariate(s) length are different')
end
elseif isempty(Cont) && ~isempty(Cat)
if size(Y,3) ~= size(Cat,1)
error('The number of trials and the number of events are different size')
end
elseif ~isempty(Cat) && ~isempty(Cont)
if size(Y,3) ~= size(Cont,1)
error('The number of trials and the covariate(s) length are of different')
elseif size(Y,3) ~= size(Cat,1)
error('The number of trials and the number of events are different size')
elseif length(Cat) ~= size(Cont,1)
error('The number of events and the covariate(s) length are different')
end
end
%% check if NaNs - that is offer the possibility to remove some trials
if ~isempty(Cat)
check = find(sum(isnan(Cat),2)==size(Cat,2));
Cat(check,:) = [];
if ~isempty(Cont)
Cont(check,:) = [];
end
Y(:,:,check) = [];
end
if ~isempty(Cont)
check = find(sum(isnan(Cont),2)==size(Cont,2));
Cont(check,:) = [];
if ~isempty(Cat)
Cat(check,:) = [];
end
Y(:,:,check) = [];
end
% additional checking for regressions
if isempty(Cat) == 0
if size(Cont,2)+1 >= size(Y,3)
error('there are too many regressors for the number of trials, reduce the model size')
% one cannot compute any statistics if size(Y,3) > size(Cont,2)+1
end
if size(Cont,2) >1
if det(Cont'*Cont) == 0
errordlg('the regression matrix is singular, 1 or more predictors are function to each other, please consider another model', ...
'Matrix definition error'); return
elseif cond(Cont'*Cont,1) == Inf
errordlg('the regression matrix is close to singular, please consider another model','Matrix definition error'); return
end
end
end
% final checking for full factorial
if full_factorial == 1 && ~isempty(Cont)
error('LIMO does not compute full factorial ANCOVAs with covariate(s), consider a factorial ANCOVA witout interaction')
end
cd(directory);
%% Make the design matrix and create files
if isempty(Cont)
add_cont = 0; % no continuous regressors
else
add_cont = 1; % add continuous regressors
end
if isempty(Cat)
nb_factors = 0;
else
nb_factors = size(Cat,2); % stands for the nb of factors
end
nb_conditions = 0; % stands for the nb_conditions in each factor
nb_interactions = 0; % stands for the nb of interactions between factors
nb_continuous = 0; % stands for the nb of continuous regressors
disp('making up the design matrix and data files ...')
% deal with covariates
if add_cont == 1
[l,w]=size(Cont);
nb_continuous = w;
if l~=size(Y,3)
errordlg('the covariate(s) must be the same length as your dependant variable(s)')
disp('please retry changing the 3rd argument');
end
% to make continuous regressors comparable data are zscored
if zscoring == 1
for i=1:w
Cont(:,i) = (Cont(:,i)-mean(Cont(:,i)))./std(Cont(:,i));
end
end
if isempty(Cat)
X = zeros(l,w+1);
X(:,1:w) = Cont;
X(:,w+1) = 1;
Yr = Y;
end
end % closes if Cont~=0
% Cat is a vector or a matrix and we want X
% if Cont is used; it is reorganized accordingly
% Y is also reorganized accordingly (i.e. grouped per condition)
if ~isempty(Cat)
if size(Cat,1)~=size(Y,3)
errordlg('the categorical regressor must be the same length as your dependant variables')
disp('please retry changing the 2nd argument');error('error line 225 in limo_design_matrix');
end
% sort Cat column wise - apply to Cont and Y
[Cat,index] = sortrows(Cat,[1:size(Cat,2)]);
if add_cont == 1
for i=1:size(Cont,2)
Cont(:,i) = Cont(index,i);
end
end
Yr = NaN(size(Y)); % Y reorganized
for electrode = 1:size(Y,1)
for time = 1:size(Y,2)
Yr(electrode,time,:) = Y(electrode,time,index);
end
end
clear Y
% find out the number of trial per condition per factor
% create as many columns as conditions
condition_count = 0;
index_nb_conditions =1;
index_indices_conditions =1;
for c=1:size(Cat,2)
column = Cat(:,c);
for i=min(column):max(column)
tmp = find(column==i);
if ~isempty(tmp) % use this trick so that any number can be used as event marker
nb_conditions(index_nb_conditions) = condition_count+1;
condition_count = condition_count+1;
indices_conditions{index_indices_conditions}=tmp;
index_indices_conditions = index_indices_conditions+1;
end
end
index_nb_conditions = index_nb_conditions+1;
condition_count = 0;
end
% create the design matrix for the main conditions
x = zeros(size(Yr,3),length(indices_conditions));
for f=1:length(indices_conditions)
x(indices_conditions{f},f) = 1;
end
full_design = x;
% add interactions if requested
if full_factorial == 1
if nb_factors == 1
disp('full factorial impossible, only 1 factor specificied')
else
[x, nb_interactions] = limo_make_interactions(x, nb_conditions);
end
end
% add the continuous regressors and the constant
if add_cont == 1
X = [x Cont ones(size(Yr,3),1)];
else
X = [x ones(size(Yr,3),1)];
end
% finally check the balance of the design and adjust X and Yr
if full_factorial == 1
start_at = sum(nb_conditions) + sum(nb_interactions(1:end-1)) + 1;
higher_interaction = X(:,start_at:(end-nb_continuous-1));
if size(higher_interaction,2) ~= prod(nb_conditions)
errordlg(sprintf('the design is too unbalanced to be corrected \n can''t run full factorial'))
nb_interactions = 0; X = [full_design ones(size(Yr,3),1)];
full_factorial = 0; LIMO.design.fullfactorial = 0;
save('LIMO.mat','LIMO')
else
nb_trials = sum(higher_interaction);
if length(unique(nb_trials)) > 1
sample_to_n = min(nb_trials);
if sample_to_n == 1
errordlg(sprintf('the design is over-specified, \n only one observation per condition \n can''t run full factorial'))
nb_interactions = 0;
X = [full_design ones(size(Yr,3),1)];
else % sample
s = 1; % now list rows to keep
for c=1:size(higher_interaction,2)
indices = find(higher_interaction(:,c));
if length(indices) > sample_to_n
new_sample{s} = randsample(indices,sample_to_n);
s = s+1;
else
new_sample{s} = indices;
s = s+1;
end
end
% sample
sample_index = [];
for s = 1:size(new_sample,2)
v = new_sample{s};
sample_index = [ sample_index ; v];
end
X = X(sample_index,:);
Yr = Yr(:,:,sample_index);
disp('DATA HAVE BEEN RESAMPLED TO PERFERM A FULL FACTORIAL ANALYSIS')
end
end
end
end
end % closes if Cat ~=0
% if Cat and Cont are empty (ie just comnpute the mean)
if ~exist('Yr','var')
Yr = Y;
X = ones(size(Yr,3),1);
end
clear Y
% --------------------------------------------------------
% create files to be used by LIMO in all cases - dim = expected chanlocs
% Data - update with expected chanlocs
if ~isempty(expected_chanlocs) && size(Yr,1) > 1
Yr = limo_match_elec(chanlocs,expected_chanlocs,1,size(Yr,2),Yr);
end
try
% no matter the analysis we have Beta, Yhat, Res - create them all here
% also R2 for univariate analyses - also test if memory hold for tmp
% files to be created in limo_eeg
Yhat = NaN(size(Yr,1),size(Yr,2),size(Yr,3));
Res = NaN(size(Yr,1),size(Yr,2),size(Yr,3));
Betas = NaN(size(Yr,1),size(Yr,2),size(X,2));
% only for univariate analyses
if strcmp(type_of_analysis,'Mass-univariate')
R2 = NaN(size(Yr,1),size(Yr,2),3);
save('R2.mat','R2');
end
% these ones will be created in limo_eeg
if nb_conditions ~=0
tmp_Condition_effect = NaN(size(Yr,1),size(Yr,2),length(nb_conditions),2);
end
if nb_interactions ~=0
tmp_Interaction_effect = NaN(size(Yr,1),size(Yr,2),length(nb_interactions),2);
end
if nb_continuous ~=0
tmp_Covariate_effect = NaN(size(Yr,1),size(Yr,2),nb_continuous,2);
end
save('Yhat.mat','Yhat'); clear Yhat
save('Betas.mat','Betas'); clear Betas
save('Res.mat','Res'); clear Res
save('Yr.mat','Yr') ; clear Yr R2
if nb_conditions ~=0; clear tmp_Condition_effect; end
if nb_interactions ~=0; clear tmp_Interaction_effect; end
if nb_continuous ~=0; clear tmp_Covariate_effect; end
catch FileError
sprintf('%s',FileError.message)
error('error while memory mapping future results')
end
% ------
% figure
if flag == 1
figure('Name','LIMO design','Color','w','NumberTitle','off')
Xdisplay = X;
if add_cont == 1
cat_column = sum(nb_conditions)+sum(nb_interactions);
REGdisplay = Cont + abs(min(Cont(:)));
Xdisplay(:,cat_column+1:size(X,2)-1) = REGdisplay ./ max(REGdisplay(:));
end
imagesc(Xdisplay); colormap('gray'); drawnow;
title('Design matrix'); xlabel('regressors');ylabel('trials');
set(gca,'XTick',1:size(X,2))
end