|
41 | 41 |
|
42 | 42 | % |
43 | 43 | [pth, file, ext] = spm_fileparts(tsvFile); |
44 | | - tsvFile = validationInputFile(pth, [file, ext]); |
| 44 | + tsv.file = validationInputFile(pth, [file, ext]); |
45 | 45 |
|
46 | | - tsvContent = bids.util.tsvread(tsvFile); |
| 46 | + tsv.content = bids.util.tsvread(tsv.file); |
47 | 47 |
|
48 | | - if ~all(isnumeric(tsvContent.onset)) |
| 48 | + if ~all(isnumeric(tsv.content.onset)) |
49 | 49 |
|
50 | 50 | errorID = 'onsetsNotNumeric'; |
51 | | - msg = sprintf('%s\n%s', 'Onset column contains non numeric values in file:', tsvFile); |
| 51 | + msg = sprintf('%s\n%s', 'Onset column contains non numeric values in file:', tsv.file); |
52 | 52 | errorHandling(mfilename(), errorID, msg, false, opt.verbosity); |
53 | 53 |
|
54 | 54 | end |
55 | 55 |
|
56 | | - if ~all(isnumeric(tsvContent.duration)) |
| 56 | + if ~all(isnumeric(tsv.content.duration)) |
57 | 57 |
|
58 | 58 | errorID = 'durationsNotNumeric'; |
59 | | - msg = sprintf('%s\n%s', 'Duration column contains non numeric values in file:', tsvFile); |
| 59 | + msg = sprintf('%s\n%s', 'Duration column contains non numeric values in file:', tsv.file); |
60 | 60 | errorHandling(mfilename(), errorID, msg, false, opt.verbosity); |
61 | 61 |
|
62 | 62 | end |
63 | 63 |
|
64 | | - variablesToConvolve = opt.model.bm.getVariablesToConvolve(); |
| 64 | + varToConvolve = opt.model.bm.getVariablesToConvolve(); |
65 | 65 | designMatrix = opt.model.bm.getBidsDesignMatrix(); |
66 | 66 | designMatrix = removeIntercept(designMatrix); |
67 | 67 |
|
68 | | - % create empty cell to be filled in according to the conditions present in each run |
69 | | - names = {}; |
70 | | - onsets = {}; |
71 | | - durations = {}; |
72 | | - pmod = struct('name', {''}, 'param', {}, 'poly', {}); |
| 68 | + % conditions to be filled in according to the conditions present in each run |
| 69 | + condToModel.names = {}; |
| 70 | + condToModel.onsets = {}; |
| 71 | + condToModel.durations = {}; |
| 72 | + condToModel.pmod = struct('name', {''}, 'param', {}, 'poly', {}); |
| 73 | + condToModel.idx = 1; |
73 | 74 |
|
74 | 75 | if ~isfield(opt.model, 'bm') |
75 | 76 | opt.model.bm = BidsModel('file', opt.model.file); |
76 | 77 | end |
77 | 78 | % TODO get / apply transformers from a specific node |
78 | 79 | transformers = opt.model.bm.getBidsTransformers(); |
79 | | - tsvContent = bids.transformers(transformers, tsvContent); |
| 80 | + tsv.content = bids.transformers(transformers, tsv.content); |
80 | 81 |
|
81 | | - conditionIdx = 1; |
82 | | - |
83 | | - for iCond = 1:numel(variablesToConvolve) |
| 82 | + for iVar = 1:numel(varToConvolve) |
84 | 83 |
|
85 | 84 | trialTypeNotFound = false; % should be dead code by now |
86 | 85 | variableNotFound = false; |
87 | 86 | extra = ''; |
88 | 87 |
|
89 | 88 | % first assume the input is from events.tsv |
90 | | - tokens = regexp(variablesToConvolve{iCond}, '\.', 'split'); |
| 89 | + tokens = regexp(varToConvolve{iVar}, '\.', 'split'); |
91 | 90 |
|
92 | 91 | % if the variable is present in namespace |
93 | | - if ismember(tokens{1}, fieldnames(tsvContent)) |
| 92 | + if ismember(tokens{1}, fieldnames(tsv.content)) |
94 | 93 |
|
95 | | - if ~ismember(variablesToConvolve{iCond}, designMatrix) |
| 94 | + if ~ismember(varToConvolve{iVar}, designMatrix) |
96 | 95 | % TODO does not account for edge cases where design matrix uses globbing pattern |
97 | 96 | % like "face_*" |
98 | 97 | % but variablesToConvolve only includes a subset of conditions |
99 | 98 | % like "face_1" but not "face_2" |
100 | 99 | continue |
101 | 100 | end |
102 | 101 |
|
103 | | - trialTypes = tsvContent.(tokens{1}); |
104 | | - conditionName = strjoin(tokens(2:end), '.'); |
| 102 | + trialTypes = tsv.content.(tokens{1}); |
| 103 | + condName = strjoin(tokens(2:end), '.'); |
105 | 104 |
|
106 | 105 | % deal with any globbing search like 'face_familiar*' |
107 | | - hasGlobPattern = ~cellfun('isempty', regexp({conditionName}, '\*|\?')); |
| 106 | + hasGlobPattern = ~cellfun('isempty', regexp({condName}, '\*|\?')); |
108 | 107 |
|
109 | | - if any(hasGlobPattern) |
| 108 | + if hasGlobPattern |
110 | 109 |
|
111 | | - pattern = strrep(conditionName, '*', '[\_\-0-9a-zA-Z]*'); |
| 110 | + pattern = strrep(condName, '*', '[\_\-0-9a-zA-Z]*'); |
112 | 111 | pattern = strrep(pattern, '?', '[0-9a-zA-Z]?'); |
113 | 112 | pattern = regexify(pattern); |
114 | 113 | containsPattern = ~cellfun('isempty', regexp(trialTypes, pattern)); |
115 | 114 |
|
116 | | - conditionsList = unique(trialTypes(containsPattern)); |
117 | | - |
118 | | - for iCdt = 1:numel(conditionsList) |
119 | | - |
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'; |
| 115 | + condList = unique(trialTypes(containsPattern)); |
141 | 116 |
|
142 | | - msg = sprintf('%s %s not found in \n %s\n %s', ... |
143 | | - input1, ... |
144 | | - variablesToConvolve{iCond}, ... |
145 | | - tsvFile, ... |
146 | | - extra); |
| 117 | + for iCdt = 1:numel(condList) |
147 | 118 |
|
148 | | - errorHandling(mfilename(), errorID, msg, true, opt.verbosity); |
149 | | - |
150 | | - end |
| 119 | + condToModel = addCondition(opt, ... |
| 120 | + condList{iCdt}, ... |
| 121 | + trialTypes, ... |
| 122 | + tsv, ... |
| 123 | + condToModel, ... |
| 124 | + varToConvolve{iVar}); |
151 | 125 |
|
152 | 126 | end |
153 | 127 |
|
154 | 128 | else |
155 | 129 |
|
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 |
| 130 | + condToModel = addCondition(opt, ... |
| 131 | + condName, ... |
| 132 | + trialTypes, ... |
| 133 | + tsv, ... |
| 134 | + condToModel, ... |
| 135 | + varToConvolve{iVar}); |
187 | 136 |
|
188 | 137 | end |
189 | 138 |
|
|
195 | 144 |
|
196 | 145 | end |
197 | 146 |
|
198 | | - if variableNotFound || trialTypeNotFound |
| 147 | + if variableNotFound |
199 | 148 |
|
200 | 149 | if opt.glm.useDummyRegressor |
201 | | - [names, onsets, durations] = addDummyRegressor(names, onsets, durations); |
202 | | - extra = 'Adding dummy regressor instead.'; |
203 | | - conditionIdx = conditionIdx + 1; |
| 150 | + condToModel = addDummyRegressor(condToModel); |
204 | 151 | end |
205 | 152 |
|
206 | | - msg = sprintf('%s %s not found in \n %s\n %s', ... |
| 153 | + msg = sprintf('%s %s not found in \n %s\n Adding dummy regressor instead.', ... |
207 | 154 | input1, ... |
208 | | - variablesToConvolve{iCond}, ... |
209 | | - tsvFile, ... |
210 | | - extra); |
| 155 | + varToConvolve{iVar}, ... |
| 156 | + tsv.file); |
211 | 157 |
|
212 | 158 | errorHandling(mfilename(), errorID, msg, true, opt.verbosity); |
213 | 159 |
|
|
216 | 162 | end |
217 | 163 |
|
218 | 164 | %% save the onsets as a matfile |
219 | | - [pth, file] = spm_fileparts(tsvFile); |
| 165 | + [pth, file] = spm_fileparts(tsv.file); |
220 | 166 |
|
221 | 167 | bf = bids.File(file); |
222 | 168 | bf.suffix = 'onsets'; |
223 | 169 | bf.extension = '.mat'; |
224 | 170 |
|
225 | 171 | fullpathOnsetFilename = fullfile(pth, bf.filename); |
226 | 172 |
|
| 173 | + names = condToModel.names; %#ok<*NASGU> |
| 174 | + onsets = condToModel.onsets; |
| 175 | + durations = condToModel.durations; |
| 176 | + pmod = condToModel.pmod; |
| 177 | + |
227 | 178 | save(fullpathOnsetFilename, ... |
228 | 179 | 'names', 'onsets', 'durations', 'pmod', ... |
229 | 180 | '-v7'); |
230 | 181 |
|
231 | 182 | end |
232 | 183 |
|
233 | | -function pmod = parametricModulation(pmod, tsvContent, rows, conditionIdx) |
| 184 | +function condToModel = addCondition(opt, condName, trialTypes, tsv, condToModel, varToConvolve) |
| 185 | + |
| 186 | + rows = find(strcmp(condName, trialTypes)); |
| 187 | + |
| 188 | + printToScreen(sprintf(' Condition %s: %i trials found.\n', ... |
| 189 | + condName, ... |
| 190 | + numel(rows)), ... |
| 191 | + opt); |
| 192 | + |
| 193 | + if ~isempty(rows) |
| 194 | + |
| 195 | + condToModel.names{1, condToModel.idx} = condName; |
| 196 | + condToModel.onsets{1, condToModel.idx} = tsv.content.onset(rows)'; |
| 197 | + condToModel.durations{1, condToModel.idx} = tsv.content.duration(rows)'; |
| 198 | + condToModel = parametricModulation(condToModel, tsv, rows); |
| 199 | + |
| 200 | + condToModel.idx = condToModel.idx + 1; |
| 201 | + |
| 202 | + else |
| 203 | + |
| 204 | + msg = sprintf('Trial type %s not found in \n\t%s\n', ... |
| 205 | + varToConvolve, ... |
| 206 | + tsv.file); |
| 207 | + |
| 208 | + errorHandling(mfilename(), 'trialTypeNotFound', msg, true, opt.verbosity); |
| 209 | + |
| 210 | + if opt.glm.useDummyRegressor |
| 211 | + condToModel = addDummyRegressor(condToModel); |
| 212 | + end |
| 213 | + |
| 214 | + end |
| 215 | + |
| 216 | +end |
| 217 | + |
| 218 | +function conditionsToModel = parametricModulation(conditionsToModel, tsv, rows) |
234 | 219 | % parametric modulation (pmod) |
235 | 220 | % |
236 | | - % skipped if parametric modulation is 1 for all onsets |
| 221 | + % skipped if parametric modulation == 1 for all onsets |
237 | 222 | % |
238 | 223 | % coerces NaNs into 1 |
239 | 224 |
|
240 | | - fields = fieldnames(tsvContent); |
| 225 | + fields = fieldnames(tsv.content); |
241 | 226 | pmodIdx = ~cellfun('isempty', regexp(fields, '^pmod_.*', 'match')); |
242 | 227 | pmodIdx = find(pmodIdx); |
243 | 228 |
|
244 | 229 | for iMod = 1:numel(pmodIdx) |
245 | 230 |
|
246 | 231 | thisMod = fields{pmodIdx(iMod)}; |
247 | 232 |
|
248 | | - amplitude = tsvContent.(thisMod)(rows); |
| 233 | + amplitude = tsv.content.(thisMod)(rows); |
249 | 234 | amplitude(isnan(amplitude)) = 1; |
250 | 235 |
|
251 | 236 | if ~all(amplitude == 1) |
252 | | - pmod(1, conditionIdx).name{iMod} = strrep(thisMod, 'pmod_', ''); |
253 | | - pmod(end).param{iMod} = amplitude; |
254 | | - pmod(end).poly{iMod} = 1; |
| 237 | + conditionsToModel.pmod(1, conditionsToModel.idx).name{iMod} = strrep(thisMod, 'pmod_', ''); |
| 238 | + conditionsToModel.pmod(end).param{iMod} = amplitude; |
| 239 | + conditionsToModel.pmod(end).poly{iMod} = 1; |
255 | 240 | end |
256 | 241 |
|
257 | 242 | end |
258 | 243 | end |
259 | 244 |
|
260 | | -function [names, onsets, durations] = addDummyRegressor(names, onsets, durations) |
| 245 | +function conditionsToModel = addDummyRegressor(conditionsToModel) |
261 | 246 |
|
262 | | - names{1, end + 1} = 'dummyRegressor'; |
263 | | - onsets{1, end + 1} = nan; |
264 | | - durations{1, end + 1} = nan; |
| 247 | + conditionsToModel.names{1, end + 1} = 'dummyRegressor'; |
| 248 | + conditionsToModel.onsets{1, end + 1} = nan; |
| 249 | + conditionsToModel.durations{1, end + 1} = nan; |
| 250 | + conditionsToModel.idx = conditionsToModel.idx + 1; |
265 | 251 |
|
266 | 252 | end |
0 commit comments