From 949a616ffe140d2f10fb0110a55a9439a0b5af88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Boris=20Cl=C3=A9net?= Date: Mon, 20 Nov 2023 11:23:11 +0100 Subject: [PATCH] [DOC] for the core module --- docs/README.md | 1 + docs/core.md | 117 ++++++++++++++++++++++++++++++++++++++ tests/core/test_common.py | 4 +- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 docs/core.md diff --git a/docs/README.md b/docs/README.md index 8c4fd662..f9f6d193 100644 --- a/docs/README.md +++ b/docs/README.md @@ -11,4 +11,5 @@ Here are the available topics : * :microscope: [testing](/docs/testing.md) details the testing features of the project, i.e.: how is the code tested ? * :package: [ci-cd](/docs/ci-cd.md) contains the information on how continuous integration and delivery (knowned as CI/CD) is set up. * :writing_hand: [pipeline](/docs/pipelines.md) tells you all you need to know in order to write pipelines +* :compass: [core](/docs/core.md) a list of helpful functions when writing pipelines * :vertical_traffic_light: [status](/docs/status.md) contains the information on how to get the work progress status for a pipeline. diff --git a/docs/core.md b/docs/core.md new file mode 100644 index 00000000..74a2041c --- /dev/null +++ b/docs/core.md @@ -0,0 +1,117 @@ +# Core functions you can use to write pipelines + +Here are a few functions that could be useful for creating a pipeline with Nipype. These functions are meant to stay as unitary as possible. + +These are intented to be inserted in a nipype.Workflow inside a [nipype.Function](https://nipype.readthedocs.io/en/latest/api/generated/nipype.interfaces.utility.wrappers.html#function) interface, or for some of them (see associated docstring) as part of a [nipype.Workflow.connect](https://nipype.readthedocs.io/en/latest/api/generated/nipype.pipeline.engine.workflows.html#nipype.pipeline.engine.workflows.Workflow.connect) method. + +In the following example, we use the `list_intersection` function of `narps_open.core.common`, in both of the mentionned cases. + +```python +from nipype import Node, Function, Workflow +from narps_open.core.common import list_intersection + +# First case : a Function Node +intersection_node = Node(Function( + function = list_intersection, + input_names = ['list_1', 'list_2'], + output_names = ['output'] + ), name = 'intersection_node') +intersection_node.inputs.list_1 = ['001', '002', '003', '004'] +intersection_node.inputs.list_2 = ['002', '004', '005'] +print(intersection_node.run().outputs.output) # ['002', '004'] + +# Second case : inside a connect node +# We assume that there is a node_0 returning ['001', '002', '003', '004'] as `output` value +test_workflow = Workflow( + base_dir = '/path/to/base/dir', + name = 'test_workflow' + ) +test_workflow.connect([ + # node_1 will receive the evaluation of : + # list_intersection(['001', '002', '003', '004'], ['002', '004', '005']) + # as in_value + (node_0, node_1, [(('output', list_intersection, ['002', '004', '005']), 'in_value')]) + ]) +test_workflow.run() +``` + +> [!TIP] +> Use a [nipype.MapNode](https://nipype.readthedocs.io/en/latest/api/generated/nipype.pipeline.engine.nodes.html#nipype.pipeline.engine.nodes.MapNode) to run these functions on lists insted of unitary contents. E.g.: the `remove_file` function of `narps_open.core.common` only removes one file at a time, but feel free to pass a list of files using a `nipype.MapNode`. + +```python +from nipype import MapNode, Function +from narps_open.core.common import remove_file + +# Create the MapNode so that the `remove_file` function handles lists of files +remove_files_node = MapNode(Function( + function = remove_file, + input_names = ['_', 'file_name'], + output_names = [] + ), name = 'remove_files_node', iterfield = ['file_name']) + +# ... A couple of lines later, in the Worlflow definition +test_workflow = Workflow(base_dir = '/home/bclenet/dev/tests/nipype_merge/', name = 'test_workflow') +test_workflow.connect([ + # ... + # Here we assume the select_node's ouptut `out_files` is a list of files + (select_node, remove_files_node, [('out_files', 'file_name')]) + # ... + ]) +``` + +## narps_open.core.common + +This module contains a set of functions that nearly every pipeline could use. + +* `remove_file` remove a file when it is not needed anymore (to save disk space) + +```python +from narps_open.core.common import remove_file + +# Remove the /path/to/the/image.nii.gz file +remove_file('/path/to/the/image.nii.gz') +``` + +* `elements_in_string` : return the first input parameter if it contains one element of second parameter (None otherwise). + +```python +from narps_open.core.common import elements_in_string + +# Here we test if the file 'sub-001_file.nii.gz' belongs to a group of subjects. +elements_in_string('sub-001_file.nii.gz', ['005', '006', '007']) # Returns None +elements_in_string('sub-001_file.nii.gz', ['001', '002', '003']) # Returns 'sub-001_file.nii.gz' +``` + +> [!TIP] +> This can be generalised to a group of files, using a `nipype.MapNode`! + +* `clean_list` : remove elements of the first input parameter (list) if it is equal to the second parameter. + +```python +from narps_open.core.common import clean_list + +# Here we remove subject 002 from a group of subjects. +clean_list(['002', '005', '006', '007'], '002') +``` + +* `list_intersection` : return the intersection of two lists. + +```python +from narps_open.core.common import list_intersection + +# Here we keep only subjects that are in the equalRange group and selected for the analysis. +equal_range_group = ['002', '004', '006', '008'] +selected_for_analysis = ['002', '006', '010'] +list_intersection(equal_range_group, selected_for_analysis) # Returns ['002', '006'] +``` + +## narps_open.core.image + +This module contains a set of functions dedicated to computations on images. + + * `get_voxel_dimensions` : returns the voxel dimensions of an image + +```python +# Get dimensions of voxels along x, y, and z in mm (returns e.g.: [1.0, 1.0, 1.0]). +get_voxel_dimensions('/path/to/the/image.nii.gz') +``` diff --git a/tests/core/test_common.py b/tests/core/test_common.py index 0d50c05b..3e00fd1b 100644 --- a/tests/core/test_common.py +++ b/tests/core/test_common.py @@ -237,7 +237,7 @@ def test_connect_clean_list(remove_test_dir): def test_node_list_intersection(): """ Test the list_intersection function as a nipype.Node """ - # Inputs / ouptuts + # Inputs / outputs input_list_1 = ['001', '002', '003', '004'] input_list_2 = ['002', '004'] input_list_3 = ['001', '003', '005'] @@ -272,7 +272,7 @@ def test_node_list_intersection(): def test_connect_list_intersection(remove_test_dir): """ Test the list_intersection function as evaluated in a connect """ - # Inputs / ouptuts + # Inputs / outputs input_list_1 = ['001', '002', '003', '004'] input_list_2 = ['002', '004'] input_list_3 = ['001', '003', '005']