Nipype - how to run standalone function multiple times for each subject?

Dear All,

I am fairly new to python and Nipype so I appreciate this is probably very basic question. I would be most grateful for some advice as to how to run a standalone function multiple times for each participant giving multiple outputs for each participant.

The function is simply to reset the origin of the images. Each participant has a structural and functional image. This is a summary of what I have set up so far, and this works fine for resetting the origin for solely one of the images - i.e. the struc or the func.


def origin_reset(niftis):
    import required modules
    (function resets the origin)
     return reset_niftis

reset_origin = pe.Node(name=‘reset_origin’, interface=util.Function(input_names=[‘niftis’],
output_names=[‘reset_niftis’],
function=origin_reset))
rs_prepro.connect([(infosource, selectfiles, [(‘subject_id’, ‘subject_id’)]),
(selectfiles, reset_origin, [(‘struc’, ‘niftis’)]),
(reset_origin, datasink, [(‘reset_niftis’, ‘reset’)])
])

I have got a bit stuck, however, in my attempts to run it for both the images.


rs_prepro.connect([(infosource, selectfiles, [('subject_id', 'subject_id')]),
               (selectfiles, reset_origin, [('struc', 'niftis')]),
               (selectfiles, reset_origin, [('func', 'niftis')]),
               (reset_origin, datasink, [('reset_niftis', 'reset')])
               ])

The above method and variants of it does not seem to be the correct approach. I wondered whether I should be setting the reset_origin as a MapNode, or having a reset_origin.iterables line beneath it? Or perhaps I need to change the function to have multiple inputs/outputs?

Any help would be most appreciated

Best Wishes

Rob

Hi @rob, this sounds like you’d want to use a MapNode - as they are specifically used when you want to use a function over a list of subjects.

MapNodes have a special property, iterfield, that break down a list one at a time. In your example function, I assume you are passing in a list of niftis. If you replace reset_origin with below, it will loop over all the niftis in a list and run the function for each nifti in the list.

reset_origin = pe.MapNode(interface=util.Function(input_names=['niftis'],
                                                  output_names=['reset_niftis'],
                                                  function=origin_reset),
                                            iterfield=['niftis'], name='reset_origin')

Also unique to a MapNode, the outputs will be recombined to form a list, mapping each input to one output.

Thank you!

I ended up using a different method as it seemed to make more sense to have a separate function for resetting structural and functional files.

In general though if one wanted to used the MapNode here what would be the correct method to generate the iterfield ‘niftis’ list. E.g. How would one use selectfiles to generate the list of both structural and functional files to be inputted, and also would it be possible to later distinguish between the structural and functional outputs of the MapNode?

Thanks again

Rob

@rob what I would do in that case:

from nipype import SelectFiles, Node, Workflow, MapNode, Function

templates = dict(T1="/full/path/to/{subject_id}*T1w.nii",
                 task="full/path/to/{subject_id}*task*.nii.gz")
sf = Node(SelectFiles(templates, force_lists=True), name='selecter')
sf.inputs.subject_id = {SUBJ_ID}

def pfile(files):
    #do some stuff here
    return files

func1 = MapNode(Function(input_names=['files'],
                         output_names=['out'],
                         function=pfile),
                iterfield=['files'],
                name='func1')

func2 = func1.clone(name='func2')

wf = Workflow(base_dir='.', name='test')
wf.connect(sf, 'task', func1, 'files')
wf.connect(sf, 'T1', func2, 'files')
wf.run()

Which sounds like what you ending up doing anyways!