Skip to content

Commit c65c09e

Browse files
committed
upgrade transformers and allow for globbing patterns in conditions in design matrix of bids stats model
1 parent 1f82f84 commit c65c09e

File tree

3 files changed

+134
-23
lines changed

3 files changed

+134
-23
lines changed

src/subject_level/convertOnsetTsvToMat.m

Lines changed: 81 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -92,34 +92,98 @@
9292
% if the variable is present in namespace
9393
if ismember(tokens{1}, fieldnames(tsvContent))
9494

95+
if ~ismember(variablesToConvolve{iCond}, designMatrix)
96+
% TODO does not account for edge cases where design matrix uses globbing pattern
97+
% like "face_*"
98+
% but variablesToConvolve only includes a subset of conditions
99+
% like "face_1" but not "face_2"
100+
continue
101+
end
102+
95103
trialTypes = tsvContent.(tokens{1});
96104
conditionName = strjoin(tokens(2:end), '.');
97105

98-
rows = find(strcmp(conditionName, trialTypes));
106+
% deal with any globbing search like 'face_familiar*'
107+
hasGlobPattern = ~cellfun('isempty', regexp({conditionName}, '\*|\?'));
99108

100-
printToScreen(sprintf(' Condition %s: %i trials found.\n', ...
101-
conditionName, ...
102-
numel(rows)), ...
103-
opt);
109+
if any(hasGlobPattern)
104110

105-
if ~isempty(rows)
111+
pattern = strrep(conditionName, '*', '[\_\-0-9a-zA-Z]*');
112+
pattern = strrep(pattern, '?', '[0-9a-zA-Z]?');
113+
pattern = regexify(pattern);
114+
containsPattern = ~cellfun('isempty', regexp(trialTypes, pattern));
106115

107-
if ~ismember(variablesToConvolve{iCond}, designMatrix)
108-
continue
109-
end
116+
conditionsList = unique(trialTypes(containsPattern));
110117

111-
names{1, conditionIdx} = conditionName;
112-
onsets{1, conditionIdx} = tsvContent.onset(rows)'; %#ok<*AGROW,*NASGU>
113-
durations{1, conditionIdx} = tsvContent.duration(rows)';
114-
pmod = parametricModulation(pmod, tsvContent, rows, conditionIdx);
118+
for iCdt = 1:numel(conditionsList)
115119

116-
conditionIdx = conditionIdx + 1;
120+
rows = find(strcmp(conditionsList{iCdt}, trialTypes));
121+
122+
printToScreen(sprintf(' Condition %s: %i trials found.\n', ...
123+
conditionsList{iCdt}, ...
124+
numel(rows)), ...
125+
opt);
126+
127+
if ~isempty(rows)
128+
129+
names{1, conditionIdx} = conditionsList{iCdt};
130+
onsets{1, conditionIdx} = tsvContent.onset(rows)'; %#ok<*AGROW,*NASGU>
131+
durations{1, conditionIdx} = tsvContent.duration(rows)';
132+
pmod = parametricModulation(pmod, tsvContent, rows, conditionIdx);
133+
134+
conditionIdx = conditionIdx + 1;
135+
136+
else
137+
138+
trialTypeNotFound = true;
139+
errorID = 'trialTypeNotFound';
140+
input1 = 'Trial type';
141+
142+
msg = sprintf('%s %s not found in \n %s\n %s', ...
143+
input1, ...
144+
variablesToConvolve{iCond}, ...
145+
tsvFile, ...
146+
extra);
147+
148+
errorHandling(mfilename(), errorID, msg, true, opt.verbosity);
149+
150+
end
151+
152+
end
117153

118154
else
119155

120-
trialTypeNotFound = true;
121-
errorID = 'trialTypeNotFound';
122-
input1 = 'Trial type';
156+
rows = find(strcmp(conditionName, trialTypes));
157+
158+
printToScreen(sprintf(' Condition %s: %i trials found.\n', ...
159+
conditionName, ...
160+
numel(rows)), ...
161+
opt);
162+
163+
if ~isempty(rows)
164+
165+
names{1, conditionIdx} = conditionName;
166+
onsets{1, conditionIdx} = tsvContent.onset(rows)'; %#ok<*AGROW,*NASGU>
167+
durations{1, conditionIdx} = tsvContent.duration(rows)';
168+
pmod = parametricModulation(pmod, tsvContent, rows, conditionIdx);
169+
170+
conditionIdx = conditionIdx + 1;
171+
172+
else
173+
174+
trialTypeNotFound = true;
175+
errorID = 'trialTypeNotFound';
176+
input1 = 'Trial type';
177+
178+
msg = sprintf('%s %s not found in \n %s\n %s', ...
179+
input1, ...
180+
variablesToConvolve{iCond}, ...
181+
tsvFile, ...
182+
extra);
183+
184+
errorHandling(mfilename(), errorID, msg, true, opt.verbosity);
185+
186+
end
123187

