Skip to content

Commit

Permalink
fix and implement F test
Browse files Browse the repository at this point in the history
  • Loading branch information
Remi-Gau committed Jul 30, 2024
1 parent b3722d5 commit 1101fa4
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 37 deletions.
37 changes: 37 additions & 0 deletions demos/openneuro/models/model-ds003397_smdl.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,35 @@
"Test": "t"
}
},
{
"Level": "Dataset",
"Name": "dataset_level",
"Description": "average across all subjects",
"GroupBy": [
"contrast"
],
"Model": {
"Type": "glm",
"X": [
1
]
}
},
{
"Level": "Dataset",
"Name": "within_group",
"Description": "within group t-test",
"GroupBy": [
"contrast",
"group"
],
"Model": {
"Type": "glm",
"X": [
1
]
}
},
{
"Level": "Dataset",
"Name": "between_groups",
Expand Down Expand Up @@ -161,6 +190,14 @@
"Source": "run_level",
"Destination": "subject_level"
},
{
"Source": "subject_level",
"Destination": "dataset_level"
},
{
"Source": "subject_level",
"Destination": "within_group"
},
{
"Source": "subject_level",
"Destination": "between_groups",
Expand Down
75 changes: 57 additions & 18 deletions src/batches/stats/setBatchGroupLevelContrasts.m
Original file line number Diff line number Diff line change
Expand Up @@ -88,35 +88,37 @@

thisContrast = bm.get_contrasts('Name', nodeName);

if strcmp(groupGlmType, 'two_sample_t_test')
label = '2samplesTTest';
else
label = '1WayANOVA';
end

for j = 1:numel(contrastsList)

spmMatFile = fullfile(getRFXdir(opt, ...
nodeName, ...
contrastsList{j}, ...
'1WayANOVA'), ...
label), ...
'SPM.mat');

for iCon = 1:numel(thisContrast)

% Sort conditions and weights
ConditionList = strrep(thisContrast{iCon}.ConditionList, ...
[groupColumnHdr, '.'], ...
'');
[ConditionList, I] = sort(ConditionList);
Weights = thisContrast{iCon}.Weights(I);
convec = generateConStruct(thisContrast{iCon}, ...
availableGroups, ...
groupColumnHdr);

% Create contrast vectors by what was passed in the model
convec = zeros(size(availableGroups));
for iGroup = 1:numel(availableGroups)
index = strcmp(availableGroups{iGroup}, ConditionList);
if any(index)
convec(iGroup) = Weights(index);
end
end
if strcmp(thisContrast{iCon}.Test, 't')
consess{iCon}.tcon.name = thisContrast{iCon}.Name; %#ok<*AGROW>
consess{iCon}.tcon.sessrep = 'none';
consess{iCon}.tcon.convec = convec;

elseif strcmp(thisContrast{iCon}.Test, 'F')
consess{iCon}.fcon.name = thisContrast{iCon}.Name;
consess{iCon}.fcon.sessrep = 'none';
consess{iCon}.fcon.convec = convec;

consess{iCon}.tcon.name = thisContrast{iCon}.Name;
consess{iCon}.tcon.convec = convec;
consess{iCon}.tcon.sessrep = 'none';
end
end

matlabbatch = setBatchContrasts(matlabbatch, opt, spmMatFile, consess);
Expand Down Expand Up @@ -144,3 +146,40 @@
matlabbatch = setBatchContrasts(matlabbatch, opt, spmMatFile, consess);

end

function convec = generateConStruct(thisContrast, availableGroups, groupColumnHdr)
% Sort conditions and weights
ConditionList = strrep(thisContrast.ConditionList, ...
[groupColumnHdr, '.'], ...
'');
[ConditionList, I] = sort(ConditionList);

if strcmp(thisContrast.Test, 't')

Weights = thisContrast.Weights(I);

% Create contrast vectors by what was passed in the model
convec = zeros(size(availableGroups));
for iGroup = 1:numel(availableGroups)
index = strcmp(availableGroups{iGroup}, ConditionList);
if any(index)
convec(iGroup) = Weights(index);
end
end

elseif strcmp(thisContrast.Test, 'F')

Weights = thisContrast.Weights(I, :);

% Create contrast vectors by what was passed in the mode
convec = zeros(size(Weights, 1), numel(availableGroups));
for iGroup = 1:numel(availableGroups)
index = strcmp(availableGroups{iGroup}, ConditionList);
if any(index)
convec(:, iGroup) = Weights(:, index);
end
end

end

end
24 changes: 9 additions & 15 deletions src/workflows/stats/bidsResults.m
Original file line number Diff line number Diff line change
Expand Up @@ -497,25 +497,19 @@

end

