diff --git a/narps_open/pipelines/team_4SZ2.py b/narps_open/pipelines/team_4SZ2.py index c68ef425..07ceed47 100644 --- a/narps_open/pipelines/team_4SZ2.py +++ b/narps_open/pipelines/team_4SZ2.py @@ -214,6 +214,7 @@ def get_two_sample_t_test_regressors( equal_range_ids: list, equal_indifference_ids: list, subject_list: list, + run_list: list ) -> dict: """ Create dictionary of regressors for two sample t-test group analysis. @@ -222,19 +223,24 @@ def get_two_sample_t_test_regressors( - equal_range_ids: ids of subjects in equal range group - equal_indifference_ids: ids of subjects in equal indifference group - subject_list: ids of subject for which to do the analysis - + - run_list: ids of runs for which to do the analysis Returns: - regressors, dict: containing named lists of regressors. - groups, list: group identifiers to distinguish groups in FSL analysis. """ - # Create 2 lists containing n_sub values which are + # Create 2 lists containing a value for each run, which is # * 1 if the participant is on the group # * 0 otherwise - equal_range_regressors = [1 if i in equal_range_ids else 0 for i in subject_list] - equal_indifference_regressors = [ - 1 if i in equal_indifference_ids else 0 for i in subject_list - ] + equal_range_regressors = [] + equal_indifference_regressors = [] + + for subject_id in subject_list: + value_er = 1 if subject_id in equal_range_ids else 0 + value_ei = 1 if subject_id in equal_indifference_ids else 0 + for _ in run_list: + equal_range_regressors.append(value_er) + equal_indifference_regressors.append(value_ei) # Create regressors output : a dict with the two list regressors = dict( @@ -268,9 +274,14 @@ def get_group_level_analysis_sub_workflow(self, method): Returns: - group_level: nipype.WorkFlow """ - # Compute the number of participants used to do the analysis + # Compute the number of participants in the analysis nb_subjects = len(self.subject_list) + # Compute the number of participants in the group + nb_subjects_in_group = nb_subjects + if method in ['equalIndifference', 'equalRange']: + nb_subjects_in_group = len([s for s in self.subject_list if s in get_group(method)]) + # Declare the workflow group_level = Workflow( base_dir = self.directories.working_dir, @@ -294,9 +305,13 @@ def get_group_level_analysis_sub_workflow(self, method): 'sub-*_task-MGT_run-*_bold_space-MNI152NLin2009cAsym_brainmask.nii.gz') } select_files = Node(SelectFiles(templates), name = 'select_files') - select_files.inputs.base_directory = self.directories.results_dir + select_files.inputs.base_directory = self.directories.dataset_dir group_level.connect(information_source, 'contrast_id', select_files, 'contrast_id') + # Create a function to complete the subject ids out from the get_*_subjects node + complete_subject_ids = lambda l : [f'_subject_id_{a}' for a in l] + complete_sub_ids = lambda l : [f'sub-{a}' for a in l] + # Function Node elements_in_string # Get contrast of parameter estimates (cope) for subjects in a given group # Note : using a MapNode with elements_in_string requires using clean_list to remove @@ -348,14 +363,15 @@ def get_group_level_analysis_sub_workflow(self, method): # Split Node - Split mask list to serve them as inputs of the MultiImageMaths node. split_masks = Node(Split(), name = 'split_masks') - split_masks.inputs.splits = [1, len(self.subject_list) - 1] + split_masks.inputs.splits = [1, (nb_subjects_in_group * len(self.run_list)) - 1] split_masks.inputs.squeeze = True # Unfold one-element splits removing the list group_level.connect(get_masks, ('out_list', clean_list), split_masks, 'inlist') # MultiImageMaths Node - Create a subject mask by # computing the intersection of all run masks. mask_intersection = Node(MultiImageMaths(), name = 'mask_intersection') - mask_intersection.inputs.op_string = '-mul %s ' * (len(self.subject_list) - 1) + mask_intersection.inputs.op_string = '-mul %s ' * \ + ((nb_subjects_in_group * len(self.run_list)) - 1) group_level.connect(split_masks, 'out1', mask_intersection, 'in_file') group_level.connect(split_masks, 'out2', mask_intersection, 'operand_files') @@ -411,9 +427,12 @@ def get_group_level_analysis_sub_workflow(self, method): ) get_group_subjects.inputs.list_1 = get_group(method) get_group_subjects.inputs.list_2 = self.subject_list - group_level.connect(get_group_subjects, 'out_list', get_copes, 'elements') - group_level.connect(get_group_subjects, 'out_list', get_varcopes, 'elements') - group_level.connect(get_group_subjects, 'out_list', get_masks, 'elements') + group_level.connect( + get_group_subjects, ('out_list', complete_subject_ids), get_copes, 'elements') + group_level.connect( + get_group_subjects, ('out_list', complete_subject_ids), get_varcopes, 'elements') + group_level.connect( + get_group_subjects, ('out_list', complete_sub_ids), get_masks, 'elements') # Function Node get_one_sample_t_test_regressors # Get regressors in the equalRange and equalIndifference method case @@ -425,16 +444,17 @@ def get_group_level_analysis_sub_workflow(self, method): ), name = 'regressors_one_sample', ) - group_level.connect(get_group_subjects, 'out_list', regressors_one_sample, 'subject_list') + regressors_one_sample.inputs.subject_list = range( + nb_subjects_in_group * len(self.run_list)) group_level.connect(regressors_one_sample, 'regressors', specify_model, 'regressors') elif method == 'groupComp': # Select copes and varcopes corresponding to the selected subjects # Indeed the SelectFiles node asks for all (*) subjects available - get_copes.inputs.elements = self.subject_list - get_varcopes.inputs.elements = self.subject_list - get_masks.inputs.elements = self.subject_list + get_copes.inputs.elements = complete_subject_ids(self.subject_list) + get_varcopes.inputs.elements = complete_subject_ids(self.subject_list) + get_masks.inputs.elements = complete_sub_ids(self.subject_list) # Setup a two sample t-test specify_model.inputs.contrasts = [ @@ -474,12 +494,14 @@ def get_group_level_analysis_sub_workflow(self, method): 'equal_range_ids', 'equal_indifference_ids', 'subject_list', + 'run_list' ], output_names = ['regressors', 'groups'] ), name = 'regressors_two_sample', ) regressors_two_sample.inputs.subject_list = self.subject_list + regressors_two_sample.inputs.run_list = self.run_list # Add missing connections group_level.connect( @@ -500,9 +522,7 @@ def get_group_level_outputs(self): 'contrast_id': self.contrast_list, 'method': ['equalRange', 'equalIndifference'], 'file': [ - '_cluster0/zstat1_pval.nii.gz', '_cluster0/zstat1_threshold.nii.gz', - '_cluster1/zstat2_pval.nii.gz', '_cluster1/zstat2_threshold.nii.gz', 'tstat1.nii.gz', 'tstat2.nii.gz', @@ -524,7 +544,6 @@ def get_group_level_outputs(self): parameters = { 'contrast_id': self.contrast_list, 'file': [ - '_cluster0/zstat1_pval.nii.gz', # TODO : output for randomise '_cluster0/zstat1_threshold.nii.gz', 'tstat1.nii.gz', 'zstat1.nii.gz' @@ -564,13 +583,13 @@ def get_hypotheses_outputs(self): join(f'group_level_analysis_equalRange_nsub_{nb_sub}', '_contrast_id_1', 'zstat1.nii.gz'), join(f'group_level_analysis_equalIndifference_nsub_{nb_sub}', - '_contrast_id_2', '_cluster0', 'zstat1_threshold.nii.gz'), + '_contrast_id_2', '_cluster1', 'zstat2_threshold.nii.gz'), join(f'group_level_analysis_equalIndifference_nsub_{nb_sub}', - '_contrast_id_2', 'zstat1.nii.gz'), + '_contrast_id_2', 'zstat2.nii.gz'), join(f'group_level_analysis_equalRange_nsub_{nb_sub}', - '_contrast_id_2', '_cluster0', 'zstat1_threshold.nii.gz'), + '_contrast_id_2', '_cluster1', 'zstat2_threshold.nii.gz'), join(f'group_level_analysis_equalRange_nsub_{nb_sub}', - '_contrast_id_2', 'zstat1.nii.gz'), + '_contrast_id_2', 'zstat2.nii.gz'), join(f'group_level_analysis_equalIndifference_nsub_{nb_sub}', '_contrast_id_2', '_cluster0', 'zstat1_threshold.nii.gz'), join(f'group_level_analysis_equalIndifference_nsub_{nb_sub}', diff --git a/tests/pipelines/test_team_4SZ2.py b/tests/pipelines/test_team_4SZ2.py index 291b0704..1f3c08ff 100644 --- a/tests/pipelines/test_team_4SZ2.py +++ b/tests/pipelines/test_team_4SZ2.py @@ -52,11 +52,11 @@ def test_outputs(): # 1 - 1 subject outputs pipeline.subject_list = ['001'] - helpers.test_pipeline_outputs(pipeline, [0, 2*4*1*4, 0, 8*2*2 + 4*2, 18]) + helpers.test_pipeline_outputs(pipeline, [0, 2*4*1*4, 0, 6*2*2 + 3*2, 18]) # 2 - 4 subjects outputs pipeline.subject_list = ['001', '002', '003', '004'] - helpers.test_pipeline_outputs(pipeline, [0, 2*4*4*4, 0, 8*2*2 + 4*2, 18]) + helpers.test_pipeline_outputs(pipeline, [0, 2*4*4*4, 0, 6*2*2 + 3*2, 18]) @staticmethod @mark.unit_test @@ -86,6 +86,31 @@ def test_subject_information(): [6.0, 14.0, 19.0, 15.0, 17.0] ]) + @staticmethod + @mark.unit_test + def test_one_sample_t_test_regressors(): + """ Test the get_one_sample_t_test_regressors method """ + + result = PipelineTeam4SZ2.get_one_sample_t_test_regressors(['001', '002', '003', '004']) + assert result == {'group_mean' : [1]*4} + + @staticmethod + @mark.unit_test + def test_two_sample_t_test_regressors(): + """ Test the get_two_sample_t_test_regressors method """ + + result_1, result_2 = PipelineTeam4SZ2.get_two_sample_t_test_regressors( + ['001', '003'], # equal_range_ids + ['002', '004'], # equal_indifference_ids + ['001', '002', '003', '004'], # subject_list + ['01', '02'] # run_list + ) + assert result_1 == { + 'equalRange' : [1, 1, 0, 0, 1, 1, 0, 0], + 'equalIndifference' : [0, 0, 1, 1, 0, 0, 1, 1] + } + assert result_2 == [1, 1, 2, 2, 1, 1, 2, 2] + @staticmethod @mark.pipeline_test def test_execution():