124188
end
125189

tests/tests_subject_level/test_convertOnsetTsvToMat.m

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,47 @@
1111

1212
end
1313

14+
function test_convertOnsetTsvToMat_globbing_conditions()
15+
16+
% GIVEN
17+
tsvFile = fullfile(getDummyDataDir(), ...
18+
'tsv_files', ...
19+
'sub-01_task-FaceRepetitionAfter_events.tsv');
20+
opt = setOptions('vismotion');
21+
opt.model.bm = BidsModel('file', opt.model.file);
22+
23+
opt.model.bm.Input.task = {'FaceRepetitionAfter'};
24+
opt.model.bm.Nodes{1}.Model.X = {
25+
'trial_type.N*'
26+
'trial_type.F*'
27+
'trans_?'
28+
'rot_?'};
29+
30+
opt.model.bm.Nodes{1}.Model.HRF.Variables = {
31+
'trial_type.N*'
32+
'trial_type.F*'
33+
};
34+
35+
% WHEN
36+
fullpathOnsetFilename = convertOnsetTsvToMat(opt, tsvFile);
37+
38+
% THEN
39+
assertEqual(fullfile(getDummyDataDir(), ...
40+
'tsv_files', ...
41+
'sub-01_task-FaceRepetitionAfter_onsets.mat'), ...
42+
fullpathOnsetFilename);
43+
assertEqual(exist(fullpathOnsetFilename, 'file'), 2);
44+
45+
load(fullpathOnsetFilename);
46+
47+
assertEqual(names, {'N1', 'N2', 'F1', 'F2'});
48+
assertEqual(cellfun('size', onsets, 2), [26 26 26 26]);
49+
assertEqual(cellfun('size', durations, 2), [26 26 26 26]);
50+
51+
cleanUp(fullpathOnsetFilename);
52+
53+
end
54+
1455
function test_convertOnsetTsvToMat_parametric_modulation()
1556

1657
% GIVEN
@@ -190,8 +231,11 @@ function test_convertOnsetTsvToMat_dummy_regressor()
190231
'tsv_files', ...
191232
'sub-01_task-vismotion_events.tsv');
192233
opt = setOptions('vismotion');
193-
opt.model.file = fullfile(getDummyDataDir(), 'models', 'model-vismotionMVPA_smdl.json');
234+
194235
opt.model.bm = BidsModel('file', opt.model.file);
236+
opt.model.bm.Nodes{1}.Model.X{1} = 'trial_type.foo';
237+
opt.model.bm.Nodes{1}.Model.HRF.Variables{1} = 'trial_type.foo';
238+
195239
opt.glm.useDummyRegressor = true;
196240

197241
% WHEN
@@ -200,9 +244,9 @@ function test_convertOnsetTsvToMat_dummy_regressor()
200244
% THEN
201245
load(fullpathOnsetFilename);
202246

203-
assertEqual(names, {'dummyRegressor'});
204-
assertEqual(onsets, {nan});
205-
assertEqual(durations, {nan});
247+
assertEqual(names, {'dummyRegressor', 'VisStat'});
248+
assertEqual(onsets, {nan, 4});
249+
assertEqual(durations, {nan, 2});
206250

207251
cleanUp(fullpathOnsetFilename);
208252

@@ -219,8 +263,11 @@ function test_convertOnsetTsvToMat_missing_trial_type()
219263
'tsv_files', ...
220264
'sub-01_task-vismotion_events.tsv');
221265
opt = setOptions('vismotion');
222-
opt.model.file = fullfile(getDummyDataDir(), 'models', 'model-vismotionMVPA_smdl.json');
266+
223267
opt.model.bm = BidsModel('file', opt.model.file);
268+
opt.model.bm.Nodes{1}.Model.X{1} = 'trial_type.foo';
269+
opt.model.bm.Nodes{1}.Model.HRF.Variables{1} = 'trial_type.foo';
270+
224271
opt.verbosity = 1;
225272

226273
assertWarning(@() convertOnsetTsvToMat(opt, tsvFile), ...

0 commit comments

Comments
 (0)