case 'two_sample_t_test'
case {'two_sample_t_test', 'one_way_anova'}

thisContrast = bm.get_contrasts('Name', result.nodeName);

result.dir = getRFXdir(opt, result.nodeName, name);

for iCon = 1:numel(thisContrast)
result.name = [thisContrast{iCon}.Name ' - ' name];
result.contrastNb = iCon;
matlabbatch = appendToBatch(matlabbatch, opt, result);
end

case 'one_way_anova'

contrastsList = getContrastsListForFactorialDesign(opt, result.nodeName);
edge = bm.get_edge('Destination', result.nodeName);
contrastsList = edge.Filter.contrast;

for iCon = 1:numel(contrastsList)

label = '1WayANOVA';
if strcmp(glmType, 'two_sample_t_test')
label = '2samplesTTest';

Check warning on line 508 in src/workflows/stats/bidsResults.m

View check run for this annotation

Codecov / codecov/patch

src/workflows/stats/bidsResults.m#L508

Added line #L508 was not covered by tests
else
label = '1WayANOVA';
end

result.dir = getRFXdir(opt, result.nodeName, contrastsList{iCon}, label);

load(fullfile(result.dir, 'SPM.mat'), 'SPM');
Expand Down
18 changes: 18 additions & 0 deletions tests/data/models/model-vismotionOneWayANOVA_smdl.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,24 @@
1
],
"Test": "t"
},
{
"Name": "F_test",
"ConditionList": [
"diagnostic.blind",
"diagnostic.relative"
],
"Weights": [
[
1,
0
],
[
0,
1
]
],
"Test": "F"
}
]
}
Expand Down
2 changes: 2 additions & 0 deletions tests/tests_stats/subject_level/test_specifyContrasts.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
function test_specifyContrasts_bug_854()
% no error when no contrast to build

skipIfOctave('mixed-string-concat warning thrown');

% GIVEN
subLabel = '01';

Expand Down
13 changes: 9 additions & 4 deletions tests/tests_workflows/stats/test_bidsRFX_3_groups.m
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,25 @@ function test_bidsRFX_one_way_anova_contrast()
rfxDir = getRFXdir(opt, nodeName, contrastName, '1WayANOVA');
assertEqual(fileparts(matlabbatch{1}.spm.stats.con.spmmat{1}), rfxDir);

assertEqual(numel(matlabbatch{1}.spm.stats.con.consess), 2);
assertEqual(numel(matlabbatch{1}.spm.stats.con.consess), 3);

assertEqual(matlabbatch{1}.spm.stats.con.consess{1}.tcon.name, 'relative_gt_ctrl');
assertEqual(matlabbatch{1}.spm.stats.con.consess{1}.tcon.convec, [0; -1; 1]);
assertEqual(matlabbatch{1}.spm.stats.con.consess{2}.tcon.name, 'blind_gt_relative');
assertEqual(matlabbatch{1}.spm.stats.con.consess{2}.tcon.convec, [-1; 0; 1]);
assertEqual(matlabbatch{1}.spm.stats.con.consess{3}.fcon.name, 'F_test');
assertEqual(matlabbatch{1}.spm.stats.con.consess{3}.fcon.convec, [1, 0, 0; 0, 0, 1]);

contrastName = 'VisStat_gt_VisMot';
rfxDir = getRFXdir(opt, nodeName, contrastName, '1WayANOVA');
assertEqual(fileparts(matlabbatch{2}.spm.stats.con.spmmat{1}), rfxDir);

assertEqual(numel(matlabbatch{2}.spm.stats.con.consess), 2);
assertEqual(matlabbatch{1}.spm.stats.con.consess{1}.tcon.name, 'relative_gt_ctrl');
assertEqual(numel(matlabbatch{2}.spm.stats.con.consess), 3);
assertEqual(matlabbatch{2}.spm.stats.con.consess{1}.tcon.name, 'relative_gt_ctrl');
assertEqual(matlabbatch{2}.spm.stats.con.consess{1}.tcon.convec, [0; -1; 1]);
assertEqual(matlabbatch{1}.spm.stats.con.consess{2}.tcon.name, 'blind_gt_relative');
assertEqual(matlabbatch{2}.spm.stats.con.consess{2}.tcon.name, 'blind_gt_relative');
assertEqual(matlabbatch{2}.spm.stats.con.consess{2}.tcon.convec, [-1; 0; 1]);
assertEqual(matlabbatch{1}.spm.stats.con.consess{3}.fcon.name, 'F_test');
assertEqual(matlabbatch{1}.spm.stats.con.consess{3}.fcon.convec, [1, 0, 0; 0, 0, 1]);

end

0 comments on commit 1101fa4

Please sign in to comment.