diff --git a/Dockerfile b/Dockerfile index 3c39ba3..eadeb1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -137,12 +137,12 @@ RUN mkdir /output && chmod 777 /output && chmod a+s /output USER neuro -# User-defined BASH instruction -RUN bash -c "source activate neuro && cd /data && datalad install -r ///workshops/nih-2017/ds000114 \ - && cd /data/ds000114 && datalad get -r -J4 sub-*/ses-test/anat && datalad get -r -J4 sub-*/ses-test/func/*fingerfootlips* && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*space-mni152nlin2009casym_preproc.nii.gz && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*t1w_preproc.nii.gz && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*h5 && datalad get -r -J4 derivatives/freesurfer/sub-01" +# # User-defined BASH instruction +# RUN bash -c "source activate neuro && cd /data && datalad install -r ///workshops/nih-2017/ds000114 \ +# && cd /data/ds000114 && datalad get -r -J4 sub-*/ses-test/anat && datalad get -r -J4 sub-*/ses-test/func/*fingerfootlips* && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*space-mni152nlin2009casym_preproc.nii.gz && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*t1w_preproc.nii.gz && datalad get -r -J4 derivatives/fmriprep/sub-*/anat/*h5 && datalad get -r -J4 derivatives/freesurfer/sub-01" -# User-defined BASH instruction -RUN bash -c "curl -L https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9 -o /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz && tar xf /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz -C /data/ds000114/derivatives/fmriprep/. && rm /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz" +# # User-defined BASH instruction +# RUN bash -c "curl -L https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9 -o /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz && tar xf /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz -C /data/ds000114/derivatives/fmriprep/. && rm /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz" COPY [".", "/home/neuro/nipype_tutorial"] diff --git a/README.md b/README.md index 4d1c961..4260444 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,9 @@ # Nipype Tutorial Notebooks -This is the Nipype Tutorial in Notebooks. There are multiple ways of how you can profit from this tutorial: +Notebooks for a basic preprocessing and GLM analyis of block design experiments. -1. [Nipype Tutorial Homepage](https://miykael.github.io/nipype_tutorial/): You can find all notebooks used in this tutorial on this homepage. -2. [Nipype Tutorial Docker Image](https://miykael.github.io/nipype_tutorial/notebooks/introduction_docker.html): Run the notebooks of this tutorial in an interactive docker image and on real example data. The nipype tutorial docker image is the best interactive way to learn Nipype. +# Cloud Computing +You can run the notebook on the "cloud" by clicking on Binder icon. Note that you have only access to 1GB to 4GB of RAM. -# Feedback, Help & Support - -If you want to help with this tutorial or have any questions, fell free to fork the repo of the [Notebooks](https://github.com/miykael/nipype_tutorial) or interact with other contributors on the slack channel [brainhack.slack.com/messages/nipype/](https://brainhack.slack.com/messages/nipype/). If you have any questions or found a problem, open a new [issue on github](https://github.com/miykael/nipype_tutorial/issues). - - -# Thanks and Acknowledgment - -A huge thanks to [Michael Waskom](https://github.com/mwaskom), [Oscar Esteban](https://github.com/oesteban), [Chris Gorgolewski](https://github.com/chrisfilo) and [Satrajit Ghosh](https://github.com/satra) for their input to this tutorial! And a huge thanks to [Dorota Jarecka](https://github.com/djarecka/) who updated this tutorial to Python 3 and added much more content to all the notebooks! +[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/arash-ash/nipype_tutorial/master) diff --git a/data/sub-1/anat/sub-1_T1w.txt b/data/sub-1/anat/sub-1_T1w.txt new file mode 100644 index 0000000..c9ddb60 --- /dev/null +++ b/data/sub-1/anat/sub-1_T1w.txt @@ -0,0 +1 @@ +/home/arash/data/PSYC405/sub1/T1_MPR_NS_SAG_P2_1MM_ISO_MK_32CH_MODIFIED_0002/PSYCH405.MR.BOYACILAB_DEMO_EXAMS.0002.0001.2015.10.27.14.20.19.937500.128378006.IMA Field Strength: 3 ProtocolName: t1mprnssagp21mmisoMK32chmodified ScanningSequence00180020: GR\IR TE: 2.920000076 TR: 2600 SeriesNum: 2 AcquNum: 1001 ImageNum: 1 ImageComments: DateTime: 27-10-15 13:06:18 Name: PSYCH405 PatientHistory: DoB: 19880103 Gender: F Age(Years): 27.8145237 DimXYZT: 224 256 176 1 diff --git a/data/sub-1/anat/sub-1_run-13_T1w.nii b/data/sub-1/anat/sub-1_run-13_T1w.nii new file mode 100644 index 0000000..8c22bc4 Binary files /dev/null and b/data/sub-1/anat/sub-1_run-13_T1w.nii differ diff --git a/data/sub-1/anat/sub-1_run-3_T1w.nii b/data/sub-1/anat/sub-1_run-3_T1w.nii new file mode 100644 index 0000000..8c22bc4 Binary files /dev/null and b/data/sub-1/anat/sub-1_run-3_T1w.nii differ diff --git a/data/sub-1/func/sub-1_run-13_bold.nii b/data/sub-1/func/sub-1_run-13_bold.nii new file mode 100644 index 0000000..1f5845b Binary files /dev/null and b/data/sub-1/func/sub-1_run-13_bold.nii differ diff --git a/data/sub-1/func/sub-1_run-13_events.tsv b/data/sub-1/func/sub-1_run-13_events.tsv new file mode 100644 index 0000000..5aa4427 --- /dev/null +++ b/data/sub-1/func/sub-1_run-13_events.tsv @@ -0,0 +1,30 @@ +onset duration stimulus +0 2 rest +2 2 rest +4 2 rest +6 2 rest +8 2 rest +10 2 right +12 2 right +14 2 right +16 2 right +18 2 right +20 2 right +22 2 left +24 2 left +26 2 left +28 2 left +30 2 left +32 2 left +34 2 right +36 2 right +38 2 right +40 2 right +42 2 right +44 2 right +46 2 left +48 2 left +50 2 left +52 2 left +54 2 left +56 2 left diff --git a/data/sub-1/func/sub-1_run-3_bold.nii b/data/sub-1/func/sub-1_run-3_bold.nii new file mode 100644 index 0000000..4227c00 Binary files /dev/null and b/data/sub-1/func/sub-1_run-3_bold.nii differ diff --git a/data/sub-1/func/sub-1_run-3_events.tsv b/data/sub-1/func/sub-1_run-3_events.tsv new file mode 100644 index 0000000..5aa4427 --- /dev/null +++ b/data/sub-1/func/sub-1_run-3_events.tsv @@ -0,0 +1,30 @@ +onset duration stimulus +0 2 rest +2 2 rest +4 2 rest +6 2 rest +8 2 rest +10 2 right +12 2 right +14 2 right +16 2 right +18 2 right +20 2 right +22 2 left +24 2 left +26 2 left +28 2 left +30 2 left +32 2 left +34 2 right +36 2 right +38 2 right +40 2 right +42 2 right +44 2 right +46 2 left +48 2 left +50 2 left +52 2 left +54 2 left +56 2 left diff --git a/data/task_bold.json b/data/task_bold.json new file mode 100644 index 0000000..c3a45b7 --- /dev/null +++ b/data/task_bold.json @@ -0,0 +1,4 @@ +{ + "RepetitionTime": 2.0, + "TaskName": "handsqueeze" +} diff --git a/notebooks/basic_configuration.ipynb b/notebooks/basic_configuration.ipynb deleted file mode 100644 index f1b33cb..0000000 --- a/notebooks/basic_configuration.ipynb +++ /dev/null @@ -1,147 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Execution Configuration Options\n", - "\n", - "Nipype gives you many liberties on how to create workflows, but the execution of them uses a lot of default parameters. But you have of course all the freedom to change them as you like.\n", - "\n", - "Nipype looks for the configuration options in the local folder under the name ``nipype.cfg`` and in ``~/.nipype/nipype.cfg`` (in this order). It can be divided into **Logging** and **Execution** options. A few of the possible options are the following:\n", - "\n", - "### Logging\n", - "\n", - "- **workflow_level**: How detailed the logs regarding workflow should be\n", - "- **log_to_file**: Indicates whether logging should also send the output to a file\n", - "\n", - "### Execution\n", - "\n", - "- **stop_on_first_crash**: Should the workflow stop upon first node crashing or try to execute as many nodes as possible?\n", - "- **remove_unnecessary_outputs**: This will remove any interface outputs not needed by the workflow. If the required outputs from a node changes, rerunning the workflow will rerun the node. Outputs of leaf nodes (nodes whose outputs are not connected to any other nodes) will never be deleted independent of this parameter.\n", - "- **use_relative_paths**: Should the paths stored in results (and used to look for inputs) be relative or absolute. Relative paths allow moving the whole working directory around but may cause problems with symlinks. \n", - "- **job_finished_timeout**: When batch jobs are submitted through, SGE/PBS/Condor they could be killed externally. Nipype checks to see if a results file exists to determine if the node has completed. This timeout determines for how long this check is done after a job finish is detected. (float in seconds; default value: 5)\n", - "- **poll_sleep_duration**: This controls how long the job submission loop will sleep between submitting all pending jobs and checking for job completion. To be nice to cluster schedulers the default is set to 2\n", - "\n", - "\n", - "For the full list, see [Configuration File](http://nipype.readthedocs.io/en/latest/users/config_file.html)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Global, workflow & node level\n", - "\n", - "The configuration options can be changed globally (i.e. for all workflows), for just a workflow, or for just a node. The implementations look as follows (note that you should first create directories if you want to change `crashdump_dir` and `log_directory`):" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### At the global level:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import config, logging\n", - "import os\n", - "os.makedirs('/output/log_folder', exist_ok=True)\n", - "os.makedirs('/output/crash_folder', exist_ok=True)\n", - "\n", - "config_dict={'execution': {'remove_unnecessary_outputs': 'true',\n", - " 'keep_inputs': 'false',\n", - " 'poll_sleep_duration': '60',\n", - " 'stop_on_first_rerun': 'false',\n", - " 'hash_method': 'timestamp',\n", - " 'local_hash_check': 'true',\n", - " 'create_report': 'true',\n", - " 'crashdump_dir': '/output/crash_folder',\n", - " 'use_relative_paths': 'false',\n", - " 'job_finished_timeout': '5'},\n", - " 'logging': {'workflow_level': 'INFO',\n", - " 'filemanip_level': 'INFO',\n", - " 'interface_level': 'INFO',\n", - " 'log_directory': '/output/log_folder',\n", - " 'log_to_file': 'true'}}\n", - "config.update_config(config_dict)\n", - "logging.update_logging(config)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### At the workflow level:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Workflow\n", - "wf = Workflow(name=\"config_test\")\n", - "\n", - "# Change execution parameters\n", - "wf.config['execution']['stop_on_first_crash'] = 'true'\n", - "\n", - "# Change logging parameters\n", - "wf.config['logging'] = {'workflow_level' : 'DEBUG',\n", - " 'filemanip_level' : 'DEBUG',\n", - " 'interface_level' : 'DEBUG',\n", - " 'log_to_file' : 'True',\n", - " 'log_directory' : '/output/log_folder'}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### At the node level:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Node\n", - "from nipype.interfaces.fsl import BET\n", - "\n", - "bet = Node(BET(), name=\"config_test\")\n", - "\n", - "bet.config = {'execution': {'keep_unnecessary_outputs': 'false'}}" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_data_input.ipynb b/notebooks/basic_data_input.ipynb deleted file mode 100644 index d260792..0000000 --- a/notebooks/basic_data_input.ipynb +++ /dev/null @@ -1,516 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Data Input\n", - "\n", - "To do any computation, you need to have data. Getting the data in the framework of a workflow is therefore the first step of every analysis. Nipype provides many different modules to grab or select the data:\n", - "\n", - " DataFinder\n", - " DataGrabber\n", - " FreeSurferSource\n", - " JSONFileGrabber\n", - " S3DataGrabber\n", - " SSHDataGrabber\n", - " SelectFiles\n", - " XNATSource\n", - "\n", - "This tutorial will only cover some of them. For the rest, see the section [``interfaces.io``](http://nipype.readthedocs.io/en/latest/interfaces/generated/nipype.interfaces.io.html) on the official homepage." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Dataset structure\n", - "\n", - "To be able to import data, you first need to be aware about the structure of your dataset. The structure of the dataset for this tutorial is according to BIDS, and looks as follows:\n", - "\n", - " ds000114\n", - " ├── CHANGES\n", - " ├── dataset_description.json\n", - " ├── derivatives\n", - " │   ├── fmriprep\n", - " │   │   └── sub01...sub10\n", - " │   │   └── ...\n", - " │   ├── freesurfer\n", - " │   ├── fsaverage\n", - " │   ├── fsaverage5\n", - " │   │   └── sub01...sub10\n", - " │   │   └── ...\n", - " ├── dwi.bval\n", - " ├── dwi.bvec\n", - " ├── sub-01\n", - " │   ├── ses-retest \n", - " │   ├── anat\n", - " │   │   └── sub-01_ses-retest_T1w.nii.gz\n", - " │   ├──func\n", - " │   ├── sub-01_ses-retest_task-covertverbgeneration_bold.nii.gz\n", - " │   ├── sub-01_ses-retest_task-fingerfootlips_bold.nii.gz\n", - " │   ├── sub-01_ses-retest_task-linebisection_bold.nii.gz\n", - " │   ├── sub-01_ses-retest_task-linebisection_events.tsv\n", - " │   ├── sub-01_ses-retest_task-overtverbgeneration_bold.nii.gz\n", - " │   └── sub-01_ses-retest_task-overtwordrepetition_bold.nii.gz\n", - " │ └── dwi\n", - " │ └── sub-01_ses-retest_dwi.nii.gz\n", - " │   ├── ses-test \n", - " │   ├── anat\n", - " │   │   └── sub-01_ses-test_T1w.nii.gz\n", - " │   ├──func\n", - " │   ├── sub-01_ses-test_task-covertverbgeneration_bold.nii.gz\n", - " │   ├── sub-01_ses-test_task-fingerfootlips_bold.nii.gz\n", - " │   ├── sub-01_ses-test_task-linebisection_bold.nii.gz\n", - " │   ├── sub-01_ses-test_task-linebisection_events.tsv\n", - " │   ├── sub-01_ses-test_task-overtverbgeneration_bold.nii.gz\n", - " │   └── sub-01_ses-test_task-overtwordrepetition_bold.nii.gz\n", - " │ └── dwi\n", - " │ └── sub-01_ses-retest_dwi.nii.gz\n", - " ├── sub-02..sub-10\n", - " │   └── ...\n", - " ├── task-covertverbgeneration_bold.json\n", - " ├── task-covertverbgeneration_events.tsv\n", - " ├── task-fingerfootlips_bold.json\n", - " ├── task-fingerfootlips_events.tsv\n", - " ├── task-linebisection_bold.json\n", - " ├── task-overtverbgeneration_bold.json\n", - " ├── task-overtverbgeneration_events.tsv\n", - " ├── task-overtwordrepetition_bold.json\n", - " └── task-overtwordrepetition_events.tsv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# DataGrabber\n", - "\n", - "``DataGrabber`` is a generic data grabber module that wraps around ``glob`` to select your neuroimaging data in an intelligent way. As an example, let's assume we want to grab the anatomical and functional images of a certain subject.\n", - "\n", - "First, we need to create the ``DataGrabber`` node. This node needs to have some input fields for all dynamic parameters (e.g. subject identifier, task identifier), as well as the two desired output fields ``anat`` and ``func``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import DataGrabber, Node\n", - "\n", - "# Create DataGrabber node\n", - "dg = Node(DataGrabber(infields=['subject_id', 'ses_name', 'task_name'],\n", - " outfields=['anat', 'func']),\n", - " name='datagrabber')\n", - "\n", - "# Location of the dataset folder\n", - "dg.inputs.base_directory = '/data/ds000114'\n", - "\n", - "# Necessary default parameters\n", - "dg.inputs.template = '*'\n", - "dg.inputs.sort_filelist = True" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Second, we know that the two files we desire are the the following location:\n", - "\n", - " anat = /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz\n", - " func = /data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz\n", - "\n", - "We see that the two files only have three dynamic parameters between subjects and task names:\n", - "\n", - " subject_id: in this case 'sub-01'\n", - " task_name: in this case fingerfootlips\n", - " ses_name: test\n", - "\n", - "This means that we can rewrite the paths as follows:\n", - "\n", - " anat = /data/ds102/[subject_id]/ses-[ses_name]/anat/sub-[subject_id]_ses-[ses_name]_T1w.nii.gz\n", - " func = /data/ds102/[subject_id]/ses-[ses_name]/func/sub-[subject_id]_ses-[ses_name]_task-[task_name]_bold.nii.gz\n", - "\n", - "Therefore, we need the parameters ``subject_id`` and ``ses_name`` for the anatomical image and the parameters ``subject_id``, ``ses_name`` and ``task_name`` for the functional image. In the context of DataGabber, this is specified as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dg.inputs.template_args = {'anat': [['subject_id', 'ses_name']],\n", - " 'func': [['subject_id', 'ses_name', 'task_name']]}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, comes the most important part of DataGrabber. We need to specify the template structure to find the specific data. This can be done as follows." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dg.inputs.field_template = {'anat': 'sub-%02d/ses-%s/anat/*_T1w.nii.gz',\n", - " 'func': 'sub-%02d/ses-%s/func/*task-%s_bold.nii.gz'}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You'll notice that we use ``%s``, ``%02d`` and ``*`` for placeholders in the data paths. ``%s`` is a placeholder for a string and is filled out by ``task_name`` or ``ses_name``. ``%02d`` is a placeholder for a integer number and is filled out by ``subject_id``. ``*`` is used as a wild card, e.g. a placeholder for any possible string combination. This is all to set up the ``DataGrabber`` node." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now it is up to you how you want to feed the dynamic parameters into the node. You can either do this by using another node (e.g. ``IdentityInterface``) and feed ``subject_id``, ``ses_name`` and ``task_name`` as connections to the ``DataGrabber`` node or specify them directly as node inputs." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Using the IdentityInterface\n", - "from nipype import IdentityInterface\n", - "infosource = Node(IdentityInterface(fields=['subject_id', 'task_name']),\n", - " name=\"infosource\")\n", - "infosource.inputs.task_name = \"fingerfootlips\"\n", - "infosource.inputs.ses_name = \"test\"\n", - "subject_id_list = [1, 2]\n", - "infosource.iterables = [('subject_id', subject_id_list)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you only have to connect ``infosource`` with your ``DataGrabber`` and run the workflow to iterate over subjects 1 and 2." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also provide the inputs to the ``DataGrabber`` node directly, for one subject you can do this as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Specifying the input fields of DataGrabber directly\n", - "dg.inputs.subject_id = 1\n", - "dg.inputs.ses_name = \"test\"\n", - "dg.inputs.task_name = \"fingerfootlips\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's run the ``DataGrabber`` node and let's look at the output:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dg.run().outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# SelectFiles\n", - "\n", - "`SelectFiles` is a more flexible alternative to `DataGrabber`. It uses the {}-based string formating syntax to plug values into string templates and collect the data. These templates can also be combined with glob wild cards. The field names in the formatting template (i.e. the terms in braces) will become inputs fields on the interface, and the keys in the templates dictionary will form the output fields.\n", - "\n", - "Let's focus again on the data we want to import:\n", - "\n", - " anat = /data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz\n", - " func = /data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz\n", - " \n", - "Now, we can replace those paths with the accoridng {}-based strings.\n", - "\n", - " anat = /data/ds000114/sub-{subject_id}/ses-{ses_name}/anat/sub-{subject_id}_ses-{ses_name}_T1w.nii.gz\n", - " func = /data/ds000114/sub-{subject_id}/ses-{ses_name}/func/ \\\n", - " sub-{subject_id}_ses-{ses_name}_task-{task_name}_bold.nii.gz\n", - "\n", - "How would this look like as a `SelectFiles` node?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import SelectFiles, Node\n", - "\n", - "# String template with {}-based strings\n", - "templates = {'anat': 'sub-{subject_id}/ses-{ses_name}/anat/sub-{subject_id}_ses-{ses_name}_T1w.nii.gz',\n", - " 'func': 'sub-{subject_id}/ses-{ses_name}/func/sub-{subject_id}_ses-{ses_name}_task-{task_name}_bold.nii.gz'}\n", - "\n", - "# Create SelectFiles node\n", - "sf = Node(SelectFiles(templates),\n", - " name='selectfiles')\n", - "\n", - "# Location of the dataset folder\n", - "sf.inputs.base_directory = '/data/ds000114'\n", - "\n", - "# Feed {}-based placeholder strings with values\n", - "sf.inputs.subject_id = '01'\n", - "sf.inputs.ses_name = \"test\"\n", - "sf.inputs.task_name = 'fingerfootlips'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's check if we get what we wanted." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "sf.run().outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Perfect! But why is `SelectFiles` more flexible than `DataGrabber`? First, you perhaps noticed that with the {}-based string, we can reuse the same input (e.g. `subject_id`) multiple time in the same string, without feeding it multiple times into the template.\n", - "\n", - "Additionally, you can also select multiple files without the need of an iterable node. For example, let's assume we want to select both anatomical images (`'sub-01'` and `'sub-02'`) at once. We can do this by using the following file template:\n", - "\n", - " 'sub-0[1,2]/anat/sub-0[1,2]_T1w.nii.gz'\n", - "\n", - "Let's see how this works:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import SelectFiles, Node\n", - "from os.path import abspath as opap\n", - "\n", - "# String template with {}-based strings\n", - "templates = {'anat': 'sub-0[1,2]/ses-{ses_name}/anat/sub-0[1,2]_ses-{ses_name}_T1w.nii.gz'}\n", - "\n", - "\n", - "# Create SelectFiles node\n", - "sf = Node(SelectFiles(templates),\n", - " name='selectfiles')\n", - "\n", - "# Location of the dataset folder\n", - "sf.inputs.base_directory = '/data/ds000114'\n", - "\n", - "# Feed {}-based placeholder strings with values\n", - "sf.inputs.ses_name = 'test'\n", - "\n", - "# Print SelectFiles output\n", - "sf.run().outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, now `anat` contains two file paths, one for the first and one for the second subject. As a side node, you could have also gotten them same thing with the wild card `*`:\n", - "\n", - " 'sub-0*/ses-test/anat/sub-0*_ses-test_T1w.nii.gz'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## FreeSurferSource\n", - "\n", - "`FreeSurferSource` is a specific case of a file grabber that felicitates the data import of outputs from the FreeSurfer recon-all algorithm. This of course requires that you've already run `recon-all` on your subject." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the tutorial dataset ``ds000114``, `recon-all` was already run. So, let's make sure that you have the anatomy output of one subject on your system:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!datalad get -r -J4 /data/ds000114/derivatives/freesurfer/sub-01/" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, before you can run `FreeSurferSource`, you first have to specify the path to the FreeSurfer output folder, i.e. you have to specify the SUBJECTS_DIR variable. This can be done as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.freesurfer import FSCommand\n", - "from os.path import abspath as opap\n", - "\n", - "# Path to your freesurfer output folder\n", - "fs_dir = opap('/data/ds000114/derivatives/freesurfer/')\n", - "\n", - "# Set SUBJECTS_DIR\n", - "FSCommand.set_default_subjects_dir(fs_dir)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To create the `FreeSurferSource` node, do as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Node\n", - "from nipype.interfaces.io import FreeSurferSource\n", - "\n", - "# Create FreeSurferSource node\n", - "fssource = Node(FreeSurferSource(subjects_dir=fs_dir),\n", - " name='fssource')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's now run it for a specific subject." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fssource.inputs.subject_id = 'sub-01'\n", - "result = fssource.run() " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Did it work? Let's try to access multiple FreeSurfer outputs:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print('aparc_aseg: %s\\n' % result.outputs.aparc_aseg)\n", - "print('brainmask: %s\\n' % result.outputs.brainmask)\n", - "print('inflated: %s\\n' % result.outputs.inflated)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It seems to be working as it should. But as you can see, the `inflated` output actually contains the file location for both hemispheres. With `FreeSurferSource` we can also restrict the file selection to a single hemisphere. To do this, we use the `hemi` input filed:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fssource.inputs.hemi = 'lh'\n", - "result = fssource.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a look again at the `inflated` output." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "result.outputs.inflated" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Perfect!" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_data_input_bids.ipynb b/notebooks/basic_data_input_bids.ipynb deleted file mode 100644 index 35eb436..0000000 --- a/notebooks/basic_data_input_bids.ipynb +++ /dev/null @@ -1,383 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Data input for BIDS datasets\n", - "`DataGrabber` and `SelectFiles` are great if you are dealing with generic datasets with arbitrary organization. However if you have decided to use Brain Imaging Data Structure (BIDS) to organized your data (or got your hands on a BIDS dataset) you can take advanted of a formal structure BIDS imposes. In this short tutorial you will learn how to do this." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## `pybids` - a Python API for working with BIDS datasets\n", - "`pybids` is a lightweight python API for querying BIDS folder structure for specific files and metadata. You can install it from PyPi:\n", - "```\n", - "pip install pybids\n", - "```\n", - "Please note it should be already installed in the tutorial Docker image." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## The `layout` object and simple queries\n", - "To begin working with pubids we need to initalize a layout object. We will need it to do all of our queries" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from bids.grabbids import BIDSLayout\n", - "layout = BIDSLayout(\"/data/ds000114/\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree -L 4 /data/ds000114/" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's figure out what are the subject labels in this dataset" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get_subjects()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What modalities are included in this dataset?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get_modalities()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What different data types are included in this dataset?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get_types(modality='func')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What are the different tasks included in this dataset?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get_tasks()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also ask for all of the data for a particular subject and one modality." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get(subject='01', modality=\"anat\", session=\"test\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also ask for a specific subset of data. Note that we are using extension filter to get just the imaging data (BIDS allows both .nii and .nii.gz so we need to include both)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get(subject='01', type='bold', extensions=['nii', 'nii.gz'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You probably noticed that this method does not only return the file paths, but objects with relevant query fields. We can easily extract just the file paths." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "[f.filename for f in layout.get(subject='01', type='bold', extensions=['nii', 'nii.gz'])]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercise 1:\n", - "List all files for the \"linebisection\" task for subject 02." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Including `pybids` in your `nipype` workflow\n", - "This is great, but what we really want is to include this into our `nipype` workflows. How to do this? We can create our own custom `BIDSDataGrabber` using a `Function` Interface. First we need a plain Python function that for a given subject label and dataset location will return list of BOLD files." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_niftis(subject_id, data_dir):\n", - " # Remember that all the necesary imports need to be INSIDE the function for the Function Interface to work!\n", - " from bids.grabbids import BIDSLayout\n", - " \n", - " layout = BIDSLayout(data_dir)\n", - " \n", - " bolds = [f.filename for f in layout.get(subject=subject_id, type=\"bold\", extensions=['nii', 'nii.gz'])]\n", - " \n", - " return bolds" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "get_niftis('01', '/data/ds000114')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok we got our function. Now we need to wrap it inside a Node object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.pipeline import Node, MapNode, Workflow\n", - "from nipype.interfaces.utility import IdentityInterface, Function" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "BIDSDataGrabber = Node(Function(function=get_niftis, input_names=[\"subject_id\",\n", - " \"data_dir\"],\n", - " output_names=[\"bolds\"]), name=\"BIDSDataGrabber\")\n", - "BIDSDataGrabber.inputs.data_dir = \"/data/ds000114\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "BIDSDataGrabber.inputs.subject_id='01'\n", - "res = BIDSDataGrabber.run()\n", - "res.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Works like a charm! (hopefully :) Lets put it in a workflow. We are not going to analyze any data, but for demostrantion purposes we will add a couple of nodes that pretend to analyze their inputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def printMe(paths):\n", - " print(\"\\n\\nanalyzing \" + str(paths) + \"\\n\\n\")\n", - " \n", - "analyzeBOLD = Node(Function(function=printMe, input_names=[\"paths\"],\n", - " output_names=[]), name=\"analyzeBOLD\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf = Workflow(name=\"bids_demo\")\n", - "wf.connect(BIDSDataGrabber, \"bolds\", analyzeBOLD, \"paths\")\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercise 2:\n", - "Modify the `BIDSDataGrabber` and the workflow to include T1ws." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Iterating over subject labels\n", - "In the previous example we demostrated how to use `pybids` to \"analyze\" one subject. How can we scale it for all subjects? Easy - using `iterables` (more in [Iteration/Iterables](basic_iteration.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "BIDSDataGrabber.iterables = ('subject_id', layout.get_subjects()[:2])\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Accessing additional metadata\n", - "Querying different files is nice, but sometimes you want to access more metadata. For example `RepetitionTime`. `pybids` can help with that as well" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "layout.get_metadata('/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Can we incorporate this into our pipeline? Yes we can!\n", - "(More about MapNode in [MapNode](basic_mapnodes.ipynb))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def printMetadata(path, data_dir):\n", - " from bids.grabbids import BIDSLayout\n", - " layout = BIDSLayout(data_dir)\n", - " print(\"\\n\\nanalyzing \" + path + \"\\nTR: \"+ str(layout.get_metadata(path)[\"RepetitionTime\"]) + \"\\n\\n\")\n", - " \n", - "analyzeBOLD2 = MapNode(Function(function=printMetadata, input_names=[\"path\", \"data_dir\"],\n", - " output_names=[]), name=\"analyzeBOLD2\", iterfield=\"path\")\n", - "analyzeBOLD2.inputs.data_dir = \"/data/ds000114/\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf = Workflow(name=\"bids_demo\")\n", - "wf.connect(BIDSDataGrabber, \"bolds\", analyzeBOLD2, \"path\")\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Exercise 3:\n", - "Modify the `printMetadata` function to also print `EchoTime` " - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_data_output.ipynb b/notebooks/basic_data_output.ipynb deleted file mode 100644 index bc1e381..0000000 --- a/notebooks/basic_data_output.ipynb +++ /dev/null @@ -1,304 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Data Output\n", - "\n", - "Similarly important to data input is data output. Using a data output module allows you to restructure and rename computed output and to spatial differentiate relevant output files from the temporary computed intermediate files in the working directory. Nipype provides the following modules to handle data stream output:\n", - "\n", - " DataSink\n", - " JSONFileSink\n", - " MySQLSink\n", - " SQLiteSink\n", - " XNATSink\n", - "\n", - "This tutorial covers only `DataSink`. For the rest, see the section [``interfaces.io``](http://nipype.readthedocs.io/en/latest/interfaces/generated/nipype.interfaces.io.html) on the official homepage." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Preparation\n", - "\n", - "Before we can use `DataSink` we first need to run a workflow. For this purpose, let's create a very short preprocessing workflow that realigns and smooths one functional image of one subject." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's create a `SelectFiles` node. For an explanation about this step, see the [Data Input](basic_data_input.ipynb) tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import SelectFiles, Node\n", - "\n", - "# Create SelectFiles node\n", - "templates={'func': '{subject}/{session}/func/{subject}_{session}_task-fingerfootlips_bold.nii.gz'}\n", - "sf = Node(SelectFiles(templates),\n", - " name='selectfiles')\n", - "sf.inputs.base_directory = '/data/ds000114'\n", - "sf.inputs.subject = 'sub-01'\n", - "sf.inputs.session = 'ses-test'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Second, let's create the motion correction and smoothing node. For an explanation about this step, see the [Nodes](basic_nodes.ipynb) and [Interfaces](basic_interfaces.ipynb) tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.fsl import MCFLIRT, IsotropicSmooth\n", - "\n", - "# Create Motion Correction Node\n", - "mcflirt = Node(MCFLIRT(mean_vol=True,\n", - " save_plots=True),\n", - " name='mcflirt')\n", - "\n", - "# Create Smoothing node\n", - "smooth = Node(IsotropicSmooth(fwhm=4),\n", - " name='smooth')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Third, let's create the workflow that will contain those three nodes. For an explanation about this step, see the [Workflow](basic_workflow.ipynb) tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Workflow\n", - "from os.path import abspath\n", - "\n", - "# Create a preprocessing workflow\n", - "wf = Workflow(name=\"preprocWF\")\n", - "wf.base_dir = '/output/working_dir'\n", - "\n", - "# Connect the three nodes to each other\n", - "wf.connect([(sf, mcflirt, [(\"func\", \"in_file\")]),\n", - " (mcflirt, smooth, [(\"out_file\", \"in_file\")])])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that everything is set up, let's run the preprocessing workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the execution of the workflow we have all the data hidden in the working directory `'working_dir'`. Let's take a closer look at the content of this folder:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! tree /output/working_dir/preprocWF" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we can see, there is way too much content that we might not really care about. To relocate and rename all the files that are relevant for you, you can use `DataSink`?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# DataSink\n", - "\n", - "`DataSink` is Nipype's standard output module to restructure your output files. It allows you to relocate and rename files that you deem relevant.\n", - "\n", - "Based on the preprocessing pipeline above, let's say we want to keep the smoothed functional images as well as the motion correction paramters. To do this, we first need to create the `DataSink` object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.io import DataSink\n", - "\n", - "# Create DataSink object\n", - "sinker = Node(DataSink(), name='sinker')\n", - "\n", - "# Name of the output folder\n", - "sinker.inputs.base_directory = '/output/working_dir/preprocWF_output'\n", - "\n", - "# Connect DataSink with the relevant nodes\n", - "wf.connect([(smooth, sinker, [('out_file', 'in_file')]),\n", - " (mcflirt, sinker, [('mean_img', 'mean_img'),\n", - " ('par_file', 'par_file')]),\n", - " ])\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a look at the `output` folder:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! tree /output/working_dir/preprocWF_output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This looks nice. It is what we asked it to do. But having a specific output folder for each individual output file might be suboptimal. So let's change the code above to save the output in one folder, which we will call `'preproc'`.\n", - "\n", - "For this we can use the same code as above. We only have to change the connection part:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.connect([(smooth, sinker, [('out_file', 'preproc.@in_file')]),\n", - " (mcflirt, sinker, [('mean_img', 'preproc.@mean_img'),\n", - " ('par_file', 'preproc.@par_file')]),\n", - " ])\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a look at the new output folder structure:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! tree /output/working_dir/preprocWF_output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is already much better. But what if you want to rename the output files to represent something a bit readable. For this `DataSink` has the `substitution` input field.\n", - "\n", - "For example, let's assume we want to get rid of the string `'task-fingerfootlips'` and `'bold_mcf'` and that we want to rename the mean file, as well as adapt the file ending of the motion parameter file:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Define substitution strings\n", - "substitutions = [('_task-fingerfootlips', ''),\n", - " (\"_ses-test\", \"\"),\n", - " ('_bold_mcf', ''),\n", - " ('.nii.gz_mean_reg', '_mean'),\n", - " ('.nii.gz.par', '.par')]\n", - "\n", - "# Feed the substitution strings to the DataSink node\n", - "sinker.inputs.substitutions = substitutions\n", - "\n", - "# Run the workflow again with the substitutions in place\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's take a final look at the output folder:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! tree /output/working_dir/preprocWF_output" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Cool, much more clearly!" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_error_and_crashes.ipynb b/notebooks/basic_error_and_crashes.ipynb deleted file mode 100644 index 4587c11..0000000 --- a/notebooks/basic_error_and_crashes.ipynb +++ /dev/null @@ -1,698 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Errors and Crashes\n", - "\n", - "Probably the most important chapter in this section is about how to handle error and crashes. Because at the beginning you will run into a few.\n", - "\n", - "For example:\n", - "\n", - "1. You specified file names or paths that **don't exist**.\n", - "2. You try to give an interface a ``string`` as input, where a ``float`` value is expected or you try to specify a parameter that doesn't exist. Be sure to use the right **``input type``** and input name.\n", - "3. You wanted to give a list of inputs ``[func1.nii, func2.nii, func3.nii]`` to a node that only expects one input file . **``MapNode``** is your solution.\n", - "4. You wanted to run SPM's motion correction on compressed NIfTI files, i.e. ``*.nii.gz``? **SPM** cannot handle that. Nipype's **``Gunzip``** interface can help.\n", - "5. You haven't set up all necessary **environment variables**. Nipype for example doesn't find your MATLAB or SPM version.\n", - "6. You **forget** to specify a **mandatory input** field.\n", - "7. You try to **connect** a node to an input field that another node is **already connected** to.\n", - "\n", - "**Important** note about ``crashfiles``. ``Crashfiles`` are only created when you run a workflow, not during building a workflow. If you have a typo in a folder path, because they didn't happen during runtime, but still during workflow building.\n", - "\n", - "We will start from removing old ``crashfiles``:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "%%bash\n", - "rm $(pwd)/crash-*" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 1: File doesn't exist\n", - "\n", - "When creating a new workflow, very often the initial errors are ``OSError``, meaning Nipype cannot find the right files. For example, let's try to run a workflow on ``sub-11``, that in our dataset doesn't exist." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Creating the crash" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype import SelectFiles, Node, Workflow\n", - "from os.path import abspath as opap\n", - "from nipype.interfaces.fsl import MCFLIRT, IsotropicSmooth\n", - "\n", - "# Create SelectFiles node\n", - "templates={'func': '{subject_id}/ses-test/func/{subject_id}_ses-test_task-fingerfootlips_bold.nii.gz'}\n", - "sf = Node(SelectFiles(templates),\n", - " name='selectfiles')\n", - "sf.inputs.base_directory = opap('/data/ds000114')\n", - "sf.inputs.subject_id = 'sub-11'\n", - "\n", - "# Create Motion Correction Node\n", - "mcflirt = Node(MCFLIRT(mean_vol=True,\n", - " save_plots=True),\n", - " name='mcflirt')\n", - "\n", - "# Create Smoothing node\n", - "smooth = Node(IsotropicSmooth(fwhm=4),\n", - " name='smooth')\n", - "\n", - "# Create a preprocessing workflow\n", - "wf = Workflow(name=\"preprocWF\")\n", - "wf.base_dir = 'working_dir'\n", - "\n", - "# Connect the three nodes to each other\n", - "wf.connect([(sf, mcflirt, [(\"func\", \"in_file\")]),\n", - " (mcflirt, smooth, [(\"out_file\", \"in_file\")])])\n", - "\n", - "# Let's run the workflow\n", - "try:\n", - " wf.run()\n", - "except(RuntimeError) as err:\n", - " print(\"RuntimeError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Investigating the crash\n", - "\n", - "Hidden, in the log file you can find the relevant information:\n", - "\n", - " OSError: No files were found matching func template: /data/ds000114/sub-11/ses-test/func/sub-11_ses-test_task-fingerfootlips_bold.nii.gz\n", - " Interface SelectFiles failed to run. \n", - "\n", - " 170904-05:48:13,727 workflow INFO:\n", - " ***********************************\n", - " 170904-05:48:13,728 workflow ERROR:\n", - " could not run node: preprocWF.selectfiles\n", - " 170904-05:48:13,730 workflow INFO:\n", - " crashfile: /repos/nipype_tutorial/notebooks/crash-20170904-054813-neuro-selectfiles-15f5400a-452e-4e0c-ae99-fc0d4b9a44f3.pklz\n", - " 170904-05:48:13,731 workflow INFO:\n", - " ***********************************\n", - " \n", - "This part tells you that it's an **``OSError``** and that it looked for the file **``/data/ds000114/sub-11/ses-test/func/sub-11_ses-test_task-fingerfootlips_bold.nii.gz``**.\n", - "\n", - "After the line ``***********************************``, you can additional see, that it's the node **``preprocWF.selectfiles``** that crasehd and that you can find a **``crashfile``** to this crash under **``/opt/tutorial/notebooks``**." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reading the ``crashfile``\n", - "\n", - "To get the full picture of the error, we can read the content of the ``crashfile`` (that has `pklz` format by default) with the ``bash`` command ``nipypecli crash``. We will get the same information as above, but additionally, we can also see directly the input values of the Node that crashed." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "!nipypecli crash $(pwd)/crash-*selectfiles-*.pklz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "`nipypecli` allows you to rerun the crashed node using an additional option `-r`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "!nipypecli crash -r $(pwd)/crash-*selectfiles-*.pklz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When running in terminal you can also try options that **enable the Python or Ipython debugger when re-executing: `-d` or `-i`**.\n", - "\n", - "**If you don't want to have an option to rerun the crashed workflow, you can change the format of crashfile to a text format.** You can either change this in a configuration file (you can read more [here](http://nipype.readthedocs.io/en/0.13.1/users/config_file.html#config-file)), or you can directly change the `wf.config` dictionary before running the workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "wf.config['execution']['crashfile_format'] = 'txt'\n", - "try:\n", - " wf.run()\n", - "except(RuntimeError) as err:\n", - " print(\"RuntimeError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now you should have a new text file with your crash report. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "!cat $(pwd)/crash-*selectfiles-*.txt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 2: Wrong Input Type or Typo in the parameter\n", - "\n", - "Very simple, if an interface expects a ``float`` as input, but you give it a ``string``, it will crash:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.fsl import IsotropicSmooth\n", - "try:\n", - " smooth = IsotropicSmooth(fwhm='4')\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will give you the error: **``TraitError``**``: The 'fwhm' trait of an IsotropicSmoothInput instance must be a float, but a value of '4' was specified.``\n", - "\n", - "To make sure that you are using the right input types, just check the ``help`` section of a given interface. There you can see **``fwhm: (a float)``**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "IsotropicSmooth.help()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In a similar way, you will also get an error message if the input type is correct but you have a type in the name:\n", - "\n", - " TraitError: The 'output_type' trait of an IsotropicSmoothInput instance must be u'NIFTI_PAIR' or u'NIFTI_PAIR_GZ' or u'NIFTI_GZ' or u'NIFTI', but a value of 'NIFTIiii' was specified." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.fsl import IsotropicSmooth\n", - "try:\n", - " smooth = IsotropicSmooth(output_type='NIFTIiii')\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 3: Giving an array as input where a single file is expected\n", - "\n", - "As you an see in the [MapNode](basic_mapnodes.ipynb) example, if you try to feed an array as an input into a field that only expects a single file, you will get a **``TraitError``**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.algorithms.misc import Gunzip\n", - "from nipype.pipeline.engine import Node\n", - "\n", - "files = ['/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz',\n", - " '/data/ds000114/sub-02/ses-test/func/sub-02_ses-test_task-fingerfootlips_bold.nii.gz']\n", - "\n", - "gunzip = Node(Gunzip(), name='gunzip',)\n", - "\n", - "try:\n", - " gunzip.inputs.in_file = files\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This can be solved by using a ``MapNode``:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.pipeline.engine import MapNode\n", - "gunzip = MapNode(Gunzip(), name='gunzip', iterfield=['in_file'])\n", - "gunzip.inputs.in_file = files" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, make sure that you specify files that actually exist, otherwise you will have a ``TraitError`` again:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "files = ['/data/ds000114/sub-01/func/sub-01_task-fingerfootlips_bold.nii.gz',\n", - " '/data/ds000114/sub-03/func/sub-03_task-fingerfootlips_bold.nii.gz']\n", - "\n", - "try:\n", - " gunzip.inputs.in_file = files\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**By the way, not that those crashes don't create a ``crashfile``, because they didn't happen during runtime, but still during workflow building.**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 4: SPM doesn't like ``*.nii.gz`` files\n", - "\n", - "SPM12 cannot handle compressed NIfTI files (``*nii.gz``). If you try to run the node nonetheless, it can give you different kind of problems:\n", - "\n", - "### SPM Problem 1 with ``*.nii.gz`` files\n", - "\n", - "SPM12 has a problem with handling ``*.nii.gz`` files. For it a compressed functional image has no temporal dimension and therefore seems to be just a 3D file. So if we try to run the ``Realign`` interface on a compressed file, we will get a **``TraitError``** error." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.spm import Smooth\n", - "\n", - "try:\n", - " smooth = Smooth(in_files='/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz')\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### SPM problem 2 with ``*.nii.gz`` files\n", - "\n", - "Sometimes **``TraitError``** can be more misleading." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.spm import Realign\n", - "\n", - "try:\n", - " realign = Realign(in_files='/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz')\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**This issue can be solved by unzipping the compressed NIfTI file before giving it as an input to an SPM node.** This can either be done by using the ``Gunzip`` interface from Nipype or even better, if the input is coming from a FSL interface, most of them have an input filed `output_type='NIFTI'`, that you can set to NIFIT." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 5: Nipype cannot find the right software\n", - "\n", - "Especially at the beginning, just after installation, you sometimes forgot to specify some environment variables. If you try to use an interface where the environment variables of the software are not specified, e.g. if you try to run:\n", - "\n", - "```python\n", - "from nipype.interfaces.freesurfer import MRIConvert\n", - "convert = MRIConvert(in_file='/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz',\n", - " out_type='nii')\n", - "```\n", - "\n", - "you migh get an errors, such as:\n", - "\n", - " IOError: command 'mri_convert' could not be found on host mnotter\n", - " Interface MRIConvert failed to run." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Or if you try to use SPM, but forgot to tell Nipype where to find it. If you forgot to tell the system where to find MATLAB (or MCR), than you will get same kind of error as above. But if you forgot to specify which SPM you want to use, you'll get the following **``RuntimeError``**:\n", - "\n", - " Standard error:\n", - " MATLAB code threw an exception:\n", - " SPM not in matlab path\n", - "\n", - "\n", - "You can solve this issue by specifying the path to your SPM version:\n", - "\n", - "```python\n", - "from nipype.interfaces.matlab import MatlabCommand\n", - "MatlabCommand.set_default_paths('/usr/local/MATLAB/R2017a/toolbox/spm12')\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 6: You forget mandatory inputs or use input fields that don't exist\n", - "\n", - "One of the simpler errors are the ones connected to input and output fields.\n", - "\n", - "### Forgetting mandatory input fields\n", - "\n", - "Let's see what happens if you forget a **``[Mandatory]``** input field." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.spm import Realign\n", - "realign = Realign(register_to_mean=True)\n", - "\n", - "try:\n", - " realign.run()\n", - "except(ValueError) as err:\n", - " print(\"ValueError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This gives you the error:\n", - "\n", - " ValueError: Realign requires a value for input 'in_files'. For a list of required inputs, see Realign.help()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As described by the error text, if we use the ``help()`` function, we can actually see, which inputs are mandatory and which are optional." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "realign.help()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Using input fields that don't exist\n", - "\n", - "Let's see what happens if we try to specify a parameter that doesn't exist as an input field:\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype.interfaces.afni import Despike\n", - "\n", - "try:\n", - " despike = Despike(in_file='/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz',\n", - " output_type='NIFTI')\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This results in the **``TraitError``**:\n", - "\n", - " TraitError: Cannot set the undefined 'output_type' attribute of a 'DespikeInputSpec' object.\n", - "\n", - "So what went wrong? If you use the ``help()`` function, you will see that the correct input filed is called **``outputtype``** and not **``output_type``**." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example Crash 7: Trying to connect a node to an input field that is already occupied\n", - "\n", - "Sometimes when you build a new workflow, you might forget that an output field was already connected and you try to connect a new node to the already occupied field.\n", - "\n", - "First, let's create a simple workflow:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "from nipype import SelectFiles, Node, Workflow\n", - "from os.path import abspath as opap\n", - "from nipype.interfaces.fsl import MCFLIRT, IsotropicSmooth\n", - "\n", - "# Create SelectFiles node\n", - "templates={'func': '{subject_id}/func/{subject_id}_task-fingerfootlips_bold.nii.gz'}\n", - "sf = Node(SelectFiles(templates),\n", - " name='selectfiles')\n", - "sf.inputs.base_directory = opap('/data/ds000114')\n", - "sf.inputs.subject_id = 'sub-01'\n", - "\n", - "# Create Motion Correction Node\n", - "mcflirt = Node(MCFLIRT(mean_vol=True,\n", - " save_plots=True),\n", - " name='mcflirt')\n", - "\n", - "# Create Smoothing node\n", - "smooth = Node(IsotropicSmooth(fwhm=4),\n", - " name='smooth')\n", - "\n", - "# Create a preprocessing workflow\n", - "wf = Workflow(name=\"preprocWF\")\n", - "wf.base_dir = 'working_dir'\n", - "\n", - "# Connect the three nodes to each other\n", - "wf.connect([(sf, mcflirt, [(\"func\", \"in_file\")]),\n", - " (mcflirt, smooth, [(\"out_file\", \"in_file\")])])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's create a new node and connect it to the already occupied input field ``in_file`` of the ``smooth`` node:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [ - "# Create a new node\n", - "mcflirt_NEW = Node(MCFLIRT(mean_vol=True),\n", - " name='mcflirt_NEW')\n", - "\n", - "# Connect it to an already connected input field\n", - "try:\n", - " wf.connect([(mcflirt_NEW, smooth, [(\"out_file\", \"in_file\")])])\n", - "except(Exception) as err:\n", - " print(\"Exception:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will lead to the error:\n", - "\n", - "```python\n", - "Exception: \n", - "Trying to connect preprocWF.mcflirt_NEW:out_file to preprocWF.smooth:in_file but input 'in_file' of node 'preprocWF.smooth' is already connected.\n", - "```" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_function_nodes.ipynb b/notebooks/basic_function_nodes.ipynb deleted file mode 100644 index dda1f42..0000000 --- a/notebooks/basic_function_nodes.ipynb +++ /dev/null @@ -1,170 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Function Node\n", - "\n", - "Satra once called the `Function` module, the \"do anything you want card\". Which is a perfect description. Because it allows you to put any code you want into an empty node, which you than can put in your workflow exactly where it needs to be.\n", - "\n", - "You might have already seen the `Function` module in the [example section in the Node tutorial](basic_nodes.ipynb#Example-of-a-simple-node). Let's take a closer look at it again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import Node and Function module\n", - "from nipype import Node, Function\n", - "\n", - "# Create a small example function\n", - "def add_two(x_input):\n", - " return x_input + 2\n", - "\n", - "# Create Node\n", - "addtwo = Node(Function(input_names=[\"x_input\"],\n", - " output_names=[\"val_output\"],\n", - " function=add_two),\n", - " name='add_node')\n", - "\n", - "addtwo.inputs.x_input =4\n", - "addtwo.run()\n", - "addtwo.result.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Trap 1\n", - "\n", - "There are only two traps that you should be aware when you're using the `Function` module. The first one is about naming the input variables. The variable name for the node input has to be the exactly the same name as the function input parameter, in this case this is `x_input`. \n", - "\n", - "Otherwise you get the following error:\n", - "\n", - " TypeError: add_two() got an unexpected keyword argument 'x_input'\n", - " Interface Function failed to run.\n", - " \n", - "**Note** that in the current version of `Nipype` you don't have to provide `input_names` as an argument of `Function`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Trap 2\n", - "\n", - "If you want to use another module inside a function, you have to import it again inside the function. Let's take a look at the following example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Node, Function\n", - "\n", - "# Create the Function object\n", - "def get_random_array(array_shape):\n", - "\n", - " # Import random function\n", - " from numpy.random import random\n", - " \n", - " return random(array_shape)\n", - "\n", - "# Create Function Node that executes get_random_array\n", - "rndArray = Node(Function(input_names=[\"array_shape\"],\n", - " output_names=[\"random_array\"],\n", - " function=get_random_array),\n", - " name='rndArray_node')\n", - "\n", - "# Specify the array_shape of the random array\n", - "rndArray.inputs.array_shape = (3, 3)\n", - "\n", - "# Run node\n", - "rndArray.run()\n", - "\n", - "# Print output\n", - "print(rndArray.result.outputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's see what happens if we move the import of `random` outside the scope of `get_random_array`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Node, Function\n", - "\n", - "# Import random function\n", - "from numpy.random import random\n", - "\n", - "\n", - "# Create the Function object\n", - "def get_random_array(array_shape):\n", - " \n", - " return random(array_shape)\n", - "\n", - "# Create Function Node that executes get_random_array\n", - "rndArray = Node(Function(input_names=[\"array_shape\"],\n", - " output_names=[\"random_array\"],\n", - " function=get_random_array),\n", - " name='rndArray_node')\n", - "\n", - "# Specify the array_shape of the random array\n", - "rndArray.inputs.array_shape = (3, 3)\n", - "\n", - "# Run node\n", - "try:\n", - " rndArray.run()\n", - "except(NameError) as err:\n", - " print(\"NameError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, if we don't import `random` inside the scope of the function, we receive the following error:\n", - "\n", - " NameError: global name 'random' is not defined\n", - " Interface Function failed to run. " - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_graph_visualization.ipynb b/notebooks/basic_graph_visualization.ipynb deleted file mode 100644 index 7e68b58..0000000 --- a/notebooks/basic_graph_visualization.ipynb +++ /dev/null @@ -1,296 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Graph Visualization\n", - "\n", - "We've learned from the [Workflow](./basic_workflow.ipynb) tutorial that every Nipype workflow is a directed acyclic graphs. Some workflow structures are easy to understand directly from the script and some others are too complex for that. Luckily, there is the ``write_graph`` method!\n", - "\n", - "## ``write_graph``\n", - "\n", - "**``write_graph``** allows us to visualize any workflow in five different ways:\n", - "\n", - "- **``orig``** - creates a top level graph without expanding internal workflow nodes\n", - "- **``flat``** - expands workflow nodes recursively\n", - "- **``hierarchical``** - expands workflow nodes recursively with a notion on hierarchy\n", - "- **``colored``** - expands workflow nodes recursively with a notion on hierarchy in color\n", - "- **``exec``** - expands workflows to depict iterables\n", - "\n", - "Which graph visualization should be used is chosen by the **``graph2use``** parameter.\n", - "\n", - "Additionally, we can also choose the format of the output file (png or svg) with the **``format``** parameter.\n", - "\n", - "A third parameter, called **``simple_form``** can be used to specify if the node names used in the graph should be of the form ***``nodename (package)``*** or ***``nodename.Class.package``***." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Preparation\n", - "\n", - "Instead of creating a new workflow from scratch, let's just import one from the Nipype workflow library." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import the function to create an spm fmri preprocessing workflow\n", - "from nipype.workflows.fmri.spm import create_spm_preproc\n", - "\n", - "# Create the workflow object\n", - "spmflow = create_spm_preproc()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For a reason that will become clearer under the ``exec`` visualization, let's add an iternode at the beginning of the ``spmflow`` and connect them together under a new workflow, called ``metaflow``. The iternode will cause the workflow to be executed three times, once with the ``fwhm`` value set to 4, once set to 6 and once set to 8. For more about this see the [Iteration](./basic_iteration.ipynb) tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import relevant modules\n", - "from nipype import IdentityInterface, Node, Workflow\n", - "\n", - "# Create an iternode that iterates over three different fwhm values\n", - "inputNode = Node(IdentityInterface(fields=['fwhm']), name='iternode')\n", - "inputNode.iterables = ('fwhm', [4, 6, 8])\n", - "\n", - "# Connect inputNode and spmflow in a workflow\n", - "metaflow = Workflow(name='metaflow')\n", - "metaflow.connect(inputNode, \"fwhm\", spmflow, \"inputspec.fwhm\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``orig`` graph\n", - "\n", - "This visualization gives us a basic overview of all the nodes and internal workflows in a workflow and shows in a simple way the dependencies between them." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type orig\n", - "spmflow.write_graph(graph2use='orig', dotfilename='./graph_orig.dot')\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_orig.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``flat`` graph\n", - "\n", - "This visualization gives us already more information about the internal structure of the ``spmflow`` workflow. As we can, the internal workflow ``getmask`` from the ``orig`` visualization above was replaced by the individual nodes contained in this internal workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type flat\n", - "spmflow.write_graph(graph2use='flat', dotfilename='./graph_flat.dot')\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_flat.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``hierarchical`` graph\n", - "\n", - "To better appreciate this visualization, let's look at the ``metaflow`` workflow that has one hierarchical level more than the ``spmflow``.\n", - "\n", - "As you can see, this visualization makes it much clearer which elements of a workflow are nodes and which ones are internal workflows. Also, each connection is shown as an individual arrow, and not just represented by one single arrow between two nodes. Additionally, iternodes and mapnodes are visualized differently than normal nodes to make them pop out more." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type hierarchical\n", - "metaflow.write_graph(graph2use='hierarchical', dotfilename='./graph_hierarchical.dot')\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_hierarchical.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``colored`` graph\n", - "\n", - "This visualization is almost the same as the ``hierarchical`` above. The only difference is that individual nodes and different hierarchy levels are colored coded differently." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type colored\n", - "metaflow.write_graph(graph2use='colored', dotfilename='./graph_colored.dot')\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_colored.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``exec`` graph\n", - "\n", - "This visualization is the most different from the rest. Like the ``flat`` visualization, it depicts all individual nodes. But additionally, it drops the ``utility`` nodes from the workflow and expands workflows to depict iterables (can be seen in the ``detailed_graph`` visualization further down below)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type exec\n", - "metaflow.write_graph(graph2use='exec', dotfilename='./graph_exec.dot')\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_exec.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Detailed graphs\n", - "\n", - "The ``orig``, ``flat`` and ``exec`` visualization also create a **detailed graph** whenever ``write_graph`` is executed. A detailed graph shows a node with not just the node name, but also with all its input and output parameters.\n", - "\n", - "## detailed ``flat`` graph\n", - "\n", - "For example, the detailed graph of the ``flat`` graph looks as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import Image\n", - "Image(filename=\"graph_flat_detailed.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Such a visualization might be more complicated to read, but it gives you complete overview of a workflow and all its components." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## detailed ``exec`` graph\n", - "\n", - "Now, if we look at the detailed graph of the ``exec`` visualization, we can see where the iteration takes place:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import Image\n", - "Image(filename=\"graph_exec_detailed.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the middle left of the figure we have three ``preproc.smooth`` nodes of the ``spm`` interface with the names \"a0\", \"a1\" and \"a2\". Those represent the three smoothing nodes with the ``fwhm`` parameter set to 4, 6 and 8. Now if those nodes would be connected to another workflow, this would mean that the workflow that follows would be depicted three times, each time for another input coming from the ``preproc.smooth`` node.\n", - "\n", - "Therefore, the **detailed ``exec``** visualization makes all individual execution elements very clear and allows it to see which elements can be executed in parallel." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``simple_form``\n", - "\n", - "Last but not least is the third ``write_graph`` argument, ``simple_form``. If this parameter is set to ``False``, this means that the node names in the visualization will be written in the form of ***``nodename.Class.package``***, instead of ***``nodename (package)``***. For example, let's look at the ``orig``visualization with ``simple_form`` set to ``False``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Write graph of type orig\n", - "spmflow.write_graph(graph2use='orig', dotfilename='./graph_orig_notSimple.dot', simple_form=False)\n", - "\n", - "# Visulaize graph\n", - "from IPython.display import Image\n", - "Image(filename=\"graph_orig_notSimple.dot.png\")" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_import_workflows.ipynb b/notebooks/basic_import_workflows.ipynb deleted file mode 100644 index 109a1c5..0000000 --- a/notebooks/basic_import_workflows.ipynb +++ /dev/null @@ -1,283 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Reusable workflows\n", - "\n", - "Nipype doesn't just allow you to create your own workflows. It also already comes with predefined workflows, developed by the community, for the community. For a full list of all workflows, look under the [Workflows](http://nipype.readthedocs.io/en/latest/documentation.html) section of the main homepage.\n", - "\n", - "But to give you a short overview, there are workflows about:\n", - "\n", - "**Functional MRI** workflows:\n", - " - from **``fsl``** about ``resting state``, ``fixed_effects``, ``modelfit``, ``featreg``, ``susan_smooth`` and many more\n", - " - from **``spm``** about ``DARTEL`` and ``VBM``\n", - "\n", - "**Structural MRI** workflows\n", - " - from **``ants``** about ``ANTSBuildTemplate`` and ``antsRegistrationBuildTemplate``\n", - " - from **``freesurfer``** about ``bem``, ``recon`` and tessellation\n", - " \n", - "**Diffusion** workflows:\n", - " - from **``camino``** about ``connectivity_mapping``, ``diffusion`` and ``group_connectivity``\n", - " - from **``dipy``** about ``denoise``\n", - " - from **``fsl``** about ``artifacts``, ``dti``, ``epi``, ``tbss`` and many more\n", - " - from **``mrtrix``** about ``connectivity_mapping``, ``diffusion`` and ``group_connectivity``" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to load a workflow from Nipype\n", - "\n", - "Let's consider the example of a functional MRI workflow, that uses FSL's Susan algorithm to smooth some data. To load such a workflow, we only need the following command:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.workflows.fmri.fsl.preprocess import create_susan_smooth\n", - "smoothwf = create_susan_smooth()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once a workflow is created, we need to make sure that the mandatory inputs are specified. To see which inputs we have to define, we can use the command:\n", - "\n", - "``create_susan_smooth?``\n", - "\n", - "Which gives us the output:\n", - "\n", - "```\n", - "Create a SUSAN smoothing workflow\n", - "\n", - "Parameters\n", - "----------\n", - "Inputs:\n", - " inputnode.in_files : functional runs (filename or list of filenames)\n", - " inputnode.fwhm : fwhm for smoothing with SUSAN\n", - " inputnode.mask_file : mask used for estimating SUSAN thresholds (but not for smoothing)\n", - "\n", - "Outputs:\n", - " outputnode.smoothed_files : functional runs (filename or list of filenames)\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we can see, we also need a mask file. For the sake of convenience, let's take the mean image of a functional image and threshold it at the 50% percentil:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!fslmaths /data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz \\\n", - " -Tmean -thrP 50 /output/sub-01_ses-test_task-fingerfootlips_mask.nii.gz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we're ready to finish up our smooth workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "smoothwf.inputs.inputnode.in_files = '/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz'\n", - "smoothwf.inputs.inputnode.mask_file = '/output/sub-01_ses-test_task-fingerfootlips_mask.nii.gz'\n", - "smoothwf.inputs.inputnode.fwhm = 4\n", - "smoothwf.base_dir = '/output'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we run it, let's visualize the graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pylab inline\n", - "from IPython.display import Image\n", - "smoothwf.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "Image(filename='/output/susan_smooth/graph.dot.png')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we're ready to go:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "smoothwf.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once it's finished, we can look at the results:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!fslmaths /data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz -Tmean fmean.nii.gz\n", - "!fslmaths /output/susan_smooth/smooth/mapflow/_smooth0/sub-01_ses-test_task-fingerfootlips_bold_smooth.nii.gz \\\n", - " -Tmean smean.nii.gz\n", - "\n", - "from nilearn import image, plotting\n", - "plotting.plot_epi(\n", - " 'fmean.nii.gz', title=\"mean (no smoothing)\", display_mode='z',\n", - " cmap='gray', cut_coords=(-45, -30, -15, 0, 15))\n", - "plotting.plot_epi(\n", - " 'smean.nii.gz', title=\"mean (susan smoothed)\", display_mode='z',\n", - " cmap='gray', cut_coords=(-45, -30, -15, 0, 15))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to change node parameters from existing workflows\n", - "\n", - "What if we want to change certain parameters of a loaded or already existing workflow? Let's first get the names of all the nodes in the workflow:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(smoothwf.list_node_names())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Ok. Hmm, what if we want to change the 'median' node, from 50% to 99%? For this, we first need to get the node." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "median = smoothwf.get_node('median')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have the node, we can change it's value as we want:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "median.inputs.op_string = '-k %s -p 99'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can run the workflow again..." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "smoothwf.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now the output is:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!fslmaths /output/susan_smooth/smooth/mapflow/_smooth0/sub-01_ses-test_task-fingerfootlips_bold_smooth.nii.gz \\\n", - " -Tmean mmean.nii.gz\n", - "\n", - "from nilearn import image, plotting\n", - "plotting.plot_epi(\n", - " 'smean.nii.gz', title=\"mean (susan smooth)\", display_mode='z',\n", - " cmap='gray', cut_coords=(-45, -30, -15, 0, 15))\n", - "plotting.plot_epi(\n", - " 'mmean.nii.gz', title=\"mean (smoothed, median=99%)\", display_mode='z',\n", - " cmap='gray', cut_coords=(-45, -30, -15, 0, 15))" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_interfaces.ipynb b/notebooks/basic_interfaces.ipynb deleted file mode 100644 index 69fe7cd..0000000 --- a/notebooks/basic_interfaces.ipynb +++ /dev/null @@ -1,595 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Interfaces\n", - "\n", - "In Nipype, 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. Such an interface knows what sort of options an external program has and how to execute it.\n", - "\n", - "To illustrate why interfaces are so useful, let's have a look at the brain extraction algorithm [BET](http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/BET) from FSL. Once in its original framework and once in the Nipype framework." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## BET in the origional framework\n", - "\n", - "Let's take a look at one of the T1 images we have in our dataset on which we want to run BET." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pylab inline\n", - "from nilearn.plotting import plot_anat\n", - "plot_anat('/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz', title='original',\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In its simplest form, you can run BET by just specifying the input image and tell it what to name the output image:\n", - "\n", - " bet " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "FILENAME=/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w\n", - "\n", - "bet ${FILENAME}.nii.gz /output/sub-01_ses-test_T1w_bet.nii.gz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's take a look at the results:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_anat('/output/sub-01_ses-test_T1w_bet.nii.gz', title='original',\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Perfect! Exactly what we want. Hmm... what else could we want from BET? Well, it's actually a fairly complicated program. As is the case for all FSL binaries, just call it with no arguments to see all its options." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "bet" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that BET can also return a binary brain mask as a result of the skull-strip, which can be useful for masking our GLM analyses (among other things). Let's run it again including that option and see the result." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "\n", - "FILENAME=/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w\n", - "\n", - "bet ${FILENAME}.nii.gz /output/sub-01_ses-test_T1w_bet.nii.gz -m" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_anat('/output/sub-01_ses-test_T1w_bet_mask.nii.gz', title='original',\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's look at the BET interface in Nipype. First, we have to import it." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## BET in the Nipype framework\n", - "\n", - "So how can we run BET in the Nipype framework?\n", - "\n", - "First things first, we need to import the ``BET`` class from Nipype's ``interfaces`` module:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.fsl import BET" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now that we have the BET function accessible, we just have to specify the input and output file. And finally we have to run the command. So exactly like in the original framework." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "skullstrip = BET()\n", - "skullstrip.inputs.in_file = \"/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz\"\n", - "skullstrip.inputs.out_file = \"/output/T1w_nipype_bet.nii.gz\"\n", - "res = skullstrip.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we now look at the results from Nipype, we see that it is exactly the same as before." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_anat('/output/T1w_nipype_bet.nii.gz', title='original',\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is not surprising, because Nipype used exactly the same bash code that we were using in the original framework example above. To verify this, we can call the ``cmdline`` function of the constructed BET instance." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(skullstrip.cmdline)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Another way to set the inputs on an interface object is to use them as keyword arguments when you construct the interface instance. Let's write the Nipype code from above in this way, but let's also add the option to create a brain mask." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "skullstrip = BET(in_file=\"/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/T1w_nipype_bet.nii.gz\",\n", - " mask=True)\n", - "res = skullstrip.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now if we plot this, we see again that this worked exactly as before. No surprise there." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_anat('/output/T1w_nipype_bet_mask.nii.gz', title='original',\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Help Function\n", - "\n", - "But how did we know what the names of the input parameters are? In the original framework we were able to just run ``BET``, without any additional parameters to get an information page. In the Nipype framework we can achieve the same thing by using the ``help()`` function on an interface class. For the BET example, this is:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "BET.help()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, we get three different informations. ***First***, a general explanation of the class.\n", - "\n", - " Wraps command **bet**\n", - "\n", - " Use FSL BET command for skull stripping.\n", - "\n", - " For complete details, see the `BET Documentation.\n", - " `_\n", - "\n", - " Examples\n", - " --------\n", - " >>> from nipype.interfaces import fsl\n", - " >>> from nipype.testing import example_data\n", - " >>> btr = fsl.BET()\n", - " >>> btr.inputs.in_file = example_data('structural.nii')\n", - " >>> btr.inputs.frac = 0.7\n", - " >>> res = btr.run() # doctest: +SKIP\n", - "\n", - "***Second***, a list of all possible input parameters.\n", - "\n", - " Inputs::\n", - "\n", - " [Mandatory]\n", - " in_file: (an existing file name)\n", - " input file to skull strip\n", - " flag: %s, position: 0\n", - "\n", - " [Optional]\n", - " args: (a string)\n", - " Additional parameters to the command\n", - " flag: %s\n", - " center: (a list of at most 3 items which are an integer (int or\n", - " long))\n", - " center of gravity in voxels\n", - " flag: -c %s\n", - " environ: (a dictionary with keys which are a value of type 'str' and\n", - " with values which are a value of type 'str', nipype default value:\n", - " {})\n", - " Environment variables\n", - " frac: (a float)\n", - " fractional intensity threshold\n", - " flag: -f %.2f\n", - " functional: (a boolean)\n", - " apply to 4D fMRI data\n", - " flag: -F\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " ignore_exception: (a boolean, nipype default value: False)\n", - " Print an error message instead of throwing an exception in case the\n", - " interface fails to run\n", - " mask: (a boolean)\n", - " create binary mask image\n", - " flag: -m\n", - " mesh: (a boolean)\n", - " generate a vtk mesh brain surface\n", - " flag: -e\n", - " no_output: (a boolean)\n", - " Don't generate segmented output\n", - " flag: -n\n", - " out_file: (a file name)\n", - " name of output skull stripped image\n", - " flag: %s, position: 1\n", - " outline: (a boolean)\n", - " create surface outline image\n", - " flag: -o\n", - " output_type: ('NIFTI_PAIR' or 'NIFTI_PAIR_GZ' or 'NIFTI_GZ' or\n", - " 'NIFTI')\n", - " FSL output type\n", - " padding: (a boolean)\n", - " improve BET if FOV is very small in Z (by temporarily padding end\n", - " slices)\n", - " flag: -Z\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " radius: (an integer (int or long))\n", - " head radius\n", - " flag: -r %d\n", - " reduce_bias: (a boolean)\n", - " bias field and neck cleanup\n", - " flag: -B\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " remove_eyes: (a boolean)\n", - " eye & optic nerve cleanup (can be useful in SIENA)\n", - " flag: -S\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " robust: (a boolean)\n", - " robust brain centre estimation (iterates BET several times)\n", - " flag: -R\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " skull: (a boolean)\n", - " create skull image\n", - " flag: -s\n", - " surfaces: (a boolean)\n", - " run bet2 and then betsurf to get additional skull and scalp surfaces\n", - " (includes registrations)\n", - " flag: -A\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " t2_guided: (a file name)\n", - " as with creating surfaces, when also feeding in non-brain-extracted\n", - " T2 (includes registrations)\n", - " flag: -A2 %s\n", - " mutually_exclusive: functional, reduce_bias, robust, padding,\n", - " remove_eyes, surfaces, t2_guided\n", - " terminal_output: ('stream' or 'allatonce' or 'file' or 'none')\n", - " Control terminal output: `stream` - displays to terminal immediately\n", - " (default), `allatonce` - waits till command is finished to display\n", - " output, `file` - writes output to file, `none` - output is ignored\n", - " threshold: (a boolean)\n", - " apply thresholding to segmented brain image and mask\n", - " flag: -t\n", - " vertical_gradient: (a float)\n", - " vertical gradient in fractional intensity threshold (-1, 1)\n", - " flag: -g %.2f\n", - "\n", - "And ***third***, a list of all possible output parameters.\n", - "\n", - " Outputs::\n", - "\n", - " inskull_mask_file: (a file name)\n", - " path/name of inskull mask (if generated)\n", - " inskull_mesh_file: (a file name)\n", - " path/name of inskull mesh outline (if generated)\n", - " mask_file: (a file name)\n", - " path/name of binary brain mask (if generated)\n", - " meshfile: (a file name)\n", - " path/name of vtk mesh file (if generated)\n", - " out_file: (a file name)\n", - " path/name of skullstripped file (if generated)\n", - " outline_file: (a file name)\n", - " path/name of outline file (if generated)\n", - " outskin_mask_file: (a file name)\n", - " path/name of outskin mask (if generated)\n", - " outskin_mesh_file: (a file name)\n", - " path/name of outskin mesh outline (if generated)\n", - " outskull_mask_file: (a file name)\n", - " path/name of outskull mask (if generated)\n", - " outskull_mesh_file: (a file name)\n", - " path/name of outskull mesh outline (if generated)\n", - " skull_mask_file: (a file name)\n", - " path/name of skull mask (if generated)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So here we see that Nipype also has output parameters. This is very practical. Because instead of typing the full path name to the mask volume, we can also more directly use the ``mask_file`` parameter." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(res.outputs.mask_file)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Interface errors" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To execute any interface class we use the ``run`` method on that object. For FSL, Freesurfer, and other programs, this will just make a system call with the command line we saw above. For MATLAB-based programs like SPM, it will actually generate a ``.m`` file and run a MATLAB process to execute it. All of that is handled in the background.\n", - "\n", - "But what happens if we didn't specify all necessary inputs? For instance, you need to give BET a file to work on. If you try and run it without setting the input ``in_file``, you'll get a Python exception before anything actually gets executed:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "skullstrip2 = BET()\n", - "try:\n", - " skullstrip2.run()\n", - "except(ValueError) as err:\n", - " print(\"ValueError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Nipype also knows some things about what sort of values should get passed to the inputs, and will raise (hopefully) informative exceptions when they are violated -- before anything gets processed. For example, BET just lets you say \"create a mask,\" it doesn't let you name it. You may forget this, and try to give it a name. In this case, Nipype will raise a ``TraitError`` telling you what you did wrong:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " skullstrip.inputs.mask = \"mask_file.nii\"\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Additionally, Nipype knows that, for inputs corresponding to files you are going to process, they should exist in your file system. If you pass a string that doesn't correspond to an existing file, it will error and let you know:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " skullstrip.inputs.in_file = \"/data/oops_a_typo.nii\"\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It turns out that for default output files, you don't even need to specify a name. Nipype will know what files are going to be created and will generate a name for you:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "skullstrip = BET(in_file=\"/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz\")\n", - "print(skullstrip.cmdline)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that it is going to write the output file to the local directory.\n", - "\n", - "What if you just ran this interface and wanted to know what it called the file that was produced? As you might have noticed before, calling the ``run`` method returned an object called ``InterfaceResult`` that we saved under the variable ``res``. Let's inspect that object:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res = skullstrip.run()\n", - "print(res.outputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that four possible files can be generated by BET. Here we ran it in the most simple way possible, so it just generated an ``out_file``, which is the skull-stripped image. Let's see what happens when we generate a mask. By the way, you can also set inputs at runtime by including them as arguments to the ``run`` method:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res2 = skullstrip.run(mask=True)\n", - "print(res2.outputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Nipype knows that if you ask for a mask, BET is going to generate it in a particular way and makes that information available to you." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Why this is amazing!\n", - "\n", - "**A major motivating objective for Nipype is to streamline the integration of different analysis packages, so that you can use the algorithms you feel are best suited to your particular problem.**\n", - "\n", - "Say that you want to use BET, as SPM does not offer a way to create an explicit mask from functional data, but that otherwise you want your processing to occur in SPM. Although possible to do this in a MATLAB script, it might not be all that clean, particularly if you want your skullstrip to happen in the middle of your workflow (for instance, after realignment). Nipype provides a unified representation of interfaces across analysis packages.\n", - "\n", - "For more on this, check out the [Interfaces](basic_interfaces.ipynb) and the [Workflow](basic_workflow.ipynb) tutorial." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_interfaces_caching.ipynb b/notebooks/basic_interfaces_caching.ipynb deleted file mode 100644 index 006bf2d..0000000 --- a/notebooks/basic_interfaces_caching.ipynb +++ /dev/null @@ -1,170 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Memory caching\n", - "\n", - "In [Workflow notebook](basic_worflow.ipynb) you learnt about ``Workflows`` that specify processing by an execution graph and offer efficient recomputing. However, sometimes you might want to use ``Interfaces`` that gives better control of the execution of each step and can be easily combine with any Python code. Unfortunately, ``Interfaces`` do not offer any caching and you always dully recompute your task. \n", - "\n", - "Solution to this problem can be a ``caching`` mechanism supported by Nipype. Nipype caching relies on the ``Memory`` class and creates an execution context that is bound to a disk cache.\n", - "When you instantiate the class you should provide ``base_dir`` (that has to be an existing directory) and additional subdirectory called ``nipype_mem`` will be automatically created. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "mkdir -p /output/workingdir_mem" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.caching import Memory\n", - "mem = Memory(base_dir='/output/workingdir_mem')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we want to ask for caching for the ``BET`` interface, we can use ``cache`` method that takes interfaces classes as an argument." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces import fsl\n", - "bet_mem = mem.cache(fsl.BET)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, ``bet_mem`` can be applied as a function with inputs of the ``BET`` interface as the function arguments. Those inputs are given as keyword arguments, bearing the same name as the name in the inputs specs of the interface." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "bet_mem(in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_brain.nii.gz\",\n", - " mask=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can seen ``bet`` command was run as expected. We can now check the content of caching file:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! ls -l /output/workingdir_mem/nipype_mem" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A special subdirectory for our interface has been created. Let's try to run this command again:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "bet_mem(in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_brain.nii.gz\",\n", - " mask=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, the ``bet`` command was not run, but precomputed outputs were collected!\n", - "\n", - "If you created cached results that you're not going reuse, you can use [Memory.clear_runs_since()](http://nipy.org/nipype/0.10.0/users/caching_tutorial.html#nipype.caching.Memory.clear_runs_since) to flush the cache. Note, that if you use the method without any argument it will remove results used before current date, so will keep the results we've just calculated, let's check:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mem.clear_runs_since()\n", - "bet_mem(in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_brain.nii.gz\",\n", - " mask=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, Nipype again collected the old results. If we want to remove everything, we have to put some future date:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mem.clear_runs_since(year=2020, month=1, day=1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also check [Memory.clear_runs_since()](http://nipy.org/nipype/0.10.0/users/caching_tutorial.html#nipype.caching.Memory.clear_runs_since)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_iteration.ipynb b/notebooks/basic_iteration.ipynb deleted file mode 100644 index c745637..0000000 --- a/notebooks/basic_iteration.ipynb +++ /dev/null @@ -1,350 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# Iterables\n", - "\n", - "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``**. \n", - "\n", - "The main homepage has a [nice section](http://nipype.readthedocs.io/en/latest/users/mapnode_and_iterables.html) about ``MapNode`` and ``iterables`` if you want to learn more. Also, if you are interested in more advanced procedures, such as synchronizing multiple iterables or using conditional iterables, check out [synchronize and intersource](http://nipype.readthedocs.io/en/latest/users/joinnode_and_itersource.html#synchronize).\n", - "\n", - "For example, 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." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Node, Workflow\n", - "from nipype.interfaces.fsl import BET, IsotropicSmooth\n", - "\n", - "# Initiate a skull stripping Node with BET\n", - "skullstrip = Node(BET(mask=True,\n", - " in_file='/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz'),\n", - " name=\"skullstrip\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a smoothing Node with IsotropicSmooth" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "isosmooth = Node(IsotropicSmooth(), name='iso_smooth')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, to use ``iterables`` and therefore smooth with different ``fwhm`` is as simple as that:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "isosmooth.iterables = (\"fwhm\", [4, 8, 16])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And to wrap it up. We need to create a workflow, connect the nodes and finally, can run the workflow in parallel." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create the workflow\n", - "wf = Workflow(name=\"smoothflow\")\n", - "wf.base_dir = \"/output\"\n", - "wf.connect(skullstrip, 'out_file', isosmooth, 'in_file')\n", - "\n", - "# Run it in parallel (one core for each smoothing kernel)\n", - "wf.run('MultiProc', plugin_args={'n_procs': 3})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note**, that ``iterables`` is set on a specific node (``isosmooth`` in this case), but ``Workflow`` is needed to expend the graph to three subgraphs with three different versions of the ``isosmooth`` node.\n", - "\n", - "If we visualize the graph with ``exec``, we can see where the parallelization actually takes place." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Visualize the detailed graph\n", - "from IPython.display import Image\n", - "wf.write_graph(graph2use='exec', format='png', simple_form=True)\n", - "Image(filename='/output/smoothflow/graph_detailed.dot.png')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you look at the structure in the workflow directory, you can also see, that for each smoothing, a specific folder was created, i.e. ``_fwhm_16``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree /output/smoothflow -I '*txt|*pklz|report*|*.json|*js|*.dot|*.html'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's visualize the results!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pylab inline\n", - "from nilearn import plotting\n", - "plotting.plot_anat(\n", - " '/data/ds000114/sub-01/ses-test/anat/sub-01_ses-test_T1w.nii.gz', title='original',\n", - " display_mode='z', cut_coords=(-50, -35, -20, -5), annotate=False)\n", - "plotting.plot_anat(\n", - " '/output/smoothflow/skullstrip/sub-01_ses-test_T1w_brain.nii.gz', title='skullstripped',\n", - " display_mode='z', cut_coords=(-50, -35, -20, -5), annotate=False)\n", - "plotting.plot_anat(\n", - " '/output/smoothflow/_fwhm_4/iso_smooth/sub-01_ses-test_T1w_brain_smooth.nii.gz', title='FWHM=4',\n", - " display_mode='z', cut_coords=(-50, -35, -20, -5), annotate=False)\n", - "plotting.plot_anat(\n", - " '/output/smoothflow/_fwhm_8/iso_smooth/sub-01_ses-test_T1w_brain_smooth.nii.gz', title='FWHM=8',\n", - " display_mode='z', cut_coords=(-50, -35, -20, -5), annotate=False)\n", - "plotting.plot_anat(\n", - " '/output/smoothflow/_fwhm_16/iso_smooth/sub-01_ses-test_T1w_brain_smooth.nii.gz', title='FWHM=16',\n", - " display_mode='z', cut_coords=(-50, -35, -20, -5), annotate=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# ``IdentityInterface`` (special use case of ``iterables``)\n", - "\n", - "We often want to start our worflow from creating subgraphs, e.g. for running preprocessing for all subjects. We can easily do it with setting ``iterables`` on the ``IdentityInterface``. The ``IdentityInterface`` interface allows you to create ``Nodes`` that does simple identity mapping, i.e. ``Nodes`` that only work on parameters/strings.\n", - "\n", - "\n", - "For example you want to start your workflow from collecting anatomical files for 5 subjects." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# First, let's specify the list of subjects\n", - "subject_list = ['sub-01', 'sub-02', 'sub-03', 'sub-04', 'sub-05']" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we can create the IdentityInterface Node" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import IdentityInterface\n", - "infosource = Node(IdentityInterface(fields=['subject_id']),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('subject_id', subject_list)]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's it. Now, we can connect the output fields of this ``infosource`` node to ``SelectFiles`` and ``DataSink`` nodes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import join as opj\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "\n", - "anat_file = opj('derivatives', 'fmriprep', '{subject_id}', 'anat', '{subject_id}_t1w_preproc.nii.gz')\n", - "templates = {'anat': anat_file}\n", - "\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory='/data/ds000114'),\n", - " name=\"selectfiles\")\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=\"/output\",\n", - " container=\"datasink\"),\n", - " name=\"datasink\")\n", - "\n", - "wf_sub = Workflow(name=\"choosing_subjects\")\n", - "wf_sub.connect(infosource, \"subject_id\", selectfiles, \"subject_id\")\n", - "wf_sub.connect(selectfiles, \"anat\", datasink, \"anat_files\")\n", - "wf_sub.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we can check that five anatomicl images are in ``anat_files`` directory:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "! ls -l /output/datasink/anat_files/" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This was just a simple example of using ``IdentityInterface``, but a complete example of preprocessing workflow you can find in [Preprocessing Example](example_preprocessing.ipynb))." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Exercise 1\n", - "Create a workflow to calculate a various powers of ``2`` using two nodes, one for ``IdentityInterface`` with ``iterables``, and one for ``Function`` interface to calculate power of ``2``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden", - "solution2_first": true - }, - "outputs": [], - "source": [ - "# write your solution here" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "# lets start from the Identity node\n", - "from nipype import Function, Node, Workflow\n", - "from nipype.interfaces.utility import IdentityInterface\n", - "\n", - "iden = Node(IdentityInterface(fields=['number']), name=\"identity\")\n", - "iden.iterables = [(\"number\", range(8))]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "# the second node should use the Function interface\n", - "def power_of_two(n):\n", - " return 2**n\n", - "\n", - "# Create Node\n", - "power = Node(Function(input_names=[\"n\"],\n", - " output_names=[\"pow\"],\n", - " function=power_of_two),\n", - " name='power')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "#and now the workflow\n", - "wf_ex1 = Workflow(name=\"exercise1\")\n", - "wf_ex1.connect(iden, \"number\", power, \"n\")\n", - "res_ex1 = wf_ex1.run()\n", - "\n", - "# we can print the results\n", - "for i in range(8):\n", - " print(list(res_ex1.nodes())[i].result.outputs)" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_joinnodes.ipynb b/notebooks/basic_joinnodes.ipynb deleted file mode 100644 index 0b73983..0000000 --- a/notebooks/basic_joinnodes.ipynb +++ /dev/null @@ -1,250 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# JoinNode\n", - "\n", - "JoinNode have the opposite effect of [iterables](basic_iteration.ipynb). Where `iterables` split up the execution workflow into many different branches, a JoinNode merges them back into on node. For a more detailed explanation, check out [JoinNode, synchronize and itersource](http://nipype.readthedocs.io/en/latest/users/joinnode_and_itersource.html) from the main homepage." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simple example\n", - "\n", - "Let's consider the very simple example depicted at the top of this page:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "from nipype import Node, JoinNode, Workflow\n", - "\n", - "# Specify fake input node A\n", - "a = Node(interface=A(), name=\"a\")\n", - "\n", - "# Iterate over fake node B's input 'in_file?\n", - "b = Node(interface=B(), name=\"b\")\n", - "b.iterables = ('in_file', [file1, file2])\n", - "\n", - "# Pass results on to fake node C\n", - "c = Node(interface=C(), name=\"c\")\n", - "\n", - "# Join forked execution workflow in fake node D\n", - "d = JoinNode(interface=D(),\n", - " joinsource=\"b\",\n", - " joinfield=\"in_files\",\n", - " name=\"d\")\n", - "\n", - "# Put everything into a workflow as usual\n", - "workflow = Workflow(name=\"workflow\")\n", - "workflow.connect([(a, b, [('subject', 'subject')]),\n", - " (b, c, [('out_file', 'in_file')])\n", - " (c, d, [('out_file', 'in_files')])\n", - " ])\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, setting up a ``JoinNode`` is rather simple. The only difference to a normal ``Node`` are the ``joinsource`` and the ``joinfield``. ``joinsource`` specifies from which node the information to join is coming and the ``joinfield`` specifies the input field of the JoinNode where the information to join will be entering the node." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## More realistic example\n", - "\n", - "Let's consider another example where we have one node that iterates over 3 different numbers and generates randome numbers. Another node joins those three different numbers (each coming from a separate branch of the workflow) into one list. To make the whole thing a bit more realistic, the second node will use the ``Function`` interface to do something with those numbers, before we spit them out again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import JoinNode, Node, Workflow\n", - "from nipype.interfaces.utility import Function, IdentityInterface" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_data_from_id(id):\n", - " \"\"\"Generate a random number based on id\"\"\"\n", - " import numpy as np\n", - " return id + np.random.rand()\n", - "\n", - "def merge_and_scale_data(data2):\n", - " \"\"\"Scale the input list by 1000\"\"\"\n", - " import numpy as np\n", - " return (np.array(data2) * 1000).tolist()\n", - "\n", - "\n", - "node1 = Node(Function(input_names=['id'],\n", - " output_names=['data1'],\n", - " function=get_data_from_id),\n", - " name='get_data')\n", - "node1.iterables = ('id', [1, 2, 3])\n", - "\n", - "node2 = JoinNode(Function(input_names=['data2'],\n", - " output_names=['data_scaled'],\n", - " function=merge_and_scale_data),\n", - " name='scale_data',\n", - " joinsource=node1,\n", - " joinfield=['data2'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf = Workflow(name='testjoin')\n", - "wf.connect(node1, 'data1', node2, 'data2')\n", - "eg = wf.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='exec')\n", - "from IPython.display import Image\n", - "Image(filename='graph_detailed.dot.png')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's look at the input and output of the joinnode:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res = [node for node in eg.nodes() if 'scale_data' in node.name][0].result\n", - "res.outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res.inputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Extending to multiple nodes\n", - "\n", - "We extend the workflow by using three nodes. Note that even this workflow, the joinsource corresponds to the node containing iterables and the joinfield corresponds to the input port of the JoinNode that aggregates the iterable branches. As before the graph below shows how the execution process is setup." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def get_data_from_id(id):\n", - " import numpy as np\n", - " return id + np.random.rand()\n", - "\n", - "def scale_data(data2):\n", - " import numpy as np\n", - " return data2\n", - "\n", - "def replicate(data3, nreps=2):\n", - " return data3 * nreps\n", - "\n", - "node1 = Node(Function(input_names=['id'],\n", - " output_names=['data1'],\n", - " function=get_data_from_id),\n", - " name='get_data')\n", - "node1.iterables = ('id', [1, 2, 3])\n", - "\n", - "node2 = Node(Function(input_names=['data2'],\n", - " output_names=['data_scaled'],\n", - " function=scale_data),\n", - " name='scale_data')\n", - "\n", - "node3 = JoinNode(Function(input_names=['data3'],\n", - " output_names=['data_repeated'],\n", - " function=replicate),\n", - " name='replicate_data',\n", - " joinsource=node1,\n", - " joinfield=['data3'])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf = Workflow(name='testjoin')\n", - "wf.connect(node1, 'data1', node2, 'data2')\n", - "wf.connect(node2, 'data_scaled', node3, 'data3')\n", - "eg = wf.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='exec')\n", - "Image(filename='graph_detailed.dot.png')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_mapnodes.ipynb b/notebooks/basic_mapnodes.ipynb deleted file mode 100644 index b7ca65b..0000000 --- a/notebooks/basic_mapnodes.ipynb +++ /dev/null @@ -1,255 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "# MapNode\n", - "\n", - "If you want to iterate over a list of inputs, but need to feed all iterated outputs afterwards 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. (The main homepage has a [nice section](http://nipype.readthedocs.io/en/latest/users/mapnode_and_iterables.html) about ``MapNode`` and ``iterables`` if you want to learn more).\n", - "\n", - "Let's demonstrate this with a simple function interface:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Function\n", - "def square_func(x):\n", - " return x ** 2\n", - "square = Function([\"x\"], [\"f_x\"], square_func)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that this function just takes a numeric input and returns its squared value." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "square.run(x=2).outputs.f_x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What if we wanted to square a list of numbers? We could set an iterable and just split up the workflow in multiple sub-workflows. But say we were making a simple workflow that squared a list of numbers and then summed them. The sum node would expect a list, but using an iterable would make a bunch of sum nodes, and each would get one number from the list. The solution here is to use a `MapNode`.\n", - "\n", - "The `MapNode` constructor has a field called `iterfield`, which tells it what inputs should be expecting a list." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import MapNode\n", - "square_node = MapNode(square, name=\"square\", iterfield=[\"x\"])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "square_node.inputs.x = [0, 1, 2, 3]\n", - "square_node.run().outputs.f_x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Because `iterfield` can take a list of names, you can operate over multiple sets of data, as long as they're the same length. The values in each list will be paired; it does not compute a combinatoric product of the lists." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def power_func(x, y):\n", - " return x ** y" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "power = Function([\"x\", \"y\"], [\"f_xy\"], power_func)\n", - "power_node = MapNode(power, name=\"power\", iterfield=[\"x\", \"y\"])\n", - "power_node.inputs.x = [0, 1, 2, 3]\n", - "power_node.inputs.y = [0, 1, 2, 3]\n", - "print(power_node.run().outputs.f_xy)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But not every input needs to be an iterfield." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "power_node = MapNode(power, name=\"power\", iterfield=[\"x\"])\n", - "power_node.inputs.x = [0, 1, 2, 3]\n", - "power_node.inputs.y = 3\n", - "print(power_node.run().outputs.f_xy)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As in the case of `iterables`, each underlying `MapNode` execution can happen in **parallel**. Hopefully, you see how these tools allow you to write flexible, reusable workflows that will help you processes large amounts of data efficiently and reproducibly." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Why is this important?\n", - "\n", - "Let's consider we have multiple functional images (A) and each of them should be motioned corrected (B1, B2, B3,..). But afterwards, we want to put them all together into a GLM, i.e. the input for the GLM should be an array of [B1, B2, B3, ...]. [Iterables](basic_iteration.ipynb) can't do that. They would split up the pipeline. Therefore, we need **MapNodes**.\n", - "\n", - "\n", - "\n", - "Let's look at a simple example, where we want to motion correct two functional images. For this we need two nodes:\n", - " - Gunzip, to unzip the files (plural)\n", - " - Realign, to do the motion correction" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.algorithms.misc import Gunzip\n", - "from nipype.interfaces.spm import Realign\n", - "from nipype.pipeline.engine import Node, MapNode, Workflow\n", - "\n", - "files = ['/data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-fingerfootlips_bold.nii.gz',\n", - " '/data/ds000114/sub-02/ses-test/func/sub-02_ses-test_task-fingerfootlips_bold.nii.gz']\n", - "\n", - "realign = Node(Realign(register_to_mean=True),\n", - " name='motion_correction')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we try to specify the input for the **Gunzip** node with a simple **Node**, we get the following error:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "gunzip = Node(Gunzip(), name='gunzip',)\n", - "try:\n", - " gunzip.inputs.in_file = files\n", - "except(Exception) as err:\n", - " if \"TraitError\" in str(err.__class__):\n", - " print(\"TraitError:\", err)\n", - " else:\n", - " raise\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```bash\n", - "TraitError: The 'in_file' trait of a GunzipInputSpec instance must be an existing file name, but a value of ['/data/ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz', '/data/ds102/sub-01/func/sub-01_task-flanker_run-2_bold.nii.gz'] was specified.\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But if we do it with a **MapNode**, it works:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "gunzip = MapNode(Gunzip(), name='gunzip',\n", - " iterfield=['in_file'])\n", - "gunzip.inputs.in_file = files" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we just have to create a workflow, connect the nodes and we can run it:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mcflow = Workflow(name='realign_with_spm')\n", - "mcflow.connect(gunzip, 'out_file', realign, 'in_files')\n", - "mcflow.base_dir = '/output'\n", - "mcflow.run('MultiProc', plugin_args={'n_procs': 4})" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_model_specification.ipynb b/notebooks/basic_model_specification.ipynb deleted file mode 100644 index 2e20543..0000000 --- a/notebooks/basic_model_specification.ipynb +++ /dev/null @@ -1,165 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Model Specification for 1st-Level fMRI Analysis\n", - "\n", - "Nipype provides also an interfaces to create a first level Model for an fMRI analysis. Such a model is needed to specify the study specific information, such as **condition**, their **onsets** and **durations**. For more information, make sure to check out [Model Specificaton](http://nipype.readthedocs.io/en/latest/users/model_specification.html) and [nipype.algorithms.modelgen](http://nipype.readthedocs.io/en/latest/interfaces/generated/nipype.algorithms.modelgen.html)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Simple Example\n", - "\n", - "Let's consider a simple experiment, where we have three different stimuli such as ``'faces'``, ``'houses'`` and ``'scrambled pix'``. Now each of those three conditions has different stimuli onsets, but all of them have a stimuli presentation duration of 3 seconds.\n", - "\n", - "So to summarize:\n", - "\n", - " conditions = ['faces', 'houses', 'scrambled pix']\n", - " onsets = [[0, 30, 60, 90],\n", - " [10, 40, 70, 100],\n", - " [20, 50, 80, 110]]\n", - " durations = [[3], [3], [3]]\n", - " \n", - "The way we would create this model with Nipype is almsot as simple as that. The only step that is missing is to put this all into a ``Bunch`` object. This can be done as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.base import Bunch\n", - "\n", - "conditions = ['faces', 'houses', 'scrambled pix']\n", - "onsets = [[0, 30, 60, 90],\n", - " [10, 40, 70, 100],\n", - " [20, 50, 80, 110]]\n", - "durations = [[3], [3], [3]]\n", - "\n", - "subject_info = Bunch(conditions=conditions,\n", - " onsets=onsets,\n", - " durations=durations)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It's also possible to specify additional regressors. For this you need to additionally specify:\n", - "\n", - "- **``regressors``**: list of regressors that you want to include in the model (must correspond to the number of volumes in the functional run)\n", - "- **``regressor_names``**: name of the regressors that you want to include" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example based on dataset\n", - "\n", - "Now let's look at a TSV file from our tutorial dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cat /data/ds000114/task-fingerfootlips_events.tsv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also use [pandas](http://pandas.pydata.org/) to create a data frame from our dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "trialinfo = pd.read_table('/data/ds000114/task-fingerfootlips_events.tsv')\n", - "trialinfo.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before we can use the onsets, we first need to split them into the three conditions:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for group in trialinfo.groupby('trial_type'):\n", - " print(group)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The last thing we now need to to is to put this into a ``Bunch`` object and we're done:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.base import Bunch\n", - "\n", - "conditions = []\n", - "onsets = []\n", - "durations = []\n", - "\n", - "for group in trialinfo.groupby('trial_type'):\n", - " conditions.append(group[0])\n", - " onsets.append(group[1].onset.tolist())\n", - " durations.append(group[1].duration.tolist())\n", - "\n", - "subject_info = Bunch(conditions=conditions,\n", - " onsets=onsets,\n", - " durations=durations)\n", - "subject_info.items()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_nodes.ipynb b/notebooks/basic_nodes.ipynb deleted file mode 100644 index 67bd316..0000000 --- a/notebooks/basic_nodes.ipynb +++ /dev/null @@ -1,231 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Nodes\n", - "\n", - "From the [Interface](basic_interfaces.ipynb) tutorial, you learned that 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``.\n", - "\n", - "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 category and at least one input field and at least one output field.\n", - "\n", - "Following is a simple node from the `utility` interface, with the name `name_of_node`, the input field `IN` and the output field `OUT`:\n", - "\n", - "![](../static/images/node_sinlge_node.png)\n", - "\n", - "Once you connect multiple nodes to each other, you create a directed graph. In Nipype we call such graphs either workflows or pipelines. Directed connections can only be established from an output field (below `node1_out`) of a node to an input field (below `node2_in`) of another node.\n", - "\n", - "![](../static/images/node_two_nodes.png)\n", - "\n", - "This is all there is to Nipype. Connecting specific nodes with certain functions to other specific nodes with other functions. So let us now take a closer look at the different kind of nodes that exist and see when they should be used." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example of a simple node\n", - "\n", - "First, let us take a look at a simple stand-alone node. In general, a node consists of the following elements:\n", - "\n", - " nodename = Nodetype(interface_function(), name='labelname')\n", - "\n", - "- **nodename**: Variable name of the node in the python environment.\n", - "- **Nodetype**: Type of node to be created. This can be a `Node`, `MapNode` or `JoinNode`.\n", - "- **interface_function**: Function the node should execute. Can be user specific or coming from an `Interface`.\n", - "- **labelname**: Label name of the node in the workflow environment (defines the name of the working directory)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let us take a look at an example: For this we need the `Node` module from Nipype, as well as the `Function` module. The second only serves a support function for this example. It isn't a prerequisite for a `Node`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import Node and Function module\n", - "from nipype import Node, Function\n", - "\n", - "# Create a small example function\n", - "def add_two(x_input):\n", - " return x_input + 2\n", - "\n", - "# Create Node\n", - "addtwo = Node(Function(input_names=[\"x_input\"],\n", - " output_names=[\"val_output\"],\n", - " function=add_two),\n", - " name='add_node')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As specified before, `addtwo` is the **nodename**, `Node` is the **Nodetype**, `Function(...)` is the **interface_function** and `add_node` is the **labelname** of the this node. In this particular case, we created an artificial input field, called `x_input`, an artificial output field called `val_output` and specified that this node should run the function `add_two()`.\n", - "\n", - "But before we can run this node, we need to declare the value of the input field `x_input`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "addtwo.inputs.x_input = 4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After all input fields are specified, we can run the node with `run()`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "addtwo.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "temp_res = addtwo.run()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "temp_res.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And what is the output of this node?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "addtwo.result.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example of a neuroimaging node\n", - "\n", - "Let's get back to the BET example from the [Interface](basic_interfaces.ipynb) tutorial. The only thing that differs from this example, is that we will put the ``BET()`` constructor inside a ``Node`` and give it a name." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import BET from the FSL interface\n", - "from nipype.interfaces.fsl import BET\n", - "\n", - "# Import the Node module\n", - "from nipype import Node\n", - "\n", - "# Create Node\n", - "bet = Node(BET(frac=0.3), name='bet_node')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In the [Interface](basic_interfaces.ipynb) tutorial, we were able to specify the input file with the ``in_file`` parameter. This works exactly the same way in this case, where the interface is in a node. The only thing that we have to be careful about when we use a node is to specify where this node should be executed. This is only relevant for when we execute a node by itself, but not when we use them in a [Workflow](basic_workflow.ipynb)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Specify node inputs\n", - "bet.inputs.in_file = '/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz'\n", - "bet.inputs.out_file = '/output/node_T1w_bet.nii.gz'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "res = bet.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As we know from the [Interface](basic_interfaces.ipynb) tutorial, the skull stripped output is stored under ``res.outputs.out_file``. So let's take a look at the before and the after:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pylab inline\n", - "from nilearn.plotting import plot_anat\n", - "plot_anat(bet.inputs.in_file, title='BET input', cut_coords=(10,10,10),\n", - " display_mode='ortho', dim=-1, draw_cross=False, annotate=False)\n", - "plot_anat(res.outputs.out_file, title='BET output', cut_coords=(10,10,10),\n", - " display_mode='ortho',draw_cross=False, annotate=False)" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_plugins.ipynb b/notebooks/basic_plugins.ipynb deleted file mode 100644 index b5724c5..0000000 --- a/notebooks/basic_plugins.ipynb +++ /dev/null @@ -1,88 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Execution Plugins\n", - "\n", - "As you learned in the [Workflow](basic_workflow.ipynb) tutorial, a workflow is executed with the ``run`` method. For example:\n", - "\n", - " workflow.run()\n", - "\n", - "Whenever you execute a workflow like this, it will be executed in serial order. This means that no node will be executed in parallel, even if they are completely independent of each other. Now, while this might be preferable under certain circumstances, we usually want to executed workflows in parallel. For this, Nipype provides many different plugins." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Local execution\n", - "\n", - "### ``Linear`` Plugin\n", - "\n", - "If you want to run your workflow in a linear fashion, just use the following code:\n", - "\n", - " workflow.run(plugin='Linear')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### ``MultiProc`` Plugin\n", - "\n", - "The easiest way to executed a workflow locally in parallel is the ``MultiProc`` plugin:\n", - "\n", - " workflow.run(plugin='MultiProc', plugin_args={'n_procs': 4})\n", - "\n", - "The additional plugin argument ``n_procs``, specifies how many cores should be used for the parallel execution. In this case, it's 4.\n", - "\n", - "The `MultiProc` plugin uses the [multiprocessing](http://docs.python.org/library/multiprocessing.html) package in the standard library, and is the only parallel plugin that is guaranteed to work right out of the box." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Cluster execution\n", - "\n", - "There are many different plugins to run Nipype on a cluster, such as: ``PBS``, ``SGE``, ``LSF``, ``Condor`` and ``IPython``. Implementing them is as easy as ``'MultiProc'``.\n", - "\n", - " workflow.run('PBS', plugin_args={'qsub_args': '-q many'})\n", - " workflow.run('SGE', plugin_args={'qsub_args': '-q many'})\n", - " workflow.run('LSF', plugin_args={'qsub_args': '-q many'})\n", - " workflow.run('Condor')\n", - " workflow.run('IPython')\n", - " \n", - " workflow.run('PBSGraph', plugin_args={'qsub_args': '-q many'})\n", - " workflow.run('SGEGraph', plugin_args={'qsub_args': '-q many'})\n", - " workflow.run('CondorDAGMan')\n", - "\n", - "For a complete list and explanation of all supported plugins, see: http://nipype.readthedocs.io/en/latest/users/plugins.html" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/basic_workflow.ipynb b/notebooks/basic_workflow.ipynb deleted file mode 100644 index 21e2ff0..0000000 --- a/notebooks/basic_workflow.ipynb +++ /dev/null @@ -1,715 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Workflows\n", - "\n", - "Although it would be possible to write analysis scripts using just Nipype [Interfaces](basic_interfaces.ipynb), and this may provide some advantages over directly making command-line calls, the main benefits of Nipype will come by creating workflows.\n", - "\n", - "A workflow controls the setup and the execution of individual interfaces. Let's assume you want to run multiple interfaces in a specific order, where some have to wait for others to finish while others can be executed in parallel. The nice thing about a nipype workflow is, that the workflow will take care of input and output of each interface and arrange the execution of each interface in the most efficient way.\n", - "\n", - "A workflow therefore consists of multiple [Nodes](basic_nodes.ipynb), each representing a specific [Interface](basic_interfaces.ipynb) and directed connection between those nodes. Those connections specify which output of which node should be used as an input for another node. To better understand why this is so great, let's look at an example." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Preparation\n", - "\n", - "Before we can start, let's first load some helper functions:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%pylab inline\n", - "import nibabel as nb\n", - "\n", - "# Let's create a short helper function to plot 3D NIfTI images\n", - "def plot_slice(fname):\n", - "\n", - " # Load the image\n", - " img = nb.load(fname)\n", - " data = img.get_data()\n", - "\n", - " # Cut in the middle of the brain\n", - " cut = int(data.shape[-1]/2) + 10\n", - "\n", - " # Plot the data\n", - " imshow(np.rot90(data[..., cut]), cmap=\"gray\")\n", - " gca().set_axis_off()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 1 - ``Command-line`` execution\n", - "\n", - "Let's take a look at a small preprocessing analysis where we would like to perform the following steps of processing:\n", - "\n", - " - Skullstrip an image to obtain a mask\n", - " - Smooth the original image\n", - " - Mask the smoothed image\n", - "\n", - "This could all very well be done with the following shell script:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "ANAT_NAME=sub-02_ses-test_T1w\n", - "ANAT=/data/ds000114/sub-02/ses-test/anat/${ANAT_NAME}\n", - "bet ${ANAT} /output/${ANAT_NAME}_brain -m -f 0.3\n", - "fslmaths ${ANAT} -s 2 /output/${ANAT_NAME}_smooth\n", - "fslmaths /output/${ANAT_NAME}_smooth -mas /output/${ANAT_NAME}_brain_mask /output/${ANAT_NAME}_smooth_mask" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is simple and straightforward. We can see that this does exactly what we wanted by plotting the four steps of processing." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = plt.figure(figsize=(12, 4))\n", - "for i, img in enumerate([\"T1w\", \"T1w_smooth\",\n", - " \"T1w_brain_mask\", \"T1w_smooth_mask\"]):\n", - " f.add_subplot(1, 4, i + 1)\n", - " if i == 0:\n", - " plot_slice(\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_%s.nii.gz\" % img)\n", - " else:\n", - " plot_slice(\"/output/sub-02_ses-test_%s.nii.gz\" % img)\n", - " plt.title(img)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 2 - ``Interface`` execution" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's see what this would look like if we used Nipype, but only the Interfaces functionality. It's simple enough to write a basic procedural script, this time in Python, to do the same thing as above:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces import fsl\n", - "\n", - "# Skullstrip process\n", - "skullstrip = fsl.BET(\n", - " in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_brain.nii.gz\",\n", - " mask=True)\n", - "skullstrip.run()\n", - "\n", - "# Smoothing process\n", - "smooth = fsl.IsotropicSmooth(\n", - " in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_smooth.nii.gz\",\n", - " fwhm=4)\n", - "smooth.run()\n", - "\n", - "# Masking process\n", - "mask = fsl.ApplyMask(\n", - " in_file=\"/output/sub-02_T1w_smooth.nii.gz\",\n", - " out_file=\"/output/sub-02_T1w_smooth_mask.nii.gz\",\n", - " mask_file=\"/output/sub-02_T1w_brain_mask.nii.gz\")\n", - "mask.run()\n", - "\n", - "f = plt.figure(figsize=(12, 4))\n", - "for i, img in enumerate([\"T1w\", \"T1w_smooth\",\n", - " \"T1w_brain_mask\", \"T1w_smooth_mask\"]):\n", - " f.add_subplot(1, 4, i + 1)\n", - " if i == 0:\n", - " plot_slice(\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_%s.nii.gz\" % img)\n", - " else:\n", - " plot_slice(\"/output/sub-02_%s.nii.gz\" % img)\n", - " plt.title(img)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This is more verbose, although it does have its advantages. There's the automated input validation we saw previously, some of the options are named more meaningfully, and you don't need to remember, for example, that fslmaths' smoothing kernel is set in sigma instead of FWHM -- Nipype does that conversion behind the scenes.\n", - "\n", - "### Can't we optimize that a bit?\n", - "\n", - "As we can see above, the inputs for the **``mask``** routine ``in_file`` and ``mask_file`` are actually the output of **``skullstrip``** and **``smooth``**. We therefore somehow want to connect them. This can be accomplisehd by saving the executed routines under a given object and than using the output of those objects as input for other routines." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces import fsl\n", - "\n", - "# Skullstrip process\n", - "skullstrip = fsl.BET(\n", - " in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\", mask=True)\n", - "bet_result = skullstrip.run() # skullstrip object\n", - "\n", - "# Smooth process\n", - "smooth = fsl.IsotropicSmooth(\n", - " in_file=\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\", fwhm=4)\n", - "smooth_result = smooth.run() # smooth object\n", - "\n", - "# Mask process\n", - "mask = fsl.ApplyMask(in_file=smooth_result.outputs.out_file,\n", - " mask_file=bet_result.outputs.mask_file)\n", - "mask_result = mask.run()\n", - "\n", - "f = plt.figure(figsize=(12, 4))\n", - "for i, img in enumerate([skullstrip.inputs.in_file, smooth_result.outputs.out_file,\n", - " bet_result.outputs.mask_file, mask_result.outputs.out_file]):\n", - " f.add_subplot(1, 4, i + 1)\n", - " plot_slice(img)\n", - " plt.title(img.split('/')[-1].split('.')[0].split('test_')[-1])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here we didn't need to name the intermediate files; Nipype did that behind the scenes, and then we passed the result object (which knows those names) onto the next step in the processing stream. This is somewhat more concise than the example above, but it's still a procedural script. And the dependency relationship between the stages of processing is not particularly obvious. To address these issues, and to provide solutions to problems we might not know we have yet, Nipype offers **Workflows.**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 3 - ``Workflow`` execution\n", - "\n", - "What we've implicitly done above is to encode our processing stream as a directed acyclic graphs: each stage of processing is a node in this graph, and some nodes are unidirectionally dependent on others. In this case there is one input file and several output files, but there are no cycles -- there's a clear line of directionality to the processing. What the Node and Workflow classes do is make these relationships more explicit.\n", - "\n", - "The basic architecture is that the Node provides a light wrapper around an Interface. It exposes the inputs and outputs of the Interface as its own, but it adds some additional functionality that allows you to connect Nodes into a Workflow.\n", - "\n", - "Let's rewrite the above script with these tools:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import Node and Workflow object and FSL interface\n", - "from nipype import Node, Workflow\n", - "from nipype.interfaces import fsl\n", - "\n", - "# For reasons that will later become clear, it's important to\n", - "# pass filenames to Nodes as absolute paths\n", - "from os.path import abspath\n", - "in_file = abspath(\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\")\n", - "\n", - "# Skullstrip process\n", - "skullstrip = Node(fsl.BET(in_file=in_file, mask=True), name=\"skullstrip\")\n", - "\n", - "# Smooth process\n", - "smooth = Node(fsl.IsotropicSmooth(in_file=in_file, fwhm=4), name=\"smooth\")\n", - "\n", - "# Mask process\n", - "mask = Node(fsl.ApplyMask(), name=\"mask\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This looks mostly similar to what we did above, but we've left out the two crucial inputs to the ApplyMask step. We'll set those up by defining a Workflow object and then making *connections* among the Nodes." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiation of a workflow\n", - "wf = Workflow(name=\"smoothflow\", base_dir=\"/output/working_dir\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The Workflow object has a method called ``connect`` that is going to do most of the work here. This routine also checks if inputs and outputs are actually provided by the nodes that are being connected.\n", - "\n", - "There are two different ways to call ``connect``:\n", - "\n", - " connect(source, \"source_output\", dest, \"dest_input\")\n", - "\n", - " connect([(source, dest, [(\"source_output1\", \"dest_input1\"),\n", - " (\"source_output2\", \"dest_input2\")\n", - " ])\n", - " ])\n", - "\n", - "With the first approach you can establish one connection at a time. With the second you can establish multiple connects between two nodes at once. In either case, you're providing it with four pieces of information to define the connection:\n", - "\n", - "- The source node object\n", - "- The name of the output field from the source node\n", - "- The destination node object\n", - "- The name of the input field from the destination node\n", - "\n", - "We'll illustrate each method in the following cell:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# First the \"simple\", but more restricted method\n", - "wf.connect(skullstrip, \"mask_file\", mask, \"mask_file\")\n", - "\n", - "# Now the more complicated method\n", - "wf.connect([(smooth, mask, [(\"out_file\", \"in_file\")])])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now the workflow is complete!\n", - "\n", - "Above, we mentioned that the workflow can be thought of as a directed acyclic graph. In fact, that's literally how it's represented behind the scenes, and we can use that to explore the workflow visually:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(\"workflow_graph.dot\")\n", - "from IPython.display import Image\n", - "Image(filename=\"/output/working_dir/smoothflow/workflow_graph.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This representation makes the dependency structure of the workflow obvious. (By the way, the names of the nodes in this graph are the names we gave our Node objects above, so pick something meaningful for those!)\n", - "\n", - "Certain graph types also allow you to further inspect the individual connections between the nodes. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='flat')\n", - "from IPython.display import Image\n", - "Image(filename=\"/output/working_dir/smoothflow/graph_detailed.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here you see very clearly, that the output ``mask_file`` of the ``skullstrip`` node is used as the input ``mask_file`` of the ``mask`` node. For more information on graph visualization, see the [Graph Visualization](./basic_graph_visualization.ipynb) section." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "But let's come back to our example. At this point, all we've done is define the workflow. We haven't executed any code yet. Much like Interface objects, the Workflow object has a ``run`` method that we can call so that it executes. Let's do that and then examine the results." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Specify the base directory for the working directory\n", - "wf.base_dir = \"/output/working_dir\"\n", - "\n", - "# Execute the workflow\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**The specification of ``base_dir`` is very important (and is why we needed to use absolute paths above), because otherwise all the outputs would be saved somewhere in the temporary files.** Unlike interfaces, which by default spit out results to the local directry, the Workflow engine executes things off in its own directory hierarchy.\n", - "\n", - "Let's take a look at the resulting images to convince ourselves we've done the same thing as before:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = plt.figure(figsize=(12, 4))\n", - "for i, img in enumerate([\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\",\n", - " \"/output/working_dir/smoothflow/smooth/sub-02_ses-test_T1w_smooth.nii.gz\",\n", - " \"/output/working_dir/smoothflow/skullstrip/sub-02_ses-test_T1w_brain_mask.nii.gz\",\n", - " \"/output/working_dir/smoothflow/mask/sub-02_ses-test_T1w_smooth_masked.nii.gz\"]):\n", - " f.add_subplot(1, 4, i + 1)\n", - " plot_slice(img)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Perfet!\n", - "\n", - "Let's also have a closer look at the working directory:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree /output/working_dir/smoothflow/ -I '*js|*json|*html|*pklz|_report'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can see, the name of the working directory is the name we gave the workflow ``base_dir``. And the name of the folder within is the name of the workflow object ``smoothflow``. Each node of the workflow has its' own subfolder in the ``smoothflow`` folder. And each of those subfolders contains the output of the node as well as some additional files." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# A workflow inside a workflow" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "When you start writing full-fledged analysis workflows, things can get quite complicated. Some aspects of neuroimaging analysis can be thought of as a coherent step at a level more abstract than the execution of a single command line binary. For instance, in the standard FEAT script in FSL, several calls are made in the process of using `susan` to perform nonlinear smoothing on an image. In Nipype, you can write **nested workflows**, where a sub-workflow can take the place of a Node in a given script.\n", - "\n", - "Let's use the prepackaged `susan` workflow that ships with Nipype to replace our Gaussian filtering node and demonstrate how this works." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.workflows.fmri.fsl import create_susan_smooth" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Calling this function will return a pre-written `Workflow` object:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "susan = create_susan_smooth(separate_masks=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's display the graph to see what happens here." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "susan.write_graph(\"susan_workflow.dot\")\n", - "from IPython.display import Image\n", - "Image(filename=\"susan_workflow.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We see that the workflow has an `inputnode` and an `outputnode`. While not strictly necessary, this is standard practice for workflows (especially those that are intended to be used as nested workflows in the context of a longer analysis graph) and makes it more clear how to connect inputs and outputs from this workflow.\n", - "\n", - "Let's take a look at what those inputs and outputs are. Like Nodes, Workflows have `inputs` and `outputs` attributes that take a second sub-attribute corresponding to the specific node we want to make connections to." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Inputs:\\n\", susan.inputs.inputnode)\n", - "print(\"Outputs:\\n\", susan.outputs.outputnode)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that `inputnode` and `outputnode` are just conventions, and the Workflow object exposes connections to all of its component nodes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "susan.inputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's see how we would write a new workflow that uses this nested smoothing step.\n", - "\n", - "The susan workflow actually expects to receive and output a list of files (it's intended to be executed on each of several runs of fMRI data). We'll cover exactly how that works in later tutorials, but for the moment we need to add an additional ``Function`` node to deal with the fact that ``susan`` is outputting a list. We can use a simple `lambda` function to do this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import Function\n", - "extract_func = lambda list_out: list_out[0]\n", - "list_extract = Node(Function(input_names=[\"list_out\"],\n", - " output_names=[\"out_file\"],\n", - " function=extract_func),\n", - " name=\"list_extract\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now let's create a new workflow ``susanflow`` that contains the ``susan`` workflow as a sub-node. To be sure, let's also recreate the ``skullstrip`` and the ``mask`` node from the examples above." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiate workflow with name and base directory\n", - "wf2 = Workflow(name=\"susanflow\", base_dir=\"/output/working_dir\")\n", - "\n", - "# Create new skullstrip and mask nodes\n", - "skullstrip2 = Node(fsl.BET(in_file=in_file, mask=True), name=\"skullstrip\")\n", - "mask2 = Node(fsl.ApplyMask(), name=\"mask\")\n", - "\n", - "# Connect the nodes to each other and to the susan workflow\n", - "wf2.connect([(skullstrip2, mask2, [(\"mask_file\", \"mask_file\")]),\n", - " (skullstrip2, susan, [(\"mask_file\", \"inputnode.mask_file\")]),\n", - " (susan, list_extract, [(\"outputnode.smoothed_files\",\n", - " \"list_out\")]),\n", - " (list_extract, mask2, [(\"out_file\", \"in_file\")])\n", - " ])\n", - "\n", - "# Specify the remaining input variables for the susan workflow\n", - "susan.inputs.inputnode.in_files = abspath(\n", - " \"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\")\n", - "susan.inputs.inputnode.fwhm = 4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's see what this new processing graph looks like." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf2.write_graph(dotfilename='/output/working_dir/full_susanflow.dot', graph2use='colored')\n", - "from IPython.display import Image\n", - "Image(filename=\"/output/working_dir/full_susanflow.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can see how there is a nested smoothing workflow (blue) in the place of our previous `smooth` node. This provides a very detailed view, but what if you just wanted to give a higher-level summary of the processing steps? After all, that is the purpose of encapsulating smaller streams in a nested workflow. That, fortunately, is an option when writing out the graph:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf2.write_graph(dotfilename='/output/working_dir/full_susanflow_toplevel.dot', graph2use='orig')\n", - "from IPython.display import Image\n", - "Image(filename=\"/output/working_dir/full_susanflow_toplevel.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That's much more managable. Now let's execute the workflow" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf2.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As a final step, let's look at the input and the output. It's exactly what we wanted." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = plt.figure(figsize=(12, 4))\n", - "for i, e in enumerate([[\"/data/ds000114/sub-02/ses-test/anat/sub-02_ses-test_T1w.nii.gz\", 'input'],\n", - " [\"/output/working_dir//susanflow/mask/sub-02_ses-test_T1w_smooth_masked.nii.gz\", \n", - " 'output']]):\n", - " f.add_subplot(1, 2, i + 1)\n", - " plot_slice(e[0])\n", - " plt.title(e[1])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# So, why are workflows so great?\n", - "\n", - "So far, we've seen that you can build up rather complex analysis workflows. But at the moment, it's not been made clear why this is worth the extra trouble from writing a simple procedural script. To demonstrate the first added benefit of the Nipype, let's just rerun the ``susanflow`` workflow from above and measure the execution times." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%time\n", - "wf2.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "That happened quickly! **Workflows (actually this is handled by the Node code) are smart, and know if their inputs have changed from the last time they are run. If they have not, they don't recompute; they just turn around and pass out the resulting files from the previous run.** This is done on a node-by-node basis, also.\n", - "\n", - "Let's go back to the first workflow example. What happened if we just tweak one thing:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.inputs.smooth.fwhm = 1\n", - "wf.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "By changing an input value of the ``smooth`` node, this node will be re-executed. This triggers a cascade such that any file depending on the ``smooth`` node (in this case, the ``mask`` node, also recompute). However, the ``skullstrip`` node hasn't changed since the first time it ran, so it just coughed up its original files.\n", - "\n", - "That's one of the main benefit of using Workflows: **efficient recomputing**. \n", - "\n", - "Another benefits of Workflows is parallel execution, which is covered under [Plugins and Distributed Computing](./basic_plugins.ipynb). With Nipype it is very easy to up a workflow to an extremely parallel cluster computing environment.\n", - "\n", - "In this case, that just means that the `skullstrip` and `smooth` Nodes execute together, but when you scale up to Workflows with many subjects and many runs per subject, each can run together, such that (in the case of unlimited computing resources), you could process 50 subjects with 10 runs of functional data in essentially the time it would take to process a single run.\n", - "\n", - "To emphasize the contribution of Nipype here, you can write and test your workflow on one subject computing on your local CPU, where it is easier to debug. Then, with the change of a single function parameter, you can scale your processing up to a 1000+ node SGE cluster." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/example_1stlevel.ipynb b/notebooks/example_1stlevel.ipynb deleted file mode 100644 index c25e4e3..0000000 --- a/notebooks/example_1stlevel.ipynb +++ /dev/null @@ -1,580 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 2: 1st-level Analysis\n", - "\n", - "In this example we will take the preprocessed output from the first example and run for each subject a 1st-level analysis. For this we need to do the following steps:\n", - "\n", - "1. Extract onset times of stimuli from TVA file\n", - "2. Specify the model (TR, high pass filter, onset times, etc.)\n", - "3. Specify contrasts to compute\n", - "4. Estimate contrasts\n", - "\n", - "In the previous example, we used two different smoothing kernels of fwhm=4 and fwhm=8. Therefore, let us also run the 1st-level analysis for those two versions.\n", - "\n", - "**So, let's begin!**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports\n", - "\n", - "First, we need to import all modules we later want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "from os.path import join as opj\n", - "import json\n", - "from nipype.interfaces.spm import Level1Design, EstimateModel, EstimateContrast\n", - "from nipype.algorithms.modelgen import SpecifySPMModel\n", - "from nipype.interfaces.utility import Function, IdentityInterface\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "from nipype.pipeline.engine import Workflow, Node" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Experiment parameters\n", - "\n", - "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_dir = '/output'\n", - "output_dir = 'datasink'\n", - "working_dir = 'workingdir'\n", - "\n", - "# list of subject identifiers\n", - "subject_list = ['sub-01', 'sub-02', 'sub-03', 'sub-04', 'sub-05',\n", - " 'sub-06', 'sub-07', 'sub-08', 'sub-09', 'sub-10']\n", - "\n", - "# TR of functional images\n", - "with open('/data/ds000114/task-fingerfootlips_bold.json', 'rt') as fp:\n", - " task_info = json.load(fp)\n", - "TR = task_info['RepetitionTime']\n", - "\n", - "# Smoothing withds used during preprocessing\n", - "fwhm = [4, 8]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Nodes\n", - "\n", - "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# SpecifyModel - Generates SPM-specific Model\n", - "modelspec = Node(SpecifySPMModel(concatenate_runs=False,\n", - " input_units='secs',\n", - " output_units='secs',\n", - " time_repetition=TR,\n", - " high_pass_filter_cutoff=128),\n", - " name=\"modelspec\")\n", - "\n", - "# Level1Design - Generates an SPM design matrix\n", - "level1design = Node(Level1Design(bases={'hrf': {'derivs': [1, 0]}},\n", - " timing_units='secs',\n", - " interscan_interval=TR,\n", - " model_serial_correlations='FAST'),\n", - " name=\"level1design\")\n", - "\n", - "# EstimateModel - estimate the parameters of the model\n", - "level1estimate = Node(EstimateModel(estimation_method={'Classical': 1}),\n", - " name=\"level1estimate\")\n", - "\n", - "# EstimateContrast - estimates contrasts\n", - "level1conest = Node(EstimateContrast(), name=\"level1conest\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify GLM contrasts\n", - "\n", - "To do any GLM analysis, we need to also define the contrasts that we want to investigate. If we recap, we had three different conditions in the **fingerfootlips** task in this dataset:\n", - "\n", - "- **finger**\n", - "- **foot**\n", - "- **lips**\n", - "\n", - "Therefore, we could create the following contrasts (seven T-contrasts and two F-contrasts):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Condition names\n", - "condition_names = ['Finger', 'Foot', 'Lips']\n", - "\n", - "# Contrasts\n", - "cont01 = ['average', 'T', condition_names, [1/3., 1/3., 1/3.]]\n", - "cont02 = ['Finger', 'T', condition_names, [1, 0, 0]]\n", - "cont03 = ['Foot', 'T', condition_names, [0, 1, 0]]\n", - "cont04 = ['Lips', 'T', condition_names, [0, 0, 1]]\n", - "cont05 = ['Finger > others','T', condition_names, [1, -0.5, -0.5]]\n", - "cont06 = ['Foot > others', 'T', condition_names, [-0.5, 1, -0.5]]\n", - "cont07 = ['Lips > others', 'T', condition_names, [-0.5, -0.5, 1]]\n", - "\n", - "cont08 = ['activation', 'F', [cont02, cont03, cont04]]\n", - "cont09 = ['differences', 'F', [cont05, cont06, cont07]]\n", - "\n", - "contrast_list = [cont01, cont02, cont03, cont04, cont05, cont06, cont07, cont08, cont09]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify GLM Model\n", - "\n", - "The next step is now to get information such as stimuli onset, duration and other regressors into the GLM model. For this we need to create a helper function, in our case called ``subjectinfo``.\n", - "\n", - "To recap, let's see what we have in the TSV file for each run:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cat /data/ds000114/task-fingerfootlips_events.tsv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also create a data frame using pandas library." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "trialinfo = pd.read_table('/data/ds000114/task-fingerfootlips_events.tsv')\n", - "trialinfo" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And finally we need to separate the onsets of the three conditions, i.e. group by ``trial_type``. This can be done as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for group in trialinfo.groupby('trial_type'):\n", - " print(group)\n", - " print(\"\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let us incorporate all this in the helper function ``subjectinfo``." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def subjectinfo(subject_id):\n", - "\n", - " import pandas as pd\n", - " from nipype.interfaces.base import Bunch\n", - " \n", - " trialinfo = pd.read_table('/data/ds000114/task-fingerfootlips_events.tsv')\n", - " trialinfo.head()\n", - " conditions = []\n", - " onsets = []\n", - " durations = []\n", - "\n", - " for group in trialinfo.groupby('trial_type'):\n", - " conditions.append(group[0])\n", - " onsets.append(list(group[1].onset - 10)) # subtracting 10s due to removing of 4 dummy scans\n", - " durations.append(group[1].duration.tolist())\n", - "\n", - " subject_info = [Bunch(conditions=conditions,\n", - " onsets=onsets,\n", - " durations=durations,\n", - " #amplitudes=None,\n", - " #tmod=None,\n", - " #pmod=None,\n", - " #regressor_names=None,\n", - " #regressors=None\n", - " )]\n", - "\n", - " return subject_info # this output will later be returned to infosource\n", - "\n", - "# Get Subject Info - get subject specific condition information\n", - "getsubjectinfo = Node(Function(input_names=['subject_id'],\n", - " output_names=['subject_info'],\n", - " function=subjectinfo),\n", - " name='getsubjectinfo')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify input & output stream\n", - "\n", - "Specify where the input data can be found & where and how to save the output data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Infosource - a function free node to iterate over the list of subject names\n", - "infosource = Node(IdentityInterface(fields=['subject_id',\n", - " 'fwhm_id',\n", - " 'contrasts'],\n", - " contrasts=contrast_list),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('subject_id', subject_list),\n", - " ('fwhm_id', fwhm)]\n", - "\n", - "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", - "templates = {'func': opj(output_dir, 'preproc', '{subject_id}', 'task-{task_id}',\n", - " 'fwhm-{fwhm_id}_s{subject_id}_ses-test_task-{task_id}_bold.nii'),\n", - " 'mc_param': opj(output_dir, 'preproc', '{subject_id}', 'task-{task_id}',\n", - " '{subject_id}_ses-test_task-{task_id}_bold.par'),\n", - " 'outliers': opj(output_dir, 'preproc', '{subject_id}', 'task-{task_id}', \n", - " 'art.{subject_id}_ses-test_task-{task_id}_bold_outliers.txt')}\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory=experiment_dir,\n", - " sort_filelist=True),\n", - " name=\"selectfiles\")\n", - "selectfiles.inputs.task_id = 'fingerfootlips'\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=experiment_dir,\n", - " container=output_dir),\n", - " name=\"datasink\")\n", - "\n", - "# Use the following DataSink output substitutions\n", - "substitutions = [('_subject_id_', '')]\n", - "subjFolders = [('_fwhm_id_%s%s' % (f, sub), '%s/fwhm-%s' % (sub, f))\n", - " for f in fwhm\n", - " for sub in subject_list]\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Workflow\n", - "\n", - "Create a workflow and connect the interface nodes and the I/O stream to each other." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiation of the 1st-level analysis workflow\n", - "l1analysis = Workflow(name='l1analysis')\n", - "l1analysis.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect up the 1st-level analysis components\n", - "l1analysis.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", - " ('fwhm_id', 'fwhm_id')]),\n", - " (infosource, getsubjectinfo, [('subject_id',\n", - " 'subject_id')]),\n", - " (getsubjectinfo, modelspec, [('subject_info',\n", - " 'subject_info')]),\n", - " (infosource, level1conest, [('contrasts', 'contrasts')]),\n", - " (selectfiles, modelspec, [('func', 'functional_runs')]),\n", - " (selectfiles, modelspec, [('mc_param', 'realignment_parameters'),\n", - " ('outliers', 'outlier_files')]),\n", - " (modelspec, level1design, [('session_info',\n", - " 'session_info')]),\n", - " (level1design, level1estimate, [('spm_mat_file',\n", - " 'spm_mat_file')]),\n", - " (level1estimate, level1conest, [('spm_mat_file',\n", - " 'spm_mat_file'),\n", - " ('beta_images',\n", - " 'beta_images'),\n", - " ('residual_image',\n", - " 'residual_image')]),\n", - " (level1conest, datasink, [('spm_mat_file', '1stLevel.@spm_mat'),\n", - " ('spmT_images', '1stLevel.@T'),\n", - " ('con_images', '1stLevel.@con'),\n", - " ('spmF_images', '1stLevel.@F'),\n", - " ('ess_images', '1stLevel.@ess'),\n", - " ]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize the workflow\n", - "\n", - "It always helps to visualize your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create 1st-level analysis output graph\n", - "l1analysis.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "\n", - "# Visualize the graph\n", - "from IPython.display import Image\n", - "Image(filename=opj(l1analysis.base_dir, 'l1analysis', 'graph.dot.png'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Workflow\n", - "\n", - "Now that everything is ready, we can run the 1st-level analysis workflow. Change ``n_procs`` to the number of jobs/cores you want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l1analysis.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Inspect output\n", - "\n", - "Let's check the structure of the output folder, to see if we have everything we wanted to save. You should have nine contrast images (``con_*.nii`` for T-contrasts and ``ess_*.nii`` for T-contrasts) and nine statistic images (``spmT_*.nii`` and ``spmF_*.nii``) for every subject and smoothing kernel." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree /output/datasink/1stLevel" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize results\n", - "\n", - "Let's look at the contrasts of one subject that we've just computed. First, let's see what the difference of smoothing is for the contrast **`average`**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nilearn.plotting import plot_stat_map\n", - "anatimg = '/data/ds000114/derivatives/fmriprep/sub-02/anat/sub-02_t1w_preproc.nii.gz'\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0001.nii', title='average - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-8/spmT_0001.nii', title='average - fwhm=8',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's look at the three contrasts **`Finger`**, **`Foot`**, **`Lips`**." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0002.nii', title='finger - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0003.nii', title='foot - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0004.nii', title='lips - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also check three additional contrasts **Finger > others**, **Foot > others** and **Lips > others**. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0005.nii', title='finger - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0006.nii', title='foot - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0007.nii', title='lips - fwhm=4',\n", - " bg_img=anatimg, threshold=3, display_mode='y', cut_coords=(-5, 0, 5, 10, 15), dim=-1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Special case\n", - "\n", - "There is something special with the **Finger** contrast in all subjects. So let's take a look at all of them." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-01/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-01',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-01/anat/sub-01_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-02/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-02',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-02/anat/sub-02_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-03/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-03',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-03/anat/sub-03_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-04/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-04',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-04/anat/sub-04_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-05/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-05',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-05/anat/sub-05_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-06/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-06',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-06/anat/sub-06_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-07/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-07',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-07/anat/sub-07_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-08/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-08',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-08/anat/sub-08_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-09/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-09',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-09/anat/sub-09_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n", - "plot_stat_map(\n", - " '/output/datasink/1stLevel/sub-10/fwhm-4/spmT_0002.nii', title='finger - fwhm=4 - sub-10',\n", - " bg_img='/data/ds000114/derivatives/fmriprep/sub-10/anat/sub-10_t1w_preproc.nii.gz',\n", - " threshold=3, display_mode='y', cut_coords=(5, 10, 15, 20), dim=-1)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "What you might see is that the hemisphere of the main cluster differs significantly between subjects. This is because all subjects were asked to use the dominant hand, either right or left. There were three subjects (``sub-01``, ``sub-06`` and ``sub-10``) that were left handed. This can be seen in the pictures above, where we find the main cluster in the left hemisphere for right handed subject and on the right hemisphere for left handed subjects.\n", - "\n", - "**Because of this, We will use only right handed subjects for the following anlyses**." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/example_2ndlevel.ipynb b/notebooks/example_2ndlevel.ipynb deleted file mode 100644 index 70ff946..0000000 --- a/notebooks/example_2ndlevel.ipynb +++ /dev/null @@ -1,479 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 4: 2nd-level Analysis\n", - "\n", - "Last but not least, the 2nd-level analysis. After we removed left handed subjects and normalized all subject data into template space, we can now do the group analysis. To show the flexibility of Nipype, we will run the group analysis on data with two different smoothing kernel (``fwhm= [4, 8]``) and two different normalization (ANTs and SPM).\n", - "\n", - "This example will also directly include thresholding of the output, as well as some visualization.\n", - "\n", - "**Let's start!**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Group Analysis with SPM\n", - "\n", - "Let's first run the group analysis with the SPM normalized data.\n", - "\n", - "## Imports (SPM12)\n", - "\n", - "First, we need to import all modules we later want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "from os.path import join as opj\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "from nipype.interfaces.spm import (OneSampleTTestDesign, EstimateModel,\n", - " EstimateContrast, Threshold)\n", - "from nipype.interfaces.utility import IdentityInterface\n", - "from nipype.pipeline.engine import Workflow, Node\n", - "from nipype.interfaces.fsl import Info\n", - "from nipype.algorithms.misc import Gunzip" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Experiment parameters (SPM12)\n", - "\n", - "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_dir = '/output'\n", - "output_dir = 'datasink'\n", - "working_dir = 'workingdir'\n", - "\n", - "# Smoothing withds used during preprocessing\n", - "fwhm = [4, 8]\n", - "\n", - "# Which contrasts to use for the 2nd-level analysis\n", - "contrast_list = ['con_0001', 'con_0002', 'con_0003', 'con_0004', 'con_0005', 'con_0006', 'con_0007']\n", - "\n", - "mask = \"/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_brainmask.nii.gz\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Nodes (SPM12)\n", - "\n", - "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Gunzip - unzip the mask image\n", - "gunzip = Node(Gunzip(in_file=mask), name=\"gunzip\")\n", - "\n", - "# OneSampleTTestDesign - creates one sample T-Test Design\n", - "onesamplettestdes = Node(OneSampleTTestDesign(),\n", - " name=\"onesampttestdes\")\n", - "\n", - "# EstimateModel - estimates the model\n", - "level2estimate = Node(EstimateModel(estimation_method={'Classical': 1}),\n", - " name=\"level2estimate\")\n", - "\n", - "# EstimateContrast - estimates group contrast\n", - "level2conestimate = Node(EstimateContrast(group_contrast=True),\n", - " name=\"level2conestimate\")\n", - "cont1 = ['Group', 'T', ['mean'], [1]]\n", - "level2conestimate.inputs.contrasts = [cont1]\n", - "\n", - "# Threshold - thresholds contrasts\n", - "level2thresh = Node(Threshold(contrast_index=1,\n", - " use_topo_fdr=True,\n", - " use_fwe_correction=False,\n", - " extent_threshold=0,\n", - " height_threshold=0.005,\n", - " height_threshold_type='p-value',\n", - " extent_fdr_p_threshold=0.05),\n", - " name=\"level2thresh\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify input & output stream (SPM12)\n", - "\n", - "Specify where the input data can be found & where and how to save the output data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Infosource - a function free node to iterate over the list of subject names\n", - "infosource = Node(IdentityInterface(fields=['contrast_id', 'fwhm_id']),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('contrast_id', contrast_list),\n", - " ('fwhm_id', fwhm)]\n", - "\n", - "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", - "templates = {'cons': opj(output_dir, 'norm_spm', 'sub-*_fwhm{fwhm_id}',\n", - " 'w{contrast_id}.nii')}\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory=experiment_dir,\n", - " sort_filelist=True),\n", - " name=\"selectfiles\")\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=experiment_dir,\n", - " container=output_dir),\n", - " name=\"datasink\")\n", - "\n", - "# Use the following DataSink output substitutions\n", - "substitutions = [('_contrast_id_', '')]\n", - "subjFolders = [('%s_fwhm_id_%s' % (con, f), 'spm_%s_fwhm%s' % (con, f))\n", - " for f in fwhm\n", - " for con in contrast_list]\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Workflow (SPM12)\n", - "\n", - "Create a workflow and connect the interface nodes and the I/O stream to each other." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiation of the 2nd-level analysis workflow\n", - "l2analysis = Workflow(name='spm_l2analysis')\n", - "l2analysis.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect up the 2nd-level analysis components\n", - "l2analysis.connect([(infosource, selectfiles, [('contrast_id', 'contrast_id'),\n", - " ('fwhm_id', 'fwhm_id')]),\n", - " (selectfiles, onesamplettestdes, [('cons', 'in_files')]),\n", - " (gunzip, onesamplettestdes, [('out_file',\n", - " 'explicit_mask_file')]),\n", - " (onesamplettestdes, level2estimate, [('spm_mat_file',\n", - " 'spm_mat_file')]),\n", - " (level2estimate, level2conestimate, [('spm_mat_file',\n", - " 'spm_mat_file'),\n", - " ('beta_images',\n", - " 'beta_images'),\n", - " ('residual_image',\n", - " 'residual_image')]),\n", - " (level2conestimate, level2thresh, [('spm_mat_file',\n", - " 'spm_mat_file'),\n", - " ('spmT_images',\n", - " 'stat_image'),\n", - " ]),\n", - " (level2conestimate, datasink, [('spm_mat_file',\n", - " '2ndLevel.@spm_mat'),\n", - " ('spmT_images',\n", - " '2ndLevel.@T'),\n", - " ('con_images',\n", - " '2ndLevel.@con')]),\n", - " (level2thresh, datasink, [('thresholded_map',\n", - " '2ndLevel.@threshold')]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize the workflow (SPM12)\n", - "\n", - "It always helps to visualize your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create 1st-level analysis output graph\n", - "l2analysis.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "\n", - "# Visualize the graph\n", - "from IPython.display import Image\n", - "Image(filename=opj(l2analysis.base_dir, 'spm_l2analysis', 'graph.dot.png'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Workflow (SPM12)\n", - "\n", - "Now that everything is ready, we can run the 1st-level analysis workflow. Change ``n_procs`` to the number of jobs/cores you want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l2analysis.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Group Analysis with ANTs\n", - "\n", - "Now to run the same group analysis, but on the ANTs normalized images, we just need to change a few parameters:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Change the SelectFiles template and recreate the node\n", - "templates = {'cons': opj(output_dir, 'norm_ants', 'sub-*_fwhm{fwhm_id}',\n", - " '{contrast_id}_trans.nii')}\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory=experiment_dir,\n", - " sort_filelist=True),\n", - " name=\"selectfiles\")\n", - "\n", - "# Change the substituion parameters for the datasink\n", - "substitutions = [('_contrast_id_', '')]\n", - "subjFolders = [('%s_fwhm_id_%s' % (con, f), 'ants_%s_fwhm%s' % (con, f))\n", - " for f in fwhm\n", - " for con in contrast_list]\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we just have to recreate the workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiation of the 2nd-level analysis workflow\n", - "l2analysis = Workflow(name='ants_l2analysis')\n", - "l2analysis.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect up the 2nd-level analysis components\n", - "l2analysis.connect([(infosource, selectfiles, [('contrast_id', 'contrast_id'),\n", - " ('fwhm_id', 'fwhm_id')]),\n", - " (selectfiles, onesamplettestdes, [('cons', 'in_files')]),\n", - " (gunzip, onesamplettestdes, [('out_file',\n", - " 'explicit_mask_file')]),\n", - " (onesamplettestdes, level2estimate, [('spm_mat_file',\n", - " 'spm_mat_file')]),\n", - " (level2estimate, level2conestimate, [('spm_mat_file',\n", - " 'spm_mat_file'),\n", - " ('beta_images',\n", - " 'beta_images'),\n", - " ('residual_image',\n", - " 'residual_image')]),\n", - " (level2conestimate, level2thresh, [('spm_mat_file',\n", - " 'spm_mat_file'),\n", - " ('spmT_images',\n", - " 'stat_image'),\n", - " ]),\n", - " (level2conestimate, datasink, [('spm_mat_file',\n", - " '2ndLevel.@spm_mat'),\n", - " ('spmT_images',\n", - " '2ndLevel.@T'),\n", - " ('con_images',\n", - " '2ndLevel.@con')]),\n", - " (level2thresh, datasink, [('thresholded_map',\n", - " '2ndLevel.@threshold')]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can run it!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l2analysis.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Visualize results\n", - "\n", - "Now we create a lot of outputs, but how do they look like? And also, what was the influence of different smoothing kernels and normalization?\n", - "\n", - "**Keep in mind, that the group analysis was only done on *`N=7`* subjects, and that we chose a voxel-wise threshold of *`p<0.005`*. Nonetheless, we corrected for multiple comparisons with a cluster-wise FDR threshold of *`p<0.05`*.**\n", - "\n", - "So let's first look at the contrast **average**:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "from nilearn.plotting import plot_stat_map\n", - "anatimg = '/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_T1.nii.gz'\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/ants_con_0001_fwhm4/spmT_0001_thr.nii', title='ants fwhm=4', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, display_mode='y', cut_coords=(-45, -30, -15, 0, 15), cmap='viridis')\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/spm_con_0001_fwhm4/spmT_0001_thr.nii', title='spm fwhm=4', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, display_mode='y', cut_coords=(-45, -30, -15, 0, 15), cmap='viridis')\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/ants_con_0001_fwhm8/spmT_0001_thr.nii', title='ants fwhm=8', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, display_mode='y', cut_coords=(-45, -30, -15, 0, 15), cmap='viridis')\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/spm_con_0001_fwhm8/spmT_0001_thr.nii', title='spm fwhm=8',\n", - " bg_img=anatimg, threshold=2, vmax=8, display_mode='y', cut_coords=(-45, -30, -15, 0, 15), cmap='viridis')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The results are more or less what you would expect: The peaks are more or less at the same places for the two normalization approaches and a wider smoothing has the effect of bigger clusters, while losing the sensitivity for smaller clusters." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's see other contrast -- **Finger > others**. Since we removed left handed subjects, the activation is seen on the left part of the brain." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nilearn.plotting import plot_stat_map\n", - "anatimg = '/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_T1.nii.gz'\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/ants_con_0005_fwhm4/spmT_0001_thr.nii', title='ants fwhm=4', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, cmap='viridis', display_mode='y', cut_coords=(-45, -30, -15, 0, 15))\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/spm_con_0005_fwhm4/spmT_0001_thr.nii', title='spm fwhm=4', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, cmap='viridis', display_mode='y', cut_coords=(-45, -30, -15, 0, 15))\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/ants_con_0005_fwhm8/spmT_0001_thr.nii', title='ants fwhm=8', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, cmap='viridis', display_mode='y', cut_coords=(-45, -30, -15, 0, 15))\n", - "\n", - "plot_stat_map(\n", - " '/output/datasink/2ndLevel/spm_con_0005_fwhm8/spmT_0001_thr.nii', title='spm fwhm=8', dim=1,\n", - " bg_img=anatimg, threshold=2, vmax=8, cmap='viridis', display_mode='y', cut_coords=(-45, -30, -15, 0, 15))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's see the results using the glass brain plotting method." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nilearn.plotting import plot_glass_brain\n", - "plot_glass_brain(\n", - " '/output/datasink/2ndLevel/spm_con_0005_fwhm4/spmT_0001_thr.nii',\n", - " threshold=2, display_mode='lyrz', black_bg=True, vmax=10, title='spm_fwhm4')\n", - "\n", - "plot_glass_brain(\n", - " '/output/datasink/2ndLevel/ants_con_0005_fwhm4/spmT_0001_thr.nii',\n", - " threshold=2, display_mode='lyrz', black_bg=True, vmax=10, title='ants_fwhm4')\n", - "\n", - "plot_glass_brain(\n", - " '/output/datasink/2ndLevel/spm_con_0005_fwhm8/spmT_0001_thr.nii',\n", - " threshold=2, display_mode='lyrz', black_bg=True, vmax=10, title='spm_fwhm8')\n", - "\n", - "plot_glass_brain(\n", - " '/output/datasink/2ndLevel/ants_con_0005_fwhm8/spmT_0001_thr.nii',\n", - " threshold=2, display_mode='lyrz', black_bg=True, vmax=10, title='ants_fwhm8')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/example_normalize.ipynb b/notebooks/example_normalize.ipynb deleted file mode 100644 index 6819de3..0000000 --- a/notebooks/example_normalize.ipynb +++ /dev/null @@ -1,586 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 3: Normalize data to MNI template\n", - "\n", - "This example covers the normalization of data. Some people prefer to normalize the data during the preprocessing, just before smoothing. I prefer to do the 1st-level analysis completely in subject space and only normalize the contrasts for the 2nd-level analysis. But both approaches are fine.\n", - "\n", - "For the current example, we will take the computed 1st-level contrasts from the previous experiment (again once done with fwhm=4mm and fwhm=8mm) and normalize them into MNI-space. To show two different approaches, we will do the normalization once with ANTs and once with SPM." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Normalization with ANTs\n", - "\n", - "The normalization with ANTs requires that you first compute the transformation matrix that would bring the anatomical images of each subject into template space. Depending on your system this might take a few hours per subject. To facilitate this step, the transformation matrix is already computed for the T1 images.\n", - "\n", - "The data for it can be found under:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!ls /data/ds000114/derivatives/fmriprep/sub-*/anat/*h5" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you want to compute the transformation matrix yourself, either use [fmriprep](http://fmriprep.readthedocs.io), as in our example dataset.\n", - "\n", - "Alternatively, you can also create a ANTS registration pipeline yourself. An example of such a pipeline would look as follows (**note, that you can load the script to see the workflow, but you don't have to run it, we will NOT use the output of the workflow in this nothebook**):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%load scripts/ANTS_registration.py" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Now, let's start with the ANTs normalization workflow!**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports (ANTs)\n", - "\n", - "First, we need to import all modules we later want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import join as opj\n", - "from nipype.interfaces.ants import ApplyTransforms\n", - "from nipype.interfaces.utility import IdentityInterface\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "from nipype.pipeline.engine import Workflow, Node, MapNode\n", - "from nipype.interfaces.fsl import Info" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Experiment parameters (ANTs)\n", - "\n", - "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script. And remember that we decided to run the group analysis without subject ``sub-01``, ``sub-06`` and ``sub-10`` because they are left handed (see [this section](https://miykael.github.io/nipype_tutorial/notebooks/example_1stlevel.html#Special-case))." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_dir = '/output'\n", - "output_dir = 'datasink'\n", - "working_dir = 'workingdir'\n", - "\n", - "# list of subject identifiers (remember we use only right handed subjects)\n", - "subject_list = ['sub-02', 'sub-03', 'sub-04', 'sub-05', 'sub-07', 'sub-08', 'sub-09']\n", - "\n", - "# task name\n", - "task_name = \"fingerfootlips\"\n", - "\n", - "# Smoothing widths used during preprocessing\n", - "fwhm = [4, 8]\n", - "\n", - "# Template to normalize to\n", - "template = '/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_T1.nii.gz'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note** that the **``template``** file might not be in your ``data`` directory. To get ``mni_icbm152_nlin_asym_09c``, either download it from this [website](https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9), unpack it and move it to ``/data/ds000114/derivatives/fmriprep/`` or run the following command in a cell:" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```bash\n", - "%%bash\n", - "curl -L https://files.osf.io/v1/resources/fvuh8/providers/osfstorage/580705089ad5a101f17944a9 \\\n", - " -o /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz\n", - " \n", - "tar xf /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz \\\n", - " -C /data/ds000114/derivatives/fmriprep/.\n", - " \n", - "rm /data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c.tar.gz\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Nodes (ANTs)\n", - "\n", - "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Apply Transformation - applies the normalization matrix to contrast images\n", - "apply2con = MapNode(ApplyTransforms(args='--float',\n", - " input_image_type=3,\n", - " interpolation='BSpline',\n", - " invert_transform_flags=[False],\n", - " num_threads=1,\n", - " reference_image=template,\n", - " terminal_output='file'),\n", - " name='apply2con', iterfield=['input_image'])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify input & output stream (ANTs)\n", - "\n", - "Specify where the input data can be found & where and how to save the output data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Infosource - a function free node to iterate over the list of subject names\n", - "infosource = Node(IdentityInterface(fields=['subject_id', 'fwhm_id']),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('subject_id', subject_list),\n", - " ('fwhm_id', fwhm)]\n", - "\n", - "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", - "templates = {'con': opj(output_dir, '1stLevel',\n", - " '{subject_id}/fwhm-{fwhm_id}', '???_00??.nii'),\n", - " 'transform': opj('/data/ds000114/derivatives/fmriprep/', '{subject_id}', 'anat',\n", - " '{subject_id}_t1w_space-mni152nlin2009casym_warp.h5')}\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory=experiment_dir,\n", - " sort_filelist=True),\n", - " name=\"selectfiles\")\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=experiment_dir,\n", - " container=output_dir),\n", - " name=\"datasink\")\n", - "\n", - "# Use the following DataSink output substitutions\n", - "substitutions = [('_subject_id_', '')]\n", - "subjFolders = [('_fwhm_id_%s%s' % (f, sub), '%s_fwhm%s' % (sub, f))\n", - " for f in fwhm\n", - " for sub in subject_list]\n", - "subjFolders += [('_apply2con%s/' % (i), '') for i in range(9)] # number of contrast used in 1stlevel an.\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Workflow (ANTs)\n", - "\n", - "Create a workflow and connect the interface nodes and the I/O stream to each other." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Initiation of the ANTs normalization workflow\n", - "antsflow = Workflow(name='antsflow')\n", - "antsflow.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect up the ANTs normalization components\n", - "antsflow.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", - " ('fwhm_id', 'fwhm_id')]),\n", - " (selectfiles, apply2con, [('con', 'input_image'),\n", - " ('transform', 'transforms')]),\n", - " (apply2con, datasink, [('output_image', 'norm_ants.@con')]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize the workflow (ANTs)\n", - "\n", - "It always helps to visualize your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create ANTs normalization graph\n", - "antsflow.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "\n", - "# Visualize the graph\n", - "from IPython.display import Image\n", - "Image(filename=opj(antsflow.base_dir, 'antsflow', 'graph.dot.png'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Workflow (ANTs)\n", - "\n", - "Now that everything is ready, we can run the ANTs normalization workflow. Change ``n_procs`` to the number of jobs/cores you want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "antsflow.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Normalization with SPM12\n", - "\n", - "The normalization with SPM12 is rather straight forward. The only thing we need to do is run the Normalize12 module. **So let's start!**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports (SPM12)\n", - "\n", - "First, we need to import all modules we later want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import join as opj\n", - "from nipype.interfaces.spm import Normalize12\n", - "from nipype.interfaces.utility import IdentityInterface\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "from nipype.algorithms.misc import Gunzip\n", - "from nipype.pipeline.engine import Workflow, Node" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Experiment parameters (SPM12)\n", - "\n", - "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script. And remember that we decided to run the group analysis without subject ``sub-01``, ``sub-06`` and ``sub-10`` because they are left handed (see [this section](https://miykael.github.io/nipype_tutorial/notebooks/example_1stlevel.html#Special-case))." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_dir = '/output'\n", - "output_dir = 'datasink'\n", - "working_dir = 'workingdir'\n", - "\n", - "# list of subject identifiers\n", - "subject_list = ['sub-02', 'sub-03', 'sub-04', 'sub-05', 'sub-07', 'sub-08', 'sub-09']\n", - "\n", - "# task name\n", - "task_name = \"fingerfootlips\"\n", - "\n", - "# Smoothing withds used during preprocessing\n", - "fwhm = [4, 8]\n", - "\n", - "template = '/opt/spm12/spm12_mcr/spm/spm12/tpm/TPM.nii'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Nodes (SPM12)\n", - "\n", - "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Gunzip - unzip the anatomical image\n", - "gunzip = Node(Gunzip(), name=\"gunzip\")\n", - "\n", - "# Normalize - normalizes functional and structural images to the MNI template\n", - "normalize = Node(Normalize12(jobtype='estwrite',\n", - " tpm=template,\n", - " write_voxel_sizes=[1, 1, 1]),\n", - " name=\"normalize\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify input & output stream (SPM12)\n", - "\n", - "Specify where the input data can be found & where and how to save the output data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Infosource - a function free node to iterate over the list of subject names\n", - "infosource = Node(IdentityInterface(fields=['subject_id', 'fwhm_id']),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('subject_id', subject_list),\n", - " ('fwhm_id', fwhm)]\n", - "\n", - "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", - "templates = {'con': opj(output_dir, '1stLevel',\n", - " '{subject_id}/fwhm-{fwhm_id}', '???_00??.nii'),\n", - " 'anat': opj('/data/ds000114/derivatives', 'fmriprep', '{subject_id}', \n", - " 'anat', '{subject_id}_t1w_preproc.nii.gz')}\n", - "\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory=experiment_dir,\n", - " sort_filelist=True),\n", - " name=\"selectfiles\")\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=experiment_dir,\n", - " container=output_dir),\n", - " name=\"datasink\")\n", - "\n", - "# Use the following DataSink output substitutions\n", - "substitutions = [('_subject_id_', '')]\n", - "subjFolders = [('_fwhm_id_%s%s' % (f, sub), '%s_fwhm%s' % (sub, f))\n", - " for f in fwhm\n", - " for sub in subject_list]\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Workflow (SPM12)\n", - "\n", - "Create a workflow and connect the interface nodes and the I/O stream to each other." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Specify Normalization-Workflow & Connect Nodes\n", - "spmflow = Workflow(name='spmflow')\n", - "spmflow.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect up SPM normalization components\n", - "spmflow.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", - " ('fwhm_id', 'fwhm_id')]),\n", - " (selectfiles, normalize, [('con', 'apply_to_files')]),\n", - " (selectfiles, gunzip, [('anat', 'in_file')]),\n", - " (gunzip, normalize, [('out_file', 'image_to_align')]),\n", - " (normalize, datasink, [('normalized_files', 'norm_spm.@files'),\n", - " ('normalized_image', 'norm_spm.@image'),\n", - " ]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize the workflow (SPM12)\n", - "\n", - "It always helps to visualize your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create SPM normalization graph\n", - "spmflow.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "\n", - "# Visualize the graph\n", - "from IPython.display import Image\n", - "Image(filename=opj(spmflow.base_dir, 'spmflow', 'graph.dot.png'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Workflow (SPM12)\n", - "\n", - "Now that everything is ready, we can run the SPM normalization workflow. Change ``n_procs`` to the number of jobs/cores you want to use." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "spmflow.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Comparison between ANTs and SPM normalization\n", - "\n", - "Now that we ran the normalization with ANTs and SPM, let us compare their output." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "from nilearn.plotting import plot_stat_map\n", - "anatimg = '/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_T1.nii.gz'" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, let's compare the normalization of the **anatomical** images:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stat_map(\n", - " '/data/ds000114/derivatives/fmriprep/sub-02/anat/sub-02_t1w_space-mni152nlin2009casym_preproc.nii.gz',\n", - " title='anatomy - ANTs (normalized to ICBM152)', bg_img=anatimg,\n", - " threshold=200, display_mode='ortho', cut_coords=(-50, 0, -10))\n", - "plot_stat_map(\n", - " '/output/datasink/norm_spm/sub-02_fwhm4/wsub-02_t1w_preproc.nii',\n", - " title='anatomy - SPM (normalized to SPM\\'s TPM)', bg_img=anatimg,\n", - " threshold=200, display_mode='ortho', cut_coords=(-50, 0, -10))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And what about the **contrast** images for **Finger > others**?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "plot_stat_map(\n", - " '/output/datasink/norm_ants/sub-02_fwhm8/con_0005_trans.nii', title='contrast5 - fwhm=8 - ANTs',\n", - " bg_img=anatimg, threshold=2, vmax=5, display_mode='ortho', cut_coords=(-39, -37, 56))\n", - "plot_stat_map(\n", - " '/output/datasink/norm_spm/sub-02_fwhm8/wcon_0005.nii', title='contrast5 - fwhm=8 - SPM',\n", - " bg_img=anatimg, threshold=2, vmax=5, display_mode='ortho', cut_coords=(-39, -37, 56))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nilearn.plotting import plot_glass_brain\n", - "plot_glass_brain(\n", - " '/output/datasink/norm_ants/sub-02_fwhm8/con_0005_trans.nii', colorbar=True,\n", - " threshold=3, display_mode='lyrz', black_bg=True, vmax=6, title='contrast5 - fwhm=8 - ANTs')\n", - "plot_glass_brain(\n", - " '/output/datasink/norm_spm/sub-02_fwhm8/wcon_0005.nii', colorbar=True,\n", - " threshold=3, display_mode='lyrz', black_bg=True, vmax=6, title='contrast5 - fwhm=8 - SPM')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/example_preprocessing.ipynb b/notebooks/example_preprocessing.ipynb deleted file mode 100644 index eff0bf1..0000000 --- a/notebooks/example_preprocessing.ipynb +++ /dev/null @@ -1,503 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Example 1: Preprocessing Workflow\n", - "\n", - "This is meant as a very simple example for a preprocessing workflow. In this workflow we will conduct the following steps:\n", - "\n", - "1. Motion correction of functional images with FSL's MCFLIRT\n", - "2. Coregistration of functional images to anatomical images (according to FSL's FEAT pipeline)\n", - "3. Smoothing of coregistrated functional images with FWHM set to 4mm and 8mm\n", - "4. Artifact Detection in functional images (to detect outlier volumes)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For every subject we have one anatomical T1w and 5 functional images. As a short recap, the image properties of the anatomy and the **fingerfootlips** functional image are:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "cd /data/ds000114/sub-01/ses-test\n", - "nib-ls a*/*.nii.gz f*/*fingerfootlips*.nii.gz" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**So, let's start!**" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Imports\n", - "\n", - "First, let's import all modules we later will be needing." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%matplotlib inline\n", - "from os.path import join as opj\n", - "import os\n", - "import json\n", - "from nipype.interfaces.fsl import (BET, ExtractROI, FAST, FLIRT, ImageMaths,\n", - " MCFLIRT, SliceTimer, Threshold)\n", - "from nipype.interfaces.spm import Smooth\n", - "from nipype.interfaces.utility import IdentityInterface\n", - "from nipype.interfaces.io import SelectFiles, DataSink\n", - "from nipype.algorithms.rapidart import ArtifactDetect\n", - "from nipype.pipeline.engine import Workflow, Node" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Experiment parameters\n", - "\n", - "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script. We will use one functional image for fingerfootlips task for ten subjects." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "experiment_dir = '/output'\n", - "output_dir = 'datasink'\n", - "working_dir = 'workingdir'\n", - "\n", - "# list of subject identifiers\n", - "subject_list = ['sub-01', 'sub-02', 'sub-03', 'sub-04', 'sub-05',\n", - " 'sub-06', 'sub-07', 'sub-08', 'sub-09', 'sub-10']\n", - "\n", - "# list of session identifiers\n", - "task_list = ['fingerfootlips']\n", - "\n", - "# Smoothing widths to apply\n", - "fwhm = [4, 8]\n", - "\n", - "# TR of functional images\n", - "with open('/data/ds000114/task-fingerfootlips_bold.json', 'rt') as fp:\n", - " task_info = json.load(fp)\n", - "TR = task_info['RepetitionTime']\n", - "\n", - "# Isometric resample of functional images to voxel size (in mm)\n", - "iso_size = 4" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Nodes for the main workflow\n", - "\n", - "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# ExtractROI - skip dummy scans\n", - "extract = Node(ExtractROI(t_min=4, t_size=-1),\n", - " output_type='NIFTI',\n", - " name=\"extract\")\n", - "\n", - "# MCFLIRT - motion correction\n", - "mcflirt = Node(MCFLIRT(mean_vol=True,\n", - " save_plots=True,\n", - " output_type='NIFTI'),\n", - " name=\"mcflirt\")\n", - "\n", - "# SliceTimer - correct for slice wise acquisition\n", - "slicetimer = Node(SliceTimer(index_dir=False,\n", - " interleaved=True,\n", - " output_type='NIFTI',\n", - " time_repetition=TR),\n", - " name=\"slicetimer\")\n", - "\n", - "# Smooth - image smoothing\n", - "smooth = Node(Smooth(), name=\"smooth\")\n", - "smooth.iterables = (\"fwhm\", fwhm)\n", - "\n", - "# Artifact Detection - determines outliers in functional images\n", - "art = Node(ArtifactDetect(norm_threshold=2,\n", - " zintensity_threshold=3,\n", - " mask_type='spm_global',\n", - " parameter_source='FSL',\n", - " use_differences=[True, False],\n", - " plot_type='svg'),\n", - " name=\"art\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Coregistration Workflow\n", - "\n", - "Initiate a workflow that coregistrates the functional images to the anatomical image (according to FSL's FEAT pipeline)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# BET - Skullstrip anatomical Image\n", - "bet_anat = Node(BET(frac=0.5,\n", - " robust=True,\n", - " output_type='NIFTI_GZ'),\n", - " name=\"bet_anat\")\n", - "\n", - "# FAST - Image Segmentation\n", - "segmentation = Node(FAST(output_type='NIFTI_GZ'),\n", - " name=\"segmentation\")\n", - "\n", - "# Select WM segmentation file from segmentation output\n", - "def get_wm(files):\n", - " return files[-1]\n", - "\n", - "# Threshold - Threshold WM probability image\n", - "threshold = Node(Threshold(thresh=0.5,\n", - " args='-bin',\n", - " output_type='NIFTI_GZ'),\n", - " name=\"threshold\")\n", - "\n", - "# FLIRT - pre-alignment of functional images to anatomical images\n", - "coreg_pre = Node(FLIRT(dof=6, output_type='NIFTI_GZ'),\n", - " name=\"coreg_pre\")\n", - "\n", - "# FLIRT - coregistration of functional images to anatomical images with BBR\n", - "coreg_bbr = Node(FLIRT(dof=6,\n", - " cost='bbr',\n", - " schedule=opj(os.getenv('FSLDIR'),\n", - " 'etc/flirtsch/bbr.sch'),\n", - " output_type='NIFTI_GZ'),\n", - " name=\"coreg_bbr\")\n", - "\n", - "# Apply coregistration warp to functional images\n", - "applywarp = Node(FLIRT(interp='spline',\n", - " apply_isoxfm=iso_size,\n", - " output_type='NIFTI'),\n", - " name=\"applywarp\")\n", - "\n", - "# Apply coregistration warp to mean file\n", - "applywarp_mean = Node(FLIRT(interp='spline',\n", - " apply_isoxfm=iso_size,\n", - " output_type='NIFTI_GZ'),\n", - " name=\"applywarp_mean\")\n", - "\n", - "# Create a coregistration workflow\n", - "coregwf = Workflow(name='coregwf')\n", - "coregwf.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect all components of the coregistration workflow\n", - "coregwf.connect([(bet_anat, segmentation, [('out_file', 'in_files')]),\n", - " (segmentation, threshold, [(('partial_volume_files', get_wm),\n", - " 'in_file')]),\n", - " (bet_anat, coreg_pre, [('out_file', 'reference')]),\n", - " (threshold, coreg_bbr, [('out_file', 'wm_seg')]),\n", - " (coreg_pre, coreg_bbr, [('out_matrix_file', 'in_matrix_file')]),\n", - " (coreg_bbr, applywarp, [('out_matrix_file', 'in_matrix_file')]),\n", - " (bet_anat, applywarp, [('out_file', 'reference')]),\n", - " (coreg_bbr, applywarp_mean, [('out_matrix_file', 'in_matrix_file')]),\n", - " (bet_anat, applywarp_mean, [('out_file', 'reference')]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify input & output stream\n", - "\n", - "Specify where the input data can be found & where and how to save the output data." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Infosource - a function free node to iterate over the list of subject names\n", - "infosource = Node(IdentityInterface(fields=['subject_id', 'task_name']),\n", - " name=\"infosource\")\n", - "infosource.iterables = [('subject_id', subject_list),\n", - " ('task_name', task_list)]\n", - "\n", - "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", - "anat_file = opj('derivatives', 'fmriprep', '{subject_id}', 'anat', '{subject_id}_t1w_preproc.nii.gz')\n", - "func_file = opj('{subject_id}', 'ses-test', 'func',\n", - " '{subject_id}_ses-test_task-{task_name}_bold.nii.gz')\n", - "\n", - "templates = {'anat': anat_file,\n", - " 'func': func_file}\n", - "selectfiles = Node(SelectFiles(templates,\n", - " base_directory='/data/ds000114'),\n", - " name=\"selectfiles\")\n", - "\n", - "# Datasink - creates output folder for important outputs\n", - "datasink = Node(DataSink(base_directory=experiment_dir,\n", - " container=output_dir),\n", - " name=\"datasink\")\n", - "\n", - "## Use the following DataSink output substitutions\n", - "substitutions = [('_subject_id_', ''),\n", - " ('_task_name_', '/task-'),\n", - " ('_fwhm_', 'fwhm-'),\n", - " ('_roi', ''),\n", - " ('_mcf', ''),\n", - " ('_st', ''),\n", - " ('_flirt', ''),\n", - " ('.nii_mean_reg', '_mean'),\n", - " ('.nii.par', '.par'),\n", - " ]\n", - "subjFolders = [('fwhm-%s/' % f, 'fwhm-%s_' % f) for f in fwhm]\n", - "substitutions.extend(subjFolders)\n", - "datasink.inputs.substitutions = substitutions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Specify Workflow\n", - "\n", - "Create a workflow and connect the interface nodes and the I/O stream to each other." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create a preprocessing workflow\n", - "preproc = Workflow(name='preproc')\n", - "preproc.base_dir = opj(experiment_dir, working_dir)\n", - "\n", - "# Connect all components of the preprocessing workflow\n", - "preproc.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", - " ('task_name', 'task_name')]),\n", - " (selectfiles, extract, [('func', 'in_file')]),\n", - " (extract, mcflirt, [('roi_file', 'in_file')]),\n", - " (mcflirt, slicetimer, [('out_file', 'in_file')]),\n", - "\n", - " (selectfiles, coregwf, [('anat', 'bet_anat.in_file'),\n", - " ('anat', 'coreg_bbr.reference')]),\n", - " (mcflirt, coregwf, [('mean_img', 'coreg_pre.in_file'),\n", - " ('mean_img', 'coreg_bbr.in_file'),\n", - " ('mean_img', 'applywarp_mean.in_file')]),\n", - " (slicetimer, coregwf, [('slice_time_corrected_file', 'applywarp.in_file')]),\n", - " \n", - " (coregwf, smooth, [('applywarp.out_file', 'in_files')]),\n", - "\n", - " (mcflirt, datasink, [('par_file', 'preproc.@par')]),\n", - " (smooth, datasink, [('smoothed_files', 'preproc.@smooth')]),\n", - " (coregwf, datasink, [('applywarp_mean.out_file', 'preproc.@mean')]),\n", - "\n", - " (coregwf, art, [('applywarp.out_file', 'realigned_files')]),\n", - " (mcflirt, art, [('par_file', 'realignment_parameters')]),\n", - "\n", - " (coregwf, datasink, [('coreg_bbr.out_matrix_file', 'preproc.@mat_file'),\n", - " ('bet_anat.out_file', 'preproc.@brain')]),\n", - " (art, datasink, [('outlier_files', 'preproc.@outlier_files'),\n", - " ('plot_files', 'preproc.@plot_files')]),\n", - " ])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize the workflow\n", - "\n", - "It always helps to visualize your workflow." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Create preproc output graph\n", - "preproc.write_graph(graph2use='colored', format='png', simple_form=True)\n", - "\n", - "# Visualize the graph\n", - "from IPython.display import Image\n", - "Image(filename=opj(preproc.base_dir, 'preproc', 'graph.dot.png'))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Visualize the detailed graph\n", - "preproc.write_graph(graph2use='flat', format='png', simple_form=True)\n", - "Image(filename=opj(preproc.base_dir, 'preproc', 'graph_detailed.dot.png'))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run the Workflow\n", - "\n", - "Now that everything is ready, we can run the preprocessing workflow. Change ``n_procs`` to the number of jobs/cores you want to use. **Note** that if you're using a Docker container and FLIRT fails to run without any good reason, you might need to change memory settings in the Docker preferences (6 GB should be enough for this workflow)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "preproc.run('MultiProc', plugin_args={'n_procs': 4})" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Inspect output\n", - "\n", - "Let's check the structure of the output folder, to see if we have everything we wanted to save." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree /output/datasink/preproc" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Visualize results\n", - "\n", - "Let's check the effect of the different smoothing kernels." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nilearn import image, plotting\n", - "plotting.plot_epi(\n", - " '/data/ds000114/derivatives/fmriprep/sub-01/anat/sub-01_t1w_preproc.nii.gz',\n", - " title=\"T1\", display_mode='ortho', annotate=False, draw_cross=False, cmap='gray')\n", - "\n", - "out_path = '/output/datasink/preproc/sub-01/task-fingerfootlips'\n", - "plotting.plot_epi(opj(out_path, 'sub-01_ses-test_task-fingerfootlips_bold_mean.nii.gz'),\n", - " title=\"fwhm = 0mm\", display_mode='ortho', annotate=False, draw_cross=False, cmap='gray')\n", - "\n", - "plotting.plot_epi(image.mean_img(opj(out_path, 'fwhm-4_ssub-01_ses-test_task-fingerfootlips_bold.nii')),\n", - " title=\"fwhm = 4mm\", display_mode='ortho', annotate=False, draw_cross=False, cmap='gray')\n", - "\n", - "plotting.plot_epi(image.mean_img(opj(out_path, 'fwhm-8_ssub-01_ses-test_task-fingerfootlips_bold.nii')),\n", - " title=\"fwhm = 8mm\", display_mode='ortho', annotate=False, draw_cross=False, cmap='gray')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, let's investigate the motion parameters. How much did the subject move and turn in the scanner?" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pylab as plt\n", - "par = np.loadtxt('/output/datasink/preproc/sub-01/task-fingerfootlips/sub-01_ses-test_task-fingerfootlips_bold.par')\n", - "fig, axes = plt.subplots(2, 1, figsize=(15, 5))\n", - "axes[0].set_ylabel('rotation (radians)')\n", - "axes[0].plot(par[0:, :3])\n", - "axes[1].plot(par[0:, 3:])\n", - "axes[1].set_xlabel('time (TR)')\n", - "axes[1].set_ylabel('translation (mm)')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "There seems to be a rather drastic motion around volume 102. Let's check if the outliers detection algorithm was able to pick this up." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "outlier_ids = np.loadtxt('/output/datasink/preproc/sub-01/task-fingerfootlips/art.sub-01_ses-test_task-fingerfootlips_bold_outliers.txt')\n", - "print('Outliers were detected at volumes: %s' % outlier_ids)\n", - "\n", - "from IPython.display import SVG\n", - "SVG(filename='/output/datasink/preproc/sub-01/task-fingerfootlips/plot.sub-01_ses-test_task-fingerfootlips_bold.svg')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/introduction_dataset.ipynb b/notebooks/introduction_dataset.ipynb deleted file mode 100644 index d4f7083..0000000 --- a/notebooks/introduction_dataset.ipynb +++ /dev/null @@ -1,149 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "

\n", - "

BRAIN IMAGING

\n", - "

DATA STRUCTURE

" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The dataset for this tutorial is structured according to the [Brain Imaging Data Structure (BIDS)](http://bids.neuroimaging.io/). BIDS is a simple and intuitive way to organize and describe your neuroimaging and behavioral data. Neuroimaging experiments result in complicated data that can be arranged in many different ways. So far there is no consensus how to organize and share data obtained in neuroimaging experiments. BIDS tackles this problem by suggesting a new standard for the arrangement of neuroimaging datasets." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The idea of BIDS is that the file and folder names follow a strict set of rules:\n", - "\n", - "![](../static/images/bids.png)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using the same structure for all of your studies will allow you to easily reuse all of your scripts between studies. But additionally, it also has the advantage that sharing code with and using scripts from other researchers will be much easier." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Tutorial Dataset\n", - "\n", - "For this tutorial we will be using a subset of the [fMRI dataset (ds000114)](https://openfmri.org/dataset/ds000114/) publicly available on [openfmri.org](https://openfmri.org). **If you're using the suggested Docker image you probably have all data needed to run the tutorial within the Docker container.**\n", - "If you want to have data locally you can use [Datalad](http://datalad.org/) to download a subset of the dataset, via the [datalad repository](http://datasets.datalad.org/?dir=/workshops/nih-2017/ds000114). In order to install dataset with all subrepositories you can run:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "cd /data\n", - "datalad install -r ///workshops/nih-2017/ds000114" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In order to download data you can use ``datalad get foldername`` command, to download all files in the folder ``foldername``. For this tutorial we only want to download part of the dataset, i.e. the anatomial and the first functional images:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%bash\n", - "cd /data/ds000114\n", - "datalad get sub-*/ses-test/anat\n", - "datalad get sub-*/ses-test/func/*fingerfootlips*\n", - "datalad get derivatives/fmriprep/sub-*/anat\n", - "datalad get derivatives/fmriprep/sub-*/ses-test/func/*fingerfootlips*" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "So let's have a look at the tutorial dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!tree -L 4 /data/ds000114/" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "As you can, for every subject we have one anatomical T1w image, five functional images and one diffusion weighted image. In addition, we have directory with derivatives. \n", - "\n", - "**Note**: If you used `datalad` or `git annex` to get the dataset, you can see symlinks for the image files." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Behavioral Task\n", - "\n", - "Subject from the ds000114 dataset did five behavioral tasks. In our dataset two of them are included. \n", - "\n", - "The **motor task** consisted of ***finger tapping***, ***foot twitching*** and ***lip poaching*** interleaved with fixation at a cross.\n", - "\n", - "The **landmark task** was designed to mimic the ***line bisection task*** used in neurological practice to diagnose spatial hemineglect. Two conditions were contrasted, specifically judging if a horizontal line had been bisected exactly in the middle, versus judging if a horizontal line was bisected at all. More about the dataset and studies you can find [here](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3641991/).\n", - "\n", - "To each of the functional images above, we therefore also have a tab-separated values file (``tva``), containing information such as stimuli onset, duration, type, etc. So let's have a look at one of them:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cat /data/ds000114/sub-01/ses-test/func/sub-01_ses-test_task-linebisection_events.tsv" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/introduction_docker.ipynb b/notebooks/introduction_docker.ipynb deleted file mode 100644 index 0b76f4a..0000000 --- a/notebooks/introduction_docker.ipynb +++ /dev/null @@ -1,223 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "# Docker\n", - "\n", - "[Docker](https://www.docker.com) is an open-source project that automates the deployment of applications inside software containers. Those containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, system tools, software libraries, such as Python, FSL, AFNI, SPM, FreeSurfer, ANTs, etc. This guarantees that it will always run the same, regardless of the environment it is running in.\n", - "\n", - "Important: **You don't need Docker to run Nipype on your system**. For Mac and Linux users, it probably is much simpler to install Nipype directly on your system. For more information on how to do this see the [Nipype website](http://nipype.readthedocs.io/en/latest/users/install.html). But for Windows user, or users that don't want to setup all the dependencies themselves, Docker is the way to go." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Docker Image for the interactive Nipype Tutorial\n", - "\n", - "If you want to run this Nipype Tutorial with the example dataset locally on your own system, you need to use the docker image, provided under [djarecka/nipype_tutorial](https://hub.docker.com/r/djarecka/nipype_tutorial/). This docker image sets up a Linux environment on your system, with functioning Python, Nipype, FSL, ANTs and SPM12 software package, some example data and all the tutorial notebooks to learn Nipype. Alternatively, you can also build your own docker image from Dockerfile or create a different Dockerfile using [Neurodocker](https://github.com/kaczmarj/neurodocker)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Install Docker\n", - "\n", - "Before you can do anything, you first need to install [Docker](https://www.docker.com) on your system. The installation process differes per system. Luckily, the docker homepage has nice instructions for...\n", - "\n", - " - [Ubuntu](https://docs.docker.com/engine/installation/linux/ubuntu/) or [Debian](https://docs.docker.com/engine/installation/linux/docker-ce/debian/)\n", - " - [Windows 7/8/9/10](https://docs.docker.com/toolbox/toolbox_install_windows/) or [Windows 10Pro](https://docs.docker.com/docker-for-windows/install/)\n", - " - [OS X (from El Capitan 10.11 on)](https://docs.docker.com/docker-for-mac/install/) or [OS X (before El Capitan 10.11)](https://docs.docker.com/toolbox/toolbox_install_mac/).\n", - "\n", - "Once Docker is installed, open up the docker terminal and test it works with the command:\n", - "\n", - " docker run hello-world\n", - "\n", - "**Note:** Linux users might need to use ``sudo`` to run ``docker`` commands or follow [post-installation steps](https://docs.docker.com/engine/installation/linux/linux-postinstall/)." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Pulling the Docker image\n", - "\n", - "You can download various Docker images, but for this tutorial we will suggest ``djarecka/nipype_tutorial``:\n", - "\n", - " docker pull djarecka/nipype_tutorial:latest\n", - " \n", - "Once it's done you can check available images on your system:\n", - "\n", - " docker images" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# How to run the Docker image\n", - "\n", - "After installing docker on your system and making sure that the ``hello-world`` example was running, we are good to go to start the Nipype Tutorial image. The exact implementation is a bit different for Windows user, but the general commands look similar.\n", - "\n", - "The suggested Docker image, djarecka/nipype_tutorial, already contains all tutorial notebooks and data used in the tutorial, so the simplest way to run container is:\n", - "\n", - " docker run -it --rm -p 8888:8888 djarecka/nipype_tutorial jupyter notebook\n", - " \n", - "However if you want use your version of notebooks, safes notebooks output locally or use you local data, you can also mount your local directories, e.g.: \n", - "\n", - " docker run -it --rm -v /path/to/nipype_tutorial/:/home/neuro/nipype_tutorial -v /path/to/data/:/data -v /path/to/output/:/output -p 8888:8888 djarecka/nipype_tutorial jupyter notebook\n", - "\n", - "But what do those flags mean?\n", - "\n", - "- The ``-it`` flag tells docker that it should open an interactive container instance.\n", - "- The ``--rm`` flag tells docker that the container should automatically be removed after we close docker.\n", - "- The ``-p`` flag specifies which port we want to make available for docker.\n", - "- The ``-v`` flag tells docker which folders should be mount to make them accesible inside the container. Here: ``/path/to/nipype_tutorial`` is your local directory where you downloaded [Nipype Tutorial repository](https://github.com/miykael/nipype_tutorial/). ``/path/to/data/`` is a directory where you have dataset [``ds000114``](https://openfmri.org/dataset/ds000114/), and ``/path/to/output`` can be an empty directory that will be used for output. The second part of the ``-v`` flag (here: ``/home/neuro/nipype_tutorial``, ``/data`` or ``/output``) specifies under which path the mounted folders can be found inside the container. **Important**: To use the ``tutorial``, ``data`` and ``output`` folder, you first need to create them on your system!\n", - "- ``sdjarecka/nipype_tutorial`` tells docker which image you want to run.\n", - "- ``jupyter notebook`` tells that you want to run directly the jupyter notebook command within the container. Alternatively, you can also use ``jupyter-lab``, ``bash`` or ``ipython``.\n", - "\n", - "**Note** that when you run this docker image without any more specification, than it will prompt you a URL link in your terminal that you will need to copy paste into your browser to get to the notebooks. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run a docker image on Linux or Mac\n", - "\n", - "Running a docker image on a Linux or Mac OS is very simple. Make sure that the folders ``tutorial``, ``data`` and ``output`` exist. Then just open a new terminal and use the command from above. Once the docker image is downloaded, open the shown URL link in your browser and you are good to go. The URL will look something like:\n", - "\n", - " http://localhost:8888/?token=0312c1ef3b61d7a44ff5346d3d150c23249a548850e13868" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Run a docker image on Windows\n", - "\n", - "Running a docker image on Windows is a bit trickier than on Ubuntu. Assuming you've installed the DockerToolbox, open the Docker Quickstart Terminal. Once the docker terminal is ready (when you see the whale), execute the following steps (see also figure):\n", - "\n", - "1. We need to check the IP adress of your docker machine. For this, use the command: \n", - "\n", - " ``docker-machine ip``\n", - "\n", - " In my case, this returned ``192.168.99.100``\n", - "\n", - "2. If you haven't already created a new folder to store your container output into, do so. You can create the folder either in the explorer as usual or do it with the command ``mkdir -p`` in the docker console. For example like this:\n", - "\n", - " ``mkdir -p /c/Users/username/output``\n", - "\n", - " Please replace ``username`` with the name of the current user on your system. **Pay attention** that the folder paths in the docker terminal are not backslash (``\\``) as we usually have in Windows. Also, ``C:\\`` needs to be specified as ``/c/``.\n", - "\n", - "3. Now, we can open run the container with the command from above:\n", - "\n", - " `` docker run -it --rm -v /c/Users/username/path/to/nipype_tutorial/:/home/neuro/nipype_tutorial -v /c/Users/username/path/to/data/:/data -v /c/Users/username/path/to/output/:/output -p 8888:8888 djarecka/nipype_tutorial``\n", - "\n", - "4. Once the docker image is downloaded, it will show you an URL that looks something like this:\n", - "\n", - " ``http://localhost:8888/?token=0312c1ef3b61d7a44ff5346d3d150c23249a548850e13868``\n", - " \n", - " This URL will not work on a Windows system. To make it work, you need to replace the string ``localhost`` with the IP address of your docker machine, that we acquired under step 1. Afterwards, your URL should look something like this:\n", - "\n", - " ``http://192.168.99.100:8888/?token=0312c1ef3b61d7a44ff5346d3d150c23249a548850e13868``\n", - "\n", - " Copy this link into your webbrowser and you're good to go!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Docker tips and tricks\n", - "\n", - "\n", - "## Access Docker Container with ``bash`` or ``ipython``\n", - "\n", - "You don't have to open a jupyter notebook when you run ``djarecka/nipype_tutorial``. You can also access the docker container directly with ``bash`` or ``ipython`` by adding it to the end of your command, i.e.:\n", - "\n", - " docker run -it --rm -v /path/to/nipype_tutorial/:/home/neuro/nipype_tutorial -v /path/to/data/:/data -v /path/to/output/:/output -p 8888:8888 djarecka/nipype_tutorial bash\n", - "\n", - "This also works with other software commands, such as ``bet`` etc." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Stop Docker Container\n", - "\n", - "To stop a running docker container, either close the docker terminal or select the terminal and uste the ``Ctrl-C`` shortcut multiple times." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## List all installed docker images\n", - "\n", - "To see a list of all installed docker images use:\n", - "\n", - " docker images" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Delete a specific docker image\n", - "\n", - "To delete a specific docker image, first use the ``docker images`` command to list all installed containers and than use the ``IMAGE ID`` and the ``rmi`` instruction to delete the container:\n", - "\n", - " docker rmi -f 7d9495d03763" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Export and Import a docker image\n", - "\n", - "If you don't want to depend on a internet connection, you can also export an already downloaded docker image and than later on import it on another PC. To do so, use the following two commands:\n", - "\n", - "\n", - " # Export docker image djarecka/nipype_tutorial\n", - " docker save -o nipype_tutorial.tar djarecka/nipype_tutorial\n", - "\n", - " # Import docker image on another PC\n", - " docker load --input nipype_tutorial.tar\n", - " \n", - "It might be possible that you run into administrator privileges isssues because you ran your docker command with ``sudo``. This means that òther users don't have access rights to ``nipype_tutorial.tar``. To avoid this, just change the rights of ``nipype_tutorial.tar`` with the command:\n", - "\n", - " sudo chmod 777 nipype_tutorial.tar" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/introduction_jupyter-notebook.ipynb b/notebooks/introduction_jupyter-notebook.ipynb deleted file mode 100644 index 1e8c075..0000000 --- a/notebooks/introduction_jupyter-notebook.ipynb +++ /dev/null @@ -1,262 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "# Jupyter Notebook\n", - "\n", - "This notebook was adapted from https://github.com/oesteban/biss2016 and is originally based on https://github.com/jvns/pandas-cookbook.\n", - "\n", - "[Jupyter Notebook](http://jupyter.org/) started as a web application, based on [IPython](https://ipython.org/) that can run Python code directly in the webbrowser. Now, Jupyter Notebook can handle over 40 programming languages and is *the* interactive, open source web application to run any scientific code.\n", - "\n", - "You might also want to try a new Jupyter environment [JupyterLab](https://github.com/jupyterlab/jupyterlab). " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## How to run a cell\n", - "\n", - "First, we need to explain how to run cells. Try to run the cell below!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "\n", - "print(\"Hi! This is a cell. Click on it and press the ▶ button above to run it\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can also run a cell with `Ctrl+Enter` or `Shift+Enter`. Experiment a bit with that." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Tab Completion" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "One of the most useful things about Jupyter Notebook is its tab completion. \n", - "\n", - "Try this: click just after `read_csv(` in the cell below and press `Shift+Tab` 4 times, slowly. Note that if you're using JupyterLab you don't have an additional help box option." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pd.read_csv(" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the first time, you should see this:\n", - "\n", - "![](../static/images/jupyter_tab-once.png)\n", - "\n", - "After the second time:\n", - "![](../static/images/jupyter_tab-twice.png)\n", - "\n", - "After the fourth time, a big help box should pop up at the bottom of the screen, with the full documentation for the `read_csv` function:\n", - "![](../static/images/jupyter_tab-4-times.png)\n", - "\n", - "I find this amazingly useful. I think of this as \"the more confused I am, the more times I should press `Shift+Tab`\".\n", - "\n", - "Okay, let's try tab completion for function names!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pd.r" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You should see this:\n", - "\n", - "![](../static/images/jupyter_function-completion.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Get Help\n", - "\n", - "There's an additional way on how you can reach the help box shown above after the fourth `Shift+Tab` press. Instead, you can also use `obj?` or `obj??` to get help or more help for an object." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "pd.read_csv?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Writing code\n", - "\n", - "Writing code in the notebook is pretty normal." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def print_10_nums():\n", - " for i in range(10):\n", - " print(i)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print_10_nums()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If you messed something up and want to revert to an older version of a code in a cell, use `Ctrl+Z` or to go than back `Ctrl+Y`.\n", - "\n", - "For a full list of all keyboard shortcuts, click on the small keyboard icon in the notebook header or click on `Help > Keyboard Shortcuts`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Saving a Notebook\n", - "\n", - "Jupyter Notebooks autosave, so you don't have to worry about losing code too much. At the top of the page you can usually see the current save status:\n", - "\n", - "- Last Checkpoint: 2 minutes ago (unsaved changes)\n", - "- Last Checkpoint: a few seconds ago (autosaved)\n", - "\n", - "If you want to save a notebook on purpose, either click on `File > Save and Checkpoint` or press `Ctrl+S`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Magic functions" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "IPython has all kinds of magic functions. Magic functions are prefixed by % or %%, and typically take their arguments without parentheses, quotes or even commas for convenience. Line magics take a single % and cell magics are prefixed with two %%.\n", - "\n", - "Some useful magic functions are:\n", - "\n", - "Magic Name | Effect\n", - "---------- | -------------------------------------------------------------\n", - "%env | Get, set, or list environment variables\n", - "%pdb | Control the automatic calling of the pdb interactive debugger\n", - "%pylab | Load numpy and matplotlib to work interactively\n", - "%%debug | Activates debugging mode in cell\n", - "%%html | Render the cell as a block of HTML\n", - "%%latex | Render the cell as a block of latex\n", - "%%sh | %%sh script magic\n", - "%%time | Time execution of a Python statement or expression\n", - "\n", - "You can run `%magic` to get a list of magic functions or `%quickref` for a reference sheet." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Example 1: Let's see how long a specific command takes with `%time` or `%%time`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%time result = sum([x for x in range(10**6)])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Example 2: Let's use `%%latex` to render a block of latex" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%latex\n", - "$$F(k) = \\int_{-\\infty}^{\\infty} f(x) e^{2\\pi i k} \\mathrm{d} x$$" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/introduction_nipype.ipynb b/notebooks/introduction_nipype.ipynb deleted file mode 100644 index 3f492e8..0000000 --- a/notebooks/introduction_nipype.ipynb +++ /dev/null @@ -1,284 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# What is Nipype?\n", - "\n", - "- **[Nipype](http://nipype.readthedocs.io/en/latest/)** is an open-source, community-developed software package written in **Python**.\n", - "- Provides unified way of **interfacing** with heterogeneous neuroimaging software like [SPM](http://www.fil.ion.ucl.ac.uk/spm/), [FSL](http://fsl.fmrib.ox.ac.uk/fsl/fslwiki/), [FreeSurfer](http://surfer.nmr.mgh.harvard.edu/), [AFNI](https://afni.nimh.nih.gov/afni), [ANTS](http://stnava.github.io/ANTs/), [Camino](http://web4.cs.ucl.ac.uk/research/medic/camino/pmwiki/pmwiki.php), [MRtrix](http://www.brain.org.au/software/mrtrix/index.html), [MNE](https://martinos.org/mne/stable/index.html), [Slicer](https://www.slicer.org/) and many more.\n", - "- Allows users to create **flexible, complex workflows** consisting of multiple processing steps using any software package above\n", - "- Efficient and optimized computation through **parallel execution** plugins" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# I don't need that, I'm happy with SPM12!\n", - "\n", - "I mean, there's no problem with SPM's batch system...\n", - "\n", - "\n", - "\n", - "ok, ok... it get's tiring to have a separate batch script for each subject and MATLAB license issues are sometimes a pain. But hey, the nice looking GUI makes it so easy to use!" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Using SPM12 with Nipype is simpler than any ``matlabbatch`` and it's intuitive to read:\n", - "\n", - "```python\n", - "from nipype.interfaces.spm import Smooth\n", - "smooth = Smooth()\n", - "smooth.inputs.in_files = 'functional.nii'\n", - "smooth.inputs.fwhm = 6\n", - "smooth.run()\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# I don't need that, I'm happy with FSL!\n", - "\n", - "The GUI might look a bit old fashion but the command line interface gives me all the flexibility I need!\n", - "\n", - "\n", - "\n", - "I don't care that it might be more difficult to learn than other neuroimaging softwares. At least it doesn't take me 20 clicks to do simple motion correction. And once you figure out the underlying commands, it's rather simple to script." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Nipype makes using FSL even easier:\n", - "\n", - "```python\n", - "from nipype.interfaces.fsl import MCFLIRT\n", - "mcflt = MCFLIRT()\n", - "mcflt.inputs.in_file = 'functional.nii'\n", - "mcflt.run()\n", - "```\n", - "\n", - "And gives you transparency to what's happening under the hood with one additional line:\n", - "\n", - "```python\n", - "In [1]: mcflt.cmdline\n", - "Out[1]: 'mcflirt -in functional.nii -out functional_mcf.nii'\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# I don't need that, I'm happy with FreeSurfer!\n", - "\n", - "You and your problems with fMRI data. I'm perfectly happy with FreeSurfer's command line interface. It gives me all I need to do surface based analyses.\n", - "\n", - "\n", - "\n", - "Of course, you can run your sequential FreeSurfer scripts as you want. But wouldn't it be nice to optimize computation time by using parallel computation?" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's imagine you want to do smoothing on the surface, with **two different FWHM** values, on **both hemispheres** and this on **six subjects**, all in **parallel**? With Nipype this is as simple as that:\n", - "\n", - "```python\n", - "from nipype.interfaces.freesurfer import SurfaceSmooth\n", - "smoother = SurfaceSmooth()\n", - "smoother.inputs.in_file = \"{hemi}.func.mgz\"\n", - "smoother.iterables = [(\"hemi\", ['lh', 'rh']),\n", - " (\"fwhm\", [4, 8]),\n", - " (\"subject_id\", ['sub01', 'sub02', 'sub03',\n", - " 'sub04', 'sub05', 'sub06']),\n", - " ]\n", - "smoother.run(mode='parallel')\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# But I like my neuorimaging toolbox\n", - "\n", - "- You can keep it! But instead of being stuck in MATLAB with SPM, or having scripting issues with FreeSurfer, ANTs or FSL,..\n", - "- **Nipype** gives you the possibility to select the algorithms that you prefer from many different sofware packages.\n", - "- In short, you can have all the advantages without the disadvantage of being stuck with a programming language or software package" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# A short Example\n", - "\n", - "Let's assume we want to do preprocessing that uses **SPM** for *motion correction*, **FreeSurfer** for *coregistration*, **ANTS** for *normalization* and **FSL** for *smoothing*. Normally this would be a hell of a mess. It would mean switching between multiple scripts in different programming languages with a lot of manual intervention. **Nipype comes to the rescue!**\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Code Example\n", - "\n", - "The code to create an Nipype workflow like the example before would look something like this:\n", - "\n", - "```python\n", - "# Import modules\n", - "import nipype\n", - "from nipype.interfaces.freesurfer import BBRegister\n", - "from nipype.interfaces.ants import WarpTimeSeriesImageMultiTransform\n", - "from nipype.interfaces.fsl import SUSAN\n", - "from nipype.interfaces.spm import Realing\n", - "\n", - "# Motion Correction (SPM)\n", - "realign = Realing(register_to_mean=True)\n", - "\n", - "# Coregistration (FreeSurfer)\n", - "coreg = BBRegister()\n", - "\n", - "# Normalization (ANTS)\n", - "normalize = WarpTimeSeriesImageMultiTransform()\n", - "\n", - "# Smoothing (FSL)\n", - "smooth = SUSAN(fwhm=6.0)\n", - "\n", - "\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "```python\n", - "# Where can the raw data be found?\n", - "grabber = nipype.DataGrabber()\n", - "grabber.inputs.base_directory = '~/experiment_folder/data'\n", - "grabber.inputs.subject_id = ['subject1', 'subject2', 'subject3']\n", - "\n", - "# Where should the output data be stored at?\n", - "sink = nipype.DataSink()\n", - "sink.inputs.base_directory = '~/experiment_folder/output_folder'\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```python\n", - "# Create a workflow to connect all those nodes\n", - "preprocflow = nipype.Workflow()\n", - "\n", - "# Connect the nodes to each other\n", - "preprocflow.connect([(grabber -> realign ),\n", - " (realign -> coreg ),\n", - " (coreg -> normalize),\n", - " (normalize -> smooth ),\n", - " (smooth -> sink )\n", - " ])\n", - "\n", - "# Run the workflow in parallel\n", - "preprocflow.run(mode='parallel')\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Important**: This code is a shortened and simplified version of the real Nipype code. But it gives you a good idea of how intuitive it is to use Nipype for your neuroimaging analysis." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# So again, what is Nipype?\n", - "\n", - "Nipype consists of many parts, but the most important ones are [Interfaces](basic_interfaces.ipynb), the [Workflow Engine](basic_workflow.ipynb) and the [Execution Plugins](basic_plugins.ipynb):\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* **Interface**: Wraps a program or function\n", - "\n", - "* **Node/MapNode**: Wraps an `Interface` for use in a Workflow that provides caching and other goodies (e.g., pseudo-sandbox)\n", - "* **Workflow**: A *graph* or *forest of graphs* whose nodes are of type `Node`, `MapNode` or `Workflow` and whose edges represent data flow\n", - "\n", - "* **Plugin**: A component that describes how a `Workflow` should be executed" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Slideshow Mode" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!jupyter-nbconvert --to slides introduction_nipype.ipynb --reveal-prefix=reveal.js" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/introduction_nipype.slides.html b/notebooks/introduction_nipype.slides.html deleted file mode 100644 index 9fe4de3..0000000 --- a/notebooks/introduction_nipype.slides.html +++ /dev/null @@ -1,12133 +0,0 @@ - - - - - - - - - - - -introduction_nipype slides - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
-
-
-
-
-

- -
-
-
-
-
-
-
-
-

What is Nipype?

    -
  • Nipype is an open-source, community-developed software package written in Python.
  • -
  • Provides unified way of interfacing with heterogeneous neuroimaging software like SPM, FSL, FreeSurfer, AFNI, ANTS, Camino, MRtrix, MNE, Slicer and many more.
  • -
  • Allows users to create flexible, complex workflows consisting of multiple processing steps using any software package above
  • -
  • Efficient and optimized computation through parallel execution plugins
  • -
- -
-
-
-
-
-
-
-
-

I don't need that, I'm happy with SPM12!

I mean, there's no problem with SPM's batch system...

-

-

ok, ok... it get's tiring to have a separate batch script for each subject and MATLAB license issues are sometimes a pain. But hey, the nice looking GUI makes it so easy to use!

- -
-
-
-
-
-
-
-
-

Using SPM12 with Nipype is simpler than any matlabbatch and it's intuitive to read:

-
from nipype.interfaces.spm import Smooth
-smooth = Smooth()
-smooth.inputs.in_files = 'functional.nii'
-smooth.inputs.fwhm = 6
-smooth.run()
-
- -
-
-
-
-
-
-
-
-

I don't need that, I'm happy with FSL!

The GUI might look a bit old fashion but the command line interface gives me all the flexibility I need!

-

-

I don't care that it might be more difficult to learn than other neuroimaging softwares. At least it doesn't take me 20 clicks to do simple motion correction. And once you figure out the underlying commands, it's rather simple to script.

- -
-
-
-
-
-
-
-
-

Nipype makes using FSL even easier:

-
from nipype.interfaces.fsl import MCFLIRT
-mcflt = MCFLIRT()
-mcflt.inputs.in_file = 'functional.nii'
-mcflt.run()
-
-

And gives you transparency to what's happening under the hood with one additional line:

-
In [1]: mcflt.cmdline
-Out[1]: 'mcflirt -in functional.nii -out functional_mcf.nii'
-
- -
-
-
-
-
-
-
-
-

I don't need that, I'm happy with FreeSurfer!

You and your problems with fMRI data. I'm perfectly happy with FreeSurfer's command line interface. It gives me all I need to do surface based analyses.

-

-

Of course, you can run your sequential FreeSurfer scripts as you want. But wouldn't it be nice to optimize computation time by using parallel computation?

- -
-
-
-
-
-
-
-
-

Let's imagine you want to do smoothing on the surface, with two different FWHM values, on both hemispheres and this on six subjects, all in parallel? With Nipype this is as simple as that:

-
from nipype.interfaces.freesurfer import SurfaceSmooth
-smoother = SurfaceSmooth()
-smoother.inputs.in_file = "{hemi}.func.mgz"
-smoother.iterables = [("hemi", ['lh', 'rh']),
-                      ("fwhm", [4, 8]),
-                      ("subject_id", ['sub01', 'sub02', 'sub03',
-                                      'sub04', 'sub05', 'sub06']),
-                      ]
-smoother.run(mode='parallel')
-
- -
-
-
-
-
-
-
-
-

But I like my neuorimaging toolbox

    -
  • You can keep it! But instead of being stuck in MATLAB with SPM, or having scripting issues with FreeSurfer, ANTs or FSL,..
  • -
  • Nipype gives you the possibility to select the algorithms that you prefer from many different sofware packages.
  • -
  • In short, you can have all the advantages without the disadvantage of being stuck with a programming language or software package
  • -
- -
-
-
-
-
-
-
-
-

A short Example

Let's assume we want to do preprocessing that uses SPM for motion correction, FreeSurfer for coregistration, ANTS for normalization and FSL for smoothing. Normally this would be a hell of a mess. It would mean switching between multiple scripts in different programming languages with a lot of manual intervention. Nipype comes to the rescue!

-

- -
-
-
-
-
-
-
-
-

Code Example

The code to create an Nipype workflow like the example before would look something like this:

-
# Import modules
-import nipype
-from nipype.interfaces.freesurfer import BBRegister
-from nipype.interfaces.ants       import WarpTimeSeriesImageMultiTransform
-from nipype.interfaces.fsl        import SUSAN
-from nipype.interfaces.spm        import Realing
-
-# Motion Correction (SPM)
-realign = Realing(register_to_mean=True)
-
-# Coregistration (FreeSurfer)
-coreg = BBRegister()
-
-# Normalization (ANTS)
-normalize = WarpTimeSeriesImageMultiTransform()
-
-# Smoothing (FSL)
-smooth = SUSAN(fwhm=6.0)
-
- -
-
-
-
-
-
-
-
-
# Where can the raw data be found?
-grabber = nipype.DataGrabber()
-grabber.inputs.base_directory = '~/experiment_folder/data'
-grabber.inputs.subject_id = ['subject1', 'subject2', 'subject3']
-
-# Where should the output data be stored at?
-sink = nipype.DataSink()
-sink.inputs.base_directory = '~/experiment_folder/output_folder'
-
- -
-
-
-
-
-
-
-
-
# Create a workflow to connect all those nodes
-preprocflow = nipype.Workflow()
-
-# Connect the nodes to each other
-preprocflow.connect([(grabber   -> realign  ),
-                     (realign   -> coreg    ),
-                     (coreg     -> normalize),
-                     (normalize -> smooth   ),
-                     (smooth    -> sink     )
-                     ])
-
-# Run the workflow in parallel
-preprocflow.run(mode='parallel')
-
- -
-
-
-
-
-
-
-
-

So again, what is Nipype?

Nipype consists of many parts, but the most important ones are Interfaces, the Workflow Engine and the Execution Plugins:

-

- -
-
-
-
-
- - - - - - - diff --git a/notebooks/introduction_python.ipynb b/notebooks/introduction_python.ipynb deleted file mode 100644 index 28f4942..0000000 --- a/notebooks/introduction_python.ipynb +++ /dev/null @@ -1,2508 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "
\n", - "\n", - "# Python\n", - "\n", - "This section is meant as a general introduction to Python and is by far not complete. It is based amongst others on the [IPython notebooks from J. R. Johansson](http://github.com/jrjohansson/scientific-python-lectures), on http://www.stavros.io/tutorials/python/ and on http://www.swaroopch.com/notes/python.\n", - "\n", - "Important: a very good interactive tutorial for Python can also be found on https://www.codecademy.com/learn/python" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The goal of this section is to give you a short introduction to Python and help beginners to get familiar with this programming language.\n", - "\n", - "Following chapters are available:\n", - "\n", - "- [Module](#Module)\n", - "- [Help and Descriptions](#Help-and-Descriptions)\n", - "- [Variables and types](#Variables-and-types)\n", - " - [Symbol names](#Symbol-names)\n", - " - [Assignment](#Assignment)\n", - " - [Fundamental types](#Fundamental-types)\n", - "- [Operators and comparisons](#Operators-and-comparisons)\n", - " - [Shortcut math operation and assignment](#Shortcut-math-operation-and-assignment)\n", - "- [Strings, List and dictionaries](#Strings,-List-and-dictionaries)\n", - " - [Strings](#Strings)\n", - " - [List](#List)\n", - " - [Tuples](#Tuples)\n", - " - [Dictionaries](#Dictionaries)\n", - "- [Indentation](#Indentation)\n", - "- [Control Flow](#Control-Flow)\n", - " - [Conditional statements: `if`, `elif`, `else`](#Conditional-statements:-if,-elif,-else)\n", - "- [Loops](#Loops)\n", - " - [`for` loops](#for-loops)\n", - " - [`break`, `continue` and `pass`](#break,-continue-and-pass)\n", - "- [Functions](#Functions)\n", - " - [Default argument and keyword arguments](#Default-argument-and-keyword-arguments)\n", - " - [`*args` and `*kwargs` parameters](#*args-and-*kwargs-parameters)\n", - " - [Unnamed functions: `lambda` function](#Unnamed-functions:-lambda-function)\n", - "- [Classes](#Classes)\n", - "- [Modules](#Modules)\n", - "- [Exceptions](#Exceptions)\n", - "- [File I/O](#File-I/O)\n", - " - [Reading CSV files](#Reading-CSV-files)\n", - " - [Writing CSV files](#Writing-CSV-files)\n", - " - [Reading TXT files](#Reading-TXT-files)\n", - " - [Writing TXT files](#Writing-TXT-files)\n", - " - [with open](#with-open)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Module\n", - "\n", - "Most of the functionality in Python is provided by *modules*.To use a module in a Python program it first has to be imported. A module can be imported using the `import` statement. For example, to import the module `math`, which contains many standard mathematical functions, we can do:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import math" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This includes the whole module and makes it available for use later in the program. For example, we can do:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import math\n", - "\n", - "x = math.cos(2 * math.pi)\n", - "\n", - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Importing the whole module us often times unnecessary and can lead to longer loading time or increase the memory consumption. Alternative to the previous method, we can also chose to import only a few selected functions from a module by explicitly listing which ones we want to import:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from math import cos, pi\n", - "\n", - "x = cos(2 * pi)\n", - "\n", - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It is also possible to give an imported module or symbol your own access name with the `as` additional:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "from math import pi as number_pi\n", - "\n", - "x = np.rad2deg(number_pi)\n", - "\n", - "print(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Help and Descriptions\n", - "\n", - "Using the function `help` we can get a description of almost all functions. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "help(math.log)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "math.log(10)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "math.log(10, 2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Variables and types\n", - "\n", - "\n", - "### Symbol names \n", - "\n", - "Variable names in Python can contain alphanumerical characters `a-z`, `A-Z`, `0-9` and some special characters such as `_`. Normal variable names must start with a letter. \n", - "\n", - "By convention, variable names start with a lower-case letter, and Class names start with a capital letter. \n", - "\n", - "In addition, there are a number of Python keywords that cannot be used as variable names. These keywords are:\n", - "\n", - " and, as, assert, break, class, continue, def, del, elif, else, except, exec, finally, for, from, global, if, import, in, is, lambda, not, or, pass, print, raise, return, try, while, with, yield" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Assignment\n", - "\n", - "The assignment operator in Python is `=`. Python is a dynamically typed language, so we do not need to specify the type of a variable when we create one.\n", - "\n", - "Assigning a value to a new variable creates the variable:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# variable assignments\n", - "x = 1.0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Although not explicitly specified, a variable does have a type associated with it. The type is derived form the value it was assigned." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "type(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we assign a new value to a variable, its type can change." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "type(x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we try to use a variable that has not yet been defined we get an `NameError` (Note, that we will use in the notebooks `try/except` blocks to handle the exception, so the notebook doesn't stop. The code below will try to execute `print` function and if the `NameError` occurs the error message will be printed. Otherwise an error will be raised. Later in this notebook you will learn more about exception handling.):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " print(y)\n", - "except(NameError) as err:\n", - " print(\"NameError\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Fundamental types" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# integers\n", - "x = 1\n", - "type(x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# float\n", - "x = 1.0\n", - "type(x)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# boolean\n", - "b1 = True\n", - "b2 = False\n", - "\n", - "type(b1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# string\n", - "s = \"hallo world\"\n", - "\n", - "type(s)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Operators and comparisons\n", - "\n", - "Most operators and comparisons in Python work as one would expect:\n", - "\n", - "* Arithmetic operators `+`, `-`, `*`, `/`, `**` power, `%` modulo\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "[1 + 2, \n", - " 1 - 2,\n", - " 1 * 2,\n", - " 1 % 2]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In Python 2.7, what kind of division (`/`) will be executed, depends on the type of the numbers involved. If all numbers are integers, the division will be an integer division, otherwise it will be a float division. In Python 3 this has been changed and fractions aren't lost when dividing integers (for integer division you can use another operator, `//`). " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# In Python 3 these two operations will give the same result\n", - "# (in Python 2 the first one will be treated as an integer division). \n", - "print(1 / 2)\n", - "print(1 / 2.0)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Note! The power operators in python isn't ^, but **\n", - "2 ** 2" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* The boolean operators are spelled out as words `and`, `not`, `or`. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "True and False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "not False" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "True or False" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "* Comparison operators `>`, `<`, `>=` (greater or equal), `<=` (less or equal), `==` (equal), `!=` (not equal) and `is` (identical)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "2 > 1, 2 < 1" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "2 > 2, 2 < 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "2 >= 2, 2 <= 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# equal to\n", - "[1,2] == [1,2]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# not equal to\n", - "2 != 3" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- boolean operator" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = True\n", - "y = False\n", - "\n", - "print(not x)\n", - "print(x and y)\n", - "print(x or y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- String comparison" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"lo W\" in \"Hello World\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "\"x\" not in \"Hello World\"" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Shortcut math operation and assignment" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 2\n", - "a = a * 2\n", - "print(a)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The command `a = a * 2`, can be shortcut to `a *= 2`. This also works with `+=`, `-=` and `/=`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "b = 3\n", - "b *= 3\n", - "print(b)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Strings, List and dictionaries\n", - "\n", - "### Strings\n", - "\n", - "Strings are the variable type that is used for storing text messages. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s = \"Hello world\"\n", - "type(s)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# length of the string: number of characters in string\n", - "len(s)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# replace a substring in a string with something else\n", - "s2 = s.replace(\"world\", \"test\")\n", - "print(s2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can index a character in a string using `[]`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Heads up MATLAB users:** Indexing start at 0!\n", - "\n", - "We can extract a part of a string using the syntax `[start:stop]`, which extracts characters between index `start` and `stop`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[0:5]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we omit either (or both) of `start` or `stop` from `[start:stop]`, the default is the beginning and the end of the string, respectively:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[:5]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[6:]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[:]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can also define the step size using the syntax `[start:end:step]` (the default value for `step` is 1, as we saw above):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[::1]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s[::2]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This technique is called *slicing*." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### String formatting examples" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"str1\" + \"str2\" + \"str3\") # strings added with + are concatenated without space" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"str1\" \"str2\" \"str3\") # The print function concatenates strings differently\n", - "print(\"str1\", \"str2\", \"str3\") # depending on how the inputs are specified\n", - "print((\"str1\", \"str2\", \"str3\")) # See the three different outputs below" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"str1\", 1.0, False) # The print function converts all arguments to strings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"value = %f\" %1.0) # we can use C-style string formatting" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python has two string formatting styles. An example of the old style is below, specifier `%.2f` transforms the input number into a string, that corresponds to a floating point number with 2 decimal places and the specifier `%d` transforms the input number into a string, corresponding to a decimal number." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s2 = \"value1 = %.2f. value2 = %d\" % (3.1415, 1.5)\n", - "\n", - "print(s2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The same string can be written using the new style string formatting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "s3 = 'value1 = {:.2f}, value2 = {}'.format(3.1415, 1.5)\n", - "\n", - "print(s3)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Newlines are indicated by \\nAnd tabs by \\t.\")\n", - "\n", - "print(r\"Newlines are indicated by \\nAnd tabs by \\t. Printed as rawstring\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"Name: {}\\nNumber: {}\\nString: {}\".format(\"Nipype\", 3, 3 * \"-\"))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "strString = \"\"\"This is\n", - "a multiline\n", - "string.\"\"\"\n", - "print(strString)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"This {verb} a {noun}.\".format(noun = \"test\", verb = \"is\"))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "\n", - "\n", - "\n", - "\n", - "#### Single Quote\n", - "You can specify strings using single quotes such as `'Quote me on this'`.\n", - "All white space i.e. spaces and tabs, within the quotes, are preserved as-is.\n", - "\n", - "#### Double Quotes\n", - "Strings in double quotes work exactly the same way as strings in single quotes. An example is `\"What's your name?\"`.\n", - "\n", - "#### Triple Quotes\n", - "\n", - "You can specify multi-line strings using triple quotes - (`\"\"\"` or `'''`). You can use single quotes and double quotes freely within the triple quotes. An example is:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "'''This is a multi-line string. This is the first line.\n", - "This is the second line.\n", - "\"What's your name?,\" I asked.\n", - "He said \"Bond, James Bond.\"\n", - "'''" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### List\n", - "\n", - "Lists are very similar to strings, except that each element can be of any type.\n", - "\n", - "The syntax for creating lists in Python is `[...]`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = [1,2,3,4]\n", - "\n", - "print(type(l))\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can use the same slicing techniques to manipulate lists as we could use on strings:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(l)\n", - "print(l[1:3])\n", - "print(l[::2])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Heads up MATLAB users:** Indexing starts at 0!" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l[0]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Elements in a list do not all have to be of the same type:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l = [1, 'a', 1.0]\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Python lists can be inhomogeneous and arbitrarily nested:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "nested_list = [1, [2, [3, [4, [5]]]]]\n", - "\n", - "nested_list" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Lists play a very important role in Python, and are for example used in loops and other flow control structures (discussed below). There are number of convenient functions for generating lists of various types, for example the `range` function (note that in Python 3 `range` creates a generator, so you have to use `list` function to get a list):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "start = 10\n", - "stop = 30\n", - "step = 2\n", - "\n", - "list(range(start, stop, step))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# convert a string to a list by type casting:\n", - "\n", - "print(s)\n", - "\n", - "s2 = list(s)\n", - "\n", - "s2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# sorting lists\n", - "s2.sort()\n", - "\n", - "print(s2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Adding, inserting, modifying, and removing elements from lists" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# create a new empty list\n", - "l = []\n", - "\n", - "# add an elements using `append`\n", - "l.append(\"A\")\n", - "l.append(\"d\")\n", - "l.append(\"d\")\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can modify lists by assigning new values to elements in the list. In technical jargon, lists are *mutable*." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l[1] = \"p\"\n", - "l[2] = \"t\"\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l[1:3] = [\"s\", \"m\"]\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Insert an element at an specific index using `insert`" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l.insert(0, \"i\")\n", - "l.insert(1, \"n\")\n", - "l.insert(2, \"s\")\n", - "l.insert(3, \"e\")\n", - "l.insert(4, \"r\")\n", - "l.insert(5, \"t\")\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remove first element with specific value using 'remove'" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l.remove(\"A\")\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Remove an element at a specific location using `del`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "del l[7]\n", - "del l[6]\n", - "\n", - "print(l)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Tuples\n", - "\n", - "Tuples are like lists, except that they cannot be modified once created, that is they are *immutable*. \n", - "\n", - "In Python, tuples are created using the syntax `(..., ..., ...)`, or even `..., ...`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "point = (10, 20)\n", - "\n", - "print(type(point))\n", - "print(point)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we try to assign a new value to an element in a tuple we get an error:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " point[0] = 20\n", - "except(TypeError) as er:\n", - " print(\"TypeError:\", er)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dictionaries\n", - "\n", - "Dictionaries are also like lists, except that each element is a key-value pair. The syntax for dictionaries is `{key1 : value1, ...}`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params = {\"parameter1\" : 1.0,\n", - " \"parameter2\" : 2.0,\n", - " \"parameter3\" : 3.0,}\n", - "\n", - "print(type(params))\n", - "print(params)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Dictionary entries can only be accessed by their key name." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params[\"parameter2\"]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(\"parameter1 = \" + str(params[\"parameter1\"]))\n", - "print(\"parameter2 = \" + str(params[\"parameter2\"]))\n", - "print(\"parameter3 = \" + str(params[\"parameter3\"]))" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "params[\"parameter1\"] = \"A\"\n", - "params[\"parameter2\"] = \"B\"\n", - "\n", - "# add a new entry\n", - "params[\"parameter4\"] = \"D\"\n", - "\n", - "print(\"parameter1 = \" + str(params[\"parameter1\"]))\n", - "print(\"parameter2 = \" + str(params[\"parameter2\"]))\n", - "print(\"parameter3 = \" + str(params[\"parameter3\"]))\n", - "print(\"parameter4 = \" + str(params[\"parameter4\"]))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Indentation\n", - "\n", - "Whitespace is important in Python. Actually, whitespace at the beginning of the line is important. This is called indentation. Leading whitespace (spaces and tabs) at the beginning of the logical line is used to determine the indentation level of the logical line, which in turn is used to determine the grouping of statements.\n", - "\n", - "This means that statements which go together must have the same indentation, for example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "i = 5\n", - "\n", - "print('Value is ', i)\n", - "print('I repeat, the value is ', i)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Each such set of statements is called a block. We will see examples of how blocks are important later on.\n", - "One thing you should remember is that wrong indentation rises `IndentationError`." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Control Flow" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Conditional statements: if, elif, else\n", - "\n", - "The Python syntax for conditional execution of code use the keywords `if`, `elif` (else if), `else`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "statement1 = False\n", - "statement2 = False\n", - "\n", - "if statement1:\n", - " print(\"statement1 is True\")\n", - " \n", - "elif statement2:\n", - " print(\"statement2 is True\")\n", - " \n", - "else:\n", - " print(\"statement1 and statement2 are False\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For the first time, here we encountered a peculiar and unusual aspect of the Python programming language: Program blocks are defined by their indentation level. In Python, the extent of a code block is defined by the indentation level (usually a tab or say four white spaces). This means that we have to be careful to indent our code correctly, or else we will get syntax errors. \n", - "\n", - "**Examples:**" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Good indentation\n", - "statement1 = statement2 = True\n", - "\n", - "if statement1:\n", - " if statement2:\n", - " print(\"both statement1 and statement2 are True\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Bad indentation! This would lead to error\n", - "#if statement1:\n", - "# if statement2:\n", - "# print(\"both statement1 and statement2 are True\") # this line is not properly indented" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "statement1 = False \n", - "\n", - "if statement1:\n", - " print(\"printed if statement1 is True\")\n", - " \n", - " print(\"still inside the if block\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "if statement1:\n", - " print(\"printed if statement1 is True\")\n", - " \n", - "print(\"now outside the if block\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Loops\n", - "\n", - "In Python, loops can be programmed in a number of different ways. The most common is the `for` loop, which is used together with iterable objects, such as lists. The basic syntax is:\n", - "\n", - "\n", - "## `for` loops" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for x in [1,2,3]:\n", - " print(x)," - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `for` loop iterates over the elements of the supplied list, and executes the containing block once for each element. Any kind of list can be used in the `for` loop. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for x in range(4): # by default range start at 0\n", - " print(x)," - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note: `range(4)` does not include 4 !" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for x in range(-3,3):\n", - " print(x)," - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for word in [\"scientific\", \"computing\", \"with\", \"python\"]:\n", - " print(word)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To iterate over key-value pairs of a dictionary:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for key, value in params.items():\n", - " print(key + \" = \" + str(value))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Sometimes it is useful to have access to the indices of the values when iterating over a list. We can use the `enumerate` function for this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "for idx, x in enumerate(range(-3,3)):\n", - " print(idx, x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `break`, `continue` and `pass`\n", - "\n", - "To control the flow of a certain loop you can also use `break`, `continue` and `pass`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "rangelist = list(range(10))\n", - "print(list(rangelist))\n", - "\n", - "for number in rangelist:\n", - " # Check if number is one of\n", - " # the numbers in the tuple.\n", - " if number in [4, 5, 7, 9]:\n", - " # \"Break\" terminates a for without\n", - " # executing the \"else\" clause.\n", - " break\n", - " else:\n", - " # \"Continue\" starts the next iteration\n", - " # of the loop. It's rather useless here,\n", - " # as it's the last statement of the loop.\n", - " print(number)\n", - " continue\n", - "else:\n", - " # The \"else\" clause is optional and is\n", - " # executed only if the loop didn't \"break\".\n", - " pass # Do nothing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**List comprehensions: Creating lists using `for` loops**:\n", - "\n", - "A convenient and compact way to initialize lists:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "l1 = [x**2 for x in range(0,5)]\n", - "\n", - "print(l1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**`while` loops**:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "i = 0\n", - "\n", - "while i < 5:\n", - " print(i)\n", - " \n", - " i = i + 1\n", - " \n", - "print(\"done\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Note that the `print \"done\"` statement is not part of the `while` loop body because of the difference in indentation." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Functions\n", - "\n", - "A function in Python is defined using the keyword `def`, followed by a function name, a signature within parentheses `()`, and a colon `:`. The following code, with one additional level of indentation, is the function body." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def say_hello():\n", - " # block belonging to the function\n", - " print('hello world')\n", - "\n", - "say_hello() # call the function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Following an example where we also feed two arguments into the function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def print_max(a, b):\n", - " if a > b:\n", - " print( a, 'is maximum')\n", - " elif a == b:\n", - " print(a, 'is equal to', b)\n", - " else:\n", - " print(b, 'is maximum')\n", - "\n", - "# directly pass literal values\n", - "print_max(3, 4)\n", - "\n", - "x = 7\n", - "y = 7\n", - "\n", - "# pass variables as arguments\n", - "print_max(x, y)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Very important**: Variables inside a function are treated as local variables and therefore don't interfere with variables outside the scope of the function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = 50\n", - "\n", - "def func(x):\n", - " print('x is', x)\n", - " x = 2\n", - " print('Changed local x to', x)\n", - "\n", - "func(x)\n", - "print('x is still', x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The local scope of a variable inside a function can be extended with the keyword `global`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x = 50\n", - "\n", - "def func():\n", - " global x\n", - "\n", - " print('x is', x)\n", - " x = 2\n", - " print('Changed global x to', x)\n", - "\n", - "func()\n", - "print('Value of x is', x)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Optionally, but highly recommended, we can define a so called \"docstring\", which is a description of the functions purpose and behavior. The docstring should follow directly after the function definition, before the code in the function body." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def func1(s):\n", - " \"\"\"\n", - " Print a string 's' and tell how many characters it has \n", - " \"\"\"\n", - " \n", - " print(s + \" has \" + str(len(s)) + \" characters\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "help(func1)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "func1(\"test\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Functions that return a value use the `return` keyword:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def square(x):\n", - " \"\"\"\n", - " Return the square of x.\n", - " \"\"\"\n", - " return x ** 2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "square(4)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can return multiple values from a function using tuples (see above):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def powers(x):\n", - " \"\"\"\n", - " Return a few powers of x.\n", - " \"\"\"\n", - " return x ** 2, x ** 3, x ** 4" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "powers(3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And if we know that a function returns multiple outputs, we can store them directly in multiple variables." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "x2, x3, x4 = powers(3)\n", - "\n", - "print(x3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Default argument and keyword arguments\n", - "\n", - "In a definition of a function, we can give default values to the arguments the function takes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def myfunc(x, p=2, debug=False):\n", - " if debug:\n", - " print(\"evaluating myfunc for x = \" + str(x) + \" using exponent p = \" + str(p))\n", - " return x**p" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we don't provide a value of the `debug` argument when calling the the function `myfunc` it defaults to the value provided in the function definition:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "myfunc(5)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "myfunc(5, debug=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we explicitly list the name of the arguments in the function calls, they do not need to come in the same order as in the function definition. This is called *keyword* arguments, and is often very useful in functions that takes a lot of optional arguments." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "myfunc(p=3, debug=True, x=7)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `*args` and `*kwargs` parameters\n", - "\n", - "Sometimes you might want to define a function that can take any number of parameters, i.e. variable number of arguments, this can be achieved by using one (`*args`) or two (`**kwargs`) asterisks in the function declaration. `*args` is used to pass a non-keyworded, variable-length argument list and the `**kwargs` is used to pass a keyworded, variable-length argument list. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def args_func(arg1, *args):\n", - " print(\"Formal arg:\", arg1)\n", - " for a in args:\n", - " print(\"additioanl arg:\", a)\n", - "\n", - "args_func(1, \"two\", 3, [1, 2, 3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def kwargs_func(arg1, **kwargs):\n", - " print(\"kwargs is now a dictionary...\\nType: %s\\nContent: %s\\n\" % (type(kwargs), kwargs))\n", - "\n", - " print(\"Formal arg:\", arg1)\n", - " for key in kwargs:\n", - " print(\"another keyword arg: %s: %s\" % (key, kwargs[key]))\n", - " \n", - "kwargs_func(arg1=1, myarg2=\"two\", myarg3=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Unnamed functions: lambda function\n", - "\n", - "In Python we can also create unnamed functions, using the `lambda` keyword:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f1 = lambda x: x**2\n", - " \n", - "# is equivalent to \n", - "\n", - "def f2(x):\n", - " return x**2" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f1(2), f2(2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This technique is useful for example when we want to pass a simple function as an argument to another function, like this:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# map is a built-in python function\n", - "list(map(lambda x: x**2, range(-3,4)))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Classes\n", - "\n", - "Classes are the key features of object-oriented programming. A class is a structure for representing an object and the operations that can be performed on the object. \n", - "\n", - "In Python a class can contain *attributes* (variables) and *methods* (functions).\n", - "\n", - "A class is defined almost like a function, but using the `class` keyword, and the class definition usually contains a number of class method definitions (a function in a class).\n", - "\n", - "* Each class method should have an argument `self` as it first argument. This object is a self-reference.\n", - "\n", - "* Some class method names have special meaning, for example:\n", - "\n", - " * `__init__`: The name of the method that is invoked when the object is first created.\n", - " * `__str__` : A method that is invoked when a simple string representation of the class is needed, as for example when printed.\n", - " * There are many more, see http://docs.python.org/3.6/reference/datamodel.html#special-method-names" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class Point:\n", - " \"\"\"\n", - " Simple class for representing a point in a Cartesian coordinate system.\n", - " \"\"\"\n", - " \n", - " def __init__(self, x, y):\n", - " \"\"\"\n", - " Create a new Point at x, y.\n", - " \"\"\"\n", - " self.x = x\n", - " self.y = y\n", - " \n", - " def translate(self, dx, dy):\n", - " \"\"\"\n", - " Translate the point by dx and dy in the x and y direction.\n", - " \"\"\"\n", - " self.x += dx\n", - " self.y += dy\n", - " \n", - " def __str__(self):\n", - " return(\"Point at [%f, %f]\" % (self.x, self.y))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To create a new instance of a class:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p1 = Point(0, 0) # this will invoke the __init__ method in the Point class\n", - "\n", - "print(p1) # this will invoke the __str__ method" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To invoke a class method in the class instance `p`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "p2 = Point(1, 1)\n", - "print(p2)\n", - "\n", - "p2.translate(0.25, 1.5)\n", - "print(p2)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can access any value of a class object directly, for example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(p1.x)\n", - "\n", - "p1.x = 10\n", - "\n", - "print(p1)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Modules\n", - "\n", - "One of the most important concepts in good programming is to reuse code and avoid repetitions.\n", - "\n", - "The idea is to write functions and classes with a well-defined purpose and scope, and reuse these instead of repeating similar code in different part of a program (modular programming). The result is usually that readability and maintainability of a program is greatly improved. What this means in practice is that our programs have fewer bugs, are easier to extend and debug/troubleshoot. \n", - "\n", - "Python supports modular programming at different levels. Functions and classes are examples of tools for low-level modular programming. Python modules are a higher-level modular programming construct, where we can collect related variables, functions and classes in a module. A python module is defined in a python file (with file-ending `.py`), and it can be made accessible to other Python modules and programs using the `import` statement. \n", - "\n", - "Consider the following example: the file `mymodule.py` contains simple example implementations of a variable, function and a class:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%file mymodule.py\n", - "\"\"\"\n", - "Example of a python module. Contains a variable called my_variable,\n", - "a function called my_function, and a class called MyClass.\n", - "\"\"\"\n", - "\n", - "my_variable = 0\n", - "\n", - "def my_function():\n", - " \"\"\"\n", - " Example function\n", - " \"\"\"\n", - " return my_variable\n", - " \n", - "class MyClass:\n", - " \"\"\"\n", - " Example class.\n", - " \"\"\"\n", - "\n", - " def __init__(self):\n", - " self.variable = my_variable\n", - " \n", - " def set_variable(self, new_value):\n", - " \"\"\"\n", - " Set self.variable to a new value\n", - " \"\"\"\n", - " self.variable = new_value\n", - " \n", - " def get_variable(self):\n", - " return self.variable" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**Note:** `%%file` is called a cell-magic function and creates a file that has the following lines as content.\n", - "\n", - "We can import the module `mymodule` into our Python program using `import`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import mymodule" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use `help(module)` to get a summary of what the module provides:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "help(mymodule)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mymodule.my_variable" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "mymodule.my_function() " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "my_class = mymodule.MyClass() \n", - "my_class.set_variable(10)\n", - "my_class.get_variable()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "If we make changes to the code in `mymodule.py`, we need to reload it using `reload`:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from importlib import reload\n", - "reload(mymodule)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Exceptions\n", - "\n", - "In Python errors are managed with a special language construct called \"Exceptions\". When errors occur exceptions can be raised, which interrupts the normal program flow and fallback to somewhere else in the code where the closest try-except statement is defined.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To generate an exception we can use the `raise` statement, which takes an argument that must be an instance of the class `BaseExpection` or a class derived from it. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " raise Exception(\"description of the error\")\n", - "except(Exception) as err:\n", - " print (\"Exception:\", err)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "A typical use of exceptions is to abort functions when some error condition occurs, for example:\n", - "\n", - " def my_function(arguments):\n", - " \n", - " if not verify(arguments):\n", - " raise Exception(\"Invalid arguments\")\n", - " \n", - " # rest of the code goes here" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To gracefully catch errors that are generated by functions and class methods, or by the Python interpreter itself, use the `try` and `except` statements:\n", - "\n", - " try:\n", - " # normal code goes here\n", - " except:\n", - " # code for error handling goes here\n", - " # this code is not executed unless the code\n", - " # above generated an error\n", - "\n", - "For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " print(\"test\")\n", - " # generate an error: the variable test is not defined\n", - " print(test)\n", - "except:\n", - " print(\"Caught an exception\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To get information about the error, we can access the `Exception` class instance that describes the exception by using for example:\n", - "\n", - " except Exception as e:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "try:\n", - " print(\"test\")\n", - " # generate an error: the variable test is not defined\n", - " print(test)\n", - "except Exception as e:\n", - " print(\"Caught an exception:\" + str(e))\n", - "finally:\n", - " print(\"This block is executed after the try- and except-block.\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def some_function():\n", - " try:\n", - " # Division by zero raises an exception\n", - " 10 / 0\n", - " except ZeroDivisionError:\n", - " print(\"Oops, invalid.\")\n", - " else:\n", - " # Exception didn't occur, we're good.\n", - " pass\n", - " finally:\n", - " # This is executed after the code block is run\n", - " # and all exceptions have been handled, even\n", - " # if a new exception is raised while handling.\n", - " print(\"We're done with that.\")\n", - "\n", - "some_function()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You will see more exception handling examples in this and other notebooks. " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## File I/O\n", - "\n", - "This section should give you a basic knowledge about how to read and write CSV or TXT files. First, let us create a CSV and TXT file about demographic information of 10 subjects (experiment_id, subject_id, gender, age)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%file demographics.csv\n", - "ds102,sub001,F,21.94\n", - "ds102,sub002,M,22.79\n", - "ds102,sub003,M,19.65\n", - "ds102,sub004,M,25.98\n", - "ds102,sub005,M,23.24\n", - "ds102,sub006,M,23.27\n", - "ds102,sub007,D,34.72\n", - "ds102,sub008,D,22.22\n", - "ds102,sub009,M,22.7\n", - "ds102,sub010,D,25.24" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%%file demographics.txt\n", - "ds102\tsub001\tF\t21.94\n", - "ds102\tsub002\tM\t22.79\n", - "ds102\tsub003\tM\t19.65\n", - "ds102\tsub004\tM\t25.98\n", - "ds102\tsub005\tM\t23.24\n", - "ds102\tsub006\tM\t23.27\n", - "ds102\tsub007\tD\t34.72\n", - "ds102\tsub008\tD\t22.22\n", - "ds102\tsub009\tM\t22.7\n", - "ds102\tsub010\tD\t25.24" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reading CSV files\n", - "\n", - "Parsing comma-separated-values (CSV) files is a common task. There are many tools available in Python to deal with this. Let's start by using the built-in `csv` module." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import csv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before you can read or write any kind of file, you first have to open the file and go through it's content with a reader function or write the output line by line with a write function." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = open('demographics.csv','r') # open the file with reading rights = 'r'\n", - "data = [i for i in csv.reader(f) ] # go through file and read each line\n", - "f.close() # close the file again\n", - "\n", - "for line in data:\n", - " print(line)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Writing CSV files\n", - "\n", - "Now, we want to write the same data without the first experiment_id column in CSV format to a csv-file. First, let's delete the first column in the dataset." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "data_new = [line[1:] for line in data]\n", - "\n", - "for line in data_new:\n", - " print(line)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we first have to open a file again, but this time with writing permissions = `'w'`. After it we can go through the file and write each line to the new csv-file." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = open('demographics_new.csv','w') # open a file with writing rights = 'w'\n", - "fw = csv.writer(f) # create csv writer\n", - "fw.writerows(data_new) # write content to file\n", - "f.close() # close file " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Lets now check the content of `demographics_new.csv`." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!cat demographics_new.csv" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Reading TXT files\n", - "\n", - "The reading of txt files is quite similar to the reading of csv-files. The only different is in the name of the reading function and the formating that has to be applied to the input or output." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = open('demographics.txt','r') # open file with reading rights = 'r'\n", - "\n", - "# go through file and trim the new line '\\n' at the end\n", - "datatxt = [i.splitlines() for i in f.readlines()]\n", - "\n", - "# go through data and split elements in line by tabulators '\\t'\n", - "datatxt = [i[0].split('\\t') for i in datatxt]\n", - "\n", - "f.close() # close file again\n", - "\n", - "for line in datatxt:\n", - " print(line)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Writing TXT files\n", - "\n", - "The writing of txt files is as follows:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "f = open('demograhics_new.txt', 'w') # open file with writing rights = 'w'\n", - "\n", - "datatxt_new = [line[1:] for line in datatxt] # delete first column of array\n", - "\n", - "# Go through datatxt array and write each line with specific format to file\n", - "for line in datatxt_new:\n", - " f.write(\"%s\\t%s\\t%s\\n\"%(line[0],line[1],line[2]))\n", - "\n", - "f.close() # close file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### `with open`\n", - "\n", - "The previous methods to open or write a file always required that you also close the file again with the `close()` function. If you don't want to worry about this, you can also use the `with open` approach. For example:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "with open('demographics.txt','r') as f:\n", - "\n", - " datatxt = [i.splitlines() for i in f.readlines()]\n", - " datatxt = [i[0].split('\\t') for i in datatxt]\n", - "\n", - "for line in datatxt:\n", - " print(line)" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/introduction_quickstart.ipynb b/notebooks/introduction_quickstart.ipynb deleted file mode 100644 index 57c76ac..0000000 --- a/notebooks/introduction_quickstart.ipynb +++ /dev/null @@ -1,1319 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Quickstart\n", - "\n", - "**This is a very quick non-imaging introduction to Nipype workflows. For more comprehensive introduction, check the next section of the tutorial.** " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "![Nipype architecture](https://raw.github.com/satra/intro2nipype/master/images/arch.png)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- [Existing documentation](http://nipype.readthedocs.io/en/latest/)\n", - "\n", - "- [Visualizing the evolution of Nipype](https://www.youtube.com/watch?v=cofpD1lhmKU)\n", - "\n", - "- This notebook taken from [reproducible-imaging repository](https://github.com/ReproNim/reproducible-imaging)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Import a few things from nipype" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from nipype import Workflow, Node, Function" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating Workflow with one Node that adds two numbers" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def sum(a, b):\n", - " return a + b\n", - "\n", - "wf = Workflow('hello')\n", - "\n", - "adder = Node(Function(input_names=['a', 'b'],\n", - " output_names=['sum'],\n", - " function=sum), \n", - " name='a_plus_b')\n", - "\n", - "adder.inputs.a = 1\n", - "adder.inputs.b = 3\n", - "\n", - "wf.add_nodes([adder])\n", - "\n", - "wf.base_dir = os.getcwd()\n", - "\n", - "eg = wf.run()\n", - "\n", - "list(eg.nodes())[0].result.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Creating a second node and connecting to the ``hello`` Workflow " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def concat(a, b):\n", - " return [a, b]\n", - "\n", - "\n", - "concater = Node(Function(input_names=['a', 'b'],\n", - " output_names=['some_list'],\n", - " function=concat), \n", - " name='concat_a_b')\n", - "\n", - "wf.connect(adder, 'sum', concater, 'a')\n", - "concater.inputs.b = 3\n", - "\n", - "eg = wf.run()\n", - "print(eg.nodes())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And we can check results of our Workflow, we should see a list:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "list(eg.nodes())[-1].result.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will try to add additional Node that adds one:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def plus_one(a):\n", - " return a + 1\n", - "\n", - "plusone = Node(Function(input_names=['a'],\n", - " output_names=['out'],\n", - " function=plus_one), \n", - " name='add_1')\n", - "\n", - "wf.connect(concater, 'some_list', plusone, 'a')\n", - "\n", - "try:\n", - " eg = wf.run()\n", - "except(RuntimeError) as err:\n", - " print(\"RuntimeError:\", err)\n", - "else:\n", - " raise" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This time the workflow didn't execute cleanly and we got an error. We can use ``nipypecli`` to read the crashfile (note, that if you have multiple crashfiles in the directory you'll have to provide a full name):" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!nipypecli crash crash*" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "It clearly shows the problematic Node and its input. We tried to add an integer to a list, this operation is not allowed in Python. \n", - "\n", - "Let's try using MapNode" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype import MapNode\n", - "\n", - "plusone = MapNode(Function(input_names=['a'],\n", - " output_names=['out'],\n", - " function=plus_one), \n", - " iterfield=['a'],\n", - " name='add_1')\n", - "\n", - "wf = Workflow('hello_mapnode')\n", - "\n", - "adder = Node(Function(input_names=['a', 'b'],\n", - " output_names=['sum'],\n", - " function=sum), \n", - " name='a_plus_b')\n", - "\n", - "adder.inputs.a = 1\n", - "adder.inputs.b = 3\n", - "wf.connect(adder, 'sum', concater, 'a')\n", - "concater.inputs.b = 3\n", - "\n", - "wf.connect(concater, 'some_list', plusone, 'a')\n", - "\n", - "wf.base_dir = os.getcwd()\n", - "\n", - "eg = wf.run()\n", - "print(eg.nodes())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now the workflow finished without problems, let's see the results from ``hello.add_1``:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "print(list(eg.nodes())[2].result.outputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And now we will run the example with ``iterables``:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "adder.iterables = ('a', [1, 2])\n", - "adder.inputs.b = 2\n", - "\n", - "eg = wf.run()\n", - "print(eg.nodes())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we have 6 nodes, we can check results for `` hello.add_1.a1``" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "list(eg.nodes())[5].result.outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='exec')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from IPython.display import Image" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can plot a general structure of the workflow:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Image(\"hello_mapnode/graph.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "And more detailed structure with all nodes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Image(\"hello_mapnode/graph_detailed.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We will introduce another iterables, for the concater Node:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "concater.iterables = ('b', [3, 4])\n", - "eg = wf.run()\n", - "eg.nodes()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='exec')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Image(\"hello_mapnode/graph_detailed.dot.png\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now we will introduce JoinNode that allows us to merge results together:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def merge_and_scale_data(data2):\n", - " import numpy as np\n", - " return (np.array(data2) * 1000).tolist()\n", - "\n", - "\n", - "from nipype import JoinNode\n", - "joiner = JoinNode(Function(input_names=['data2'],\n", - " output_names=['data_scaled'],\n", - " function=merge_and_scale_data),\n", - " name='join_scale_data',\n", - " joinsource=adder,\n", - " joinfield=['data2'])\n", - "\n", - "wf.connect(plusone, 'out', joiner, 'data2')\n", - "\n", - "eg = wf.run()\n", - "eg.nodes()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's check the output of ``hello.join_scale_data.a0`` node:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "list(eg.nodes())[0].result.outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.write_graph(graph2use='exec')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Image(\"hello_mapnode/graph.dot.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "Image(\"hello_mapnode/graph_detailed.dot.png\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%time eg = wf.run(plugin='MultiProc', plugin_args={'n_procs': 2})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "wf.base_dir = os.path.join(os.getcwd(), 'alt')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%time eg = wf.run(plugin='MultiProc', plugin_args={'n_procs': 2})" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "%time eg = wf.run(plugin='MultiProc', plugin_args={'n_procs': 2})" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden", - "solution2_first": true - }, - "source": [ - "### Exercise 1\n", - "\n", - "Create a workflow to calculate a sum of factorials of numbers from a range between $n_{min}$ and $n_{max}$, i.e.:\n", - "\n", - "$$\\sum _{k=n_{min}}^{n_{max}} k! = 0! + 1! +2! + 3! + \\cdots$$ \n", - "\n", - "if $n_{min}=0$ and $n_{max}=3$\n", - "$$\\sum _{k=0}^{3} k! = 0! + 1! +2! + 3! = 1 + 1 + 2 + 6 = 10$$\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "from nipype import Workflow, Node, MapNode, Function\n", - "import os\n", - "\n", - "def range_fun(n_min, n_max):\n", - " return list(range(n_min, n_max+1))\n", - "\n", - "def factorial(n):\n", - " # print(\"FACTORIAL, {}\".format(n))\n", - " import math\n", - " return math.factorial(n)\n", - "\n", - "def summing(terms):\n", - " return sum(terms)\n", - "\n", - "wf_ex1 = Workflow('ex1')\n", - "wf_ex1.base_dir = os.getcwd()\n", - "\n", - "range_nd = Node(Function(input_names=['n_min', 'n_max'],\n", - " output_names=['range_list'],\n", - " function=range_fun), \n", - " name='range_list')\n", - "\n", - "factorial_nd = MapNode(Function(input_names=['n'],\n", - " output_names=['fact_out'],\n", - " function=factorial), \n", - " iterfield=['n'],\n", - " name='factorial')\n", - "\n", - "summing_nd = Node(Function(input_names=['terms'],\n", - " output_names=['sum_out'],\n", - " function=summing), \n", - " name='summing')\n", - "\n", - "\n", - "range_nd.inputs.n_min = 0\n", - "range_nd.inputs.n_max = 3\n", - "\n", - "wf_ex1.add_nodes([range_nd])\n", - "wf_ex1.connect(range_nd, 'range_list', factorial_nd, 'n')\n", - "wf_ex1.connect(factorial_nd, 'fact_out', summing_nd, \"terms\")\n", - "\n", - "\n", - "eg = wf_ex1.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "let's print all nodes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "eg.nodes()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "the final result should be 10:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "list(eg.nodes())[2].result.outputs" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "we can also check the results of two other nodes:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "print(list(eg.nodes())[0].result.outputs)\n", - "print(list(eg.nodes())[1].result.outputs)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "#write your code here\n", - "\n", - "# 1. write 3 functions: one that return a list of number from specific range, \n", - "# second that returns n! (you can use math.factorial) and third that sums the elements from a list\n", - "\n", - "# 2. create a workflow and define the working directory\n", - "\n", - "# 3. define 3 nodes using Node and MapNode and connect them within the workflow\n", - "\n", - "# 4. run the workflow and check the results\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden", - "solution2_first": true - }, - "source": [ - "### Exercise 2\n", - "\n", - "Create a workflow to calculate the following sum for chosen $n$ and five different values of $x$: $0$, $\\frac{1}{2} \\pi$, $\\pi$, $\\frac{3}{2} \\pi$, and $ 2 \\pi$.\n", - "\n", - "$\\sum _{{k=0}}^{{n}}{\\frac {(-1)^{k}}{(2k+1)!}}x^{{2k+1}}\\quad =x-{\\frac {x^{3}}{3!}}+{\\frac {x^{5}}{5!}}-\\cdots $\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "# we can reuse function from previous exercise, but they need some edits\n", - "from nipype import Workflow, Node, MapNode, JoinNode, Function\n", - "import os\n", - "import math\n", - "\n", - "def range_fun(n_max):\n", - " return list(range(n_max+1))\n", - "\n", - "def term(k, x):\n", - " import math\n", - " fract = math.factorial(2 * k + 1)\n", - " polyn = x ** (2 * k + 1) \n", - " return (-1)**k * polyn / fract\n", - "\n", - "def summing(terms):\n", - " return sum(terms)\n", - "\n", - "wf_ex2 = Workflow('ex2')\n", - "wf_ex2.base_dir = os.getcwd()\n", - "\n", - "range_nd = Node(Function(input_names=['n_max'],\n", - " output_names=['range_list'],\n", - " function=range_fun), \n", - " name='range_list')\n", - "\n", - "term_nd = MapNode(Function(input_names=['k', 'x'],\n", - " output_names=['term_out'],\n", - " function=term), \n", - " iterfield=['k'],\n", - " name='term')\n", - "\n", - "summing_nd = Node(Function(input_names=['terms'],\n", - " output_names=['sum_out'],\n", - " function=summing), \n", - " name='summing')\n", - "\n", - "\n", - "range_nd.inputs.n_max = 15\n", - "\n", - "x_list = [0, 0.5 * math.pi, math.pi, 1.5 * math.pi, 2 * math.pi]\n", - "\n", - "term_nd.iterables = ('x', x_list)\n", - "\n", - "wf_ex2.add_nodes([range_nd])\n", - "wf_ex2.connect(range_nd, 'range_list', term_nd, 'k')\n", - "wf_ex2.connect(term_nd, 'term_out', summing_nd, \"terms\")\n", - "\n", - "\n", - "eg = wf_ex2.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "let's check all nodes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "eg.nodes()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "let's print all results of ``ex2.summing``" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "print(list(eg.nodes())[2].result.outputs)\n", - "print(list(eg.nodes())[4].result.outputs)\n", - "print(list(eg.nodes())[6].result.outputs)\n", - "print(list(eg.nodes())[8].result.outputs)\n", - "print(list(eg.nodes())[10].result.outputs)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "Great, we just implemented pretty good Sine function! Those number should be approximately 0, 1, 0, -1 and 0. If they are not, try to increase $n_max$." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# write your solution here\n", - "\n", - "# 1. write 3 functions: one that return a list of number from a range between 0 and some n, \n", - "# second that returns a term for a specific k, and third that sums the elements from a list\n", - "\n", - "# 2. create a workflow and define the working directory\n", - "\n", - "# 3. define 3 nodes using Node and MapNode and connect them within the workflow\n", - "\n", - "# 4. use iterables for 4 values of x\n", - "\n", - "# 5. run the workflow and check the final results for every value of x\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden", - "solution2_first": true - }, - "source": [ - "### Exercise 2a\n", - "\n", - "Use JoinNode to combine results from Exercise 2 in one container, e.g. a dictionary, that takes value $x$ as a key and the result from ``summing`` Node as a value." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "def merge_results(results, x):\n", - " return dict(zip(x, results))\n", - "\n", - "join_nd = JoinNode(Function(input_names=['results', 'x'],\n", - " output_names=['results_cont'],\n", - " function=merge_results),\n", - " name='merge',\n", - " joinsource=term_nd, # this is the node that used iterables for x\n", - " joinfield=['results'])\n", - "\n", - "# taking the list of arguments from the previous part \n", - "join_nd.inputs.x = x_list\n", - "\n", - "# connecting a new node to the summing_nd\n", - "wf_ex2.connect(summing_nd, \"sum_out\", join_nd, \"results\")\n", - "\n", - "eg = wf_ex2.run()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "let's print all nodes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "eg.nodes()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "solution2": "hidden" - }, - "source": [ - "and results from ``merge`` Node:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "solution2": "hidden" - }, - "outputs": [], - "source": [ - "list(eg.nodes())[1].result.outputs" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# write your code here\n", - "\n", - "# 1. create an additional function that takes 2 list and combines them into one container, e.g. dictionary\n", - "\n", - "# 2. use JoinNode to define a new node that merge results from Exercise 2 and connect it to the workflow\n", - "\n", - "# 3. run the workflow and check the results of the merging node" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - }, - "nbpresent": { - "slides": { - "036d9e6d-9014-47e8-ba8c-b7ff491d356e": { - "id": "036d9e6d-9014-47e8-ba8c-b7ff491d356e", - "prev": "cc6fa21e-5b8f-44a7-8578-5b58255c0e2b", - "regions": { - "69d658c5-3412-4410-96aa-45fbc91e3950": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "dcbff777-e05b-43d3-9da3-805207eadb71", - "part": "whole" - }, - "id": "69d658c5-3412-4410-96aa-45fbc91e3950" - } - } - }, - "0c3953f2-86d8-4e97-9ffd-02a8377e10c6": { - "id": "0c3953f2-86d8-4e97-9ffd-02a8377e10c6", - "prev": "5e629ace-5a9f-4bf2-a295-82901f752daa", - "regions": { - "16206fd5-e557-4f6c-8077-e824b87eff4f": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "b7a0948a-2f3d-4be5-af22-e8796ab22131", - "part": "whole" - }, - "id": "16206fd5-e557-4f6c-8077-e824b87eff4f" - } - } - }, - "1a0083a8-471b-4869-bcb3-c33c81524a2c": { - "id": "1a0083a8-471b-4869-bcb3-c33c81524a2c", - "prev": "43c259c6-ec65-4243-8a95-d2a976c6daca", - "regions": { - "5907abd6-0b04-4f6d-acd1-1f11dd39c7a2": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "c8cbc820-d362-422e-9fdf-79d6ae6af560", - "part": "whole" - }, - "id": "5907abd6-0b04-4f6d-acd1-1f11dd39c7a2" - } - } - }, - "32034499-40cf-4318-91f1-aeccdfbba380": { - "id": "32034499-40cf-4318-91f1-aeccdfbba380", - "prev": null, - "regions": { - "845af035-2d72-4258-b5da-d611edc1ba86": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "ef0d4a65-1e86-4570-bd56-0e683df3cc72", - "part": "whole" - }, - "id": "845af035-2d72-4258-b5da-d611edc1ba86" - } - } - }, - "43c259c6-ec65-4243-8a95-d2a976c6daca": { - "id": "43c259c6-ec65-4243-8a95-d2a976c6daca", - "prev": "76d40b89-085e-44b3-89b4-46f17db1746f", - "regions": { - "8192ec05-8445-4c92-9a84-d60610754d06": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "9798f6be-09b9-4cb9-8c63-1f10e4d1040c", - "part": "whole" - }, - "id": "8192ec05-8445-4c92-9a84-d60610754d06" - } - } - }, - "5288be26-b5af-48c6-8687-ff3bb55e83a9": { - "id": "5288be26-b5af-48c6-8687-ff3bb55e83a9", - "prev": "32034499-40cf-4318-91f1-aeccdfbba380", - "regions": { - "8247975a-6621-4c12-b3f0-016a235a34b2": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "f834221c-3c73-47ce-b36e-ba3f17bd3d60", - "part": "whole" - }, - "id": "8247975a-6621-4c12-b3f0-016a235a34b2" - } - } - }, - "5e629ace-5a9f-4bf2-a295-82901f752daa": { - "id": "5e629ace-5a9f-4bf2-a295-82901f752daa", - "prev": "dcc3de5f-dfc5-4a35-a583-474dbac5a5ad", - "regions": { - "c8fc9ec8-974e-426c-9d36-f55673eee3c4": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "2da7d103-ba49-495d-b986-6ef655b2a010", - "part": "whole" - }, - "id": "c8fc9ec8-974e-426c-9d36-f55673eee3c4" - } - } - }, - "69c3997a-020c-4288-ba41-da053c70c853": { - "id": "69c3997a-020c-4288-ba41-da053c70c853", - "prev": "d2a3e23f-46b6-4f0b-b96b-5341e8a368b0", - "regions": { - "5cbcbcde-1087-410d-ac46-bc5d403927ff": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "e03280a7-f6b0-48d8-a1a3-c38dd0a93cc2", - "part": "whole" - }, - "id": "5cbcbcde-1087-410d-ac46-bc5d403927ff" - } - } - }, - "6e1b1fd9-f600-4262-8bfa-0b6ef6d2ab33": { - "id": "6e1b1fd9-f600-4262-8bfa-0b6ef6d2ab33", - "prev": "b5c8cdf1-c521-4830-bdc7-537f4e33974c", - "regions": { - "7047358c-1619-4db4-84b5-b3c9f6a4165d": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "6361e837-5e6f-4df9-aff6-d20c5909af56", - "part": "whole" - }, - "id": "7047358c-1619-4db4-84b5-b3c9f6a4165d" - } - } - }, - "748fa336-fe68-4ec9-879a-18b4c253938b": { - "id": "748fa336-fe68-4ec9-879a-18b4c253938b", - "prev": "862ab379-822c-4a94-9433-1b527b2a592d", - "regions": { - "2ef88b5d-a61b-4476-a554-36864af7db8e": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "1592d986-e07f-4ac0-a06e-c9a3917e30b4", - "part": "whole" - }, - "id": "2ef88b5d-a61b-4476-a554-36864af7db8e" - } - } - }, - "76d40b89-085e-44b3-89b4-46f17db1746f": { - "id": "76d40b89-085e-44b3-89b4-46f17db1746f", - "prev": "edfccc6e-2b4e-4131-a730-eaa191ff7c81", - "regions": { - "939a8941-0ea4-4b62-abbb-05f8b793a5fb": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "2ff6c266-4437-4d37-9464-c1573b13ae51", - "part": "whole" - }, - "id": "939a8941-0ea4-4b62-abbb-05f8b793a5fb" - } - } - }, - "862ab379-822c-4a94-9433-1b527b2a592d": { - "id": "862ab379-822c-4a94-9433-1b527b2a592d", - "prev": "6e1b1fd9-f600-4262-8bfa-0b6ef6d2ab33", - "regions": { - "34178cde-c66f-4413-a29d-57c5e60794ed": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "bfe919e8-bad6-488f-a01f-ed7c3a7319b7", - "part": "whole" - }, - "id": "34178cde-c66f-4413-a29d-57c5e60794ed" - } - } - }, - "8cf4d2aa-9b35-469a-8226-74ab47621c35": { - "id": "8cf4d2aa-9b35-469a-8226-74ab47621c35", - "prev": "748fa336-fe68-4ec9-879a-18b4c253938b", - "regions": { - "197ef43c-c849-43c3-a6c4-31fd5cd99838": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "07b2fb00-3ed4-4a86-8313-7873048021ec", - "part": "whole" - }, - "id": "197ef43c-c849-43c3-a6c4-31fd5cd99838" - } - } - }, - "a81e9008-d57d-4aaf-86f0-ffe067287baa": { - "id": "a81e9008-d57d-4aaf-86f0-ffe067287baa", - "prev": "5288be26-b5af-48c6-8687-ff3bb55e83a9", - "regions": { - "970554aa-ab29-48b9-88f6-9ada37e60548": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "bb1cfcc5-5cbf-4097-b8a9-fe4d74ce6bcd", - "part": "whole" - }, - "id": "970554aa-ab29-48b9-88f6-9ada37e60548" - } - } - }, - "aee840ab-b7c4-48d7-b6ad-ce867f878951": { - "id": "aee840ab-b7c4-48d7-b6ad-ce867f878951", - "prev": "69c3997a-020c-4288-ba41-da053c70c853", - "regions": { - "c668f127-028b-4a6e-9410-81abe6a38e95": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "055c7435-88f1-45db-9562-63d5f910cac3", - "part": "whole" - }, - "id": "c668f127-028b-4a6e-9410-81abe6a38e95" - } - } - }, - "af2fe30f-1cda-4d2e-8a5a-3e265b4b404f": { - "id": "af2fe30f-1cda-4d2e-8a5a-3e265b4b404f", - "prev": "a81e9008-d57d-4aaf-86f0-ffe067287baa", - "regions": { - "180abf91-afcd-4265-846d-bfd7e4fd1850": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "9a460e90-6929-4ec6-8fa6-d7dacb45e00a", - "part": "whole" - }, - "id": "180abf91-afcd-4265-846d-bfd7e4fd1850" - } - } - }, - "b5c8cdf1-c521-4830-bdc7-537f4e33974c": { - "id": "b5c8cdf1-c521-4830-bdc7-537f4e33974c", - "prev": "dbe3527e-cafa-4fc2-b863-99954c2e4e00", - "regions": { - "2ff95d44-ba2e-4b0d-b50d-0cb12468769d": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "4c118e06-0dd3-44cf-8246-48b4abb06787", - "part": "whole" - }, - "id": "2ff95d44-ba2e-4b0d-b50d-0cb12468769d" - } - } - }, - "cc6fa21e-5b8f-44a7-8578-5b58255c0e2b": { - "id": "cc6fa21e-5b8f-44a7-8578-5b58255c0e2b", - "prev": "af2fe30f-1cda-4d2e-8a5a-3e265b4b404f", - "regions": { - "87874474-0c2f-47cc-bfe2-f7d5f9b49900": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "d0adfd78-01e1-4623-983a-bbf53a9bb858", - "part": "whole" - }, - "id": "87874474-0c2f-47cc-bfe2-f7d5f9b49900" - } - } - }, - "cf197342-f78a-4bf5-9b68-6f1430575593": { - "id": "cf197342-f78a-4bf5-9b68-6f1430575593", - "prev": "036d9e6d-9014-47e8-ba8c-b7ff491d356e", - "regions": { - "95c558ad-28b2-4c98-9ce2-22d80fd97f1b": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "f3a955ec-34a2-4a29-bdf8-e0dd8df57cf5", - "part": "whole" - }, - "id": "95c558ad-28b2-4c98-9ce2-22d80fd97f1b" - } - } - }, - "d2a3e23f-46b6-4f0b-b96b-5341e8a368b0": { - "id": "d2a3e23f-46b6-4f0b-b96b-5341e8a368b0", - "prev": "0c3953f2-86d8-4e97-9ffd-02a8377e10c6", - "regions": { - "486a31bf-5c58-4c67-b54d-d45e839167e7": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "e68b3f8a-ab55-4045-b8d3-7007a30a527b", - "part": "whole" - }, - "id": "486a31bf-5c58-4c67-b54d-d45e839167e7" - } - } - }, - "dbe3527e-cafa-4fc2-b863-99954c2e4e00": { - "id": "dbe3527e-cafa-4fc2-b863-99954c2e4e00", - "prev": "cf197342-f78a-4bf5-9b68-6f1430575593", - "regions": { - "3625ea9c-9bc9-4a2c-9d40-f230922b1edc": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "23672ce7-3781-4144-925a-cc9367dec01d", - "part": "whole" - }, - "id": "3625ea9c-9bc9-4a2c-9d40-f230922b1edc" - } - } - }, - "dcc3de5f-dfc5-4a35-a583-474dbac5a5ad": { - "id": "dcc3de5f-dfc5-4a35-a583-474dbac5a5ad", - "prev": "1a0083a8-471b-4869-bcb3-c33c81524a2c", - "regions": { - "104371ee-397d-4b6d-bb3e-4ec826b2aa27": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "6fba065c-c3c5-4c79-a2a3-75e6a2198776", - "part": "whole" - }, - "id": "104371ee-397d-4b6d-bb3e-4ec826b2aa27" - } - } - }, - "edfccc6e-2b4e-4131-a730-eaa191ff7c81": { - "id": "edfccc6e-2b4e-4131-a730-eaa191ff7c81", - "prev": "8cf4d2aa-9b35-469a-8226-74ab47621c35", - "regions": { - "6fa263a6-0bdd-4517-b49b-32da55d66d87": { - "attrs": { - "height": 0.8, - "width": 0.8, - "x": 0.1, - "y": 0.1 - }, - "content": { - "cell": "ac097fd1-7c4a-41ad-bcdb-f3ba93b58d36", - "part": "whole" - }, - "id": "6fa263a6-0bdd-4517-b49b-32da55d66d87" - } - } - } - }, - "themes": {} - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/preproc_analysis_handsqueeze.ipynb b/notebooks/preproc_analysis_handsqueeze.ipynb new file mode 100644 index 0000000..6b5f740 --- /dev/null +++ b/notebooks/preproc_analysis_handsqueeze.ipynb @@ -0,0 +1,1146 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Preprocessing Workflow\n", + "\n", + "This is meant as a very simple example for a preprocessing workflow. In this workflow we will conduct the following steps:\n", + "\n", + "1. Motion correction of functional images with FSL's MCFLIRT\n", + "2. Coregistration of functional images to anatomical images (according to FSL's FEAT pipeline)\n", + "3. Smoothing of coregistrated functional images with FWHM set to 4mm" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Imports\n", + "\n", + "First, let's import all modules we later will be needing." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171213-11:09:18,906 interface WARNING:\n", + "\t Could not get linked libraries for \"which\".\n" + ] + } + ], + "source": [ + "%matplotlib inline\n", + "from os.path import join as opj\n", + "import json\n", + "import nipype.interfaces.fsl as fsl\n", + "from nipype.interfaces.fsl import MCFLIRT, FLIRT\n", + "from nipype.interfaces.spm import Smooth\n", + "from nipype.interfaces.utility import IdentityInterface\n", + "from nipype.interfaces.io import SelectFiles, DataSink\n", + "from nipype.pipeline.engine import Workflow, Node" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Experiment parameters\n", + "\n", + "It's always a good idea to specify all parameters that might change between experiments at the beginning of your script." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "ename": "IOError", + "evalue": "[Errno 2] No such file or directory: '/home/neuro/nipype_tutorial/data/PSYC405/task_bold.json'", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mIOError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;31m# load task info file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdata_dir\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m'task_bold.json'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'rt'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 16\u001b[0m \u001b[0mtask_info\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mjson\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 17\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mIOError\u001b[0m: [Errno 2] No such file or directory: '/home/neuro/nipype_tutorial/data/PSYC405/task_bold.json'" + ] + } + ], + "source": [ + "data_dir = '/home/neuro/nipype_tutorial/data/'\n", + "\n", + "experiment_dir = '/home/neuro/nipype_tutorial/output/'\n", + "output_dir = '/home/neuro/nipype_tutorial/output/datasink'\n", + "working_dir = '/home/neuro/nipype_tutorial/output/workingdir'\n", + "\n", + "# list of subject identifiers\n", + "subject_list = ['sub-1']\n", + "\n", + "\n", + "# list of session identifiers\n", + "session_list = ['run-13']\n", + "\n", + "# load task info file\n", + "with open(data_dir+'task_bold.json', 'rt') as fp:\n", + " task_info = json.load(fp)\n", + "\n", + "# list of session identifiers\n", + "task = task_info['TaskName']\n", + "print('Task Name = %s'%task)\n", + "\n", + "# TR of functional images\n", + "TR = task_info['RepetitionTime']\n", + "print('TR = %0.2f'%TR)\n", + "\n", + "# Smoothing withds used during preprocessing\n", + "fwhm = [4, 8]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Specify Nodes for the main workflow\n", + "\n", + "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "ename": "NameError", + "evalue": "name 'fwhm' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 13\u001b[0m \u001b[0;31m# Smooth - image smoothing\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0msmooth\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mNode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mSmooth\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mname\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m\"smooth\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 15\u001b[0;31m \u001b[0msmooth\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0miterables\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;34m\"fwhm\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfwhm\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;31mNameError\u001b[0m: name 'fwhm' is not defined" + ] + } + ], + "source": [ + "# MCFLIRT - motion correction\n", + "mcflirt = Node(MCFLIRT(mean_vol=True,\n", + " save_plots=True,\n", + " output_type='NIFTI'),\n", + " name=\"mcflirt\")\n", + "\n", + "\n", + "# FLIRT - coregister functional images to anatomical images\n", + "coreg_step1 = Node(FLIRT(output_type='NIFTI'), name=\"coreg_step1\")\n", + "coreg_step2 = Node(FLIRT(output_type='NIFTI',\n", + " apply_xfm=True), name=\"coreg_step2\")\n", + "\n", + "# Smooth - image smoothing\n", + "smooth = Node(Smooth(), name=\"smooth\")\n", + "smooth.iterables = (\"fwhm\", fwhm)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Specify input & output stream\n", + "\n", + "Specify where the input data can be found & where and how to save the output data." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# Infosource - a function free node to iterate over the list of subject names\n", + "infosource = Node(IdentityInterface(fields=['subject_id', 'session_id']),\n", + " name=\"infosource\")\n", + "infosource.iterables = [('subject_id', subject_list),\n", + " ('session_id', session_list)]\n", + "\n", + "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", + "anat_file = opj('{subject_id}', 'anat', '{subject_id}_{session_id}_T1w.nii')\n", + "func_file = opj('{subject_id}', 'func',\n", + " '{subject_id}_{session_id}_bold.nii')\n", + "\n", + "templates = {'anat': anat_file,\n", + " 'func': func_file}\n", + "selectfiles = Node(SelectFiles(templates,\n", + " base_directory=data_dir),\n", + " name=\"selectfiles\")\n", + "\n", + "# Datasink - creates output folder for important outputs\n", + "datasink = Node(DataSink(base_directory=experiment_dir,\n", + " container=output_dir),\n", + " name=\"datasink\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Specify Workflow\n", + "\n", + "Create a workflow and connect the interface nodes and the I/O stream to each other." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a preprocessing workflow\n", + "preproc = Workflow(name='preproc')\n", + "preproc.base_dir = opj(experiment_dir, working_dir)\n", + "\n", + "# Connect all components of the preprocessing workflow\n", + "preproc.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", + " ('session_id', 'session_id')]),\n", + " (selectfiles, mcflirt, [('func', 'in_file')]),\n", + "\n", + " (mcflirt, coreg_step1, [('mean_img', 'in_file')]),\n", + " (selectfiles, coreg_step1, [('anat', 'reference')]),\n", + "\n", + " (mcflirt, coreg_step2, [('out_file', 'in_file')]),\n", + " (selectfiles, coreg_step2, [('anat', 'reference')]),\n", + " (coreg_step1, coreg_step2, [('out_matrix_file',\n", + " 'in_matrix_file')]),\n", + "\n", + " (coreg_step2, smooth, [('out_file', 'in_files')]),\n", + "\n", + " (mcflirt, datasink, [('par_file', 'preproc.@par')]),\n", + " (selectfiles, datasink, [('anat', 'preproc.@resample')]),\n", + " (coreg_step1, datasink, [('out_file', 'preproc.@coregmean')]),\n", + " (smooth, datasink, [('smoothed_files', 'preproc.@smooth')]),\n", + " ])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize the workflow\n", + "\n", + "It always helps to visualize your workflow." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171211-11:00:34,10 workflow INFO:\n", + "\t Generated workflow graph: /home/neuro/nipype_tutorial/output/workingdir/preproc/graph.dot.png (graph2use=colored, simple_form=True).\n" + ] + }, + { + "data": { + "image/png": "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ty+jRo0iJydHltFEMtC7d2+ePn3KkSNHhI4iEpDci4+0tDSmT5/OxIkTad++vbybE9VSR44coaioiF69egkdRaGFhITQokULrl27xokTJ1ixYkX5h72ieNPiA+Cnn34iIyODuXPnyiqWSEZMTEzw9vbmwIEDQkcRCUjuxccnn3yChoYGCxYskHdTolps3759eHt7K80OtjWtoKCAmTNn0qNHDzp06MD169d56623hI5VKVkUH9bW1ixfvpwVK1Zw5swZWUUTyUifPn04cOAAEolE6Cgigci1+Dh16hSbN29m5cqVGBsby7MpUS0mkUg4ePCgOOTyAhEREXh7e7N69Wp++eUXtm3bRr169YSO9UKyKD6gdE0Jf39/AgMDyc/Pl0U0kYz06dOHpKQkwsLChI4iEojcio/CwkImTpxIz549GThwoLyaEYm4cOECiYmJ9O3bV+goCkUqlbJixQpatWqFrq4uV69e5b333hM61ivJqviA0uGX5ORk5s2b98bnEslO06ZNsbW15ejRo0JHEQlEbsXHwoULefz4MatWrZJXEyIRUDrk4uTkROPGjYWOojASEhLo3bs306dPZ+bMmZw5cwZnZ2ehY1WJllbpnjLFxcVvfC4bG5vyy4jPnTv3xucTyU6XLl3E4qMWk0vxce/ePZYuXcqCBQtwdHSURxMiUbl9+/bRr18/oWMojL///pumTZty7949Tp06RVBQEBoaGq9+ooKQZc8HlO6m6ufnR2BgIAUFBa9+gqhGvP3225w9e5bc3Fyho4gEIJfiY8aMGbi6ujJlyhR5nF4kKhcZGcnt27fF+R5AdnY248ePZ9CgQfTq1Yvw8HClvMJM1sUHwIYNG0hISKj1Ox0rkrfffpvi4mKxR6qWknnxcerUKfbs2cM333yjVN+2RMppz549GBsb07FjR6GjCOry5cu0atWKXbt2sXv3brZs2YK+vr7QsV6LPIoPW1tblixZwrfffktoaKjMzit6fTY2Nri7u4tDL7WUTIsPqVTK9OnT6dKlC927d5flqUWiSu3bt4/evXuXzxOobUpKSli6dCkdO3bE0dGR69evK/0QlIaGBhoaGjItPgDGjRtHly5dGDlypDj8oiDefvttjh07JnQMkQBkWnxs3bqVq1ev8u2338rytCJRpdLS0jh79mytHXJ59OgRvr6+BAUF8dVXX3Ho0CFs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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create preproc output graph\n", + "preproc.write_graph(graph2use='colored', format='png', simple_form=True)\n", + "\n", + "# Visualize the graph\n", + "from IPython.display import Image\n", + "Image(filename=opj(preproc.base_dir, 'preproc', 'graph.dot.png'))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171211-11:00:34,773 workflow INFO:\n", + "\t Generated workflow graph: /home/neuro/nipype_tutorial/output/workingdir/preproc/graph.dot.png (graph2use=flat, simple_form=True).\n" + ] + }, + { + "data": { + "image/png": "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xegoqALDhw83OgIAAAAAAECN53TN38DAQA0bNszoGKgGnTt35l4DAAAAAAAAZWDNXwAAAAAAAABwQjR/AQAAAAAAAMAJ0fwFAAAAAAAAACdE8xcAAAAAAAAAnJBdmr8mk6nUV2n7AwMDdfHixQqPg/JxreCI+F4CAAAAAAAYzy7NX7PZLLPZXKH3586d04gRI1RYWFjuODePASkkJEQhISFW28q7RqXVw7E58j2zJRt/dgEAAAAAAIxX7cs++Pn5afv27XrnnXeq+9QO71YzJYuKilRUVFTh8cqqZ0am47L1HlcnR84GAAAAAACAklyq+4QxMTHq2bOnpk+fri5duqhfv37VHaHG2rdvX5XWw3iOfM8cORsAAAAAAABKqvaZv926ddO0adNkNps1atQonT59urojAAAAAAAAAIDTq/bmryS98cYbGjx4sLKzszV06FBdvXrViBhVqqwHXpW3/eaasLCwWx5XmfOXdZ7SHri3atUqS32rVq1q7JIROTk5ioyMVOvWreXu7q6mTZuqa9eumjhxopKSkqxq09LSNH78eAUGBsrNzU0BAQEKDw9XSkpKpcasaF159zglJUVjx461ZAoMDNS4ceOUmppqVXfjGGfPntXAgQPl5eUlX19fjRw5UhkZGZW6fuVlO3bsmJ566il5enrK29tbgwcP1pkzZyp1HgAAAAAAANiPIc1fSYqOjlabNm10+PBhvfLKK0bFqDJlPfCqItuLH3a3ePHiWx53O+e/+Txms1nbtm2TJPn7+ys/P1/PPvuspf7tt99Wv379auTDvEaPHq158+YpIiJCGRkZunDhgqKjo3Xq1Cl16tTJUpeamqrg4GCtXbtWS5cuVWZmplatWqUtW7aoa9euys7OtnnMitaVdV1TUlIUHBys+Ph4LVu2TBkZGfr000+1fv16derUyaoBfOMYkydP1owZM5ScnKyhQ4dqxYoVmjhxYqWuX1nZTp48qccee0zffPONNmzYoHPnzikyMlLh4eGVOg8AAAAAAADsx7Dmr7e3t9asWaP69etryZIlio6ONioKbtCzZ0916NBBFy5csJr1K0kffvihIiIiDEp2e7788ktJUkBAgDw8POTm5qa2bdvqo48+sqqbMmWKfv75Z02bNk29e/eWp6enQkJCNHfuXJ0+fVqzZs2yecyK1pXlnXfe0dmzZzVz5kz16NFD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W44PAAAAoFZZTfMXQJWYOHGi5s6dW6IRU7duXdWtW1cmk0mFhYX6z3/+U+qxP//8syTbm4Off/65PvvsM+3YsUNZWVmSpBYtWmj9+vX69a9/LUlydXUt9bzFGjRooMuXL9s0pi11pTWYijPl5+dbNW/y8/Pl7u4uV1dXXbt2rdwxytteUaUd7+LiosLCwhLZbud8tan5a+u9relsbf5mZ2frvffe09q1a5WcnKy8vDyr/RX9jpe23V5j26L48y9btkxffPFFpce5XQ0bNlTdunUlSY0bN5Z0/c9yccP8xqZxgwYNVK9ePavjihvJjRs3ttQ2bNhQ9erVk5eXl+WYG/d7eXmVus47AAAAAMOs5r/QAVSJlJQUmUwmNW7cWJcuXZK/v79+/etfy8fHR02bNlWTJk3UtGlT+fj4WH4u/t/69etXetbgkCFDNGTIEBUVFWnfvn169913tXnzZr344os6fPiwJMnX11fnzp1TZmampSlyu2PaUleaO+64Q+fPn1d6enqJ2bXF+43i4+Oj1NTUEtlycnIMy1STOPK9dQTDhw/X1q1bNWXKFP3+979XkyZNJKnUB+c50ti3snHjRp0+fVpffPGF5s2bp5SUFLVp00ZHjhzRY489poULF8rX19fqmP/85z/Kzc0td9xffvlFV69etby/cuWK8vPzJUm5ubmWf9jKzs6W2WxWYWGhLl26JOn6DOnif9gq67gzZ87IbDYrNzdX165dU05OjuWcOTk5t5xZXTwzubg53KhRI9WrV08eHh7y8vKSu7u7vL295e3trcaNG6tRo0aW9zf/7OHhUdHLDQAAAKAMNH8BVInly5dr+fLl2rZtm3r16qUvv/xSbdq0qdJzmkwmnT17VoGBgapTp45CQkIUExOjRo0a6fjx45a6QYMG6a9//at27typwYMHW42xZ88eTZo0SQkJCTaNWdG6svTv318LFizQ9u3bNWrUKMv2bdu2WfYbpXfv3vr73/9eIlvxNUL5HPneOoJ9+/ZJkl5//XXLrNTipmR1j92gQQNduXJFBQUFKigoUIsWLUosDVFRJpNJrVu31quvvqqwsDCNHTtWa9as0ZIlSzRz5kwNGDBASUlJatSokdVxxUupOKqCggLl5eXp8uXLunbtmrKyspSfn68rV64oNzdX+fn5unTpkqVhnJ2drWvXrikvL095eXn65Zdf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061PRo0aBQA4depUrfu1r12XevTogYULF+LcuXNISEjAG2+8gW3btsHLywvz5s1DWlqazmt6VFlZGZKSkkTv/C0tLUVRURHDXyIiks348ePxyiuvYO7cudXvlCIiIv3F8JdIB2JjY1FcXIyBAwfKXQpRh7Fy5Ur4+flh/vz52L9/P7KyslBQUICjR49i5syZCA8Pr742KCgIALBu3Trk5uYiOzsbYWFhcpUuiZUrV8LW1hZLly7FN998g8LCQpw7dw6fffaZrHV5eHhgxYoVSEhIwMcff4yjR4+ia9euWLt2LSoqKmSrKy4uDmq1WvTwV9tpzbEPREQkp82bN8PMzAxz586VuxQiIpIYw18iHbh+/TqMjY3xxBNPyF0KUZunUChE+djR0RGXLl1CSEgIlixZgs6dO8PPzw+ff/459uzZU6sTf9euXZg+fTq2b98OZ2dnDBs2rNYp2K2po6nEet31fezj44Nz587hmWeewfjx4+Hq6or169fjo48+AlB7hrAcTExMEBoaitu3b2PZsmVYvXo1Bg4ciOvXr8tSjyAIAB5+3cSUkZEBAOjUqZOo6xIRETWHjY0NduzYga+++gpRUVFyl0NERBJi+EukAzdu3ED37t3b/Mn2JL/S0lIsX74cvr6+UCqV1YeUNVXNg83aq0dn87b0fgCws7NDREQE4uLiUF5ejtTUVBw+fPixLvxOnTph165dSEtLQ1lZGX799VcEBweLVocuX3dDdfTs2RPHjh1DYWEh8vPzceTIEZiamgJ4/CA4uZibmyMsLAzXrl2DiYkJ/P39sXPnTp3XIQgCXFxcah2mJwbt22udnJxEXZeIiKi5RowYgblz52LevHlISkqSuxwiIpIIw18iHbh+/TqeeuopucugdmDFihVYs2YNXn31VeTn5+P48ePNer6+HVJG4lIoFNUdrVpnzpwBgDZ3IOUTTzyBs2fP4q233sKsWbPw9ttvo6qqSmf7q1Qq0Uc+AA87f42NjWFtbS362kRERM3197//HU5OTnjttdf470giIj3F8JdIB65fv46ePXvKXQa1A3v37gUAzJs3D+bm5hg1ahT/IU6imj9/PuLi4lBUVIRTp07h3XffhbW1NVauXCl3aY8xNDREeHg49uzZg08//RSzZs2CWq3Wyd6CIEgW/jo5ObXr7nwiItIf5ubm2LFjB2JiYrBr1y65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9zO381etVuP999/HypUrm7yY2+XLl+Hq6orevXtbXjCZ6N+/Pz799FMAwLJly3D16lUbV0RkfYIgoF+/fg22jx49GgBw8+bN9i6JqFPKyckBAIa/RER2iuEvERERwdXV1ax5sYbwt7i4uNn91q5dC5lMhuXLlze5z88//4z+/fvD2dnZsmKpUfPmzcOiRYtQXl6OZ599FhqNxtYlEdlEXl4eAGDAgAE2roSoc8jNzQUABAcH27gSIiKyBoa/REREBFdXV9TW1qKysrLZ/by8vODk5IT8/Pwm90lPT8fOnTvx17/+FR4eHk3ud+bMGfzHf/xHq2umhj755BMMGTIECoUC8+bNs3U5nV5FRQU2btyIQYMGwcPDA66urnjkkUewZMkSXLhwwWRfpVKJxYsXIywsDFKpFGFhYViyZAlUKpXJfoYRCRKJBAqFAtOmTYOvr2+D0QlqtRpLly41rhcaGopFixZBqVQ2qPP69et46qmn4OnpCS8vLzzxxBNIS0szOZaltFotli9fju7du8PV1RX+/v4YNWoU3nzzTaSmppo8nwef24IFC0zWMve51K83LS0NTz75JLy8vODp6Ymnn34aN27cMKv2r7/+GgCwbt06i5830cMoNzcXPj4+cHNzs3UpRERkBQx/iYiIyPgLX3l5ebP7OTo6IigoyPgW0ca8+eab6Nu3L1588cUm9ykoKMD169cxduzY1hVMjXJxccGhQ4fg6+uL77//3tbldGo6nQ6xsbHYsGEDli1bhqysLOTn5+Ozzz7D2bNnMXLkSOO+SqUSw4cPR2JiIvbs2YOCggLs3r0b3333HUaMGGESAAuCYPx86dKlePPNN5GTk4MffvjBuF2lUmH48OE4cuQIvvzySxQWFmL//v04ceIERo0aZdLVrVAo8B//8R/45Zdf8P333yMnJwdr167FokWLGj2muebNm4etW7fitddeQ0FBAXJzc7Fr1y5kZWVhxIgRja4tCAIEQcAXX3zRqudSf62FCxfiz3/+M3JycvDdd9/h8uXLGD16NG7fvt1s3b/88gs2btyINWvW4Mknn7T4eRM9jHJzcznygYjIjjH8JSIiIri6ugKAWaMfQkJCjG8RfVBKSgr++c9/YvPmzXBwaPp/M06fPg2JRMLOXyuIjIzEN998Y7MLcNmL9evX46effsJf/vIXLFiwAEFBQfD09MTv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TZuUkpIiSapXr55Wrlypli1bFjp7ZGSkBg8eTGnsJMV9vgv7epfkezEzMzPfxbMyMzPl4+Oj8uXL69y5c0Xe93I5ribnpVx4znh6eionJ+einIXJdCWl5fzfvn279u3bp8OHD2v37t36+uuv5eXlpSeffFLjx4+Xn5/fFY93fj3pBQsWFDrD1XrwwQf1448/avfu3U4bEwDgMEs8TScAAABwR+PHj9eXX36p4cOHa9euXU67GnpISIhCQkKcMpa7q1WrluLi4nTixAlVq1btivuHhoYqNDRUubm5io6O1uuvv65169bpgQce0H//+18nJIYzOfP1rlmzpuLj43X8+PG82cGSdPz48bz7i7NvSSvKORMQEKCkpKSLcqampjosX1G4wvnfvn37fDOhT5w4oVmzZmnKlClauHChIiMjdcstt1z2GHFxcWrevHmxxi+OjIwMLV++XC+99JLTxgQAOBbLPgAAABSDh4eHPvvsM/3+++96+eWXTcdBAfr16ydJ2rJly0X3bdu2TXfccUfe9zabTbGxsZL+fG07dOigiIgISdLPP//s+LBwKme/3n369JEkbdy4Md/2DRs25Lu/qPtKf85glaSsrCydOXNGAQEBxc5ZlHOmW7duBeaMiYkp9vglyRXP/+rVq2v8+PHav3+/mjRpog4dOmjdunWXfYyzl31YtWqV0tPTWfIBAEoTpy0vDAAAUArNnDnTKleunBUdHW06SqE46oJvhd3uzPGOHTtmNWnSxKpdu7a1ZMkS6/jx41ZaWpq1evVqq1GjRvkugCXJCgkJsfbu3WtlZGRYiYmJ1sSJEy1JVt++fYuUnQu+OVdxnu+ivN4l8V5MTEy06tevbwUFBVkbN2600tLSrI0bN1q1a9e26tevbyUmJhZrX8uyrNtvv92SZG3fvt1avHix1bt372LnLMo5c/DgQatq1ap5OdPT063o6GirY8eOnP+F+NlzcnKs8PBwy8fH55J/fpw7d87y8PCwli5dWqQMV6Nv375WSEiI08YDADhcJH8rBQAAuAq5ublWr169rAYNGlgpKSmm41xRccrf88XKXwuWom43Md6JEyesp59+2mrYsKFVvnx5q1atWlafPn2smJiYfPtt377dCg8Ptxo0aGCVL1/eqlKlitWiRQvr9ddft06fPl2k/JS/zlWc57uwr3dJvhcTExOtMWPGWEFB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JyYmAgDX/CUionrH5C8REREREdU5R0dHw1IRL7/8MgAgOTkZP/74o+ERHByM7OxsWFtbo0ePHhg8eLAhIdy1a1coFAozXwVR5b766isEBASUK09ISECzZs3KrQNcVXzvExFRTTH5S0REREREZqFSqaBSqTBx4kQAQHFxMeLi4vD999/j7NmziIqKwrvvvovi4mI4OzujZ8+e6N69Ox544AE8+OCD6Nq1K5o3b27mqyD6y8WLF/H777/j/fffL3csMTER7du3h4WFRY3aDg0NvdfwiOgenTp1Chs3bjR3GETVxuQvERERERE1CFZWVujRowd69OiBp59+GgCQm5uLn3/+GT/88INh2YgdO3YgNzcXFhYW6NSpEx588EGjpHCHDh1qnGQjqqlDhw7B1dUVAwcOLHcsMTHxntb7nTZt2r2ERkS1QETMHQJRjTD5S0REREREDVbz5s0xePBgDB482Khcv2REXFwcLly4gNDQUPzzn/9ESUkJbGxs0LlzZ/Tu3RvdunVD165d0a9fP7i7u5vpKqgpOHLkCEaPHg1LS8tyxxITE9GzZ08zREVERE0dk79ERERERHTfuXPJCAC4ffs24uLicO7cOfz22284f/48wsPDkZmZaTinW7du8Pb2Njy8vLzQvn17kwk7oqrKz8/HyZMn8e6775o8npiYiClTptRzVEREREz+EhERERFRI9GiRQv07dsXffv2NSpPSUnBb7/9hl9//RUXL17EuXPnEBYWhrS0NACAjY0NOnbsCB8fH3h5eRmSwt7e3lCpVOa4FLrPnDhxAnl5eRg1alS5Y3l5eUhJSbmnZR+IiIhqislfIiIiIiJq1Dw8PODh4YExY8YYlWdnZyM+Ph7x8fH4/fff8ccff+DYsWPYtm0bdDodAMDe3t6QDPby8oKPjw+8vb3RqVMnODs7m+NyqAE6evQounbtijZt2pQ7du3aNYgI2rdvX/+BERFRk8fkLxERERERNUktW7Y0eacwAGRlZSExMRGJiYm4cOEC4uLi8N///hdvvvkmcnNzAQC2trZQqVTo2LEjOnbsCA8PD6PXarWay0k0EUePHsXo0aNNHrt+/ToAoG3btvUZEhGRwdChQwEA0dHRZo6k4frmm2+wfft2nDlzBjdv3kSrVq3Qt29fPP3005g8ebJRXYVCYbKNOzcFrKheddTGRoNM/hIRERER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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Visualize the detailed graph\n", + "preproc.write_graph(graph2use='flat', format='png', simple_form=True)\n", + "Image(filename=opj(preproc.base_dir, 'preproc', 'graph_detailed.dot.png'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run the Workflow\n", + "\n", + "Now that everything is ready, we can run the preprocessing workflow. Change ``n_procs`` to the number of jobs/cores you want to use. **Note** that if you're using a Docker container and FLIRT fails to run without any good reason, you might need to change memory settings in the Docker preferences (6 GB should be enough for this workflow)." + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171211-12:15:48,549 workflow INFO:\n", + "\t Workflow preproc settings: ['check', 'execution', 'logging', 'monitoring']\n", + "171211-12:15:48,573 workflow INFO:\n", + "\t Running in parallel.\n", + "171211-12:15:48,581 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 10/10.\n", + "171211-12:15:48,859 workflow INFO:\n", + "\t Executing node preproc.selectfiles in dir: /home/neuro/nipype_tutorial/output/workingdir/preproc/_session_id_run-3_subject_id_sub-1/selectfiles\n", + "171211-12:15:48,876 workflow INFO:\n", + "\t Running node \"selectfiles\" (\"nipype.interfaces.io.SelectFiles\").\n", + "171211-12:15:50,581 workflow INFO:\n", + "\t [Job 0] Completed (preproc.selectfiles).\n", + "171211-12:15:50,585 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 10/10.\n", + "171211-12:15:50,713 workflow INFO:\n", + "\t [Job 1] Cached (preproc.mcflirt).\n", + "171211-12:15:52,732 workflow INFO:\n", + "\t [Job 2] Cached (preproc.coreg_step1).\n", + "171211-12:15:54,734 workflow INFO:\n", + "\t [Job 3] Cached (preproc.coreg_step2).\n", + "171211-12:15:56,724 workflow INFO:\n", + "\t [Job 4] Cached (preproc.smooth).\n", + "171211-12:15:58,735 workflow INFO:\n", + "\t Executing node preproc.datasink in dir: /home/neuro/nipype_tutorial/output/workingdir/preproc/_session_id_run-3_subject_id_sub-1/_fwhm_4/datasink\n", + "171211-12:15:58,756 workflow INFO:\n", + "\t Running node \"datasink\" (\"nipype.interfaces.io.DataSink\").\n", + "171211-12:15:58,762 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/preproc/_session_id_run-3_subject_id_sub-1/sub-1_handsqueeze_run-3_bold_mcf.nii.par -> /home/neuro/nipype_tutorial/output/datasink/preproc/sub-1/handsqueeze_run-3_bold_mcf.par\n", + "171211-12:15:58,765 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/preproc/_session_id_run-3_subject_id_sub-1/sub-1_handsqueeze_run-3_bold_mcf.nii_mean_reg_flirt.nii -> /home/neuro/nipype_tutorial/output/datasink/preproc/sub-1/handsqueeze_run-3_bold_mean_flirt.nii\n", + "171211-12:15:58,768 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/preproc/_session_id_run-3_subject_id_sub-1/_fwhm_4/ssub-1_handsqueeze_run-3_bold_mcf_flirt.nii -> /home/neuro/nipype_tutorial/output/datasink/preproc/sub-1/run-3_fwhm_4/ssub-1_handsqueeze_run-3_bold_mcf_flirt.nii\n", + "171211-12:16:00,592 workflow INFO:\n", + "\t [Job 5] Completed (preproc.datasink).\n", + "171211-12:16:00,595 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 0 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 10/10.\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preproc.run('MultiProc', plugin_args={'n_procs': 10})\n", + "\n", + "# !nipypecli crash /home/neuro/nipype_tutorial/crash-20171202-160850-neuro-resample.b11-28ee9940-3aa2-4176-bed8-e68cdda9bcd1.pklz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plot Motion Parameters\n", + "Now, let's investigate the motion parameters. How much did the subject move and turn in the scanner?" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pylab as plt\n", + "\n", + "par_file = '/home/neuro/nipype_tutorial/output/datasink/preproc/_session_id_run-13_subject_id_sub-1/sub-1_run-13_bold_mcf.nii.par'\n", + "par = np.loadtxt(par_file)\n", + "\n", + "fig, axes = plt.subplots(2, 1, figsize=(15, 5))\n", + "axes[0].set_ylabel('rotation (radians)')\n", + "axes[0].plot(par[0:, :3])\n", + "axes[1].plot(par[0:, 3:])\n", + "axes[1].set_xlabel('time (TR)')\n", + "axes[1].set_ylabel('translation (mm)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "********************************************************************" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 1st-level Analysis using GLM\n", + "\n", + "In this part we will take the preprocessed output from the first part and run for each subject a 1st-level analysis. For this we need to do the following steps:\n", + "\n", + "1. Extract onset times of stimuli from TVA file\n", + "2. Specify the model (TR, high pass filter, onset times, etc.)\n", + "3. Specify contrasts to compute\n", + "4. Estimate contrasts\n", + "\n", + "**So, let's begin!**" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Imports\n", + "\n", + "First, we need to import all modules we later want to use." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n", + "171212-02:46:30,92 interface WARNING:\n", + "\t Could not get linked libraries for \"which\".\n" + ] + } + ], + "source": [ + "%pylab inline\n", + "from os.path import join as opj\n", + "from nipype.interfaces.spm import Level1Design, EstimateModel, EstimateContrast\n", + "from nipype.algorithms.modelgen import SpecifySPMModel\n", + "from nipype.interfaces.utility import Function, IdentityInterface\n", + "from nipype.interfaces.io import SelectFiles, DataSink\n", + "from nipype.pipeline.engine import Workflow, Node" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Specify Nodes\n", + "\n", + "Initiate all the different interfaces (represented as nodes) that you want to use in your workflow." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# SpecifyModel - Generates SPM-specific Model\n", + "modelspec = Node(SpecifySPMModel(concatenate_runs=False,\n", + " input_units='secs',\n", + " output_units='secs',\n", + " time_repetition=TR,\n", + " high_pass_filter_cutoff=128),\n", + " name=\"modelspec\")\n", + "\n", + "# Level1Design - Generates an SPM design matrix\n", + "level1design = Node(Level1Design(bases={'hrf': {'derivs': [0, 0]}},\n", + " timing_units='secs',\n", + " interscan_interval=TR,\n", + " model_serial_correlations='AR(1)'),\n", + " name=\"level1design\")\n", + "\n", + "# EstimateModel - estimate the parameters of the model\n", + "level1estimate = Node(EstimateModel(estimation_method={'Classical': 1}),\n", + " name=\"level1estimate\")\n", + "\n", + "# EstimateContrast - estimates contrasts\n", + "level1conest = Node(EstimateContrast(), name=\"level1conest\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Specify GLM contrasts\n", + "\n", + "To do any GLM analysis, we need to also define the contrasts that we want to investigate." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# Condition names\n", + "condition_names = ['rest', 'right', 'left']\n", + "\n", + "# Contrasts\n", + "contL1= 'right > rest'\n", + "contL2= 'left > rest'\n", + "\n", + "contL7= 'right or left'\n", + "\n", + "cont01 = [contL1, 'T', condition_names, [-1, 1, 0]]\n", + "cont02 = [contL2, 'T', condition_names, [-1, 0, 1]]\n", + "\n", + "cont03 = [contL7, 'F', [cont01, cont02]]\n", + "\n", + "contrast_list = [cont01, cont02, cont03]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Specify GLM Model\n", + "\n", + "The next step is now to get information such as stimuli onset, duration and other regressors into the GLM model. For this we need to create a helper function, in our case called ``subjectinfo``.\n", + "\n", + "To recap, let's see what we have in the TSV file for first run for subject 1:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "onset\tduration\tstimulus\r\n", + "0\t2\tright\r\n", + "2\t2\tright\r\n", + "4\t2\tright\r\n", + "6\t2\tright\r\n", + "8\t2\tright\r\n", + "10\t2\tright\r\n", + "12\t2\tleft\r\n", + "14\t2\tleft\r\n", + "16\t2\tleft\r\n", + "18\t2\tleft\r\n", + "20\t2\tleft\r\n", + "22\t2\tleft\r\n", + "24\t2\tright\r\n", + "26\t2\tright\r\n", + "28\t2\tright\r\n", + "30\t2\tright\r\n", + "32\t2\tright\r\n", + "34\t2\tright\r\n", + "36\t2\tleft\r\n", + "38\t2\tleft\r\n", + "40\t2\tleft\r\n", + "42\t2\tleft\r\n", + "44\t2\tleft\r\n", + "46\t2\tleft\r\n" + ] + } + ], + "source": [ + "!cat {data_dir}sub-1/func/sub-1_{session_list[0]}_events.tsv" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So what we need is the onset and the stimuli type, i.e. **column 0** and **column 2**." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let us incorporate all this information in the helper function subjectinfo to get the GLM model.\n", + "\n", + "**Note that since the helper function will be defined as a node, all imports and variables should be redefined.**" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def subjectinfo(subject_id, session_id):\n", + "\n", + " import numpy as np\n", + " import json\n", + " from os.path import join as opj\n", + " from nipype.interfaces.base import Bunch\n", + " \n", + " data_dir = '/home/neuro/nipype_tutorial/data/'\n", + " \n", + " \n", + " condition_names = ['rest', 'right', 'left']\n", + " \n", + "\n", + " logfile_dir = opj(data_dir, subject_id, 'func')\n", + "\n", + " # Read the TSV file\n", + " filename = opj(logfile_dir,\n", + " '%s_%s_events.tsv' % (subject_id, session_id))\n", + "\n", + " # Save relevant information\n", + " trailinfo = np.genfromtxt(filename, delimiter='\\t',\n", + " dtype=None, skip_header=1)\n", + " trailinfo = [[t[0], t[2]] for t in trailinfo]\n", + "\n", + " # Separate onset of conditions\n", + " onset1 = []\n", + " onset2 = []\n", + " onset3 = []\n", + "\n", + "\n", + " for t in trailinfo:\n", + " if b'rest' in t[1]:\n", + " onset1.append(t[0])\n", + " if b'right' in t[1]:\n", + " onset2.append(t[0])\n", + " if b'left' in t[1]:\n", + " onset3.append(t[0])\n", + "\n", + "\n", + " subjectinfo = [Bunch(conditions=condition_names,\n", + " onsets=[onset1, onset2, onset3],\n", + " durations=[[2.0], [2.0], [2.0]],\n", + " amplitudes=None,\n", + " tmod=None,\n", + " pmod=None,\n", + " regressor_names=None,\n", + " regressors=None)]\n", + "\n", + "\n", + " return subjectinfo # this output will later be returned to infosource\n", + "\n", + "# Get Subject Info - get subject specific condition information\n", + "getsubjectinfo = Node(Function(input_names=['subject_id', 'session_id'],\n", + " output_names=['subject_info'],\n", + " function=subjectinfo),\n", + " name='getsubjectinfo')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "subjectinfo('sub-1', 'run-3')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Specify input & output stream\n", + "\n", + "Specify where the input data can be found & where and how to save the output data." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [], + "source": [ + "# Infosource - a function free node to iterate over the list of subject names\n", + "infosource = Node(IdentityInterface(fields=['subject_id',\n", + " 'session_id',\n", + " 'fwhm_id',\n", + " 'contrasts'],\n", + " contrasts=contrast_list),\n", + " name=\"infosource\")\n", + "infosource.iterables = [('subject_id', subject_list),\n", + " ('session_id', session_list),\n", + " ('fwhm_id', fwhm)]\n", + "\n", + "# SelectFiles - to grab the data (alternativ to DataGrabber)\n", + "templates = {'func': opj(output_dir, 'preproc', '_session_id_{session_id}_subject_id_{subject_id}',\n", + " '_fwhm_{fwhm_id}', 's{subject_id}_{session_id}_bold_mcf_flirt.nii'),\n", + "# # Here the unpreprocessed data is used for GLM analyis\n", + "# template = {'func': opj(data_dir, '{subject_id}', 'func',\n", + "# '{subject_id}_{session_id}_bold.nii'), \n", + " 'mc_param': opj(output_dir, 'preproc', '_session_id_{session_id}_subject_id_{subject_id}',\n", + " '{subject_id}_{session_id}_bold_mcf.nii.par')}\n", + "selectfiles = Node(SelectFiles(templates,\n", + " base_directory=experiment_dir,\n", + " sort_filelist=True),\n", + " name=\"selectfiles\")\n", + "\n", + "# Datasink - creates output folder for important outputs\n", + "datasink = Node(DataSink(base_directory=experiment_dir,\n", + " container=output_dir),\n", + " name=\"datasink\")\n", + "\n", + "# Use the following DataSink output substitutions\n", + "substitutions = [('_subject_id_', '')]\n", + "subjFolders = [('_fwhm_id_%s%s' % (f, sub), '%s_fwhm%s' % (sub, f))\n", + " for f in fwhm\n", + " for sub in subject_list]\n", + "substitutions.extend(subjFolders)\n", + "datasink.inputs.substitutions = substitutions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Specify Workflow\n", + "\n", + "Create a workflow and connect the interface nodes and the I/O stream to each other." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "# Initiation of the 1st-level analysis workflow\n", + "l1analysis = Workflow(name='l1analysis')\n", + "l1analysis.base_dir = opj(experiment_dir, working_dir)\n", + "\n", + "# Connect up the 1st-level analysis components\n", + "l1analysis.connect([(infosource, selectfiles, [('subject_id', 'subject_id'),\n", + " ('session_id', 'session_id'),\n", + " ('fwhm_id', 'fwhm_id')]),\n", + " (infosource, getsubjectinfo, [('subject_id', 'subject_id'),\n", + " ('session_id', 'session_id')]),\n", + " (getsubjectinfo, modelspec, [('subject_info',\n", + " 'subject_info')]),\n", + " (infosource, level1conest, [('contrasts', 'contrasts')]),\n", + " (selectfiles, modelspec, [('func', 'functional_runs')]),\n", + " (selectfiles, modelspec, [('mc_param',\n", + " 'realignment_parameters')]),\n", + " (modelspec, level1design, [('session_info',\n", + " 'session_info')]),\n", + " (level1design, level1estimate, [('spm_mat_file',\n", + " 'spm_mat_file')]),\n", + " (level1estimate, level1conest, [('spm_mat_file',\n", + " 'spm_mat_file'),\n", + " ('beta_images',\n", + " 'beta_images'),\n", + " ('residual_image',\n", + " 'residual_image')]),\n", + " (level1conest, datasink, [('spm_mat_file',\n", + " '1stLevel.@spm_mat'),\n", + " ('spmT_images', '1stLevel.@T'),\n", + " ('con_images', '1stLevel.@con'),\n", + " ('spmF_images', '1stLevel.@F'),\n", + " ('ess_images', '1stLevel.@ess'),\n", + " ]),\n", + " ])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Visualize the workflow\n", + "\n", + "It always helps to visualize your workflow." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171211-11:13:28,145 workflow INFO:\n", + "\t Generated workflow graph: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/graph.dot.png (graph2use=colored, simple_form=True).\n" + ] + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Create 1st-level analysis output graph\n", + "l1analysis.write_graph(graph2use='colored', format='png', simple_form=True)\n", + "\n", + "# Visualize the graph\n", + "from IPython.display import Image\n", + "Image(filename=opj(l1analysis.base_dir, 'l1analysis', 'graph.dot.png'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create 1st-level analysis output graph\n", + "l1analysis.write_graph(graph2use='flat', format='png', simple_form=True)\n", + "\n", + "# Visualize the graph\n", + "from IPython.display import Image\n", + "Image(filename=opj(l1analysis.base_dir, 'l1analysis', 'graph_detailed.dot.png'))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Run the Workflow\n", + "\n", + "Now that everything is ready, we can run the 1st-level analysis workflow. Change ``n_procs`` to the number of jobs/cores you want to use." + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "171211-11:15:56,464 workflow INFO:\n", + "\t Workflow l1analysis settings: ['check', 'execution', 'logging', 'monitoring']\n", + "171211-11:15:56,490 workflow INFO:\n", + "\t Running in parallel.\n", + "171211-11:15:56,496 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 2 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:15:56,645 workflow INFO:\n", + "\t [Job 0] Cached (l1analysis.getsubjectinfo).\n", + "171211-11:15:56,653 workflow INFO:\n", + "\t Executing node l1analysis.selectfiles in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/selectfiles\n", + "171211-11:15:56,669 workflow INFO:\n", + "\t Running node \"selectfiles\" (\"nipype.interfaces.io.SelectFiles\").\n", + "171211-11:15:58,497 workflow INFO:\n", + "\t [Job 1] Completed (l1analysis.selectfiles).\n", + "171211-11:15:58,500 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:15:58,582 workflow INFO:\n", + "\t Executing node l1analysis.modelspec in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/modelspec\n", + "171211-11:15:58,605 workflow INFO:\n", + "\t Running node \"modelspec\" (\"nipype.algorithms.modelgen.SpecifySPMModel\").\n", + "171211-11:16:00,499 workflow INFO:\n", + "\t [Job 2] Completed (l1analysis.modelspec).\n", + "171211-11:16:00,503 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:16:00,584 workflow INFO:\n", + "\t Executing node l1analysis.level1design in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/level1design\n", + "171211-11:16:00,605 workflow INFO:\n", + "\t Running node \"level1design\" (\"nipype.interfaces.spm.model.Level1Design\").\n", + "171211-11:16:02,502 workflow INFO:\n", + "\t [MultiProc] Running 1 tasks, and 0 jobs ready. Free memory (GB): 52.89/53.09, Free processors: 3/4.\n", + " Currently running:\n", + " * l1analysis.level1design\n", + "171211-11:16:36,536 workflow INFO:\n", + "\t [Job 3] Completed (l1analysis.level1design).\n", + "171211-11:16:36,540 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:16:36,617 workflow INFO:\n", + "\t Executing node l1analysis.level1estimate in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/level1estimate\n", + "171211-11:16:36,631 workflow INFO:\n", + "\t Running node \"level1estimate\" (\"nipype.interfaces.spm.model.EstimateModel\").\n", + "171211-11:16:38,539 workflow INFO:\n", + "\t [MultiProc] Running 1 tasks, and 0 jobs ready. Free memory (GB): 52.89/53.09, Free processors: 3/4.\n", + " Currently running:\n", + " * l1analysis.level1estimate\n", + "171211-11:17:38,596 workflow INFO:\n", + "\t [Job 4] Completed (l1analysis.level1estimate).\n", + "171211-11:17:38,600 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:17:38,677 workflow INFO:\n", + "\t Executing node l1analysis.level1conest in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/level1conest\n", + "171211-11:17:38,695 workflow INFO:\n", + "\t Running node \"level1conest\" (\"nipype.interfaces.spm.model.EstimateContrast\").\n", + "171211-11:17:40,599 workflow INFO:\n", + "\t [MultiProc] Running 1 tasks, and 0 jobs ready. Free memory (GB): 52.89/53.09, Free processors: 3/4.\n", + " Currently running:\n", + " * l1analysis.level1conest\n", + "171211-11:18:22,641 workflow INFO:\n", + "\t [Job 5] Completed (l1analysis.level1conest).\n", + "171211-11:18:22,645 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 1 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n", + "171211-11:18:22,718 workflow INFO:\n", + "\t Executing node l1analysis.datasink in dir: /home/neuro/nipype_tutorial/output/workingdir/l1analysis/_fwhm_id_4_subject_id_sub-1/datasink\n", + "171211-11:18:22,728 workflow INFO:\n", + "\t Running node \"datasink\" (\"nipype.interfaces.io.DataSink\").\n", + "171211-11:18:22,732 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/SPM.mat -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/SPM.mat\n", + "171211-11:18:22,735 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmT_0001.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmT_0001.nii\n", + "171211-11:18:22,738 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmT_0002.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmT_0002.nii\n", + "171211-11:18:22,740 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmT_0003.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmT_0003.nii\n", + "171211-11:18:22,742 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmT_0004.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmT_0004.nii\n", + "171211-11:18:22,744 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0005.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0005.nii\n", + "171211-11:18:22,746 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0006.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0006.nii\n", + "171211-11:18:22,748 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0007.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0007.nii\n", + "171211-11:18:22,751 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/con_0001.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/con_0001.nii\n", + "171211-11:18:22,754 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/con_0002.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/con_0002.nii\n", + "171211-11:18:22,757 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/con_0003.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/con_0003.nii\n", + "171211-11:18:22,760 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/con_0004.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/con_0004.nii\n", + "171211-11:18:22,762 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0005.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0005.nii\n", + "171211-11:18:22,765 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0006.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0006.nii\n", + "171211-11:18:22,767 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0007.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0007.nii\n", + "171211-11:18:22,770 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0005.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0005.nii\n", + "171211-11:18:22,772 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0006.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0006.nii\n", + "171211-11:18:22,775 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/spmF_0007.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/spmF_0007.nii\n", + "171211-11:18:22,778 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0005.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0005.nii\n", + "171211-11:18:22,780 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0006.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0006.nii\n", + "171211-11:18:22,782 interface INFO:\n", + "\t sub: /home/neuro/nipype_tutorial/output/datasink/1stLevel/_fwhm_id_4_subject_id_sub-1/ess_0007.nii -> /home/neuro/nipype_tutorial/output/datasink/1stLevel/sub-1_fwhm4/ess_0007.nii\n", + "171211-11:18:24,644 workflow INFO:\n", + "\t [Job 6] Completed (l1analysis.datasink).\n", + "171211-11:18:24,649 workflow INFO:\n", + "\t [MultiProc] Running 0 tasks, and 0 jobs ready. Free memory (GB): 53.09/53.09, Free processors: 4/4.\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l1analysis.run('MultiProc', plugin_args={'n_procs': 10})\n", + "\n", + "# !nipypecli crash /home/neuro/nipype_tutorial/crash-20171203-084943-neuro-getsubjectinfo.a0-ff371092-d758-40cb-a8ea-546235f16065.pklz" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Inspect output\n", + "\n", + "Let's check the structure of the output folder, to see if we have everything we wanted to save. You should have nine contrast images (``con_*.nii`` for T-contrasts and ``ess_*.nii`` for T-contrasts) and nine statistic images (``spmT_*.nii`` and ``spmF_*.nii``) for every subject and smoothing kernel." + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/neuro/nipype_tutorial/output/datasink/1stLevel\r\n", + "└── sub-1_fwhm4\r\n", + " ├── con_0001.nii\r\n", + " ├── con_0002.nii\r\n", + " ├── con_0003.nii\r\n", + " ├── con_0004.nii\r\n", + " ├── ess_0005.nii\r\n", + " ├── ess_0006.nii\r\n", + " ├── ess_0007.nii\r\n", + " ├── spmF_0005.nii\r\n", + " ├── spmF_0006.nii\r\n", + " ├── spmF_0007.nii\r\n", + " ├── SPM.mat\r\n", + " ├── spmT_0001.nii\r\n", + " ├── spmT_0002.nii\r\n", + " ├── spmT_0003.nii\r\n", + " └── spmT_0004.nii\r\n", + "\r\n", + "1 directory, 15 files\r\n" + ] + } + ], + "source": [ + "!tree {output_dir}/1stLevel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Visualize results\n", + "\n", + "Let's look at the contrasts of one subject that we've just computed using the anatomical coregistration." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [ + { + "ename": "ImportError", + "evalue": "No module named nilearn.plotting", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mImportError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmagic\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mu'matplotlib inline'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32mfrom\u001b[0m \u001b[0mnilearn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplotting\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mplot_stat_map\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0manatimg\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mdata_dir\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;34m'sub-1/anat/sub-1_run-3_T1w.nii'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mcut_coords\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m30\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m60\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mImportError\u001b[0m: No module named nilearn.plotting" + ] + } + ], + "source": [ + "from nilearn.plotting import plot_stat_map\n", + "anatimg = data_dir+'sub-1/anat/sub-1_run-3_T1w.nii'\n", + "cut_coords=(35, -10, 55)\n", + "threshold = 4\n", + "\n", + "\n", + "contrasts_dir = output_dir + '/1stLevel/_fwhm_id_4_session_id_run-13sub-1'\n", + "plot_stat_map(\n", + " contrasts_dir+'/spmT_0001.nii', title=contL1,\n", + " bg_img=anatimg, threshold=threshold, cut_coords=cut_coords, dim=-1)\n", + "\n", + "cut_coords=(-30, -10, 55)\n", + "plot_stat_map(\n", + " contrasts_dir+'/spmT_0002.nii', title=contL2,\n", + " bg_img=anatimg, threshold=threshold, cut_coords=cut_coords, dim=-1)\n", + "\n", + "\n", + "plot_stat_map(\n", + " contrasts_dir+'/spmF_0003.nii', title=contL7,\n", + " bg_img=anatimg, threshold=threshold, cut_coords=cut_coords, dim=-1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "from nibabel.testing import data_path\n", + "import nibabel as nib\n", + "\n", + "filename = opj(output_dir, 'preproc', '_session_id_run-13_subject_id_sub-1',\n", + " '_fwhm_4', 'ssub-1_run-13_bold_mcf_flirt.nii')\n", + "\n", + "filename = opj(data_dir, 'sub-1', 'func', 'sub-1_run-13_bold.nii')\n", + "img = nib.load(filename)\n", + "print(img.shape)\n", + "\n", + "plt.figure(figsize=(7, 5))\n", + "plt.plot(img.dataobj[32, 32, 14, :])\n", + "plt.xlabel('Time [TRs]', fontsize=16)\n", + "plt.ylabel('Intensity', fontsize=16)\n", + "# plt.xlim(0, 150)\n", + "plt.subplots_adjust(bottom=.12, top=.95, right=.95, left=.12)\n", + "\n", + "show()" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.12" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/notebooks/resources_help.ipynb b/notebooks/resources_help.ipynb deleted file mode 100644 index 95196c5..0000000 --- a/notebooks/resources_help.ipynb +++ /dev/null @@ -1,62 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Where to find help" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Neurostar\n", - "\n", - "[NeuroStars.org](https://neurostars.org/) is a platform similar to StackOverflow but dedicated to neuroscience and neuroinformatics. If you have a problem or would like to ask a question about how to do something in Nipype please submit a question to [NeuroStars.org](https://neurostars.org/) with a nipype tag.\n", - "\n", - "All previous Nipype questions are available here: https://neurostars.org/tags/nipype" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Gitter\n", - "\n", - "[gitter.im](https://gitter.im/home/explore) stands under the motto 'where developers come to talk'. It is a place where developer change thoughts, opinions, ideas and feedbacks to a specific software. Nipype's gitter channel can be found under https://gitter.im/nipy/nipype. Use it to directly speak with the community." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Github\n", - "\n", - "[github.com](https://github.com/nipy/nipype) is where the source code of Nipype is stored. Feel free to fork the repo and submit changes if you want. If you found a bug in the scripts or have a specific ideas for changes, please open a new [issue](https://github.com/nipy/nipype/issues) and let the community help you." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/resources_installation.ipynb b/notebooks/resources_installation.ipynb deleted file mode 100644 index 54f80bf..0000000 --- a/notebooks/resources_installation.ipynb +++ /dev/null @@ -1,122 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Install Nipype\n", - "\n", - "The best and most complete instruction on how to download and install Nipype can be found on the [official homepage](http://nipype.readthedocs.io/en/latest/users/install.html). Nonetheless, here's a short summary of some (but not all) approaches." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 1. Install Nipype\n", - "\n", - "Getting Nipype to run on your system is rather straight forward. And there are multiple ways to do the installation:\n", - "\n", - "\n", - "### Using conda\n", - "\n", - "If you have [conda](http://conda.pydata.org/docs/index.html), [miniconda](https://conda.io/miniconda.html) or [anaconda](https://www.continuum.io/why-anaconda) on your system, than installing Nipype is just the following command:\n", - "\n", - " conda config --add channels conda-forge\n", - " conda install nipype\n", - "\n", - "\n", - "### Using ``pip`` or ``easy_install``\n", - "\n", - "Installing Nipype via ``pip`` or ``easy_install`` is as simple as you would imagine.\n", - "\n", - " pip install nipype\n", - " \n", - "or\n", - " \n", - " easy_install nipype\n", - "\n", - "\n", - "### Using Debian or Ubuntu\n", - "\n", - "Installing Nipype on a Debian or Ubuntu system can also be done via ``apt-get``. For this use the following command:\n", - "\n", - " apt-get install python-nipype\n", - "\n", - "\n", - "### Using Github\n", - "\n", - "To make sure that you really have the newest version of Nipype on your system, you can run the pip command with a flag that points to the github repo:\n", - "\n", - " pip install git+https://github.com/nipy/nipype#egg=nipype" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 2. Install Dependencies\n", - "\n", - "For more information about the installation in general and to get a list of recommended software, go to the main page, under: http://nipype.readthedocs.io/en/latest/users/install.html\n", - "\n", - "For a more step by step installation guide for additional software dependencies like SPM, FSL, FreeSurfer and ANTs, go to the [Beginner's Guide](http://miykael.github.io/nipype-beginner-s-guide/installation.html).\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## 3. Test Nipype" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Import the nipype module\n", - "import nipype\n", - "\n", - "# Run the test\n", - "nipype.test(doctests=False)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The test will create a lot of output, but if all goes well you will see at the end something like this:\n", - "\n", - " ----------------------------------------------------------------------\n", - " 2091 passed, 68 skipped, 7 xfailed, 1 warnings in 236.94 seconds\n", - "\n", - "The number of tests and time will vary depending on which interfaces you have installed on your system.\n", - "\n", - "Don’t worry if some modules are being skipped or marked as xfailed. As long as no main modules cause any problems, you’re fine. The number of tests and time will vary depending on which interfaces you have installed on your system. But if you receive an OK, errors=0 and failures=0 then everything is ready." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.3" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/resources_python_cheat_sheet.ipynb b/notebooks/resources_python_cheat_sheet.ipynb deleted file mode 100644 index ff3fcf8..0000000 --- a/notebooks/resources_python_cheat_sheet.ipynb +++ /dev/null @@ -1,687 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Python Cheat Sheet\n", - "\n", - "The following content is taken from http://www.ias.u-psud.fr/pperso/aboucaud/python/cheatsheet.html\n", - "\n", - "This cheat sheet should serve as a short refresher to everybody who hasn't used Python for some time." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Pure Python" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Types" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 2 # integer\n", - "b = 5.0 # float\n", - "c = 8.3e5 # exponential\n", - "d = 1.5 + 0.5j # complex\n", - "e = 4 > 5 # boolean\n", - "f = 'word' # string" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Lists" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = ['red', 'blue', 'green'] # manually initialization\n", - "b = list(range(5)) # initialization through a function\n", - "c = [nu**2 for nu in b] # initialize through list comprehension\n", - "d = [nu**2 for nu in b if nu < 3] # list comprehension with condition\n", - "e = c[0] # access element\n", - "f = c[1:2] # access a slice of the list\n", - "g = ['re', 'bl'] + ['gr'] # list concatenation\n", - "h = ['re'] * 5 # repeat a list\n", - "['re', 'bl'].index('re') # returns index of 're'\n", - "'re' in ['re', 'bl'] # true if 're' in list\n", - "sorted([3, 2, 1]) # returns sorted list\n", - "z = ['red'] + ['green', 'blue'] # list concatenation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Dictionaries" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = {'red': 'rouge', 'blue': 'bleu', 'green': 'vert'} # dictionary\n", - "b = a['red'] # translate item\n", - "c = [value for key, value in a.items()] # loop through contents\n", - "d = a.get('yellow', 'no translation found') # return default" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Strings" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 'red' # assignment\n", - "char = a[2] # access individual characters\n", - "'red ' + 'blue' # string concatenation\n", - "'1, 2, three'.split(',') # split string into list\n", - "'.'.join(['1', '2', 'three']) # concatenate list into string" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Operators" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = 2 # assignment\n", - "b = [2,3] # assign a list\n", - "a += 1 # change and assign, try also `*=` and `/=`\n", - "3 + 2 # addition\n", - "3 / 2 # integer division (python2) or float division (python3)\n", - "3 // 2 # integer division\n", - "3 * 2 # multiplication\n", - "3 ** 2 # exponent\n", - "3 % 2 # remainder\n", - "abs(-3) # absolute value\n", - "1 == 1 # equal\n", - "2 > 1 # larger\n", - "2 < 1 # smaller\n", - "1 != 2 # not equal\n", - "1 != 2 and 2 < 3 # logical AND\n", - "1 != 2 or 2 < 3 # logical OR\n", - "not 1 == 2 # logical NOT\n", - "a in b # test if a is in b\n", - "a is b # test if objects point to the same memory (id)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Control Flow" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# if/elif/else\n", - "a, b = 1, 2\n", - "if a + b == 3:\n", - " print ('True')\n", - "elif a + b == 1:\n", - " print ('False')\n", - "else:\n", - " print ('?')\n", - "\n", - "# for\n", - "a = ['red', 'blue', 'green']\n", - "for color in a:\n", - " print (color)\n", - "\n", - "# while\n", - "number = 1\n", - "while number < 10:\n", - " print (number)\n", - " number += 1\n", - "\n", - "# break\n", - "number = 1\n", - "while True:\n", - " print (number)\n", - " number += 1\n", - " if number > 10:\n", - " break\n", - "\n", - "# continue\n", - "for i in range(20):\n", - " if i % 2 == 0:\n", - " continue\n", - " print (i)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Functions, Classes, Generators, Decorators" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# Function\n", - "def myfunc(a1, a2):\n", - " return a1 * a2\n", - "\n", - "a1, a2 = 4, 5\n", - "x = myfunc(a1, a2)\n", - "\n", - "# Class\n", - "class Point(object):\n", - " def __init__(self, x):\n", - " self.x = x\n", - " def __call__(self):\n", - " print (self.x)\n", - "\n", - "x = Point(3)\n", - "\n", - "# Generators\n", - "def firstn(n):\n", - " num = 0\n", - " while num < n:\n", - " yield num\n", - " num += 1\n", - "\n", - "# consume the generator with list comprehension\n", - "x = [i for i in firstn(10)]\n", - "\n", - "# Decorators\n", - "class myDecorator(object):\n", - " def __init__(self, f):\n", - " self.f = f\n", - " def __call__(self):\n", - " print (\"call\")\n", - " self.f()\n", - "\n", - "@myDecorator\n", - "def my_funct():\n", - " print ('func')\n", - "\n", - "my_funct()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## IPython" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Python console" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "? # Information about the object\n", - ". # tab completion\n", - "\n", - "# measure runtime of a function:\n", - "%timeit range(1000)\n", - "100000 loops, best of 3: 7.76 us per loop\n", - "\n", - "# run scripts and debug\n", - "%run\n", - "%run -d # run in debug mode\n", - "%run -t # measures execution time\n", - "%run -p # runs a profiler\n", - "%debug # jumps to the debugger after an exception\n", - "\n", - "%pdb # run debugger automatically on exception\n", - "\n", - "# examine history\n", - "%history\n", - "%history ~1/1-5 # lines 1-5 of last session\n", - "\n", - "# run shell commands\n", - "!make # prefix command with \"!\"\n", - "\n", - "# clean namespace\n", - "%reset" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Debugger commands" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "n # execute next line" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## NumPy" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### array initialization" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.array([2, 3, 4]) # direct initialization\n", - "np.empty(20, dtype=np.float32) # single precision array with 20 entries\n", - "np.zeros(200) # initialize 200 zeros\n", - "np.ones((3,3), dtype=np.int32) # 3 x 3 integer matrix with ones\n", - "np.eye(200) # ones on the diagonal\n", - "np.zeros_like(a) # returns array with zeros and the shape of a\n", - "np.linspace(0., 10., 100) # 100 points from 0 to 10\n", - "np.arange(0, 100, 2) # points from 0 to <100 with step width 2\n", - "np.logspace(-5, 2, 100) # 100 log-spaced points between 1e-5 and 1e2\n", - "a = np.array([[2, 3], [4, 5]]) \n", - "np.copy(a) # copy array to new memory" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### reading/ writing files" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.fromfile(fname/object, dtype=np.float32, count=5) # read binary data from file\n", - "np.loadtxt(fname/object, skiprows=2, delimiter=',') # read ascii data from file" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### array properties and operations" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a.shape # a tuple with the lengths of each axis\n", - "len(a) # length of axis 0\n", - "a.ndim # number of dimensions (axes)\n", - "a.sort(axis=1) # sort array along axis\n", - "a.flatten() # collapse array to one dimension\n", - "a.conj() # return complex conjugate\n", - "a.astype(np.int16) # cast to integer\n", - "np.argmax(a, axis=0) # return index of maximum along a given axis\n", - "np.cumsum(a) # return cumulative sum\n", - "np.any(a) # True if any element is True\n", - "np.all(a) # True if all elements are True\n", - "np.argsort(a, axis=1) # return sorted index array along axis" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### indexing" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a = np.arange(100) # initialization with 0 - 99\n", - "a[: 3] = 0 # set the first three indices to zero\n", - "a[1: 5] = 1 # set indices 1-4 to 1\n", - "start, stop, step = 10, 20, 2\n", - "a[start:stop:step] # general form of indexing/slicing\n", - "a[None, :] # transform to column vector\n", - "a[[1, 1, 3, 8]] # return array with values of the indices\n", - "a = a.reshape(10, 10) # transform to 10 x 10 matrix\n", - "a.T # return transposed view\n", - "np.transpose(a, (1, 0)) # transpose array to new axis order\n", - "a[a < 2] # returns array that fulfills element-wise condition" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### boolean arrays" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a, b = np.arange(100), 6 * np.arange(1, 101)\n", - "a < 2 # returns array with boolean values\n", - "np.logical_and(a < 2, b > 10) # element-wise logical and\n", - "np.logical_or(a < 2, b > 10) # element-wise logical or\n", - "~a # invert boolean array\n", - "np.invert(a) # invert boolean array" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### element-wise operations and math functions" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "y, x = np.arange(10), np.arange(1, 11)\n", - "a * 5 # multiplication with scalar\n", - "a + 5 # addition with scalar\n", - "a + b # addition with array b\n", - "a / b # division with b (np.NaN for division by zero)\n", - "np.exp(a) # exponential (complex and real)\n", - "np.power(a,b) # a to the power b\n", - "np.sin(a) # sine\n", - "np.cos(a) # cosine\n", - "np.arctan2(y, x) # arctan(y/x)\n", - "np.arcsin(x) # arcsin\n", - "np.radians(a) # degrees to radians\n", - "np.degrees(a) # radians to degrees\n", - "np.var(a) # variance of array\n", - "np.std(a, axis=0) # standard deviation" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### inner / outer products" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a, b = np.array([[2, 3], [4, 5]]), np.array([[20, 30], [40, 50]])\n", - "np.dot(a, b) # inner matrix product: a_mi b_in\n", - "np.einsum('ik,kl->il', a, b) # einstein summation convention\n", - "np.sum(a, axis=1) # sum over axis 1\n", - "np.abs(a) # return array with absolute values\n", - "a[None, :] + b[:, None] # outer sum\n", - "a[None, :] * b[:, None] # outer product\n", - "np.outer(a, b) # outer product\n", - "np.sum(a * a.T) # matrix norm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### interpolation, integration" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.trapz(y, x=None, dx=1.0, axis=0) # integrate along axis 0\n", - "np.interp(x=2.5, xp=[1, 2, 3], fp=[3, 2, 0]) # interpolate function xp, yp at points x" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### fft" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.fft.fft(y) # complex fourier transform of y\n", - "freqs = np.fft.fftfreq(len(y)) # fft frequencies for a given length\n", - "np.fft.fftshift(freqs) # shifts zero frequency to the middle\n", - "np.fft.rfft(y) # real fourier transform of y\n", - "np.fft.rfftfreq(len(y)) # real fft frequencies for a given length" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### rounding" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "a=3.56\n", - "np.ceil(a) # rounds to nearest upper int\n", - "np.floor(a) # rounds to nearest lower int\n", - "np.round(a) # rounds to neares int" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### random variables" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "np.random.normal(loc=0, scale=2, size=100) # 100 normal distributed random numbers\n", - "np.random.seed(23032) # resets the seed value\n", - "np.random.rand(200) # 200 random numbers in [0, 1)\n", - "np.random.uniform(1, 30, 200) # 200 random numbers in [1, 30)\n", - "np.random.randint(1, 15, 300) # 300 random integers between [1, 15]" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Matplotlib" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### figures and axes" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig = plt.figure(figsize=(5, 2), facecolor='black') # initialize figure\n", - "ax = fig.add_subplot(3, 2, 2) # add second subplot in a 3 x 2 grid\n", - "fig, axes = plt.subplots(5, 2, figsize=(5, 5)) # return fig and array of axes in a 5 x 2 grid\n", - "ax = fig.add_axes(left=.3, bottom=.1, width=.6, height=.8) # manually add axes at a certain position" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### figures and axes properties" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "fig.suptitle('title') # big figure title\n", - "fig.subplots_adjust(bottom=0.1,\n", - " right=0.8,\n", - " top=0.9,\n", - " wspace=0.2,\n", - " hspace=0.5) # adjust subplot positions\n", - "fig.tight_layout(pad=0.1,\n", - " h_pad=0.5,\n", - " w_pad=0.5,\n", - " rect=None) # adjust subplots to fit perfectly into fig\n", - "ax.set_xlabel() # set xlabel\n", - "ax.set_ylabel() # set ylabel\n", - "ax.set_xlim(1, 2) # sets x limits\n", - "ax.set_ylim(3, 4) # sets y limits\n", - "ax.set_title('blabla') # sets the axis title\n", - "ax.set(xlabel='bla') # set multiple parameters at once\n", - "ax.legend(loc='upper center') # activate legend\n", - "ax.grid(True, which='both') # activate grid\n", - "bbox = ax.get_position() # returns the axes bounding box\n", - "bbox.x0 + bbox.width # bounding box parameters" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### plotting routines" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "ax.plot(x,y, '-o', c='red', lw=2, label='bla') # plots a line\n", - "ax.scatter(x,y, s=20, c=color) # scatter plot\n", - "ax.pcolormesh(xx,yy,zz, shading='gouraud') # fast colormesh function\n", - "ax.colormesh(xx,yy,zz, norm=norm) # slower colormesh function\n", - "ax.contour(xx,yy,zz, cmap='jet') # contour line plot\n", - "ax.contourf(xx,yy,zz, vmin=2, vmax=4) # filled contours plot\n", - "n, bins, patch = ax.hist(x, 50) # histogram\n", - "ax.imshow(matrix, origin='lower', extent=(x1, x2, y1, y2)) # show image\n", - "ax.specgram(y, FS=0.1, noverlap=128, scale='linear') # plot a spectrogram" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/resources_resources.ipynb b/notebooks/resources_resources.ipynb deleted file mode 100644 index 3c07e9a..0000000 --- a/notebooks/resources_resources.ipynb +++ /dev/null @@ -1,68 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Helpful Resources\n", - "\n", - "\n", - "## Learn more about Nipype\n", - "\n", - "- [Nipype homepage](http://nipype.readthedocs.io/en/latest/): This is the best place to learn all you need to know about Nipype. For beginner's I recommend to check out the [Quickstart](http://nipype.readthedocs.io/en/latest/quickstart.html) section.\n", - "- [Beginner's Guide](http://miykael.github.io/nipype-beginner-s-guide/): This beginner's guide is an in-depth step by step tutorial to Nipype.\n", - "\n", - "\n", - "## Neuroimaging\n", - "\n", - "- [Neurostars.org](https://neurostars.org/): If you have any questions about Neuroinformatics, this is the place to go! \n", - "- [Design efficiency in FMRI](http://imaging.mrc-cbu.cam.ac.uk/imaging/DesignEfficiency): A nice and detailed guide on how to design a good fMRI study.\n", - "\n", - "\n", - "## Learn Python\n", - "\n", - "- [A Byte of Python](http://python.swaroopch.com/): A very nice introduction to Python in general.\n", - "- [A Crash Course in Python for Scientists](http://nbviewer.jupyter.org/gist/rpmuller/5920182): a very good introduction to Python and scientific programming (e.g. Numpy, Scipy, Matplotlib)\n", - "- [Codecademy - Python](https://www.codecademy.com/learn/python): An interactive online training and introduction to Python.\n", - "- [Learn Python the Hard Way](http://learnpythonthehardway.org/book/index.html): A very good step by step introduction to Python.\n", - "- [Python Scientific Lecture Notes](http://www.scipy-lectures.org/): A very good and more detailed introduction to Python and scientific programming.\n", - "- If you're looking for a Python based IDE like Eclipse or MATLAB, check out [Pycharm](https://www.jetbrains.com/pycharm/) or [Spyder](https://github.com/spyder-ide/spyder/).\n", - "- [Programming with Python](http://swcarpentry.github.io/python-novice-inflammation/): This short introduction by *software carpentry* teaches you the basics of scientific programming on very practical examples.\n", - "\n", - "\n", - "## Learn Git\n", - "\n", - "- [Got 15 minutes and want to learn Git?](https://try.github.io/levels/1/challenges/1): Github's own git tutorial. It's fun and very short.\n", - "- [Git Real](http://gitreal.codeschool.com/) on [Code School](https://www.codeschool.com/): An interactive tutorial about GIT\n", - "- [Top 10 Git Tutorials for Beginners](http://sixrevisions.com/resources/git-tutorials-beginners/)\n", - "\n", - "\n", - "## Learn Unix Shell\n", - "\n", - "- [the Unix Shell](http://swcarpentry.github.io/shell-novice/): If you're new to Linux, here's a quick starter guide by software carpentry that teaches you the basics." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/reveal.js b/notebooks/reveal.js deleted file mode 160000 index a349ff4..0000000 --- a/notebooks/reveal.js +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a349ff43c58c23f9c837b8ea9b5fc7d4761b8de3 diff --git a/notebooks/scripts/ANTS_registration.py b/notebooks/scripts/ANTS_registration.py deleted file mode 100644 index 6d1b832..0000000 --- a/notebooks/scripts/ANTS_registration.py +++ /dev/null @@ -1,99 +0,0 @@ -# Import modules -from os.path import join as opj -from nipype.interfaces.ants import Registration -from nipype.interfaces.utility import IdentityInterface -from nipype.interfaces.io import SelectFiles, DataSink -from nipype.pipeline.engine import Workflow, Node -from nipype.interfaces.fsl import Info - -# Specify variables -experiment_dir = '/output' -output_dir = 'antsdir' -working_dir = 'workingdir' -subject_list = ['sub-01', 'sub-02', 'sub-03', 'sub-04', 'sub-05', - 'sub-06', 'sub-07', 'sub-08', 'sub-09', 'sub-10'] - -# Location of template file -template = '/data/ds000114/derivatives/fmriprep/mni_icbm152_nlin_asym_09c/1mm_T1.nii.gz' -# or alternatively template = Info.standard_image('MNI152_T1_1mm.nii.gz') - -# Registration - computes registration between subject's anatomy & the MNI template -antsreg = Node(Registration(args='--float', - collapse_output_transforms=True, - fixed_image=template, - initial_moving_transform_com=True, - num_threads=4, - output_inverse_warped_image=True, - output_warped_image=True, - sigma_units=['vox'] * 3, - transforms=['Rigid', 'Affine', 'SyN'], - terminal_output='file', - winsorize_lower_quantile=0.005, - winsorize_upper_quantile=0.995, - convergence_threshold=[1e-06], - convergence_window_size=[10], - metric=['MI', 'MI', 'CC'], - metric_weight=[1.0] * 3, - number_of_iterations=[[1000, 500, 250, 100], - [1000, 500, 250, 100], - [100, 70, 50, 20]], - radius_or_number_of_bins=[32, 32, 4], - sampling_percentage=[0.25, 0.25, 1], - sampling_strategy=['Regular', 'Regular', 'None'], - shrink_factors=[[8, 4, 2, 1]] * 3, - smoothing_sigmas=[[3, 2, 1, 0]] * 3, - transform_parameters=[(0.1,), (0.1,), - (0.1, 3.0, 0.0)], - use_histogram_matching=True, - write_composite_transform=True), - name='antsreg') - -### -# Input & Output Stream - -# Infosource - a function free node to iterate over the list of subject names -infosource = Node(IdentityInterface(fields=['subject_id']), - name="infosource") -infosource.iterables = [('subject_id', subject_list)] - -# SelectFiles - to grab the data (alternative to DataGrabber) -anat_file = opj('{subject_id}', 'ses-test', 'anat', '{subject_id}_ses-test_T1w.nii.gz') -templates = {'anat': anat_file} - -selectfiles = Node(SelectFiles(templates, - base_directory='/data/ds000114'), - name="selectfiles") - -# Datasink - creates output folder for important outputs -datasink = Node(DataSink(base_directory=experiment_dir, - container=output_dir), - name="datasink") - -# Use the following DataSink output substitutions -substitutions = [('_subject_id_', '')] -datasink.inputs.substitutions = substitutions - -### -# Specify Normalization Workflow & Connect Nodes - -# Initiation of the ANTS normalization workflow -regflow = Workflow(name='regflow') -regflow.base_dir = opj(experiment_dir, working_dir) - -# Connect workflow nodes -regflow.connect([(infosource, selectfiles, [('subject_id', 'subject_id')]), - (selectfiles, antsreg, [('anat', 'moving_image')]), - (antsreg, datasink, [('warped_image', - 'antsreg.@warped_image'), - ('inverse_warped_image', - 'antsreg.@inverse_warped_image'), - ('composite_transform', - 'antsreg.@transform'), - ('inverse_composite_transform', - 'antsreg.@inverse_transform')]), - ]) - -### -# Run Workflow -regflow.write_graph(graph2use='flat') -regflow.run('Linear') diff --git a/notebooks/z_advanced_caching.ipynb b/notebooks/z_advanced_caching.ipynb deleted file mode 100644 index 57ce627..0000000 --- a/notebooks/z_advanced_caching.ipynb +++ /dev/null @@ -1,139 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/users/caching_tutorial.html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Nipype caching" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.caching import Memory\n", - "mem = Memory('.')" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Create `cacheable` objects" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.spm import Realign\n", - "from nipype.interfaces.fsl import MCFLIRT\n", - "\n", - "spm_realign = mem.cache(Realign)\n", - "fsl_realign = mem.cache(MCFLIRT)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Execute interfaces" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "spm_results = spm_realign(in_files='ds107.nii', register_to_mean=False)\n", - "fsl_results = fsl_realign(in_file='ds107.nii', ref_vol=0, save_plots=True)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "subplot(211);plot(genfromtxt(fsl_results.outputs.par_file)[:, 3:])\n", - "subplot(212);plot(genfromtxt(spm_results.outputs.realignment_parameters)[:,:3])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "spm_results = spm_realign(in_files='ds107.nii', register_to_mean=False)\n", - "fsl_results = fsl_realign(in_file='ds107.nii', ref_vol=0, save_plots=True)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### More caching" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import abspath as opap\n", - "files = [opap('../ds107/sub001/BOLD/task001_run001/bold.nii.gz'),\n", - " opap('../ds107/sub001/BOLD/task001_run002/bold.nii.gz')]\n", - "converter = mem.cache(MRIConvert)\n", - "newfiles = []\n", - "for idx, fname in enumerate(files):\n", - " newfiles.append(converter(in_file=fname,\n", - " out_type='nii').outputs.out_file)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "os.chdir(tutorial_dir)" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/z_advanced_commandline.ipynb b/notebooks/z_advanced_commandline.ipynb deleted file mode 100644 index 05012ba..0000000 --- a/notebooks/z_advanced_commandline.ipynb +++ /dev/null @@ -1,47 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/users/cli.html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/users/nipypecmd.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/z_advanced_databases.ipynb b/notebooks/z_advanced_databases.ipynb deleted file mode 100644 index 4bdd3d2..0000000 --- a/notebooks/z_advanced_databases.ipynb +++ /dev/null @@ -1,95 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "https://github.com/nipy/nipype/blob/master/examples/fmri_ants_openfmri.py" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Step 9: Connecting to Databases" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from os.path import abspath as opap\n", - "\n", - "from nipype.interfaces.io import XNATSource\n", - "from nipype.pipeline.engine import Node, Workflow\n", - "from nipype.interfaces.fsl import BET\n", - "\n", - "subject_id = 'xnat_S00001'\n", - "\n", - "dg = Node(XNATSource(infields=['subject_id'],\n", - " outfields=['struct'],\n", - " config='/Users/satra/xnat_configs/nitrc_ir_config'),\n", - " name='xnatsource')\n", - "dg.inputs.query_template = ('/projects/fcon_1000/subjects/%s/experiments/xnat_E00001'\n", - " '/scans/%s/resources/NIfTI/files')\n", - "dg.inputs.query_template_args['struct'] = [['subject_id', 'anat_mprage_anonymized']]\n", - "dg.inputs.subject_id = subject_id\n", - "\n", - "bet = Node(BET(), name='skull_stripper')\n", - "\n", - "wf = Workflow(name='testxnat')\n", - "wf.base_dir = opap('xnattest')\n", - "wf.connect(dg, 'struct', bet, 'in_file')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from nipype.interfaces.io import XNATSink\n", - "\n", - "ds = Node(XNATSink(config='/Users/satra/xnat_configs/central_config'),\n", - " name='xnatsink')\n", - "ds.inputs.project_id = 'NPTEST'\n", - "ds.inputs.subject_id = 'NPTEST_xnat_S00001'\n", - "ds.inputs.experiment_id = 'test_xnat'\n", - "ds.inputs.reconstruction_id = 'bet'\n", - "ds.inputs.share = True\n", - "wf.connect(bet, 'out_file', ds, 'brain')" - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/z_advanced_debug.ipynb b/notebooks/z_advanced_debug.ipynb deleted file mode 100644 index 6787b4d..0000000 --- a/notebooks/z_advanced_debug.ipynb +++ /dev/null @@ -1,39 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/users/debug.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/z_advanced_export_workflow.ipynb b/notebooks/z_advanced_export_workflow.ipynb deleted file mode 100644 index 5513a35..0000000 --- a/notebooks/z_advanced_export_workflow.ipynb +++ /dev/null @@ -1,39 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/users/saving_workflows.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/z_advanced_resources_and_profiling.ipynb b/notebooks/z_advanced_resources_and_profiling.ipynb deleted file mode 100644 index b2d8a98..0000000 --- a/notebooks/z_advanced_resources_and_profiling.ipynb +++ /dev/null @@ -1,40 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Look into: http://nipype.readthedocs.io/en/latest/users/resource_sched_profiler.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/z_development_github.ipynb b/notebooks/z_development_github.ipynb deleted file mode 100644 index 1a6d915..0000000 --- a/notebooks/z_development_github.ipynb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Github\n", - "\n", - "step by step guide on how to submit PR's etc." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/notebooks/z_development_interface.ipynb b/notebooks/z_development_interface.ipynb deleted file mode 100644 index 52d2eff..0000000 --- a/notebooks/z_development_interface.ipynb +++ /dev/null @@ -1,53 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/devel/cmd_interface_devel.html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/devel/matlab_interface_devel.html" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "http://nipype.readthedocs.io/en/latest/devel/python_interface_devel.html" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/notebooks/z_development_report_issue.ipynb b/notebooks/z_development_report_issue.ipynb deleted file mode 100644 index b8b1e45..0000000 --- a/notebooks/z_development_report_issue.ipynb +++ /dev/null @@ -1,35 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Report an issue\n", - "\n", - "step by step guide how to open an issue on github..." - ] - } - ], - "metadata": { - "anaconda-cloud": {}, - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.6.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -}