Nipype Quickstart

Nipype architecture

Import a few things from nipype and external libraries

import os
from os.path import abspath

from nipype import Workflow, Node, MapNode, Function
from nipype.interfaces.fsl import BET, IsotropicSmooth, ApplyMask

from nilearn.plotting import plot_anat
%matplotlib inline
import matplotlib.pyplot as plt

Interfaces

Interfaces are the core pieces of Nipype. The interfaces are python modules that allow you to use various external packages (e.g. FSL, SPM or FreeSurfer), even if they themselves are written in another programming language than python.

Let’s try to use bet from FSL:

# will use a T1w from ds000114 dataset
input_file =  abspath("/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz")
# we will be typing here

If you’re lost the code is here:

bet = BET()
bet.inputs.in_file = input_file
bet.inputs.out_file = "/output/T1w_nipype_bet.nii.gz"
res = bet.run()

let’s check the output:

res.outputs
inskull_mask_file = <undefined>
inskull_mesh_file = <undefined>
mask_file = <undefined>
meshfile = <undefined>
out_file = /output/T1w_nipype_bet.nii.gz
outline_file = <undefined>
outskin_mask_file = <undefined>
outskin_mesh_file = <undefined>
outskull_mask_file = <undefined>
outskull_mesh_file = <undefined>
skull_file = <undefined>
skull_mask_file = <undefined>

and we can plot the output file

plot_anat('/output/T1w_nipype_bet.nii.gz', 
          display_mode='ortho', dim=-1, draw_cross=False, annotate=False);
../../_images/introduction_quickstart_13_0.png

you can always check the list of arguments using help method

BET.help()
Wraps the executable command ``bet``.

FSL BET wrapper for skull stripping

For complete details, see the `BET Documentation.
<https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/BET/UserGuide>`_

Examples
--------
>>> from nipype.interfaces import fsl
>>> btr = fsl.BET()
>>> btr.inputs.in_file = 'structural.nii'
>>> btr.inputs.frac = 0.7
>>> btr.inputs.out_file = 'brain_anat.nii'
>>> btr.cmdline
'bet structural.nii brain_anat.nii -f 0.70'
>>> res = btr.run() # doctest: +SKIP

Inputs::

        [Mandatory]
        in_file: (a pathlike object or string representing an existing file)
                input file to skull strip
                argument: ``%s``, position: 0

        [Optional]
        out_file: (a pathlike object or string representing a file)
                name of output skull stripped image
                argument: ``%s``, position: 1
        outline: (a boolean)
                create surface outline image
                argument: ``-o``
        mask: (a boolean)
                create binary mask image
                argument: ``-m``
        skull: (a boolean)
                create skull image
                argument: ``-s``
        no_output: (a boolean)
                Don't generate segmented output
                argument: ``-n``
        frac: (a float)
                fractional intensity threshold
                argument: ``-f %.2f``
        vertical_gradient: (a float)
                vertical gradient in fractional intensity threshold (-1, 1)
                argument: ``-g %.2f``
        radius: (an integer)
                head radius
                argument: ``-r %d``
        center: (a list of at most 3 items which are an integer)
                center of gravity in voxels
                argument: ``-c %s``
        threshold: (a boolean)
                apply thresholding to segmented brain image and mask
                argument: ``-t``
        mesh: (a boolean)
                generate a vtk mesh brain surface
                argument: ``-e``
        robust: (a boolean)
                robust brain centre estimation (iterates BET several times)
                argument: ``-R``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        padding: (a boolean)
                improve BET if FOV is very small in Z (by temporarily padding end
                slices)
                argument: ``-Z``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        remove_eyes: (a boolean)
                eye & optic nerve cleanup (can be useful in SIENA)
                argument: ``-S``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        surfaces: (a boolean)
                run bet2 and then betsurf to get additional skull and scalp surfaces
                (includes registrations)
                argument: ``-A``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        t2_guided: (a pathlike object or string representing a file)
                as with creating surfaces, when also feeding in non-brain-extracted
                T2 (includes registrations)
                argument: ``-A2 %s``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        functional: (a boolean)
                apply to 4D fMRI data
                argument: ``-F``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        reduce_bias: (a boolean)
                bias field and neck cleanup
                argument: ``-B``
                mutually_exclusive: functional, reduce_bias, robust, padding,
                  remove_eyes, surfaces, t2_guided
        output_type: ('NIFTI' or 'NIFTI_PAIR' or 'NIFTI_GZ' or
                  'NIFTI_PAIR_GZ')
                FSL output type
        args: (a string)
                Additional parameters to the command
                argument: ``%s``
        environ: (a dictionary with keys which are a bytes or None or a value
                  of class 'str' and with values which are a bytes or None or a
                  value of class 'str', nipype default value: {})
                Environment variables

