Skip to content

Commit 156844b

Browse files
authored
[FIX] exclude late events (#1060)
* exclude late onsets * update changelog
1 parent a71c0a0 commit 156844b

File tree

6 files changed

+106
-19
lines changed

6 files changed

+106
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5757

5858
### Fixed
5959

60+
* [FIX] Exclude from GLM specification events with onsets longer than the run duration (#1060) @Remi-Gau
6061
* [FIX] remove dummies from preproc dataset and not raw dataset when using CLI (#1057) @Remi-Gau
6162
* [FIX] skip smoothing when running bidspm prepoc in dryRun (#1054) @Remi-Gau
6263
* [FIX] handle phase entity in filename (#1034) @Remi-Gau

src/batches/stats/setBatchSubjectLevelGLMSpec.m

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,15 @@
110110

111111
spmSess(spmSessCounter).scans = getBoldFilenameForFFX(BIDS, opt, subLabel, iSes, iRun);
112112

113+
runDuration = getRunDuration(opt, spmSess(spmSessCounter).scans, TR);
114+
115+
spec = struct('sub', subLabel, ...
116+
'ses', sessions{iSes}, ...
117+
'task', opt.taskName{iTask}, ...
118+
'run', runs{iRun});
113119
onsetFilename = returnOnsetsFile(BIDS, opt, ...
114-
subLabel, ...
115-
sessions{iSes}, ...
116-
opt.taskName{iTask}, ...
117-
runs{iRun});
120+
spec, ...
121+
runDuration);
118122
spmSess(spmSessCounter).onsetsFile = onsetFilename;
119123

120124
% get confounds
@@ -234,20 +238,49 @@
234238

235239
end
236240

237-
function fmriSpec = setScans(opt, fullpathBoldFilename, fmriSpec, spmSessCounter)
241+
function runDuration = getRunDuration(opt, fullpathBoldFilename, TR)
238242

239-
if opt.model.designOnly
243+
nvVols = getNbVols(opt, fullpathBoldFilename);
244+
runDuration = nvVols * TR;
245+
246+
end
240247

248+
function nbVols = getNbVols(opt, fullpathBoldFilename)
249+
if opt.glm.maxNbVols == Inf && isempty(opt.funcVolToSelect)
241250
try
242251
hdr = spm_vol(fullpathBoldFilename);
252+
nbVols = numel(hdr);
243253
catch
254+
nbVols = nan;
255+
end
256+
return
257+
end
258+
259+
if opt.glm.maxNbVols ~= Inf
260+
nbVols = opt.glm.maxNbVols;
261+
return
262+
end
263+
264+
if ~isempty(opt.funcVolToSelect)
265+
nbVols = numel(opt.funcVolToSelect);
266+
return
267+
end
268+
error('WTF');
269+
end
270+
271+
function fmriSpec = setScans(opt, fullpathBoldFilename, fmriSpec, spmSessCounter)
272+
273+
if opt.model.designOnly
274+
275+
nbVols = getNbVols(opt, fullpathBoldFilename);
276+
if isnan(nbVols)
244277
warning('Could not open %s.\nExpected during testing.', fullpathBoldFilename);
245278
% TODO a value should be passed by user for this
246279
% hard coded value for test
247-
hdr = ones(200, 1);
280+
nbVols = 200;
248281
end
249282

250-
fmriSpec.sess(spmSessCounter).nscan = numel(hdr);
283+
fmriSpec.sess(spmSessCounter).nscan = nbVols;
251284

252285
else
253286

@@ -257,14 +290,14 @@
257290

258291
end
259292

260-
function onsetFilename = returnOnsetsFile(BIDS, opt, subLabel, session, task, run)
293+
function onsetFilename = returnOnsetsFile(BIDS, opt, spec, runDuration)
261294

262295
% get events file from raw data set and convert it to a onsets.mat file
263296
% store in the subject level GLM directory
264-
filter = fileFilterForBold(opt, subLabel, 'events');
265-
filter.ses = session;
266-
filter.run = run;
267-
filter.task = task;
297+
filter = fileFilterForBold(opt, spec.sub, 'events');
298+
filter.ses = spec.ses;
299+
filter.run = spec.run;
300+
filter.task = spec.task;
268301

269302
tsvFile = bids.query(BIDS.raw, 'data', filter);
270303

@@ -282,6 +315,7 @@
282315
end
283316

284317
onsetFilename = createAndReturnOnsetFile(opt, ...
285-
subLabel, ...
286-
tsvFile);
318+
spec.sub, ...
319+
tsvFile, ...
320+
runDuration);
287321
end

src/stats/subject_level/convertOnsetTsvToMat.m

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function fullpathOnsetFilename = convertOnsetTsvToMat(opt, tsvFile)
1+
function fullpathOnsetFilename = convertOnsetTsvToMat(opt, tsvFile, runDuration)
22
%
33
% Converts an events.tsv file to an onset file suitable for SPM subject level analysis.
44
%
@@ -13,6 +13,10 @@
1313
% :param tsvFile:
1414
% :type tsvFile: char
1515
%
16+
% :param runDuration: Total duration of the run (in seconds). Optional.
17+
% Events occurring later than this will be excluded.
18+
% :type runDuration: numeric
19+
%
1620
% Use a BIDS stats model specified in a JSON file to:
1721
%
1822
% - loads events.tsv and apply the ``Node.Transformations`` to its content
@@ -51,12 +55,24 @@
5155

5256
% (C) Copyright 2019 bidspm developers
5357

58+
if nargin < 3
59+
runDuration = nan;
60+
end
61+
5462
REQUIRED_COLUMNS = {'onset', 'duration'};
5563

5664
[pth, file, ext] = spm_fileparts(tsvFile);
5765
tsv.file = validationInputFile(pth, [file, ext]);
5866
tsv.content = bids.util.tsvread(tsv.file);
5967

68+
if ~isnan(runDuration)
69+
eventsToExclude = tsv.content.onset >= runDuration;
70+
columns = fieldnames(tsv.content);
71+
for iCol = 1:numel(columns)
72+
tsv.content.(columns{iCol})(eventsToExclude) = [];
73+
end
74+
end
75+
6076
for i = 1:numel(REQUIRED_COLUMNS)
6177
if ~isfield(tsv.content, REQUIRED_COLUMNS{i})
6278
msg = sprintf(['''%s'' column is missing from file: %s', ...

src/stats/subject_level/createAndReturnOnsetFile.m

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function onsetFilename = createAndReturnOnsetFile(opt, subLabel, tsvFile)
1+
function onsetFilename = createAndReturnOnsetFile(opt, subLabel, tsvFile, runDuration)
22
%
33
% For a given ``_events.tsv`` file and ``_model.json``,
44
% it creates a ``_onset.mat`` file that can directly be used
@@ -20,6 +20,10 @@
2020
% :param tsvFile: fullpath name of the tsv file.
2121
% :type tsvFile: char
2222
%
23+
% :param runDuration: Total duration of the run (in seconds). Optional.
24+
% Events occurring later than this will be excluded.
25+
% :type runDuration: numeric
26+
%
2327
% :returns: :onsetFilename: (path) fullpath name of the file created.
2428
%
2529
% See also: convertOnsetTsvToMat
@@ -35,7 +39,7 @@
3539
msg = sprintf('\n Reading the tsv file : %s \n', pathToPrint(tsvFile));
3640
logger('INFO', msg, 'options', opt, 'filename', mfilename());
3741

38-
onsetFilename = convertOnsetTsvToMat(opt, tsvFile);
42+
onsetFilename = convertOnsetTsvToMat(opt, tsvFile, runDuration);
3943

4044
% move file into the FFX directory
4145
[~, filename, ext] = spm_fileparts(onsetFilename);

tests/tests_stats/subject_level/test_convertOnsetTsvToMat.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@
1212

1313
end
1414

15+
function test_convertOnsetTsvToMat_exclude_late_events()
16+
17+
% GIVEN
18+
tsvFile = fullfile(getTestDataDir(), ...
19+
'tsv_files', ...
20+
'sub-01_task-vismotion_events.tsv');
21+
opt = setOptions('vismotion');
22+
opt.model.bm = BidsModel('file', opt.model.file);
23+
24+
% WHEN
25+
runDuration = 3;
26+
fullpathOnsetFilename = convertOnsetTsvToMat(opt, tsvFile, runDuration);
27+
28+
% THEN
29+
assertEqual(fullfile(getTestDataDir(), ...
30+
'tsv_files', ...
31+
'sub-01_task-vismotion_onsets.mat'), ...
32+
fullpathOnsetFilename);
33+
assertEqual(exist(fullpathOnsetFilename, 'file'), 2);
34+
35+
load(fullpathOnsetFilename);
36+
37+
assertEqual(names, {'VisMot'});
38+
assertEqual(onsets, {2});
39+
assertEqual(durations, {2});
40+
41+
cleanUp(fullpathOnsetFilename);
42+
43+
end
44+
1545
function test_convertOnsetTsvToMat_parametric_modulation()
1646

1747
% GIVEN

tests/tests_stats/subject_level/test_createAndReturnOnsetFile.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ function test_createAndReturnOnsetFile_basic()
3030
'extension', '.tsv');
3131
tsvFile = bids.query(BIDS, 'data', filter);
3232

33-
onsetFilename = createAndReturnOnsetFile(opt, subLabel, tsvFile);
33+
runDuration = nan;
34+
35+
onsetFilename = createAndReturnOnsetFile(opt, subLabel, tsvFile, runDuration);
3436

3537
expectedFilename = fullfile(getTestDataDir('stats'), 'sub-01', ...
3638
'task-vislocalizer_space-IXI549Space_FWHM-6', ...

0 commit comments

Comments
 (0)