Skip to content

Commit

Permalink
fix for FirstLevelModel compute_contrast for multiple runs with varyi…
Browse files Browse the repository at this point in the history
…ng matrice size or columns (#2688)

* fix for FirstLevelModel compute_contrast in case
 of multiple runs with varying matrice size or columns ordering

* add tests for contrast with formulas on matrices with varying size

* allow 1 or n_runs contrasts otherwise raise ValueError

* assert that ValueError is raised if n_contrasts not in (n_runs,1)

* fix warning to only be raised when n_contrasts==1 and n_runs>1
check that warning is raised when n_contrasts==1 and n_runs>1

* tests: check warning for formulas
  • Loading branch information
bpinsard committed Feb 12, 2021
1 parent e372946 commit 29f64ef
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 7 deletions.
16 changes: 10 additions & 6 deletions nilearn/glm/first_level/first_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ def __init__(self, t_r=None, slice_time_ref=0., hrf_model='glover',
def fit(self, run_imgs, events=None, confounds=None,
design_matrices=None):
"""Fit the GLM
For each run:
1. create design matrix X
2. do a masker job: fMRI_data -> Y
Expand Down Expand Up @@ -568,6 +568,15 @@ def compute_contrast(self, contrast_def, stat_type=None,
raise ValueError('contrast_def must be an array or str or list of'
' (array or str)')

n_runs = len(self.labels_)
n_contrasts = len(con_vals)
if n_contrasts == 1 and n_runs > 1:
warn('One contrast given, assuming it for all %d runs' % n_runs)
con_vals = con_vals * n_runs
elif n_contrasts != n_runs:
raise ValueError('%n contrasts given, while there are %n runs' %
(n_contrasts, n_runs))

# Translate formulas to vectors
for cidx, (con, design_mat) in enumerate(zip(con_vals,
self.design_matrices_)
Expand All @@ -577,11 +586,6 @@ def compute_contrast(self, contrast_def, stat_type=None,
con_vals[cidx] = expression_to_contrast_vector(
con, design_columns)

n_runs = len(self.labels_)
if len(con_vals) != n_runs:
warn('One contrast given, assuming it for all %d runs' % n_runs)
con_vals = con_vals * n_runs

valid_types = ['z_score', 'stat', 'p_value', 'effect_size',
'effect_variance']
valid_types.append('all') # ensuring 'all' is the final entry.
Expand Down
42 changes: 41 additions & 1 deletion nilearn/glm/tests/test_first_level.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import numpy as np
import pandas as pd
import pytest
import warnings
from nibabel import Nifti1Image, load
from nibabel.tmpdirs import InTemporaryDirectory
from numpy.testing import (assert_almost_equal, assert_array_almost_equal,
Expand Down Expand Up @@ -216,6 +217,45 @@ def test_high_level_glm_different_design_matrices():
2 * get_data(z_joint))


def test_high_level_glm_different_design_matrices_formulas():
# test that one can estimate a contrast when design matrices are different
shapes, rk = ((7, 8, 7, 15), (7, 8, 7, 19)), 3
mask, fmri_data, design_matrices = generate_fake_fmri_data_and_design(shapes, rk)

# make column names identical
design_matrices[1].columns = design_matrices[0].columns
# add a column to the second design matrix
design_matrices[1]['new'] = np.ones((19, 1))

# Fit a glm with two sessions and design matrices
multi_session_model = FirstLevelModel(mask_img=mask).fit(
fmri_data, design_matrices=design_matrices)

# Compute contrast with formulas
cols_formula = tuple(design_matrices[0].columns[:2])
formula = "%s-%s" % cols_formula
with pytest.warns(UserWarning, match='One contrast given, assuming it for all 2 runs'):
z_joint_formula = multi_session_model.compute_contrast(
formula, output_type='effect_size')


def test_compute_contrast_num_contrasts():

shapes, rk = ((7, 8, 7, 15), (7, 8, 7, 19), (7, 8, 7, 13)), 3
mask, fmri_data, design_matrices = generate_fake_fmri_data_and_design(shapes, rk)

# Fit a glm with 3 sessions and design matrices
multi_session_model = FirstLevelModel(mask_img=mask).fit(
fmri_data, design_matrices=design_matrices)

# raise when n_contrast != n_runs | 1
with pytest.raises(ValueError):
multi_session_model.compute_contrast([np.eye(rk)[1]]*2)

multi_session_model.compute_contrast([np.eye(rk)[1]]*3)
with pytest.warns(UserWarning, match='One contrast given, assuming it for all 3 runs'):
multi_session_model.compute_contrast([np.eye(rk)[1]])

def test_run_glm():
rng = np.random.RandomState(42)
n, p, q = 100, 80, 10
Expand Down Expand Up @@ -280,7 +320,7 @@ def test_fmri_inputs():
# test with confounds as numpy array
FirstLevelModel(mask_img=mask).fit([fi], design_matrices=[d],
confounds=conf.values)

FirstLevelModel(mask_img=mask).fit([fi, fi],
design_matrices=[d, d])
FirstLevelModel(mask_img=None).fit((fi, fi),
Expand Down

0 comments on commit 29f64ef

Please sign in to comment.