Outputs::

        out_file: (a pathlike object or string representing a file)
                path/name of skullstripped file (if generated)
        mask_file: (a pathlike object or string representing a file)
                path/name of binary brain mask (if generated)
        outline_file: (a pathlike object or string representing a file)
                path/name of outline file (if generated)
        meshfile: (a pathlike object or string representing a file)
                path/name of vtk mesh file (if generated)
        inskull_mask_file: (a pathlike object or string representing a file)
                path/name of inskull mask (if generated)
        inskull_mesh_file: (a pathlike object or string representing a file)
                path/name of inskull mesh outline (if generated)
        outskull_mask_file: (a pathlike object or string representing a file)
                path/name of outskull mask (if generated)
        outskull_mesh_file: (a pathlike object or string representing a file)
                path/name of outskull mesh outline (if generated)
        outskin_mask_file: (a pathlike object or string representing a file)
                path/name of outskin mask (if generated)
        outskin_mesh_file: (a pathlike object or string representing a file)
                path/name of outskin mesh outline (if generated)
        skull_mask_file: (a pathlike object or string representing a file)
                path/name of skull mask (if generated)
        skull_file: (a pathlike object or string representing a file)
                path/name of skull file (if generated)

References:
-----------
BibTeX('@article{JenkinsonBeckmannBehrensWoolrichSmith2012,author={M. Jenkinson, C.F. Beckmann, T.E. Behrens, M.W. Woolrich, and S.M. Smith},title={FSL},journal={NeuroImage},volume={62},pages={782-790},year={2012},}', key='JenkinsonBeckmannBehrensWoolrichSmith2012')

Exercise 1a

Import IsotropicSmooth from nipype.interfaces.fsl and find out the FSL command that is being run. What are the mandatory inputs for this interface?

# type your code here
from nipype.interfaces.fsl import IsotropicSmooth
# all this information can be found when we run `help` method. 
# note that you can either provide `in_file` and `fwhm` or `in_file` and `sigma`
IsotropicSmooth.help()
Wraps the executable command ``fslmaths``.

Use fslmaths to spatially smooth an image with a gaussian kernel.

Inputs::

        [Mandatory]
        fwhm: (a float)
                fwhm of smoothing kernel [mm]
                argument: ``-s %.5f``, position: 4
                mutually_exclusive: sigma
        sigma: (a float)
                sigma of smoothing kernel [mm]
                argument: ``-s %.5f``, position: 4
                mutually_exclusive: fwhm
        in_file: (a pathlike object or string representing an existing file)
                image to operate on
                argument: ``%s``, position: 2

        [Optional]
        out_file: (a pathlike object or string representing a file)
                image to write
                argument: ``%s``, position: -2
        internal_datatype: ('float' or 'char' or 'int' or 'short' or 'double'
                  or 'input')
                datatype to use for calculations (default is float)
                argument: ``-dt %s``, position: 1
        output_datatype: ('float' or 'char' or 'int' or 'short' or 'double'
                  or 'input')
                datatype to use for output (default uses input type)
                argument: ``-odt %s``, position: -1
        nan2zeros: (a boolean)
                change NaNs to zeros before doing anything
                argument: ``-nan``, position: 3
        output_type: ('NIFTI' or 'NIFTI_PAIR' or 'NIFTI_GZ' or
                  'NIFTI_PAIR_GZ')
                FSL output type
        args: (a string)
                Additional parameters to the command
                argument: ``%s``
        environ: (a dictionary with keys which are a bytes or None or a value
                  of class 'str' and with values which are a bytes or None or a
                  value of class 'str', nipype default value: {})
                Environment variables

Outputs::

        out_file: (a pathlike object or string representing an existing file)
                image written after calculations

References:
-----------
BibTeX('@article{JenkinsonBeckmannBehrensWoolrichSmith2012,author={M. Jenkinson, C.F. Beckmann, T.E. Behrens, M.W. Woolrich, and S.M. Smith},title={FSL},journal={NeuroImage},volume={62},pages={782-790},year={2012},}', key='JenkinsonBeckmannBehrensWoolrichSmith2012')

