def bids_autofind( bids_dir, modality='', path_template="{bids_dir}/sub-{{subject}}/ses-{{session}}/{modality}/sub-{{subject}}_ses-{{session}}_task-{{task}}_acq-{{acquisition}}.nii.gz", match_regex='', ): """Automatically generate a BIDS path template and a substitution iterator (list of dicts, as produced by `samri.utilities.bids_substitution_iterator`, and used as a standard input SAMRI function input) from a BIDS-respecting directory. Parameters ---------- bids_dir : str Path to BIDS-formatted directory modality : {"func", "anat"} Which modality to source data for (currently only supports "func", and "anat" - ideally we could extend this to include "dwi"). Returns ------- path_template : str String which can be formatted with any of the dictionaries in `substitutions` substitutions : list of dicti A substitution iterator usable as a standard SAMRI function input, which (together with `path_template`) unambiguoulsy identifies input files for analysis. """ bids_dir = path.abspath(path.expanduser(bids_dir)) if match_regex: pass elif modality in ("func", "dwi"): match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/' + modality + '/.*?_task-(?P<task>.+).*?_acq-(?P<acquisition>.+)\.nii.gz' elif modality == "": match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/.*?_task-(?P<task>.+).*?_acq-(?P<acquisition>.+).*?\.nii.gz' elif modality == "anat": match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/anat/.*?_(?P<task>.+).*?_acq-(?P<acquisition>.+)\.nii.gz' path_template = path_template.format(bids_dir=bids_dir, modality=modality) datafind = nio.DataFinder() datafind.inputs.root_paths = bids_dir datafind.inputs.match_regex = match_regex datafind_res = datafind.run() substitutions = [] for ix, i in enumerate(datafind_res.outputs.out_paths): substitution = {} substitution["acquisition"] = datafind_res.outputs.acquisition[ix] substitution["subject"] = datafind_res.outputs.sub[ix] substitution["session"] = datafind_res.outputs.ses[ix] substitution["task"] = datafind_res.outputs.task[ix] if path_template.format(**substitution) != i: print("Original DataFinder path: " + i) print("Reconstructed path: " + path_template.format(**substitution)) raise ValueError( "The reconstructed file path based on the substitution dictionary and the path template, is not identical to the corresponding path, found by `nipype.interfaces.io.DataFinder`. See string values above." ) substitutions.append(substitution) return path_template, substitutions
def test_datafinder_unpack(tmpdir): outdir = tmpdir.strpath single_res = os.path.join(outdir, "findme.txt") open(single_res, 'a').close() open(os.path.join(outdir, "dontfindme"), 'a').close() df = nio.DataFinder() df.inputs.root_paths = outdir df.inputs.match_regex = '.+/(?P<basename>.+)\.txt' df.inputs.unpack_single = True result = df.run() print(result.outputs.out_paths) assert result.outputs.out_paths == single_res
def get_scores( bids_dir, reference, modality="func", save_as=False, ): #ideally at some point, we would also support dwi allowed_modalities = ("func", "anat") if modality not in allowed_modalities: raise ValueError("modality parameter needs to be one of " + ", ".join(allowed_modalities) + ".") bids_dir = path.abspath(path.expanduser(bids_dir)) reference = path.abspath(path.expanduser(reference)) if modality in ("func", "dwi"): match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/' + modality + '/.*?_trial-(?P<trial>.+)\.nii.gz' elif modality == "anat": match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/anat/.*?_(?P<trial>.+)\.nii.gz' datafind = nio.DataFinder() datafind.inputs.root_paths = bids_dir datafind.inputs.match_regex = match_regex datafind_res = datafind.run() files_data = [] n_jobs = mp.cpu_count() - 2 similarity_data = Parallel(n_jobs=n_jobs, verbose=0, backend="threading")( map( delayed(measure_sim), datafind_res.outputs.out_paths, datafind_res.outputs.ses, datafind_res.outputs.sub, datafind_res.outputs.trial, [modality] * len(datafind_res.outputs.out_paths), [reference] * len(datafind_res.outputs.out_paths), )) df = pd.DataFrame.from_dict(similarity_data) if save_as: save_as = path.abspath(path.expanduser(save_as)) if save_as.lower().endswith('.csv'): df.to_csv(save_as) else: raise ValueError( "Please specify an output path ending in any one of " + ",".join((".csv", )) + ".")
def test_datafinder_depth(tmpdir): outdir = str(tmpdir) os.makedirs(os.path.join(outdir, '0', '1', '2', '3')) df = nio.DataFinder() df.inputs.root_paths = os.path.join(outdir, '0') for min_depth in range(4): for max_depth in range(min_depth, 4): df.inputs.min_depth = min_depth df.inputs.max_depth = max_depth result = df.run() expected = ['{}'.format(x) for x in range(min_depth, max_depth + 1)] for path, exp_fname in zip(result.outputs.out_paths, expected): _, fname = os.path.split(path) assert fname == exp_fname
def test_datafinder_depth(tmpdir): outdir = tmpdir.strpath os.makedirs(os.path.join(outdir, "0", "1", "2", "3")) df = nio.DataFinder() df.inputs.root_paths = os.path.join(outdir, "0") for min_depth in range(4): for max_depth in range(min_depth, 4): df.inputs.min_depth = min_depth df.inputs.max_depth = max_depth result = df.run() expected = [ "{}".format(x) for x in range(min_depth, max_depth + 1) ] for path, exp_fname in zip(result.outputs.out_paths, expected): _, fname = os.path.split(path) assert fname == exp_fname
def seed_based( bids_base, seed_mask=None, debug=False, exclude={}, include={}, keep_crashdump=False, keep_work=False, match_regex='.+/sub-(?P<sub>[a-zA-Z0-9]+)/ses-(?P<ses>[a-zA-Z0-9]+)/.*?_acq-(?P<acq>[a-zA-Z0-9]+)_task-(?P<task>[a-zA-Z0-9]+)_(?P<mod>[a-zA-Z0-9]+).(?:nii|nii\.gz)', n_procs=N_PROCS, workflow_name="diagnostic", ): '''Create seed-based functional connectivity maps based on a GLM timecourse analysis (with FSL's FLAMEO) for a given seed mask and a given BIDS-formatted directory tree. Parameters ---------- bids_base : string, optional Path to the top level of a BIDS directory tree for which to perform the diagnostic. debug : bool, optional Enable full nipype debugging support for the workflow construction and execution. exclude : dict, optional A dictionary with any subset of 'subject', 'session', 'acquisition', 'task', 'modality', and 'path' as keys and corresponding identifiers as values. This is a blacklist: if this is specified only non-matching entries will be included in the analysis. include : dict, optional A dictionary with any subset of 'subject', 'session', 'acquisition', 'task', 'modality', and 'path' as keys and corresponding identifiers as values. This is a whitelist: if this is specified only matching entries will be included in the analysis. keep_crashdump : bool, optional Whether to keep the crashdump directory (containing all the crash reports for intermediary workflow steps, as managed by nipypye). This is useful for debugging and quality control. keep_work : bool, optional Whether to keep the work directory (containing all the intermediary workflow steps, as managed by nipypye). This is useful for debugging and quality control. match_regex : str, optional Regex matching pattern by which to select input files. Has to contain groups named "sub", "ses", "acq", "task", and "mod". n_procs : int, optional Maximum number of processes which to simultaneously spawn for the workflow. If not explicitly defined, this is automatically calculated from the number of available cores and under the assumption that the workflow will be the main process running for the duration that it is running. workflow_name : string, optional Name of the workflow execution. The output will be saved one level above the bids_base, under a directory bearing the name given here. ''' bids_base = path.abspath(path.expanduser(bids_base)) datafind = nio.DataFinder() datafind.inputs.root_paths = bids_base datafind.inputs.match_regex = match_regex datafind_res = datafind.run() data_selection = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res.outputs.acq, datafind_res.outputs.task, datafind_res.outputs.mod, datafind_res.outputs.out_paths ]) data_selection = [list(i) for i in data_selection] data_selection = pd.DataFrame(data_selection, columns=('subject', 'session', 'acquisition', 'task', 'modality', 'path')) data_selection = data_selection.sort_values(['session', 'subject'], ascending=[1, 1]) if exclude: for key in exclude: data_selection = data_selection[~data_selection[key]. isin(exclude[key])] if include: for key in include: data_selection = data_selection[data_selection[key].isin( include[key])] data_selection['out_path'] = '' if data_selection['path'].str.contains('.nii.gz').any(): data_selection['out_path'] = data_selection['path'].apply( lambda x: path.basename( path.splitext(path.splitext(x)[0])[0] + '_MELODIC')) else: data_selection['out_path'] = data_selection['path'].apply( lambda x: path.basename(path.splitext(x)[0] + '_MELODIC')) paths = data_selection['path'] infosource = pe.Node(interface=util.IdentityInterface( fields=['path'], mandatory_inputs=False), name="infosource") infosource.iterables = [('path', paths)] dummy_scans = pe.Node( name='dummy_scans', interface=util.Function( function=force_dummy_scans, input_names=inspect.getargspec(force_dummy_scans)[0], output_names=['out_file'])) dummy_scans.inputs.desired_dummy_scans = 10 bids_filename = pe.Node(name='bids_filename', interface=util.Function( function=out_path, input_names=inspect.getargspec(out_path)[0], output_names=['filename'])) bids_filename.inputs.selection_df = data_selection bids_container = pe.Node(name='path_container', interface=util.Function( function=container, input_names=inspect.getargspec(container)[0], output_names=['container'])) bids_container.inputs.selection_df = data_selection datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.abspath( path.join(bids_base, '..', 'diagnostic')) datasink.inputs.parameterization = False report_tr = pe.Node(name='report_tr', interface=util.Function( function=get_tr, input_names=inspect.getargspec(get_tr)[0], output_names=['tr'])) report_tr.inputs.ndim = 4 workflow_connections = [ (infosource, dummy_scans, [('path', 'in_file')]), (infosource, bids_filename, [('path', 'in_path')]), (bids_filename, bids_container, [('filename', 'out_path')]), (bids_container, datasink, [('container', 'container')]), (infosource, report_tr, [('path', 'in_file')]), (report_tr, melodic, [('tr', 'tr_sec')]), ] crashdump_dir = path.abspath( path.join(bids_base, '..', 'diagnostic_crashdump')) workflow_config = {'execution': {'crashdump_dir': crashdump_dir}} if debug: workflow_config['logging'] = { 'workflow_level': 'DEBUG', 'utils_level': 'DEBUG', 'interface_level': 'DEBUG', 'filemanip_level': 'DEBUG', 'log_to_file': 'true', } workdir_name = 'diagnostic_work' workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.base_dir = path.abspath(path.join(bids_base, '..')) workflow.config = workflow_config workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") if not keep_work or not keep_crashdump: try: workflow.run(plugin="MultiProc", plugin_args={'n_procs': n_procs}) except RuntimeError: pass else: workflow.run(plugin="MultiProc", plugin_args={'n_procs': n_procs}) if not keep_work: shutil.rmtree(path.join(workflow.base_dir, workdir_name)) if not keep_crashdump: try: shutil.rmtree(crashdump_dir) except FileNotFoundError: pass return
def l2_common_effect( l1_dir, exclude={}, groupby="session", keep_work=False, l2_dir="", loud=False, tr=1, nprocs=6, workflow_name="generic", mask="/home/chymera/ni_data/templates/ds_QBI_chr_bin.nii.gz", ): l1_dir = path.expanduser(l1_dir) if not l2_dir: l2_dir = path.abspath(path.join(l1_dir, "..", "..", "l2")) datafind = nio.DataFinder() datafind.inputs.root_paths = l1_dir datafind.inputs.match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/.*?_trial-(?P<scan>.+)_cope\.nii.gz' datafind_res = datafind.run() subjects = set(datafind_res.outputs.sub) sessions = set(datafind_res.outputs.ses) scans = set(datafind_res.outputs.scan) copemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="copemerge") varcopemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="varcopemerge") level2model = pe.Node(interface=fsl.L2Model(), name='level2model') flameo = pe.Node(interface=fsl.FLAMEO(), name="flameo") flameo.inputs.mask_file = mask flameo.inputs.run_mode = "ols" datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.join(l2_dir, workflow_name) datasink.inputs.substitutions = [ ('_iterable_', ''), ] if groupby == "subject": infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', subjects)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "group", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[['group', 'group']], varcbs=[['group', 'group']]) datasource.inputs.field_template = dict( copes="sub-%s/ses-*/sub-%s_ses-*_trial-*_cope.nii.gz", varcbs="sub-%s/ses-*/sub-%s_ses-*_trial-*_varcb.nii.gz", ) workflow_connections = [ (infosource, datasource, [('iterable', 'group')]), (infosource, copemerge, [(('iterable', add_suffix, "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "subject_scan": #does not currently work, due to missing iterator combinations (same issue as preprocessing) merge = pe.Node(interface=util.Merge(2), name="merge") infosource = pe.Node( interface=util.IdentityInterface(fields=['subject', 'scan']), name="infosource") infosource.iterables = [('subject', subjects), ('scan', scans)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "subject", "scan", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[[ "subject", "subject", "scan", ]], varcbs=[[ "subject", "subject", "scan", ]]) datasource.inputs.field_template = dict( copes="sub-%s/ses-*/sub-%s_ses-*_trial-%s_cope.nii.gz", varcbs="sub-%s/ses-*/sub-%s_ses-*_trial-%s_varcb.nii.gz", ) workflow_connections = [ (infosource, datasource, [('subject', 'subject'), ('scan', 'scan')]), (infosource, merge, [('subject', 'in1'), ('scan', 'in2')]), (merge, copemerge, [(('out', add_suffix, "_cope.nii.gz"), 'merged_file')]), (merge, varcopemerge, [(('out', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "session": infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', sessions)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "group", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[['group', 'group']], varcbs=[['group', 'group']]) datasource.inputs.field_template = dict( copes="sub-*/ses-%s/sub-*_ses-%s_trial-*_cope.nii.gz", varcbs="sub-*/ses-%s/sub-*_ses-%s_trial-*_varcb.nii.gz", ) workflow_connections = [ (infosource, datasource, [('iterable', 'group')]), (infosource, copemerge, [(('iterable', add_suffix, "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "scan": infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', scans)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "group", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[['group']], varcbs=[['group']]) datasource.inputs.field_template = dict( copes="sub-*/ses-*/sub-*_ses-*_trial-%s_cope.nii.gz ", varcbs="sub-*/ses-*/sub-*_ses-*_trial-%s_varcb.nii.gz ", ) workflow_connections = [ (infosource, datasource, [('iterable', 'group')]), (infosource, copemerge, [(('iterable', add_suffix, "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] datasource.inputs.base_directory = l1_dir datasource.inputs.sort_filelist = True datasource.inputs.template = "*" workflow_connections.extend([ (datasource, copemerge, [(('copes', datasource_exclude, exclude), 'in_files')]), (datasource, varcopemerge, [(('varcbs', datasource_exclude, exclude), 'in_files')]), (datasource, level2model, [(('copes', datasource_exclude, exclude, "len"), 'num_copes')]), (copemerge, flameo, [('merged_file', 'cope_file')]), (varcopemerge, flameo, [('merged_file', 'var_cope_file')]), (level2model, flameo, [('design_mat', 'design_file')]), (level2model, flameo, [('design_grp', 'cov_split_file')]), (level2model, flameo, [('design_con', 't_con_file')]), (flameo, datasink, [('copes', '@copes')]), (flameo, datasink, [('fstats', '@fstats')]), (flameo, datasink, [('tstats', '@tstats')]), (flameo, datasink, [('zstats', '@zstats')]), ]) workdir_name = workflow_name + "_work" workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.config = { "execution": { "crashdump_dir": path.join(l2_dir, "crashdump") } } workflow.base_dir = l2_dir workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") if not loud: try: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) except RuntimeError: print( "WARNING: Some expected scans have not been found (or another RuntimeError has occured)." ) for f in listdir(getcwd()): if re.search("crash.*?-varcopemerge|-copemerge.*", f): remove(path.join(getcwd(), f)) else: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) if not keep_work: shutil.rmtree(path.join(l2_dir, workdir_name))
def l1( preprocessing_dir, highpass_sigma=225, include={}, exclude={}, keep_work=False, l1_dir="", nprocs=10, mask="/home/chymera/ni_data/templates/ds_QBI_chr_bin.nii.gz", per_stimulus_contrast=False, habituation="", tr=1, workflow_name="generic", ): """Calculate subject level GLM statistics. Parameters ---------- include : dict A dictionary with any combination of "sessions", "subjects", "trials" as keys and corresponding identifiers as values. If this is specified ony matching entries will be included in the analysis. exclude : dict A dictionary with any combination of "sessions", "subjects", "trials" as keys and corresponding identifiers as values. If this is specified ony non-matching entries will be included in the analysis. habituation : string One value of "confound", "in_main_contrast", "separate_contrast", "" indicating how the habituation regressor should be handled. "" or any other value which evaluates to False will mean no habituation regressor is used int he model """ preprocessing_dir = path.expanduser(preprocessing_dir) if not l1_dir: l1_dir = path.abspath(path.join(preprocessing_dir, "..", "..", "l1")) datafind = nio.DataFinder() datafind.inputs.root_paths = preprocessing_dir datafind.inputs.match_regex = '.+/sub-(?P<sub>.+)/ses-(?P<ses>.+)/func/.*?_trial-(?P<scan>.+)\.nii.gz' datafind_res = datafind.run() iterfields = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res.outputs.scan ]) if include: iterfields = iterfield_selector(iterfields, include, "include") if exclude: iterfields = iterfield_selector(iterfields, exclude, "exclude") infosource = pe.Node( interface=util.IdentityInterface(fields=['subject_session_scan']), name="infosource") infosource.iterables = [('subject_session_scan', iterfields)] datafile_source = pe.Node( name='datafile_source', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['out_file'])) datafile_source.inputs.base_directory = preprocessing_dir datafile_source.inputs.source_format = "sub-{0}/ses-{1}/func/sub-{0}_ses-{1}_trial-{2}.nii.gz" eventfile_source = pe.Node( name='eventfile_source', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['out_file'])) eventfile_source.inputs.base_directory = preprocessing_dir eventfile_source.inputs.source_format = "sub-{0}/ses-{1}/func/sub-{0}_ses-{1}_trial-{2}_events.tsv" specify_model = pe.Node(interface=SpecifyModel(), name="specify_model") specify_model.inputs.input_units = 'secs' specify_model.inputs.time_repetition = tr specify_model.inputs.high_pass_filter_cutoff = highpass_sigma specify_model.inputs.one_condition_file = not per_stimulus_contrast specify_model.inputs.habituation_regressor = bool(habituation) level1design = pe.Node(interface=Level1Design(), name="level1design") level1design.inputs.interscan_interval = tr level1design.inputs.bases = { "custom": { "bfcustompath": "/mnt/data/ni_data/irfs/chr_beta1.txt" } } # level1design.inputs.bases = {'gamma': {'derivs':False, 'gammasigma':10, 'gammadelay':5}} level1design.inputs.orthogonalization = { 1: { 0: 0, 1: 0, 2: 0 }, 2: { 0: 1, 1: 1, 2: 0 } } level1design.inputs.model_serial_correlations = True if per_stimulus_contrast: level1design.inputs.contrasts = [ ('allStim', 'T', ["e0", "e1", "e2", "e3", "e4", "e5"], [1, 1, 1, 1, 1, 1]) ] #condition names as defined in specify_model elif habituation == "separate_contrast": level1design.inputs.contrasts = [ ('allStim', 'T', ["e0"], [1]), ('allStim', 'T', ["e1"], [1]) ] #condition names as defined in specify_model elif habituation == "in_main_contrast": level1design.inputs.contrasts = [ ('allStim', 'T', ["e0", "e1"], [1, 1]) ] #condition names as defined in specify_model elif habituation == "confound": level1design.inputs.contrasts = [ ('allStim', 'T', ["e0"], [1]) ] #condition names as defined in specify_model else: level1design.inputs.contrasts = [ ('allStim', 'T', ["e0"], [1]) ] #condition names as defined in specify_model modelgen = pe.Node(interface=fsl.FEATModel(), name='modelgen') glm = pe.Node(interface=fsl.GLM(), name='glm', iterfield='design') glm.inputs.out_cope = "cope.nii.gz" glm.inputs.out_varcb_name = "varcb.nii.gz" #not setting a betas output file might lead to beta export in lieu of COPEs glm.inputs.out_file = "betas.nii.gz" glm.inputs.out_t_name = "t_stat.nii.gz" glm.inputs.out_p_name = "p_stat.nii.gz" if mask: glm.inputs.mask = mask cope_filename = pe.Node( name='cope_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) cope_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_cope.nii.gz" varcb_filename = pe.Node( name='varcb_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) varcb_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_varcb.nii.gz" tstat_filename = pe.Node( name='tstat_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) tstat_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_tstat.nii.gz" zstat_filename = pe.Node( name='zstat_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) zstat_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_zstat.nii.gz" pstat_filename = pe.Node( name='pstat_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) pstat_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_pstat.nii.gz" pfstat_filename = pe.Node( name='pfstat_filename', interface=util.Function( function=sss_to_source, input_names=inspect.getargspec(sss_to_source)[0], output_names=['filename'])) pfstat_filename.inputs.source_format = "sub-{0}_ses-{1}_trial-{2}_pfstat.nii.gz" datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.join(l1_dir, workflow_name) datasink.inputs.parameterization = False workflow_connections = [ (infosource, datafile_source, [('subject_session_scan', 'subject_session_scan')]), (infosource, eventfile_source, [('subject_session_scan', 'subject_session_scan')]), (eventfile_source, specify_model, [('out_file', 'event_files')]), (datafile_source, specify_model, [('out_file', 'functional_runs')]), (specify_model, level1design, [('session_info', 'session_info')]), (level1design, modelgen, [('ev_files', 'ev_files')]), (level1design, modelgen, [('fsf_files', 'fsf_file')]), (datafile_source, glm, [('out_file', 'in_file')]), (modelgen, glm, [('design_file', 'design')]), (modelgen, glm, [('con_file', 'contrasts')]), (infosource, datasink, [(('subject_session_scan', ss_to_path), 'container')]), (infosource, cope_filename, [('subject_session_scan', 'subject_session_scan')]), (infosource, varcb_filename, [('subject_session_scan', 'subject_session_scan')]), (infosource, tstat_filename, [('subject_session_scan', 'subject_session_scan')]), (infosource, zstat_filename, [('subject_session_scan', 'subject_session_scan')]), (infosource, pstat_filename, [('subject_session_scan', 'subject_session_scan')]), (infosource, pfstat_filename, [('subject_session_scan', 'subject_session_scan')]), (cope_filename, glm, [('filename', 'out_cope')]), (varcb_filename, glm, [('filename', 'out_varcb_name')]), (tstat_filename, glm, [('filename', 'out_t_name')]), (zstat_filename, glm, [('filename', 'out_z_name')]), (pstat_filename, glm, [('filename', 'out_p_name')]), (pfstat_filename, glm, [('filename', 'out_pf_name')]), (glm, datasink, [('out_pf', '@pfstat')]), (glm, datasink, [('out_p', '@pstat')]), (glm, datasink, [('out_z', '@zstat')]), (glm, datasink, [('out_t', '@tstat')]), (glm, datasink, [('out_cope', '@cope')]), (glm, datasink, [('out_varcb', '@varcb')]), ] workdir_name = workflow_name + "_work" workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.base_dir = l1_dir workflow.config = { "execution": { "crashdump_dir": path.join(l1_dir, "crashdump") } } workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) if not keep_work: shutil.rmtree(path.join(l1_dir, workdir_name))
def l2_anova( l1_dir, keep_work=False, l2_dir="", loud=False, tr=1, nprocs=6, workflow_name="generic", mask="/usr/share/mouse-brain-atlases/dsurqec_200micron_mask.nii", exclude={}, include={}, match_regex='.+/sub-(?P<sub>[a-zA-Z0-9]+)/ses-(?P<ses>[a-zA-Z0-9]+)/.*?_acq-(?P<acq>[a-zA-Z0-9]+)_task-(?P<task>[a-zA-Z0-9]+)_(?P<mod>[a-zA-Z0-9]+)_(?P<stat>(cope|varcb)+)\.(?:nii|nii\.gz)' ): l1_dir = path.expanduser(l1_dir) if not l2_dir: l2_dir = path.abspath(path.join(l1_dir, "..", "..", "l2")) mask = path.abspath(path.expanduser(mask)) datafind = nio.DataFinder() datafind.inputs.root_paths = l1_dir datafind.inputs.match_regex = match_regex datafind_res = datafind.run() data_selection = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res. outputs.acq, datafind_res.outputs.task, datafind_res.outputs.mod, datafind_res.outputs.stat, datafind_res.outputs.out_paths ]) data_selection = [list(i) for i in data_selection] data_selection = pd.DataFrame(data_selection, columns=('subject', 'session', 'acquisition', 'task', 'modality', 'statistic', 'path')) data_selection = data_selection.sort_values(['session', 'subject'], ascending=[1, 1]) if exclude: for key in exclude: data_selection = data_selection[~data_selection[key]. isin(exclude[key])] if include: for key in include: data_selection = data_selection[data_selection[key].isin( include[key])] copes = data_selection[data_selection['statistic'] == 'cope']['path'].tolist() varcopes = data_selection[data_selection['statistic'] == 'varcb']['path'].tolist() copemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="copemerge") copemerge.inputs.in_files = copes copemerge.inputs.merged_file = 'copes.nii.gz' varcopemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="varcopemerge") varcopemerge.inputs.in_files = varcopes varcopemerge.inputs.merged_file = 'varcopes.nii.gz' copeonly = data_selection[data_selection['statistic'] == 'cope'] regressors = {} for sub in copeonly['subject'].unique(): #print(sub) regressor = [copeonly['subject'] == sub][0] regressor = [int(i) for i in regressor] key = "sub-" + str(sub) regressors[key] = regressor reference = str(copeonly['session'].unique()[0]) for ses in copeonly['session'].unique()[1:]: #print(ses) regressor = [copeonly['session'] == ses][0] regressor = [int(i) for i in regressor] key = "ses-(" + str(ses) + '-' + reference + ')' regressors[key] = regressor sessions = [[i, 'T', [i], [1]] for i in regressors.keys() if "ses-" in i] contrasts = deepcopy(sessions) contrasts.append(['anova', 'F', sessions]) level2model = pe.Node(interface=fsl.MultipleRegressDesign(), name='level2model') level2model.inputs.regressors = regressors level2model.inputs.contrasts = contrasts #print(regressors) #print(contrasts) #return flameo = pe.Node(interface=fsl.FLAMEO(), name="flameo") flameo.inputs.mask_file = mask # Using 'fe' instead of 'ols' is recommended (https://dpaniukov.github.io/2016/07/14/three-level-analysis-with-fsl-and-ants-2.html) # This has also been tested in SAMRI and shown to give better estimates. flameo.inputs.run_mode = "flame12" substitutions = [] t_counter = 1 f_counter = 1 for contrast in contrasts: if contrast[1] == 'T': for i in ['cope', 'tstat', 'zstat']: substitutions.append( (i + str(t_counter), contrast[0] + "_" + i)) t_counter += 1 if contrast[1] == 'F': for i in ['zfstat', 'fstat']: substitutions.append( (i + str(f_counter), contrast[0] + "_" + i)) f_counter += 1 datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.join(l2_dir, workflow_name) datasink.inputs.substitutions = substitutions workflow_connections = [ (copemerge, flameo, [('merged_file', 'cope_file')]), (varcopemerge, flameo, [('merged_file', 'var_cope_file')]), (level2model, flameo, [('design_mat', 'design_file')]), (level2model, flameo, [('design_grp', 'cov_split_file')]), (level2model, flameo, [('design_fts', 'f_con_file')]), (level2model, flameo, [('design_con', 't_con_file')]), (flameo, datasink, [('copes', '@copes')]), (flameo, datasink, [('tstats', '@tstats')]), (flameo, datasink, [('zstats', '@zstats')]), (flameo, datasink, [('fstats', '@fstats')]), (flameo, datasink, [('zfstats', '@zfstats')]), ] workdir_name = workflow_name + "_work" workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.config = { "execution": { "crashdump_dir": path.join(l2_dir, "crashdump") } } workflow.base_dir = l2_dir workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") if not loud: try: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) except RuntimeError: print( "WARNING: Some expected tasks have not been found (or another RuntimeError has occured)." ) for f in listdir(getcwd()): if re.search("crash.*?-varcopemerge|-copemerge.*", f): remove(path.join(getcwd(), f)) else: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) if not keep_work: shutil.rmtree(path.join(l2_dir, workdir_name))
def l2_common_effect( l1_dir, groupby="session", keep_work=False, l2_dir="", loud=False, tr=1, nprocs=6, workflow_name="generic", mask="~/ni_data/templates/ds_QBI_chr_bin.nii.gz", subjects=[], sessions=[], tasks=[], exclude={}, include={}, ): l1_dir = path.expanduser(l1_dir) if not l2_dir: l2_dir = path.abspath(path.join(l1_dir, "..", "..", "l2")) mask = path.abspath(path.expanduser(mask)) datafind = nio.DataFinder() datafind.inputs.root_paths = l1_dir datafind.inputs.match_regex = '.+/sub-(?P<sub>[a-zA-Z0-9]+)/ses-(?P<ses>[a-zA-Z0-9]+)/.*?_acq-(?P<acq>[a-zA-Z0-9]+)_task-(?P<task>[a-zA-Z0-9]+)_(?P<mod>[a-zA-Z0-9]+)_(?P<stat>[a-zA-Z0-9]+)\.(?:tsv|nii|nii\.gz)' datafind_res = datafind.run() data_selection = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res. outputs.acq, datafind_res.outputs.task, datafind_res.outputs.mod, datafind_res.outputs.stat, datafind_res.outputs.out_paths ]) data_selection = [list(i) for i in data_selection] data_selection = pd.DataFrame(data_selection, columns=('subject', 'session', 'acquisition', 'task', 'modality', 'statistic', 'path')) data_selection = data_selection.sort_values(['session', 'subject'], ascending=[1, 1]) if exclude: for key in exclude: data_selection = data_selection[~data_selection[key]. isin(exclude[key])] if include: for key in include: data_selection = data_selection[data_selection[key].isin( include[key])] copemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="copemerge") varcopemerge = pe.Node(interface=fsl.Merge(dimension='t'), name="varcopemerge") level2model = pe.Node(interface=fsl.L2Model(), name='level2model') flameo = pe.Node(interface=fsl.FLAMEO(), name="flameo") flameo.inputs.mask_file = mask flameo.inputs.run_mode = "ols" datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.join(l2_dir, workflow_name) datasink.inputs.substitutions = [ ('_iterable_', ''), ] if groupby == "subject": subjects = data_selection[['subject' ]].drop_duplicates().values.tolist() infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', subjects)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "group", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[['group', 'group']], varcbs=[['group', 'group']]) datasource.inputs.field_template = dict( copes="sub-%s/ses-*/sub-%s_ses-*_task-*_cope.nii.gz", varcbs="sub-%s/ses-*/sub-%s_ses-*_task-*_varcb.nii.gz", ) workflow_connections = [ (infosource, datasource, [('iterable', 'group')]), (infosource, copemerge, [(('iterable', add_suffix, "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "subject_task": #does not currently work, due to missing iterator combinations (same issue as preprocessing) merge = pe.Node(interface=util.Merge(2), name="merge") infosource = pe.Node( interface=util.IdentityInterface(fields=['subject', 'task']), name="infosource") infosource.iterables = [('subject', subjects), ('task', tasks)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "subject", "task", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[[ "subject", "subject", "task", ]], varcbs=[[ "subject", "subject", "task", ]]) datasource.inputs.field_template = dict( copes="sub-%s/ses-*/sub-%s_ses-*_task-%s_cope.nii.gz", varcbs="sub-%s/ses-*/sub-%s_ses-*_task-%s_varcb.nii.gz", ) workflow_connections = [ (infosource, datasource, [('subject', 'subject'), ('task', 'task')]), (infosource, merge, [('subject', 'in1'), ('task', 'in2')]), (merge, copemerge, [(('out', add_suffix, "_cope.nii.gz"), 'merged_file')]), (merge, varcopemerge, [(('out', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "session": sessions = data_selection[['session']].drop_duplicates() sessions = sessions.T.to_dict().values() infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', sessions)] copes = pe.Node( name='copes', interface=util.Function( function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection'])) copes.inputs.bids_dictionary_override = {'statistic': 'cope'} copes.inputs.df = data_selection copes.inputs.list_output = True varcopes = pe.Node( name='varcopes', interface=util.Function( function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['selection'])) varcopes.inputs.bids_dictionary_override = {'statistic': 'varcb'} varcopes.inputs.df = data_selection varcopes.inputs.list_output = True workflow_connections = [ (infosource, copemerge, [(('iterable', dict_and_suffix, "session", "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', dict_and_suffix, "session", "_varcb.nii.gz"), 'merged_file')]), ] elif groupby == "task": infosource = pe.Node( interface=util.IdentityInterface(fields=['iterable']), name="infosource") infosource.iterables = [('iterable', tasks)] datasource = pe.Node(interface=nio.DataGrabber( infields=[ "group", ], outfields=["copes", "varcbs"]), name="datasource") datasource.inputs.template_args = dict(copes=[['group']], varcbs=[['group']]) datasource.inputs.field_template = dict( copes="sub-*/ses-*/sub-*_ses-*_task-%s_cope.nii.gz ", varcbs="sub-*/ses-*/sub-*_ses-*_task-%s_varcb.nii.gz ", ) workflow_connections = [ (infosource, datasource, [('iterable', 'group')]), (infosource, copemerge, [(('iterable', add_suffix, "_cope.nii.gz"), 'merged_file')]), (infosource, varcopemerge, [(('iterable', add_suffix, "_varcb.nii.gz"), 'merged_file')]), ] workflow_connections.extend([ (copes, copemerge, [('selection', 'in_files')]), (varcopes, varcopemerge, [('selection', 'in_files')]), (varcopes, level2model, [(('selection', mylen), 'num_copes')]), (copemerge, flameo, [('merged_file', 'cope_file')]), (varcopemerge, flameo, [('merged_file', 'var_cope_file')]), (level2model, flameo, [('design_mat', 'design_file')]), (level2model, flameo, [('design_grp', 'cov_split_file')]), (level2model, flameo, [('design_con', 't_con_file')]), (flameo, datasink, [('copes', '@copes')]), (flameo, datasink, [('fstats', '@fstats')]), (flameo, datasink, [('tstats', '@tstats')]), (flameo, datasink, [('zstats', '@zstats')]), ]) workdir_name = workflow_name + "_work" workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.config = { "execution": { "crashdump_dir": path.join(l2_dir, "crashdump") } } workflow.base_dir = l2_dir workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") if not loud: try: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) except RuntimeError: print( "WARNING: Some expected tasks have not been found (or another RuntimeError has occured)." ) for f in listdir(getcwd()): if re.search("crash.*?-varcopemerge|-copemerge.*", f): remove(path.join(getcwd(), f)) else: workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) if not keep_work: shutil.rmtree(path.join(l2_dir, workdir_name))
def seed_fc( preprocessing_dir, exclude={}, habituation='confound', highpass_sigma=225, lowpass_sigma=False, include={}, keep_work=False, out_dir="", mask="", match_regex='sub-(?P<sub>[a-zA-Z0-9]+)/ses-(?P<ses>[a-zA-Z0-9]+)/func/.*?_task-(?P<task>[a-zA-Z0-9]+)_acq-(?P<acq>[a-zA-Z0-9]+)_(?P<mod>[a-zA-Z0-9]+)\.(?:tsv|nii|nii\.gz)', nprocs=N_PROCS, tr=1, workflow_name="generic", modality="cbv", ): """Calculate subject level seed-based functional connectivity via the `fsl_glm` command. Parameters ---------- exclude : dict A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values. If this is specified matching entries will be excluded in the analysis. habituation : {"", "confound", "separate_contrast", "in_main_contrast"}, optional How the habituation regressor should be handled. Anything which evaluates as False (though we recommend "") means no habituation regressor will be introduced. highpass_sigma : int, optional Highpass threshold (in seconds). include : dict A dictionary with any combination of "sessions", "subjects", "tasks" as keys and corresponding identifiers as values. If this is specified only matching entries will be included in the analysis. keep_work : bool, optional Whether to keep the work directory (containing all the intermediary workflow steps, as managed by nipypye). This is useful for debugging and quality control. out_dir : str, optional Path to the directory inside which both the working directory and the output directory will be created. mask : str, optional Path to the brain mask which shall be used to define the brain volume in the analysis. This has to point to an existing NIfTI file containing zero and one values only. match_regex : str, optional Regex matching pattern by which to select input files. Has to contain groups named "sub", "ses", "acq", "task", and "mod". n_procs : int, optional Maximum number of processes which to simultaneously spawn for the workflow. If not explicitly defined, this is automatically calculated from the number of available cores and under the assumption that the workflow will be the main process running for the duration that it is running. tr : int, optional Repetition time, in seconds. workflow_name : str, optional Name of the workflow; this will also be the name of the final output directory produced under `out_dir`. """ preprocessing_dir = path.abspath(path.expanduser(preprocessing_dir)) if not out_dir: out_dir = path.join(bids_base, 'l1') else: out_dir = path.abspath(path.expanduser(out_dir)) datafind = nio.DataFinder() datafind.inputs.root_paths = preprocessing_dir datafind.inputs.match_regex = match_regex datafind_res = datafind.run() out_paths = [ path.abspath(path.expanduser(i)) for i in datafind_res.outputs.out_paths ] data_selection = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res.outputs.acq, datafind_res.outputs.task, datafind_res.outputs.mod, out_paths ]) data_selection = [list(i) for i in data_selection] data_selection = pd.DataFrame(data_selection, columns=('subject', 'session', 'acquisition', 'task', 'modality', 'path')) if exclude: for key in exclude: data_selection = data_selection[~data_selection[key]. isin(exclude[key])] if include: for key in include: data_selection = data_selection[data_selection[key].isin( include[key])] bids_dictionary = data_selection[ data_selection['modality'] == modality].drop_duplicates().T.to_dict().values() infosource = pe.Node( interface=util.IdentityInterface(fields=['bids_dictionary']), name="infosource") infosource.iterables = [('bids_dictionary', bids_dictionary)] datafile_source = pe.Node( name='datafile_source', interface=util.Function( function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['out_file'])) datafile_source.inputs.bids_dictionary_override = {'modality': modality} datafile_source.inputs.df = data_selection seed_timecourse = pe.Node( name='seed_timecourse', interface=util.Function( function=select_from_datafind_df, input_names=inspect.getargspec(select_from_datafind_df)[0], output_names=['out_file'])) specify_model = pe.Node(interface=SpecifyModel(), name="specify_model") specify_model.inputs.input_units = 'secs' specify_model.inputs.time_repetition = tr specify_model.inputs.high_pass_filter_cutoff = highpass_sigma specify_model.inputs.habituation_regressor = bool(habituation) level1design = pe.Node(interface=Level1Design(), name="level1design") level1design.inputs.interscan_interval = tr if bf_path: bf_path = path.abspath(path.expanduser(bf_path)) level1design.inputs.bases = {"custom": {"bfcustompath": bf_path}} # level1design.inputs.bases = {'gamma': {'derivs':False, 'gammasigma':10, 'gammadelay':5}} level1design.inputs.orthogonalization = { 1: { 0: 0, 1: 0, 2: 0 }, 2: { 0: 1, 1: 1, 2: 0 } } level1design.inputs.model_serial_correlations = True if habituation == "separate_contrast": level1design.inputs.contrasts = [ ('allStim', 'T', ["e0"], [1]), ('allStim', 'T', ["e1"], [1]) ] #condition names as defined in specify_model elif habituation == "in_main_contrast": level1design.inputs.contrasts = [ ('allStim', 'T', ["e0", "e1"], [1, 1]) ] #condition names as defined in specify_model elif habituation == "confound" or not habituation: level1design.inputs.contrasts = [ ('allStim', 'T', ["e0"], [1]) ] #condition names as defined in specify_model else: raise ValueError( 'The value you have provided for the `habituation` parameter, namely "{}", is invalid. Please choose one of: {"confound","in_main_contrast","separate_contrast"}' .format(habituation)) modelgen = pe.Node(interface=fsl.FEATModel(), name='modelgen') modelgen.inputs.ignore_exception = True glm = pe.Node(interface=fsl.GLM(), name='glm', iterfield='design') glm.inputs.out_cope = "cope.nii.gz" glm.inputs.out_varcb_name = "varcb.nii.gz" #not setting a betas output file might lead to beta export in lieu of COPEs glm.inputs.out_file = "betas.nii.gz" glm.inputs.out_t_name = "t_stat.nii.gz" glm.inputs.out_p_name = "p_stat.nii.gz" if mask: glm.inputs.mask = path.abspath(path.expanduser(mask)) glm.inputs.ignore_exception = True cope_filename = pe.Node( name='cope_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) cope_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_cope.nii.gz" varcb_filename = pe.Node( name='varcb_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) varcb_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_varcb.nii.gz" tstat_filename = pe.Node( name='tstat_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) tstat_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_tstat.nii.gz" zstat_filename = pe.Node( name='zstat_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) zstat_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_zstat.nii.gz" pstat_filename = pe.Node( name='pstat_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) pstat_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_pstat.nii.gz" pfstat_filename = pe.Node( name='pfstat_filename', interface=util.Function( function=bids_dict_to_source, input_names=inspect.getargspec(bids_dict_to_source)[0], output_names=['filename'])) pfstat_filename.inputs.source_format = "sub-{subject}_ses-{session}_task-{task}_acq-{acquisition}_{modality}_pfstat.nii.gz" datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.join(out_dir, workflow_name) datasink.inputs.parameterization = False workflow_connections = [ (infosource, datafile_source, [('bids_dictionary', 'bids_dictionary') ]), (infosource, eventfile_source, [('bids_dictionary', 'bids_dictionary') ]), (eventfile_source, specify_model, [('out_file', 'event_files')]), (specify_model, level1design, [('session_info', 'session_info')]), (level1design, modelgen, [('ev_files', 'ev_files')]), (level1design, modelgen, [('fsf_files', 'fsf_file')]), (modelgen, glm, [('design_file', 'design')]), (modelgen, glm, [('con_file', 'contrasts')]), (infosource, datasink, [(('bids_dictionary', bids_dict_to_dir), 'container')]), (infosource, cope_filename, [('bids_dictionary', 'bids_dictionary')]), (infosource, varcb_filename, [('bids_dictionary', 'bids_dictionary')]), (infosource, tstat_filename, [('bids_dictionary', 'bids_dictionary')]), (infosource, zstat_filename, [('bids_dictionary', 'bids_dictionary')]), (infosource, pstat_filename, [('bids_dictionary', 'bids_dictionary')]), (infosource, pfstat_filename, [('bids_dictionary', 'bids_dictionary') ]), (cope_filename, glm, [('filename', 'out_cope')]), (varcb_filename, glm, [('filename', 'out_varcb_name')]), (tstat_filename, glm, [('filename', 'out_t_name')]), (zstat_filename, glm, [('filename', 'out_z_name')]), (pstat_filename, glm, [('filename', 'out_p_name')]), (pfstat_filename, glm, [('filename', 'out_pf_name')]), (glm, datasink, [('out_pf', '@pfstat')]), (glm, datasink, [('out_p', '@pstat')]), (glm, datasink, [('out_z', '@zstat')]), (glm, datasink, [('out_t', '@tstat')]), (glm, datasink, [('out_cope', '@cope')]), (glm, datasink, [('out_varcb', '@varcb')]), ] if highpass_sigma or lowpass_sigma: bandpass = pe.Node(interface=fsl.maths.TemporalFilter(), name="bandpass") bandpass.inputs.highpass_sigma = highpass_sigma if lowpass_sigma: bandpass.inputs.lowpass_sigma = lowpass_sigma else: bandpass.inputs.lowpass_sigma = tr workflow_connections.extend([ (datafile_source, bandpass, [('out_file', 'in_file')]), (bandpass, specify_model, [('out_file', 'functional_runs')]), (bandpass, glm, [('out_file', 'in_file')]), ]) else: workflow_connections.extend([ (datafile_source, specify_model, [('out_file', 'functional_runs') ]), (datafile_source, glm, [('out_file', 'in_file')]), ]) workdir_name = workflow_name + "_work" workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.base_dir = out_dir workflow.config = { "execution": { "crashdump_dir": path.join(out_dir, "crashdump") } } workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") workflow.run(plugin="MultiProc", plugin_args={'n_procs': nprocs}) if not keep_work: shutil.rmtree(path.join(out_dir, workdir_name))
def diagnose( bids_base, components=None, debug=False, exclude={}, include={}, keep_crashdump=False, keep_work=False, match_regex='.+/sub-(?P<sub>[a-zA-Z0-9]+)/ses-(?P<ses>[a-zA-Z0-9]+)/.*?_task-(?P<task>[a-zA-Z0-9]+)_acq-(?P<acq>[a-zA-Z0-9]+)_run-(?P<run>[a-zA-Z0-9]+)_(?P<mod>[a-zA-Z0-9]+).(?:nii|nii\.gz)', n_procs=N_PROCS, realign="time", tr=None, workflow_name="diagnostic", ): '''Run a basic independent component analysis diagnotic (using FSL's MELODIC) on functional MRI data stored in a BIDS directory tree. Parameters ---------- bids_base : string, optional Path to the top level of a BIDS directory tree for which to perform the diagnostic. components : int, optional Number of independent components to produce for each functional measurement; if evaluated as False, the number of components is automatically optimized for the given data by FSL's MELODIC. debug : bool, optional Enable full nipype debugging support for the workflow construction and execution. exclude : dict, optional A dictionary with any subset of 'subject', 'session', 'acquisition', 'task', 'modality', and 'path' as keys and corresponding identifiers as values. This is a blacklist: if this is specified only non-matching entries will be included in the analysis. include : dict, optional A dictionary with any subset of 'subject', 'session', 'acquisition', 'task', 'modality', and 'path' as keys and corresponding identifiers as values. This is a whitelist: if this is specified only matching entries will be included in the analysis. keep_crashdump : bool, optional Whether to keep the crashdump directory (containing all the crash reports for intermediary workflow steps, as managed by nipypye). This is useful for debugging and quality control. keep_work : bool, optional Whether to keep the work directory (containing all the intermediary workflow steps, as managed by nipypye). This is useful for debugging and quality control. match_regex : str, optional Regex matching pattern by which to select input files. Has to contain groups named "sub", "ses", "acq", "task", and "mod". n_procs : int, optional Maximum number of processes which to simultaneously spawn for the workflow. If not explicitly defined, this is automatically calculated from the number of available cores and under the assumption that the workflow will be the main process running for the duration that it is running. realign : {"space","time","spacetime",""} Parameter that dictates slictiming correction and realignment of slices. "time" (FSL.SliceTimer) is default, since it works safely. Use others only with caution! tr : int, optional Repetition time (in seconds); if evaluated as False, the TR will be read from the NIfTI header of each file individually. workflow_name : string, optional Name of the workflow execution. The output will be saved one level above the bids_base, under a directory bearing the name given here. ''' bids_base = path.abspath(path.expanduser(bids_base)) datafind = nio.DataFinder() datafind.inputs.root_paths = bids_base datafind.inputs.match_regex = match_regex datafind_res = datafind.run() data_selection = zip(*[ datafind_res.outputs.sub, datafind_res.outputs.ses, datafind_res.outputs.acq, datafind_res.outputs.task, datafind_res.outputs.mod, datafind_res.outputs.out_paths ]) data_selection = [list(i) for i in data_selection] data_selection = pd.DataFrame(data_selection, columns=('subject', 'session', 'acquisition', 'task', 'modality', 'path')) data_selection = data_selection.sort_values(['session', 'subject'], ascending=[1, 1]) if exclude: for key in exclude: data_selection = data_selection[~data_selection[key]. isin(exclude[key])] if include: for key in include: data_selection = data_selection[data_selection[key].isin( include[key])] data_selection['out_path'] = '' if data_selection['path'].str.contains('.nii.gz').any(): data_selection['out_path'] = data_selection['path'].apply( lambda x: path.basename( path.splitext(path.splitext(x)[0])[0] + '_MELODIC')) else: data_selection['out_path'] = data_selection['path'].apply( lambda x: path.basename(path.splitext(x)[0] + '_MELODIC')) paths = data_selection['path'] infosource = pe.Node(interface=util.IdentityInterface( fields=['path'], mandatory_inputs=False), name="infosource") infosource.iterables = [('path', paths)] dummy_scans = pe.Node( name='dummy_scans', interface=util.Function( function=force_dummy_scans, input_names=inspect.getargspec(force_dummy_scans)[0], output_names=['out_file', 'deleted_scans'])) dummy_scans.inputs.desired_dummy_scans = 10 bids_filename = pe.Node(name='bids_filename', interface=util.Function( function=out_path, input_names=inspect.getargspec(out_path)[0], output_names=['filename'])) bids_filename.inputs.selection_df = data_selection bids_container = pe.Node(name='path_container', interface=util.Function( function=container, input_names=inspect.getargspec(container)[0], output_names=['container'])) bids_container.inputs.selection_df = data_selection datasink = pe.Node(nio.DataSink(), name='datasink') datasink.inputs.base_directory = path.abspath( path.join(bids_base, '..', workflow_name)) datasink.inputs.parameterization = False melodic = pe.Node(interface=fsl.model.MELODIC(), name="melodic") if tr: melodic.inputs.tr_sec = tr melodic.inputs.report = True if components: melodic.inputs.dim = int(components) workflow_connections = [ (infosource, dummy_scans, [('path', 'in_file')]), (infosource, bids_filename, [('path', 'in_path')]), (bids_filename, bids_container, [('filename', 'out_path')]), (bids_filename, melodic, [('filename', 'out_dir')]), (bids_container, datasink, [('container', 'container')]), (melodic, datasink, [('out_dir', 'func')]), ] if not tr: report_tr = pe.Node(name='report_tr', interface=util.Function( function=get_tr, input_names=inspect.getargspec(get_tr)[0], output_names=['tr'])) report_tr.inputs.ndim = 4 workflow_connections.extend([ (infosource, report_tr, [('path', 'in_file')]), (report_tr, melodic, [('tr', 'tr_sec')]), ]) if realign == "space": realigner = pe.Node(interface=spm.Realign(), name="realigner") realigner.inputs.register_to_mean = True workflow_connections.extend([ (dummy_scans, realigner, [('out_file', 'in_file')]), (realigner, melodic, [('out_file', 'in_files')]), ]) elif realign == "spacetime": realigner = pe.Node(interface=nipy.SpaceTimeRealigner(), name="realigner") realigner.inputs.slice_times = "asc_alt_2" if tr: realigner.inputs.tr = tr else: workflow_connections.extend([ (report_tr, realigner, [('tr', 'tr')]), ]) #3 for coronal slices (2 for horizontal, 1 for sagittal) realigner.inputs.slice_info = 3 workflow_connections.extend([ (dummy_scans, realigner, [('out_file', 'in_file')]), (realigner, melodic, [('out_file', 'in_files')]), ]) elif realign == "time": realigner = pe.Node(interface=fsl.SliceTimer(), name="slicetimer") if tr: realigner.inputs.time_repetition = tr else: workflow_connections.extend([ (report_tr, realigner, [('tr', 'time_repetition')]), ]) workflow_connections.extend([ (dummy_scans, realigner, [('out_file', 'in_file')]), (realigner, melodic, [('slice_time_corrected_file', 'in_files')]), ]) else: workflow_connections.extend([ (dummy_scans, melodic, [('out_file', 'in_files')]), ]) crashdump_dir = path.abspath( path.join(bids_base, '..', workflow_name + '_crashdump')) workflow_config = {'execution': {'crashdump_dir': crashdump_dir}} if debug: workflow_config['logging'] = { 'workflow_level': 'DEBUG', 'utils_level': 'DEBUG', 'interface_level': 'DEBUG', 'filemanip_level': 'DEBUG', 'log_to_file': 'true', } workdir_name = workflow_name + '_work' workflow = pe.Workflow(name=workdir_name) workflow.connect(workflow_connections) workflow.base_dir = path.abspath(path.join(bids_base, '..')) workflow.config = workflow_config workflow.write_graph(dotfilename=path.join(workflow.base_dir, workdir_name, "graph.dot"), graph2use="hierarchical", format="png") if not keep_work or not keep_crashdump: try: workflow.run(plugin="MultiProc", plugin_args={'n_procs': n_procs}) except RuntimeError: pass else: workflow.run(plugin="MultiProc", plugin_args={'n_procs': n_procs}) if not keep_work: shutil.rmtree(path.join(workflow.base_dir, workdir_name)) if not keep_crashdump: try: shutil.rmtree(crashdump_dir) except (FileNotFoundError, OSError): pass return