Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve pmod handling in pspm_glm #700

Merged
merged 4 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 56 additions & 19 deletions src/pspm_get_timing.m
Original file line number Diff line number Diff line change
Expand Up @@ -294,27 +294,64 @@
end
end
% ensure same names exist for all sessions and re-sort if
% necesssary
% necesssary; collect number and names of all pmods
outtiming = struct('names', {}, 'onsets', {}, 'durations', {});
for iFile = 1:nFiles
for iCond = 1:numel(allnames)
name_idx = find(strcmpi(temptiming(iFile).names, allnames{iCond}));
if numel(name_idx) > 1
warning('Names must be unique within each session.');
return;
elseif numel(name_idx) == 1
outtiming(iFile).onsets{iCond} = temptiming(iFile).onsets{name_idx};
outtiming(iFile).durations{iCond} = temptiming(iFile).durations{name_idx};
if isfield(temptiming, 'pmod') && numel(temptiming(iFile).pmod) >= name_idx
outtiming(iFile).pmod(iCond) = temptiming(iFile).pmod(name_idx);
end
elseif numel(name_idx) == 0
outtiming(iFile).onsets{iCond} = [];
outtiming(iFile).durations{iCond} = [];
end
outtiming(iFile).names{iCond} = allnames{iCond};
end
pmodname = {};
try
for iFile = 1:nFiles
for iCond = 1:numel(allnames)
name_idx = find(strcmpi(temptiming(iFile).names, allnames{iCond}));
if numel(name_idx) > 1
warning('Names must be unique within each session.');
return;
elseif numel(name_idx) == 1
outtiming(iFile).onsets{iCond} = temptiming(iFile).onsets{name_idx};
outtiming(iFile).durations{iCond} = temptiming(iFile).durations{name_idx};
% assign pmods
if isfield(temptiming, 'pmod') && ...
~isempty(temptiming(iFile).pmod) && ...
numel(temptiming(iFile).pmod) >= name_idx
outtiming(iFile).pmod(iCond) = temptiming(iFile).pmod(name_idx);
% store pmod number and name for later use
pmodno(iFile, iCond) = numel(temptiming(iFile).pmod(name_idx).param);
% get pmodname from first session
if isempty(pmodname) || ...
numel(pmodname) < iCond || ...
isempty(pmodname{iCond})
pmodname{iCond} = temptiming(iFile).pmod(name_idx).name;
end
else
pmodno(iFile, iCond) = 0;
end
elseif numel(name_idx) == 0
outtiming(iFile).onsets{iCond} = [];
outtiming(iFile).durations{iCond} = [];
pmodno(iFile, iCond) = 0;
end
outtiming(iFile).names{iCond} = allnames{iCond};
end
end
if nFiles > 1
pmodno = max(pmodno);
end
% initialise pmods
for iFile = 1:nFiles
for iCond = 1:numel(allnames)
% insert pmods if they exist in other sessions
if pmodno(iCond) > 0 && ...
(numel(outtiming(iFile).pmod) < iCond || ...
isempty(outtiming(iFile).pmod(iCond).param))
for i_pmod = 1:pmodno(iCond)
outtiming(iFile).pmod(iCond).param{i_pmod} = [];
outtiming(iFile).pmod(iCond).name{i_pmod} = pmodname{iCond}{i_pmod};
end
end
end
end
catch
keyboard
end

% clear local variables
clear iParam iParamNew iCond iFile iPmod
else
Expand Down
9 changes: 8 additions & 1 deletion test/pspm_get_timing_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ function case_onsets(this)
this.verifyEqual(outtiming.names(:), names(:));
this.verifyTrue(isfield(outtiming, 'durations'));
% test 4
% this checks github issue 191: a pmod is defined on a condition
% that is not present in the first session
names = {'name1', 'name2'};
onsets = {[1 2 3], [3 4 5]};
durations = {[3 4 5]', [5 6 7]'};
Expand All @@ -139,7 +141,12 @@ function case_onsets(this)
this.verifyEqual(outtiming(1).names, {'name1', 'name2', 'name5'});
this.verifyTrue(isfield(outtiming(1), 'durations'));
this.verifyEqual(outtiming(1).durations, [durations, {[]}]);
this.verifyEqual(outtiming(1).pmod.param, {[2 3 4], [4 9 16], [4 5 6]});
this.verifyEqual(outtiming(1).pmod(1).param, {[2 3 4], [4 9 16], [4 5 6]});
this.verifyEqual(outtiming(1).pmod(2).param, []);
this.verifyEqual(outtiming(1).pmod(3).param, {[], [], []});
this.verifyEqual(outtiming(1).pmod(1).name, {'name3^1' 'name3^2' 'name4^1'});
this.verifyEqual(outtiming(1).pmod(2).name,[]);
this.verifyEqual(outtiming(1).pmod(3).name, {'name6^1' 'name6^2' 'name4^1'});
load(fn_mat2);
this.verifyTrue(sts==1);
this.verifyEqual(outtiming(2).onsets, [onsets{2}, {[]}, onsets{1}]);
Expand Down
27 changes: 27 additions & 0 deletions test/pspm_glm_test.m
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,33 @@ function test_eye(this, cutoff)
delete(model.datafile);
delete(model.modelfile);
end
function test_pmod(this)
% this checks github issue 191: a pmod is defined on a condition
% that is not present in the first session
bf1 = 1; offset = 0; sr = 100; duration = 120;
model.modelfile = 'Test_GLM_pmod_model.mat';
model.datafile = {'Test_GLM_pmod_data_1.mat', 'Test_GLM_pmod_data_2.mat'};
model.timeunits = 'seconds';
model.timing{1}.names{1} = 'cond_a';
model.timing{1}.onsets{1} = [10 40 70 100]';
model.timing{2}.names{1} = 'cond_b';
model.timing{2}.onsets{1} = [20 50 80 110]';
model.timing{2}.pmod(1).param = {[1, 2, 3, 4]; [1, 3, 3, 1]};
model.timing{2}.pmod(1).name = {'pmod1', 'pmod2'};
Y1 = pspm_glm_test.testdata_gen(model.timing{1}.onsets{1}, bf1, offset, 0, sr, duration);
Y2 = pspm_glm_test.testdata_gen(model.timing{2}.onsets{1}, bf1, offset, 0, sr, duration);
pspm_glm_test.save_datafile(Y1, sr, duration, model.datafile{1});
pspm_glm_test.save_datafile(Y2, sr, duration, model.datafile{2});
model.channel = 'scr';
model.modelspec = 'scr';
model.modality = 'scr';
[sts, glm] = this.verifyWarningFree(@() pspm_glm(model));
this.verifyEqual(glm.names, ...
{'cond_a, bf 1', 'cond_a, bf 2', 'cond_b, bf 1', 'cond_b, bf 2', ...
'cond_b x pmod1^1, bf 1', 'cond_b x pmod1^1, bf 2', 'cond_b x pmod2^1, bf 1', ...
'cond_b x pmod2^1, bf 2', 'Constant 1', 'Constant 2'}');
delete(model.modelfile);
end
end
methods(Test, ParameterCombination='exhaustive')
function glm = test_extract_missing(this, cutoff, nan_percent)
Expand Down
Loading