Exercise 1b

Run the IsotropicSmooth for /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz file with a smoothing kernel 4mm:

# type your solution here
smoothing = IsotropicSmooth()
smoothing.inputs.in_file = "/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz"
smoothing.inputs.fwhm = 4
smoothing.inputs.out_file = "/output/T1w_nipype_smooth.nii.gz"
smoothing.run()
<nipype.interfaces.base.support.InterfaceResult at 0x7f21d8c068d0>
# plotting the output
plot_anat('/output/T1w_nipype_smooth.nii.gz', 
          display_mode='ortho', dim=-1, draw_cross=False, annotate=False);
../../_images/introduction_quickstart_22_0.png

Nodes and Workflows

Interfaces are the core pieces of Nipype that run the code of your desire. But to streamline your analysis and to execute multiple interfaces in a sensible order, you have to put them in something that we call a Node and create a Workflow.

In Nipype, a node is an object that executes a certain function. This function can be anything from a Nipype interface to a user-specified function or an external script. Each node consists of a name, an interface, and at least one input field and at least one output field.

Once you have multiple nodes you can use Workflow to connect with each other and create a directed graph. Nipype workflow will take care of input and output of each interface and arrange the execution of each interface in the most efficient way.

Let’s create the first node using BET interface:

# we will be typing here

If you’re lost the code is here:

# Create Node
bet_node = Node(BET(), name='bet')
# Specify node inputs
bet_node.inputs.in_file = input_file
bet_node.inputs.mask = True

# bet node can be also defined this way:
#bet_node = Node(BET(in_file=input_file, mask=True), name='bet_node')

Exercise 2

Create a Node for IsotropicSmooth interface.

# Type your solution here:

# smooth_node = 
smooth_node = Node(IsotropicSmooth(in_file=input_file, fwhm=4), name="smooth")

We will now create one more Node for our workflow

mask_node = Node(ApplyMask(), name="mask")

Let’s check the interface:

ApplyMask.help()
Wraps the executable command ``fslmaths``.

Use fslmaths to apply a binary mask to another image.

Inputs::

        [Mandatory]
        mask_file: (a pathlike object or string representing an existing
                  file)
                binary image defining mask space
                argument: ``-mas %s``, position: 4
        in_file: (a pathlike object or string representing an existing file)
                image to operate on
                argument: ``%s``, position: 2

        [Optional]
        out_file: (a pathlike object or string representing a file)
                image to write
                argument: ``%s``, position: -2
        internal_datatype: ('float' or 'char' or 'int' or 'short' or 'double'
                  or 'input')
                datatype to use for calculations (default is float)
                argument: ``-dt %s``, position: 1
        output_datatype: ('float' or 'char' or 'int' or 'short' or 'double'
                  or 'input')
                datatype to use for output (default uses input type)
                argument: ``-odt %s``, position: -1
        nan2zeros: (a boolean)
                change NaNs to zeros before doing anything
                argument: ``-nan``, position: 3
        output_type: ('NIFTI' or 'NIFTI_PAIR' or 'NIFTI_GZ' or
                  'NIFTI_PAIR_GZ')
                FSL output type
        args: (a string)
                Additional parameters to the command
                argument: ``%s``
        environ: (a dictionary with keys which are a bytes or None or a value
                  of class 'str' and with values which are a bytes or None or a
                  value of class 'str', nipype default value: {})
                Environment variables

Outputs::

        out_file: (a pathlike object or string representing an existing file)
                image written after calculations

References:
-----------
BibTeX('@article{JenkinsonBeckmannBehrensWoolrichSmith2012,author={M. Jenkinson, C.F. Beckmann, T.E. Behrens, M.W. Woolrich, and S.M. Smith},title={FSL},journal={NeuroImage},volume={62},pages={782-790},year={2012},}', key='JenkinsonBeckmannBehrensWoolrichSmith2012')

As you can see the interface takes two mandatory inputs: in_file and mask_file. We want to use the output of smooth_node as in_file and one of the output of bet_file (the mask_file) as mask_file input.

** Let’s initialize a Workflow:**

# will be writing the code here:

if you’re lost, the full code is here:

# Initiation of a workflow
wf = Workflow(name="smoothflow", base_dir="/output/working_dir")

It’s very important to specify base_dir (as absolute path), because otherwise all the outputs would be saved somewhere in the temporary files.

let’s connect the bet_node output to mask_node input`

# we will be typing here:

if you’re lost, the code is here:

wf.connect(bet_node, "mask_file", mask_node, "mask_file")

Exercise 3

Connect out_file of smooth_node to in_file of mask_node.

# type your code here
wf.connect(smooth_node, "out_file", mask_node, "in_file")

Let’s see a graph describing our workflow:

wf.write_graph("workflow_graph.dot")
from IPython.display import Image
Image(filename="/output/working_dir/smoothflow/workflow_graph.png")
211017-18:06:28,853 nipype.workflow INFO:
	 Generated workflow graph: /output/working_dir/smoothflow/workflow_graph.png (graph2use=hierarchical, simple_form=True).
../../_images/introduction_quickstart_50_1.png

you can also plot a more detailed graph:

wf.write_graph(graph2use='flat')
from IPython.display import Image
Image(filename="/output/working_dir/smoothflow/graph_detailed.png")
211017-18:06:29,188 nipype.workflow INFO:
	 Generated workflow graph: /output/working_dir/smoothflow/graph.png (graph2use=flat, simple_form=True).
../../_images/introduction_quickstart_52_1.png

and now let’s run the workflow

# we will type our code here:

if you’re lost, the full code is here:

# Execute the workflow
res = wf.run()
211017-18:06:29,229 nipype.workflow INFO:
	 Workflow smoothflow settings: ['check', 'execution', 'logging', 'monitoring']
211017-18:06:29,235 nipype.workflow INFO:
	 Running serially.
211017-18:06:29,236 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow.smooth" in "/output/working_dir/smoothflow/smooth".
211017-18:06:29,240 nipype.workflow INFO:
	 [Node] Outdated cache found for "smoothflow.smooth".
211017-18:06:29,248 nipype.workflow INFO:
	 [Node] Running "smooth" ("nipype.interfaces.fsl.maths.IsotropicSmooth"), a CommandLine Interface with command:
fslmaths /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz -s 1.69864 /output/working_dir/smoothflow/smooth/sub-01_ses-test_T1w_smooth.nii.gz
211017-18:06:34,51 nipype.workflow INFO:
	 [Node] Finished "smoothflow.smooth".
211017-18:06:34,53 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow.bet" in "/output/working_dir/smoothflow/bet".
211017-18:06:34,62 nipype.workflow INFO:
	 [Node] Running "bet" ("nipype.interfaces.fsl.preprocess.BET"), a CommandLine Interface with command:
bet /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz /output/working_dir/smoothflow/bet/sub-01_ses-test_T1w_brain.nii.gz -m
211017-18:06:38,253 nipype.workflow INFO:
	 [Node] Finished "smoothflow.bet".
211017-18:06:38,255 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow.mask" in "/output/working_dir/smoothflow/mask".
211017-18:06:38,260 nipype.workflow INFO:
	 [Node] Outdated cache found for "smoothflow.mask".
211017-18:06:38,268 nipype.workflow INFO:
	 [Node] Running "mask" ("nipype.interfaces.fsl.maths.ApplyMask"), a CommandLine Interface with command:
fslmaths /output/working_dir/smoothflow/smooth/sub-01_ses-test_T1w_smooth.nii.gz -mas /output/working_dir/smoothflow/bet/sub-01_ses-test_T1w_brain_mask.nii.gz /output/working_dir/smoothflow/mask/sub-01_ses-test_T1w_smooth_masked.nii.gz
211017-18:06:39,321 nipype.workflow INFO:
	 [Node] Finished "smoothflow.mask".

and let’s look at the results

# we can check the output of specific nodes from workflow
list(res.nodes)[0].result.outputs
inskull_mask_file = <undefined>
inskull_mesh_file = <undefined>
mask_file = /output/working_dir/smoothflow/bet/sub-01_ses-test_T1w_brain_mask.nii.gz
meshfile = <undefined>
out_file = <undefined>
outline_file = <undefined>
outskin_mask_file = <undefined>
outskin_mesh_file = <undefined>
outskull_mask_file = <undefined>
outskull_mesh_file = <undefined>
skull_file = <undefined>
skull_mask_file = <undefined>

we can see the fie structure that has been created:

! tree -L 3 /output/working_dir/smoothflow/
/output/working_dir/smoothflow/
├── bet
│   ├── _0x2b2810107930568498391215c9d2aa2f.json
│   ├── command.txt
│   ├── _inputs.pklz
│   ├── _node.pklz
│   ├── _report
│   │   └── report.rst
│   ├── result_bet.pklz
│   └── sub-01_ses-test_T1w_brain_mask.nii.gz
├── d3.js
├── graph1.json
├── graph_detailed.dot
├── graph_detailed.png
├── graph.dot
├── graph.json
├── graph.png
├── index.html
├── mask
│   ├── _0x5eea37ef1dc0f3cace9eea2c7eada3b8.json
│   ├── command.txt
│   ├── _inputs.pklz
│   ├── _node.pklz
│   ├── _report
│   │   └── report.rst
│   ├── result_mask.pklz
│   └── sub-01_ses-test_T1w_smooth_masked.nii.gz
├── skullstrip
│   ├── _0x2b2810107930568498391215c9d2aa2f.json
│   ├── command.txt
│   ├── _inputs.pklz
│   ├── _node.pklz
│   ├── _report
│   │   └── report.rst
│   ├── result_skullstrip.pklz
│   └── sub-01_ses-test_T1w_brain_mask.nii.gz
├── smooth
│   ├── _0x9dbb0054da9b2a78c10b8c2f7ec2f8be.json
│   ├── command.txt
│   ├── _inputs.pklz
│   ├── _node.pklz
│   ├── _report
│   │   └── report.rst
│   ├── result_smooth.pklz
│   └── sub-01_ses-test_T1w_smooth.nii.gz
├── workflow_graph.dot
└── workflow_graph.png

8 directories, 38 files

and we can plot the results:

import numpy as np
import nibabel as nb
#import matplotlib.pyplot as plt

# Let's create a short helper function to plot 3D NIfTI images
def plot_slice(fname):

    # Load the image
    img = nb.load(fname)
    data = img.get_fdata()

    # Cut in the middle of the brain
    cut = int(data.shape[-1]/2) + 10

    # Plot the data
    plt.imshow(np.rot90(data[..., cut]), cmap="gray")
    plt.gca().set_axis_off()

f = plt.figure(figsize=(12, 4))
for i, img in enumerate(["/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz",
                         "/output/working_dir/smoothflow/smooth/sub-01_ses-test_T1w_smooth.nii.gz",
                         "/output/working_dir/smoothflow/bet/sub-01_ses-test_T1w_brain_mask.nii.gz",
                         "/output/working_dir/smoothflow/mask/sub-01_ses-test_T1w_smooth_masked.nii.gz"]):
    f.add_subplot(1, 4, i + 1)
    plot_slice(img)
../../_images/introduction_quickstart_62_0.png

Iterables

Some steps in a neuroimaging analysis are repetitive. Running the same preprocessing on multiple subjects or doing statistical inference on multiple files. To prevent the creation of multiple individual scripts, Nipype has as execution plugin for Workflow, called iterables.

Let’s assume we have a workflow with two nodes, node (A) does simple skull stripping, and is followed by a node (B) that does isometric smoothing. Now, let’s say, that we are curious about the effect of different smoothing kernels. Therefore, we want to run the smoothing node with FWHM set to 2mm, 8mm, and 16mm.

let’s just modify smooth_node:

# we will type the code here

if you’re lost the code is here:

smooth_node_it = Node(IsotropicSmooth(in_file=input_file), name="smooth")
smooth_node_it.iterables = ("fwhm", [4, 8, 16])

we will define again bet and smooth nodes:

bet_node_it = Node(BET(in_file=input_file, mask=True), name='bet_node')
mask_node_it = Node(ApplyMask(), name="mask")

** will create a new workflow with a new base_dir:**

# Initiation of a workflow
wf_it = Workflow(name="smoothflow_it", base_dir="/output/working_dir")
wf_it.connect(bet_node_it, "mask_file", mask_node_it, "mask_file")
wf_it.connect(smooth_node_it, "out_file", mask_node_it, "in_file")

let’s run the workflow and check the output

res_it = wf_it.run()
211017-18:06:41,241 nipype.workflow INFO:
	 Workflow smoothflow_it settings: ['check', 'execution', 'logging', 'monitoring']
211017-18:06:41,249 nipype.workflow INFO:
	 Running serially.
211017-18:06:41,251 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.smooth" in "/output/working_dir/smoothflow_it/_fwhm_16/smooth".
211017-18:06:41,259 nipype.workflow INFO:
	 [Node] Running "smooth" ("nipype.interfaces.fsl.maths.IsotropicSmooth"), a CommandLine Interface with command:
fslmaths /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz -s 6.79457 /output/working_dir/smoothflow_it/_fwhm_16/smooth/sub-01_ses-test_T1w_smooth.nii.gz
211017-18:06:54,512 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.smooth".
211017-18:06:54,514 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.smooth" in "/output/working_dir/smoothflow_it/_fwhm_8/smooth".
211017-18:06:54,522 nipype.workflow INFO:
	 [Node] Running "smooth" ("nipype.interfaces.fsl.maths.IsotropicSmooth"), a CommandLine Interface with command:
fslmaths /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz -s 3.39729 /output/working_dir/smoothflow_it/_fwhm_8/smooth/sub-01_ses-test_T1w_smooth.nii.gz
211017-18:07:01,786 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.smooth".
211017-18:07:01,787 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.smooth" in "/output/working_dir/smoothflow_it/_fwhm_4/smooth".
211017-18:07:01,793 nipype.workflow INFO:
	 [Node] Running "smooth" ("nipype.interfaces.fsl.maths.IsotropicSmooth"), a CommandLine Interface with command:
fslmaths /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz -s 1.69864 /output/working_dir/smoothflow_it/_fwhm_4/smooth/sub-01_ses-test_T1w_smooth.nii.gz
211017-18:07:06,540 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.smooth".
211017-18:07:06,542 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.bet_node" in "/output/working_dir/smoothflow_it/bet_node".
211017-18:07:06,552 nipype.workflow INFO:
	 [Node] Running "bet_node" ("nipype.interfaces.fsl.preprocess.BET"), a CommandLine Interface with command:
bet /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz /output/working_dir/smoothflow_it/bet_node/sub-01_ses-test_T1w_brain.nii.gz -m
211017-18:07:10,787 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.bet_node".
211017-18:07:10,789 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.mask" in "/output/working_dir/smoothflow_it/_fwhm_16/mask".
211017-18:07:10,801 nipype.workflow INFO:
	 [Node] Running "mask" ("nipype.interfaces.fsl.maths.ApplyMask"), a CommandLine Interface with command:
fslmaths /output/working_dir/smoothflow_it/_fwhm_16/smooth/sub-01_ses-test_T1w_smooth.nii.gz -mas /output/working_dir/smoothflow_it/bet_node/sub-01_ses-test_T1w_brain_mask.nii.gz /output/working_dir/smoothflow_it/_fwhm_16/mask/sub-01_ses-test_T1w_smooth_masked.nii.gz
211017-18:07:11,940 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.mask".
211017-18:07:11,942 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.mask" in "/output/working_dir/smoothflow_it/_fwhm_8/mask".
211017-18:07:11,954 nipype.workflow INFO:
	 [Node] Running "mask" ("nipype.interfaces.fsl.maths.ApplyMask"), a CommandLine Interface with command:
fslmaths /output/working_dir/smoothflow_it/_fwhm_8/smooth/sub-01_ses-test_T1w_smooth.nii.gz -mas /output/working_dir/smoothflow_it/bet_node/sub-01_ses-test_T1w_brain_mask.nii.gz /output/working_dir/smoothflow_it/_fwhm_8/mask/sub-01_ses-test_T1w_smooth_masked.nii.gz
211017-18:07:13,3 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.mask".
211017-18:07:13,5 nipype.workflow INFO:
	 [Node] Setting-up "smoothflow_it.mask" in "/output/working_dir/smoothflow_it/_fwhm_4/mask".
211017-18:07:13,15 nipype.workflow INFO:
	 [Node] Running "mask" ("nipype.interfaces.fsl.maths.ApplyMask"), a CommandLine Interface with command:
fslmaths /output/working_dir/smoothflow_it/_fwhm_4/smooth/sub-01_ses-test_T1w_smooth.nii.gz -mas /output/working_dir/smoothflow_it/bet_node/sub-01_ses-test_T1w_brain_mask.nii.gz /output/working_dir/smoothflow_it/_fwhm_4/mask/sub-01_ses-test_T1w_smooth_masked.nii.gz
211017-18:07:14,53 nipype.workflow INFO:
	 [Node] Finished "smoothflow_it.mask".

let’s see the graph

list(res_it.nodes)
[smoothflow_it.bet_node,
 smoothflow_it.mask.a0,
 smoothflow_it.smooth.aI.a0,
 smoothflow_it.mask.a1,
 smoothflow_it.smooth.aI.a1,
 smoothflow_it.mask.a2,
 smoothflow_it.smooth.aI.a2]

We can see the file structure that was created:

! tree -L 3 /output/working_dir/smoothflow_it/
/output/working_dir/smoothflow_it/
├── bet_node
│   ├── _0x2b2810107930568498391215c9d2aa2f.json
│   ├── command.txt
│   ├── _inputs.pklz
│   ├── _node.pklz
│   ├── _report
│   │   └── report.rst
│   ├── result_bet_node.pklz
│   └── sub-01_ses-test_T1w_brain_mask.nii.gz
├── d3.js
├── _fwhm_16
│   ├── mask
│   │   ├── _0x5bb564d19dd3a9d33377896bd76a3117.json
│   │   ├── command.txt
│   │   ├── _inputs.pklz
│   │   ├── _node.pklz
│   │   ├── _report
│   │   ├── result_mask.pklz
│   │   └── sub-01_ses-test_T1w_smooth_masked.nii.gz
│   └── smooth
│       ├── _0x6eb6392f80f598d77a3e9d3e6e89e6b6.json
│       ├── command.txt
│       ├── _inputs.pklz
│       ├── _node.pklz
│       ├── _report
│       ├── result_smooth.pklz
│       └── sub-01_ses-test_T1w_smooth.nii.gz
├── _fwhm_4
│   ├── mask
│   │   ├── _0x8d53721934e689d6195cb8f96b042204.json
│   │   ├── command.txt
│   │   ├── _inputs.pklz
│   │   ├── _node.pklz
│   │   ├── _report
│   │   ├── result_mask.pklz
│   │   └── sub-01_ses-test_T1w_smooth_masked.nii.gz
│   └── smooth
│       ├── _0x9dbb0054da9b2a78c10b8c2f7ec2f8be.json
│       ├── command.txt
│       ├── _inputs.pklz
│       ├── _node.pklz
│       ├── _report
│       ├── result_smooth.pklz
│       └── sub-01_ses-test_T1w_smooth.nii.gz
├── _fwhm_8
│   ├── mask
│   │   ├── _0x516a5ee3394733fb0cad02f86046c9eb.json
│   │   ├── command.txt
│   │   ├── _inputs.pklz
│   │   ├── _node.pklz
│   │   ├── _report
│   │   ├── result_mask.pklz
│   │   └── sub-01_ses-test_T1w_smooth_masked.nii.gz
│   └── smooth
│       ├── _0x037eaeb0c31c15fa085096765ffeda22.json
│       ├── command.txt
│       ├── _inputs.pklz
│       ├── _node.pklz
│       ├── _report
│       ├── result_smooth.pklz
│       └── sub-01_ses-test_T1w_smooth.nii.gz
├── graph1.json
├── graph.json
└── index.html

17 directories, 47 files

you have now 7 nodes instead of 3!

MapNode

If you want to iterate over a list of inputs, but need to feed all iterated outputs afterward as one input (an array) to the next node, you need to use a MapNode. A MapNode is quite similar to a normal Node, but it can take a list of inputs and operate over each input separately, ultimately returning a list of outputs.

Imagine that you have a list of items (let’s say files) and you want to execute the same node on them (for example some smoothing or masking). Some nodes accept multiple files and do exactly the same thing on them, but some don’t (they expect only one file). MapNode can solve this problem. Imagine you have the following workflow:

Node A outputs a list of files, but node B accepts only one file. Additionally, C expects a list of files. What you would like is to run B for every file in the output of A and collect the results as a list and feed it to C.

** Let’s run a simple numerical example using nipype Function interface **

def square_func(x):
    return x ** 2

square = Function(input_names=["x"], output_names=["f_x"], function=square_func)

If I want to know the results only for one x we can use Node:

square_node = Node(square, name="square")
square_node.inputs.x = 2
res = square_node.run()
res.outputs
211017-18:07:14,719 nipype.workflow INFO:
	 [Node] Setting-up "square" in "/tmp/tmpw7dfdzrl/square".
211017-18:07:14,723 nipype.workflow INFO:
	 [Node] Running "square" ("nipype.interfaces.utility.wrappers.Function")
211017-18:07:14,728 nipype.workflow INFO:
	 [Node] Finished "square".
f_x = 4

let’s try to ask for more values of x

# NBVAL_SKIP
square_node = Node(square, name="square")
square_node.inputs.x = [2, 4]
res = square_node.run()
res.outputs
211017-18:07:14,740 nipype.workflow INFO:
	 [Node] Setting-up "square" in "/tmp/tmpbsnbync0/square".
211017-18:07:14,746 nipype.workflow INFO:
	 [Node] Running "square" ("nipype.interfaces.utility.wrappers.Function")
211017-18:07:14,752 nipype.workflow WARNING:
	 Storing result file without outputs
211017-18:07:14,753 nipype.workflow WARNING:
	 [Node] Error on "square" (/tmp/tmpbsnbync0/square)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-41-e3deab85d48b> in <module>
      2 square_node = Node(square, name="square")
      3 square_node.inputs.x = [2, 4]
----> 4 res = square_node.run()
      5 res.outputs

/opt/miniconda-latest/envs/neuro/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py in run(self, updatehash)
    514 
    515         try:
--> 516             result = self._run_interface(execute=True)
    517         except Exception:
    518             logger.warning('[Node] Error on "%s" (%s)', self.fullname, outdir)

/opt/miniconda-latest/envs/neuro/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py in _run_interface(self, execute, updatehash)
    633             self._update_hash()
    634             return self._load_results()
--> 635         return self._run_command(execute)
    636 
    637     def _load_results(self):

/opt/miniconda-latest/envs/neuro/lib/python3.7/site-packages/nipype/pipeline/engine/nodes.py in _run_command(self, execute, copyfiles)
    739         logger.info(message)
    740         try:
--> 741             result = self._interface.run(cwd=outdir)
    742         except Exception as msg:
    743             result.runtime.stderr = "%s\n\n%s".format(

/opt/miniconda-latest/envs/neuro/lib/python3.7/site-packages/nipype/interfaces/base/core.py in run(self, cwd, ignore_exception, **inputs)
    417         try:
    418             runtime = self._pre_run_hook(runtime)
--> 419             runtime = self._run_interface(runtime)
    420             runtime = self._post_run_hook(runtime)
    421             outputs = self.aggregate_outputs(runtime)

/opt/miniconda-latest/envs/neuro/lib/python3.7/site-packages/nipype/interfaces/utility/wrappers.py in _run_interface(self, runtime)
    140                 args[name] = value
    141 
--> 142         out = function_handle(**args)
    143         if len(self._output_names) == 1:
    144             self._out[self._output_names[0]] = out

<string> in square_func(x)

TypeError: unsupported operand type(s) for ** or pow(): 'list' and 'int'

It will give an error since square_func do not accept list. But we can try MapNode:

square_mapnode = MapNode(square, name="square", iterfield=["x"])
square_mapnode.inputs.x = [2, 4]
res = square_mapnode.run()
res.outputs
211017-18:07:34,906 nipype.workflow INFO:
	 [Node] Setting-up "square" in "/tmp/tmpq539bt4t/square".
211017-18:07:34,913 nipype.workflow INFO:
	 [Node] Setting-up "_square0" in "/tmp/tmpq539bt4t/square/mapflow/_square0".
211017-18:07:34,916 nipype.workflow INFO:
	 [Node] Running "_square0" ("nipype.interfaces.utility.wrappers.Function")
211017-18:07:34,924 nipype.workflow INFO:
	 [Node] Finished "_square0".
211017-18:07:34,928 nipype.workflow INFO:
	 [Node] Setting-up "_square1" in "/tmp/tmpq539bt4t/square/mapflow/_square1".
211017-18:07:34,934 nipype.workflow INFO:
	 [Node] Running "_square1" ("nipype.interfaces.utility.wrappers.Function")
211017-18:07:34,942 nipype.workflow INFO:
	 [Node] Finished "_square1".
211017-18:07:34,946 nipype.workflow INFO:
	 [Node] Finished "square".
Bunch(f_x=[4, 16])

Notice that f_x is a list again!