Skip to content

Commit e684f7a

Browse files
authored
[FIX] fix group level results after contrasts smoothing (#1021)
* fix warning * fix typo * update test * add cumulative smoothing to report
1 parent 6f3e9cc commit e684f7a

File tree

6 files changed

+62
-26
lines changed

6 files changed

+62
-26
lines changed

demos/openneuro/ds002799_run.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
% The directory where the data are located
1616
root_dir = fullfile(fileparts(mfilename('fullpath')));
17+
root_dir = pwd;
1718

1819
%% Parameters
1920

@@ -75,6 +76,9 @@
7576
% update the dataset level node
7677
run_lvl_idx = 3;
7778
bm.Nodes{run_lvl_idx}.GroupBy = {'contrast'};
79+
bm.Nodes{run_lvl_idx}.Model.Software.bidspm.Results(1) = defaultResultsStructure();
80+
bm.Nodes{run_lvl_idx}.Model.Software.bidspm.Results(1).nodeName = 'dataset';
81+
bm.Nodes{run_lvl_idx}.Model.Software.bidspm.Results(1).name = {'es'};
7882

7983
% write
8084
bm.write(model_file);

src/reports/boilerplate.m

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,13 @@
8989
opt.unwarp = true;
9090
end
9191

92-
opt.smoothing = true;
93-
if opt.fwhm.func == 0
94-
opt.smoothing = false;
95-
end
92+
opt.smoothing = opt.fwhm.func > 0;
9693

9794
elseif strcmp(pipelineType, 'stats')
9895

99-
opt.smoothing = true;
100-
101-
if opt.fwhm.contrast == 0
102-
opt.smoothing = false;
96+
opt.smoothing = opt.fwhm.contrast > 0;
97+
if opt.smoothing
98+
opt.fwhm.cumulative = computeCumulativeFwhm(opt);
10399
end
104100

105101
bm = BidsModel('file', opt.model.file);

src/reports/boilerplate_stats.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ to censor any time points flagged as outlier,
4343
{{/confounds}}.
4444

4545
{{#smoothing}}
46-
Contrast images were spatially smoothed using a 3D gaussian kernel (FWHM = {{fwhm.contrast}} mm).
46+
Contrast images were spatially smoothed using a 3D gaussian kernel (FWHM = {{fwhm.contrast}} mm resulting in a final smoothing equivalent to {{fwhm.cumulative}} mm).
4747
{{/smoothing}}
4848
{{! TODO Table of conditions with duration of each event}}
4949

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
function fwhm = computeCumulativeFwhm(opt)
2+
%
3+
% Compute resulting fwhm when smoothing both time series and contrasts.
4+
%
5+
% USAGE::
6+
%
7+
% fwhm = computeCumulativeFwhm(opt)
8+
%
9+
10+
% (C) Copyright 2023 Remi Gau
11+
12+
if opt.fwhm.contrast > 0
13+
fwhm = (opt.fwhm.func^2 + opt.fwhm.contrast^2)^0.5;
14+
end
15+
16+
end

src/workflows/stats/bidsResults.m

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,9 @@
156156

157157
opt.dir.output = opt.dir.stats;
158158

159-
% TODO
160-
% bids stats model should override options
161-
if ~isfield(opt, 'results') || isempty(opt.results) || ...
162-
strcmp(opt.results(1).name{1}, '')
163-
opt.results = opt.model.bm.getResults();
159+
modelResults = opt.model.bm.getResults();
160+
if ~isempty(modelResults)
161+
opt.results = modelResults;
164162
end
165163

166164
status = checks(opt);
@@ -365,7 +363,7 @@
365363

366364
tmp.dir = getFFXdir(subLabel, opt);
367365

368-
status = checkSpmMat(tmp.dir);
366+
status = checkSpmMat(tmp.dir, opt);
369367

370368
if ~status
371369
return
@@ -486,18 +484,21 @@
486484

487485
end
488486

489-
function status = checkSpmMat(dir)
487+
function status = checkSpmMat(dir, opt)
490488
status = exist(fullfile(dir, 'SPM.mat'), 'file');
491489
if ~status
492-
msg = sprintf('\nCould not find a SPM.mat file in directory %s\n', dir);
490+
if nargin < 2
491+
opt = struct('verbosity', 2);
492+
end
493+
msg = sprintf('\nCould not find a SPM.mat file in directory:\n%s\n', dir);
493494
id = 'noSpmMatFile';
494-
logger('WARNING', msg, 'id', id);
495+
logger('WARNING', msg, 'id', id, 'options', opt, 'filename', mfilename);
495496
end
496497
end
497498

498499
function [matlabbatch, results] = appendToBatch(matlabbatch, opt, results, result)
499500

500-
if ~checkSpmMat(result.dir)
501+
if ~checkSpmMat(result.dir, opt)
501502
return
502503
end
503504

tests/tests_workflows/stats/test_bidsResults.m

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ function test_bidsResults_subject_lvl_regex()
2222

2323
opt.subjects = {'01'};
2424

25+
opt = rmResultsFromModel(opt);
26+
2527
%% WHEN
2628
matlabbatch = bidsResults(opt);
2729

@@ -64,6 +66,8 @@ function test_bidsResults_filter_by_nodeName()
6466
opt.results(2).nodeName = 'dataset_level';
6567
opt.results(2).name = {'VisMot_gt_VisStat'};
6668

69+
opt = rmResultsFromModel(opt);
70+
6771
%% WHEN
6872
matlabbatch = bidsResults(opt, 'nodeName', 'subject_level');
6973

@@ -103,6 +107,8 @@ function test_bidsResults_too_many_backgrounds()
103107
opt.results.montage.do = true;
104108
opt.results.montage.background = struct('suffix', 'probseg');
105109

110+
opt = rmResultsFromModel(opt);
111+
106112
if bids.internal.is_octave()
107113
return
108114
end
@@ -114,7 +120,8 @@ function test_bidsResults_too_many_backgrounds()
114120
function test_bidsResults_background_for_subject()
115121

116122
%% GIVEN
117-
opt = setOptions('vislocalizer', {'01', 'ctrl01', 'blind01'}, 'pipelineType', 'stats');
123+
opt = setOptions('vislocalizer', {'01', 'ctrl01', 'blind01'}, ...
124+
'pipelineType', 'stats');
118125

119126
% Specify what output we want
120127
opt.results = defaultResultsStructure();
@@ -132,14 +139,20 @@ function test_bidsResults_background_for_subject()
132139
'suffix', 'T1w');
133140

134141
opt.subjects = {'^01'};
142+
143+
opt = rmResultsFromModel(opt);
144+
135145
matlabbatch = bidsResults(opt);
146+
136147
bf = bids.File(matlabbatch{1}.spm.stats.results.export{4}.montage.background{1});
137148
assertEqual(bf.entities.desc, 'preproc');
138149
assertEqual(bf.entities.sub, '01');
139150
assertEqual(bf.suffix, 'T1w');
140151

141152
opt.subjects = {'ctrl01'};
153+
142154
matlabbatch = bidsResults(opt);
155+
143156
bf = bids.File(matlabbatch{1}.spm.stats.results.export{4}.montage.background{1});
144157
assertEqual(bf.entities.desc, 'preproc');
145158
assertEqual(bf.entities.sub, 'ctrl01');
@@ -165,6 +178,8 @@ function test_bidsResults_no_background_for_montage()
165178
opt.results.montage.do = true;
166179
opt.results.montage.background = 'aFileThatDoesNotExist.nii';
167180

181+
opt = rmResultsFromModel(opt);
182+
168183
if bids.internal.is_octave()
169184
return
170185
end
@@ -190,7 +205,7 @@ function test_bidsResults_dataset_lvl()
190205

191206
%% WHEN
192207

193-
% matlabbatch = bidsResults(opt);
208+
matlabbatch = bidsResults(opt);
194209

195210
end
196211

@@ -222,17 +237,14 @@ function test_bidsResults_subject_lvl()
222237
opt.results.MC = 'FWE';
223238
opt.results.p = 0.05;
224239
opt.results.k = 5;
225-
226240
opt.results.png = true();
227-
228241
opt.results.csv = true();
229-
230242
opt.results.threshSpm = true();
231-
232243
opt.results.binary = true();
233-
234244
opt.results.nidm = true();
235245

246+
opt = rmResultsFromModel(opt);
247+
236248
%% WHEN
237249
matlabbatch = bidsResults(opt);
238250

@@ -272,3 +284,10 @@ function test_bidsResults_subject_lvl()
272284
expected{4}.binary.basename);
273285

274286
end
287+
288+
function opt = rmResultsFromModel(opt)
289+
for i = 1:numel(opt.model.bm.Nodes)
290+
opt.model.bm.Nodes{i}.Model.Software = rmfield(opt.model.bm.Nodes{i}.Model.Software, ...
291+
'bidspm');
292+
end
293+
end

0 commit comments

Comments
 (0)