7. First Level GLM (from Nilearn)#

In this tutorial, we will go through a simple workflow of the first level general linear modeling with a BIDS dataset from openneuro. This analysis is only performed on one subject.

This tutorial is based on the Nilearn GLM tutorial.

import nest_asyncio
nest_asyncio.apply()

7.1. Preparation#

Import packages that will be used globally and set up output directory

import warnings
import sys 
if not sys.warnoptions:
    warnings.simplefilter("ignore")
    
import os
import typing as ty
from pathlib import Path

import pydra
from pydra import Workflow
from pydra.engine.specs import File
import pandas as pd
from scipy.stats import norm

import nibabel as nib
from nilearn.datasets import (
        fetch_openneuro_dataset_index,
        fetch_openneuro_dataset,
        select_from_index,
    )
from nilearn.interfaces.fsl import get_design_from_fslmat
from nilearn.glm.first_level import first_level_from_bids
from nilearn.reporting import get_clusters_table, make_glm_report
from nilearn.plotting import (
    plot_glass_brain,
    plot_img_comparison,
    plot_contrast_matrix,
)
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
Cell In[2], line 17
     14 from scipy.stats import norm
     16 import nibabel as nib
---> 17 from nilearn.datasets import (
     18         fetch_openneuro_dataset_index,
     19         fetch_openneuro_dataset,
     20         select_from_index,
     21     )
     22 from nilearn.interfaces.fsl import get_design_from_fslmat
     23 from nilearn.glm.first_level import first_level_from_bids

ImportError: cannot import name 'fetch_openneuro_dataset_index' from 'nilearn.datasets' (/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/nilearn/datasets/__init__.py)
# get current directory
pydra_tutorial_dir = os.path.dirname(os.getcwd())

# set up output directory
workflow_dir = Path(pydra_tutorial_dir) / 'outputs'
workflow_out_dir = workflow_dir / '6_glm'

# create the output directory if not exit
os.makedirs(workflow_out_dir, exist_ok=True)
workflow_out_dir
PosixPath('/tmp/outputs/6_glm')

7.2. Create tasks#

In this section, we converte major steps into tasks. Each pydra task can have multiple python functions. We recommand to put those logically more related functions into the same task.

It is very important to keep in mind what adjacent tasks of your current task will be.

  1. Your previous task will decide your arguments in the current task

  2. Your next task will be impacted by the returns in the current task

7.2.1. fetch openneuro BIDS dataset#

In this task, we do the following:

  1. get openneuro dataset index

  2. specify exclusion patterns and number of subjects

  3. download the data we need

Notes: Here we still use n_subjects as an argument. Given that we will only analyze one subject, you can also remove this argument and specify n_subjects =1 in select_from_index. If you do, do not forget to modify the argument in the workflow later.

@pydra.mark.task
@pydra.mark.annotate(
    {
        'exclusion_patterns': list,
        'n_subjects': int,
        'return': {'data_dir': str},
    }
)
def get_openneuro_dataset(exclusion_patterns, n_subjects):
    _, urls = fetch_openneuro_dataset_index()
    urls = select_from_index(
        urls, exclusion_filters=exclusion_patterns, n_subjects=n_subjects
    )
    data_dir, _ = fetch_openneuro_dataset(urls=urls)
    return data_dir

7.2.2. obtain FirstLevelModel objects automatically and fit arguments#

To get the first level model(s) we have to specify

  1. the dataset directory

  2. the task_label

  3. the space_label

  4. the folder with the desired derivatives (fMRIPrep)

In our case, we only have one subject so we will only have one first level model. Then, for this model, we will obtain

  1. the list of run images

  2. events

  3. confound regressors

Those are inferred from the confounds.tsv files available in the BIDS dataset.

@pydra.mark.task
@pydra.mark.annotate(
    {
        'data_dir': str,
        'task_label': str,
        'space_label': str,
        'derivatives_folder': str,
        'smoothing_fwhm': float,
        'return': {'model': ty.Any, 'imgs': list, 'subject': str},
    }
)
def get_info_from_bids(
    data_dir, task_label, space_label, smoothing_fwhm, derivatives_folder
):
    (
        models,
        models_run_imgs,
        models_events,
        models_confounds,
    ) = first_level_from_bids(
        dataset_path=data_dir,
        task_label=task_label,
        space_label=space_label,
        smoothing_fwhm=smoothing_fwhm,
        derivatives_folder=derivatives_folder,
    )
    model, imgs, events, confounds = (
        models[0],
        models_run_imgs[0],
        models_events[0],
        models_confounds[0],
    )
    subject = 'sub-' + model.subject_label
    return model, imgs, subject

7.2.3. Get design matrix#

This task does the following:

  1. read the design matrix in .mat

  2. rename the column

  3. save the new design matrix as .csv

Think: What if we don’t save the new design matrix, but return it directly? In other words, we return a pandas.DataFrame instead of a path. What will happen? Worth a try :)

@pydra.mark.task
@pydra.mark.annotate(
    {'data_dir': str, 'subject': str, 'return': {'dm_path': str}}
)
def get_designmatrix(data_dir, subject):
    fsl_design_matrix_path = os.path.join(
        data_dir,
        'derivatives',
        'task',
        subject,
        'stopsignal.feat',
        'design.mat',
    )
    design_matrix = get_design_from_fslmat(
        fsl_design_matrix_path, column_names=None
    )

    design_columns = [
        'cond_%02d' % i for i in range(len(design_matrix.columns))
    ]
    design_columns[0] = 'Go'
    design_columns[4] = 'StopSuccess'
    design_matrix.columns = design_columns
    dm_path = os.path.join(workflow_out_dir, 'designmatrix.csv')
    design_matrix.to_csv(dm_path, index=None)
    return dm_path

7.2.4. Fit the first level model#

What we are doing here is:

  1. use the design matrix to fit the first level model

  2. compute the contrast

  3. save the z_map and masker for futher use

  4. generate a glm report (HTML file)

@pydra.mark.task
@pydra.mark.annotate(
    {
        'model': ty.Any,
        'imgs': ty.Any,
        'dm_path': ty.Any,
        'contrast': str,
        'return': {'model': ty.Any, 'z_map_path': str, 'masker': ty.Any, 'glm_report_file': str},
    }
)
def model_fit(model, imgs, dm_path, contrast):
    design_matrix = pd.read_csv(dm_path)
    model.fit(imgs, design_matrices=[design_matrix])
    z_map = model.compute_contrast(contrast)
    z_map_path = os.path.join(workflow_out_dir, 'firstlevel_z_map.nii.gz')
    z_map.to_filename(z_map_path)
    masker_path = os.path.join(workflow_out_dir, 'firstlevel_masker.nii.gz')
    masker = model.masker_
    glm_report_file = os.path.join(workflow_out_dir, 'glm_report.html')
    report = make_glm_report(model, contrast)
    report.save_as_html(glm_report_file)
    return model, z_map_path, masker, glm_report_file

7.2.5. Get cluster table#

For publication purposes, we obtain a cluster table.

@pydra.mark.task
@pydra.mark.annotate({'z_map_path': str, 'return': {'output_file': str}})
def cluster_table(z_map_path):
    stat_img = nib.load(z_map_path)
    output_file = os.path.join(workflow_out_dir, 'cluster_table.csv')
    df = get_clusters_table(
        stat_img, stat_threshold=norm.isf(0.001), cluster_threshold=10
    )
    df.to_csv(output_file, index=None)
    return output_file

7.2.6. Make plots#

Here we want to make some plots to display our results and compare the result from FSL.

  1. plot nilearn z-map

  2. plot fsl z-map

  3. plot nilearn and fsl comparison

  4. plot design matrix contrast

You can also seperate this task into multiple sub-tasks. But it makes more sense to put them into one task as they use the same files and function nilearn.plotting repeatedly.

@pydra.mark.task
@pydra.mark.annotate(
    {
        'data_dir': str,
        'dm_path': str,
        'z_map_path': str,
        'contrast': str,
        'subject': str,
        'masker': ty.Any,
        'return': {
            'output_file1': str,
            'output_file2': str,
            'output_file3': str,
            'output_file4': str,
        },
    }
)
def plots(data_dir,dm_path,z_map_path,contrast,subject,masker):
    # plot and save nilearn z-map
    z_map = nib.load(z_map_path)
    output_file1 = os.path.join(workflow_out_dir, 'nilearn_z_map.jpg')
    plot_glass_brain(
        z_map,
        output_file=output_file1,
        colorbar=True,
        threshold=norm.isf(0.001),
        title='Nilearn Z map of "StopSuccess - Go" (unc p<0.001)',
        plot_abs=False,
        display_mode='ortho',
    )

    # plot and save fsl z-map
    fsl_z_map = nib.load(
        os.path.join(
            data_dir,
            'derivatives',
            'task',
            subject,
            'stopsignal.feat',
            'stats',
            'zstat12.nii.gz',
        )
    )
    output_file2 = os.path.join(workflow_out_dir, 'fsl_z_map.jpg')
    plot_glass_brain(
        fsl_z_map,
        output_file=output_file2,
        colorbar=True,
        threshold=norm.isf(0.001),
        title='FSL Z map of "StopSuccess - Go" (unc p<0.001)',
        plot_abs=False,
        display_mode='ortho',
    )

    # plot and save nilearn and fsl comparison
    plot_img_comparison(
        [z_map],
        [fsl_z_map],
        masker,
        output_dir=workflow_out_dir,
        ref_label='Nilearn',
        src_label='FSL',
    )
    old = os.path.join(workflow_out_dir, '0000.png')
    new = os.path.join(workflow_out_dir, 'nilearn_fsl_comp.jpg')
    os.rename(old, new)
    output_file3 = new
    print(output_file3)

    # plot and save design matrix contrast
    design_matrix = pd.read_csv(dm_path)
    output_file4 = os.path.join(workflow_out_dir, 'firstlevel_contrast.jpg')
    plot_contrast_matrix(contrast, design_matrix, output_file=output_file4)
    return output_file1, output_file2, output_file3, output_file4

7.3. Make a workflow from tasks#

Now we have created all tasks we need for this first level analysis, and there are two choices for our next step.

  1. create one workflow to connect all tasks together

  2. create sub-workflows with some closely related tasks, and connect these workflows along with other tasks into a larger workflow.

We recommand the second approach as it is alway a good practice to group tasks, especially when there are a large number of tasks in the analysis.

Our analysis can be divided into three parts: (1) get/read the data, (2) analyze the data, and (3) plot the result, where (1) and (3) only have one task each. So we can put all tasks in (2) into one workflow and name it as firstlevel or whatever you prefer.

# initiate a workflow
wf_firstlevel = Workflow(
    name='wf_firstlevel',
    input_spec=[
        'data_dir',
        'task_label',
        'space_label',
        'derivatives_folder',
        'smoothing_fwhm',
        'contrast',
        'output_dir',
    ],
)

# specify input
wf_firstlevel.inputs.task_label = 'stopsignal'
wf_firstlevel.inputs.space_label = 'MNI152NLin2009cAsym'
wf_firstlevel.inputs.derivatives_folder = 'derivatives/fmriprep'
wf_firstlevel.inputs.smoothing_fwhm = 5.0

# add task - get_info_from_bids
wf_firstlevel.add(
    get_info_from_bids(
        name='get_info_from_bids',
        data_dir=wf_firstlevel.lzin.data_dir,
        task_label=wf_firstlevel.lzin.task_label,
        space_label=wf_firstlevel.lzin.space_label,
        derivatives_folder=wf_firstlevel.lzin.derivatives_folder,
        smoothing_fwhm=wf_firstlevel.lzin.smoothing_fwhm,
    )
)
# add task - get_designmatrix
wf_firstlevel.add(
    get_designmatrix(
        name='get_designmatrix',
        data_dir=wf_firstlevel.lzin.data_dir,
        subject=wf_firstlevel.get_info_from_bids.lzout.subject,
    )
)
wf_firstlevel.add(
    model_fit(
        name='l1estimation',
        model=wf_firstlevel.get_info_from_bids.lzout.model,
        imgs=wf_firstlevel.get_info_from_bids.lzout.imgs,
        dm_path=wf_firstlevel.get_designmatrix.lzout.dm_path,
        contrast=wf_firstlevel.lzin.contrast,
    )
)
# add task - cluster_table
wf_firstlevel.add(
    cluster_table(
        name='cluster_table',
        z_map_path=wf_firstlevel.l1estimation.lzout.z_map_path,
    )
)
# specify output
wf_firstlevel.set_output(
    [
        ('z_map', wf_firstlevel.l1estimation.lzout.z_map_path),
        ('masker', wf_firstlevel.l1estimation.lzout.masker),
        ('subject', wf_firstlevel.get_info_from_bids.lzout.subject),
        ('dm_path', wf_firstlevel.get_designmatrix.lzout.dm_path),
        ('cluster_table', wf_firstlevel.cluster_table.lzout.output_file),
        ('glm_report', wf_firstlevel.l1estimation.lzout.glm_report_file),
    ]
)

7.4. The overaching workflow#

Connect other tasks and the above workflow into one

Now we need to create the overaching glm workflow that connects the above workflow and other tasks (e.g., get/read the data and plot the result)

wf = Workflow(
    name='firstlevel_glm',
    input_spec=['exclusion_patterns', 'n_subjects', 'contrast', 'output_dir'],
)

wf.inputs.exclusion_patterns = [
    '*group*',
    '*phenotype*',
    '*mriqc*',
    '*parameter_plots*',
    '*physio_plots*',
    '*space-fsaverage*',
    '*space-T1w*',
    '*dwi*',
    '*beh*',
    '*task-bart*',
    '*task-rest*',
    '*task-scap*',
    '*task-task*',
]
wf.inputs.n_subjects = 1
wf.inputs.output_dir = workflow_out_dir
wf.inputs.contrast = 'StopSuccess - Go'

wf.add(
    get_openneuro_dataset(
        name='get_openneuro_dataset',
        exclusion_patterns=wf.lzin.exclusion_patterns,
        n_subjects=wf.lzin.n_subjects,
    )
)

wf_firstlevel.inputs.data_dir = wf.get_openneuro_dataset.lzout.data_dir
wf_firstlevel.inputs.contrast = wf.inputs.contrast
wf_firstlevel.inputs.output_dir = wf.inputs.output_dir
wf.add(wf_firstlevel)

wf.add(
    plots(
        name='plots',
        data_dir=wf.get_openneuro_dataset.lzout.data_dir,
        dm_path=wf_firstlevel.lzout.dm_path,
        z_map_path=wf_firstlevel.lzout.z_map,
        contrast=wf.lzin.contrast,
        subject=wf_firstlevel.lzout.subject,
        masker=wf_firstlevel.lzout.masker,
    )
)

wf.set_output(
    [
        ('output1', wf.plots.lzout.output_file1),
        ('output2', wf.plots.lzout.output_file2),
        ('output3', wf.plots.lzout.output_file3),
        ('output4', wf.plots.lzout.output_file4),
    ]
)

7.5. Run Workflow Run#

from pydra import Submitter

with Submitter(plugin='cf', n_procs=4) as submitter:
    submitter(wf)

results = wf.result()

print(results)
Hide code cell output
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<ConcurrentFuturesWorker.exec_as_coro() done, defined at /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/workers.py:173> exception=NameError("name 'fetch_openneuro_dataset_index' is not defined")>
concurrent.futures.process._RemoteTraceback: 
"""
Traceback (most recent call last):
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/concurrent/futures/process.py", line 254, in _process_worker
    r = call_item.fn(*call_item.args, **call_item.kwargs)
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/core.py", line 529, in _run
    self._run_task()
    ~~~~~~~~~~~~~~^^
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/task.py", line 202, in _run_task
    output = cp.loads(self.inputs._func)(**inputs)
  File "/tmp/ipykernel_2783/965776143.py", line 10, in get_openneuro_dataset
    _, urls = fetch_openneuro_dataset_index()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
NameError: name 'fetch_openneuro_dataset_index' is not defined
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/tasks.py", line 306, in __step_run_and_handle_result
    result = coro.throw(exc)
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/workers.py", line 176, in exec_as_coro
    res = await self.loop.run_in_executor(self.pool, runnable._run, rerun)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/futures.py", line 286, in __await__
    yield self  # This tells Task to wait for completion.
    ^^^^^^^^^^
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/tasks.py", line 375, in __wakeup
    future.result()
    ~~~~~~~~~~~~~^^
  File "/usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/futures.py", line 199, in result
    raise self._exception.with_traceback(self._exception_tb)
NameError: name 'fetch_openneuro_dataset_index' is not defined
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/core.py:1365, in Workflow._collect_outputs(self)
   1364 try:
-> 1365     val_out = val.get_value(self)
   1366     output_wf[name] = val_out

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/specs.py:1030, in LazyOutField.get_value(self, wf, state_index)
   1028     return val
-> 1030 value = get_nested_results(result, depth=split_depth)
   1031 value = self._apply_cast(value)

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/specs.py:1020, in LazyOutField.get_value.<locals>.get_nested_results(res, depth)
   1019 if res.errored:
-> 1020     raise ValueError(
   1021         f"Cannot retrieve value for {self.field} from {self.name} as "
   1022         "the node errored"
   1023     )
   1024 val = res.get_output_field(self.field)

ValueError: Cannot retrieve value for output_file1 from plots as the node errored

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[13], line 4
      1 from pydra import Submitter
      3 with Submitter(plugin='cf', n_procs=4) as submitter:
----> 4     submitter(wf)
      6 results = wf.result()
      8 print(results)

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/submitter.py:42, in Submitter.__call__(self, runnable, cache_locations, rerun)
     40 if cache_locations is not None:
     41     runnable.cache_locations = cache_locations
---> 42 self.loop.run_until_complete(self.submit_from_call(runnable, rerun))
     43 return runnable.result()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/nest_asyncio.py:98, in _patch_loop.<locals>.run_until_complete(self, future)
     95 if not f.done():
     96     raise RuntimeError(
     97         'Event loop stopped before Future completed.')
---> 98 return f.result()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/futures.py:199, in Future.result(self)
    197 self.__log_traceback = False
    198 if self._exception is not None:
--> 199     raise self._exception.with_traceback(self._exception_tb)
    200 return self._result

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/asyncio/tasks.py:304, in Task.__step_run_and_handle_result(***failed resolving arguments***)
    300 try:
    301     if exc is None:
    302         # We use the `send` method directly, because coroutines
    303         # don't have `__iter__` and `__next__` methods.
--> 304         result = coro.send(None)
    305     else:
    306         result = coro.throw(exc)

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/submitter.py:68, in Submitter.submit_from_call(self, runnable, rerun)
     66 # 1
     67 if runnable.state is None:
---> 68     await runnable._run(self, rerun=rerun)
     69 # 3
     70 else:
     71     await self.expand_runnable(runnable, wait=True, rerun=rerun)

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/core.py:1237, in Workflow._run(self, submitter, rerun, **kwargs)
   1235     self.audit.monitor()
   1236     await self._run_task(submitter, rerun=rerun)
-> 1237     result.output = self._collect_outputs()
   1238 except Exception:
   1239     etype, eval, etr = sys.exc_info()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/pydra/engine/core.py:1371, in Workflow._collect_outputs(self)
   1369 # checking if the tasks has predecessors that raises error
   1370 if isinstance(getattr(self, val.name)._errored, list):
-> 1371     raise ValueError(
   1372         f"Tasks {getattr(self, val.name)._errored} raised an error"
   1373     )
   1374 else:
   1375     if isinstance(getattr(self, val.name).output_dir, list):

ValueError: Tasks ['get_openneuro_dataset'] raised an error

7.6. Visualization#

If you arrive here without any errors, yay, you just made your first pydra workflow for a first-level GLM!

7.7. Examine folder structure#

Let’s take a look at what you have got.

!ls ../outputs/6_glm

7.7.1. Plot figures#

7.7.1.1. First level contrast#

Hide code cell source
from IPython.display import Image

Image(filename='../outputs/6_glm/firstlevel_contrast.jpg')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[15], line 3
      1 from IPython.display import Image
----> 3 Image(filename='../outputs/6_glm/firstlevel_contrast.jpg')

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1053, in Image.__init__(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)
   1051 self.unconfined = unconfined
   1052 self.alt = alt
-> 1053 super(Image, self).__init__(data=data, url=url, filename=filename,
   1054         metadata=metadata)
   1056 if self.width is None and self.metadata.get('width', {}):
   1057     self.width = metadata['width']

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:371, in DisplayObject.__init__(self, data, url, filename, metadata)
    368 elif self.metadata is None:
    369     self.metadata = {}
--> 371 self.reload()
    372 self._check_data()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1088, in Image.reload(self)
   1086 """Reload the raw data from file or URL."""
   1087 if self.embed:
-> 1088     super(Image,self).reload()
   1089     if self.retina:
   1090         self._retina_shape()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:397, in DisplayObject.reload(self)
    395 if self.filename is not None:
    396     encoding = None if "b" in self._read_flags else "utf-8"
--> 397     with open(self.filename, self._read_flags, encoding=encoding) as f:
    398         self.data = f.read()
    399 elif self.url is not None:
    400     # Deferred import

FileNotFoundError: [Errno 2] No such file or directory: '../outputs/6_glm/firstlevel_contrast.jpg'

7.7.1.2. Nilearn Z map#

Hide code cell source
Image(filename='../outputs/6_glm/nilearn_z_map.jpg')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[16], line 1
----> 1 Image(filename='../outputs/6_glm/nilearn_z_map.jpg')

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1053, in Image.__init__(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)
   1051 self.unconfined = unconfined
   1052 self.alt = alt
-> 1053 super(Image, self).__init__(data=data, url=url, filename=filename,
   1054         metadata=metadata)
   1056 if self.width is None and self.metadata.get('width', {}):
   1057     self.width = metadata['width']

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:371, in DisplayObject.__init__(self, data, url, filename, metadata)
    368 elif self.metadata is None:
    369     self.metadata = {}
--> 371 self.reload()
    372 self._check_data()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1088, in Image.reload(self)
   1086 """Reload the raw data from file or URL."""
   1087 if self.embed:
-> 1088     super(Image,self).reload()
   1089     if self.retina:
   1090         self._retina_shape()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:397, in DisplayObject.reload(self)
    395 if self.filename is not None:
    396     encoding = None if "b" in self._read_flags else "utf-8"
--> 397     with open(self.filename, self._read_flags, encoding=encoding) as f:
    398         self.data = f.read()
    399 elif self.url is not None:
    400     # Deferred import

FileNotFoundError: [Errno 2] No such file or directory: '../outputs/6_glm/nilearn_z_map.jpg'

7.7.1.3. FSL Z map#

Hide code cell source
Image(filename='../outputs/6_glm/fsl_z_map.jpg')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[17], line 1
----> 1 Image(filename='../outputs/6_glm/fsl_z_map.jpg')

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1053, in Image.__init__(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)
   1051 self.unconfined = unconfined
   1052 self.alt = alt
-> 1053 super(Image, self).__init__(data=data, url=url, filename=filename,
   1054         metadata=metadata)
   1056 if self.width is None and self.metadata.get('width', {}):
   1057     self.width = metadata['width']

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:371, in DisplayObject.__init__(self, data, url, filename, metadata)
    368 elif self.metadata is None:
    369     self.metadata = {}
--> 371 self.reload()
    372 self._check_data()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1088, in Image.reload(self)
   1086 """Reload the raw data from file or URL."""
   1087 if self.embed:
-> 1088     super(Image,self).reload()
   1089     if self.retina:
   1090         self._retina_shape()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:397, in DisplayObject.reload(self)
    395 if self.filename is not None:
    396     encoding = None if "b" in self._read_flags else "utf-8"
--> 397     with open(self.filename, self._read_flags, encoding=encoding) as f:
    398         self.data = f.read()
    399 elif self.url is not None:
    400     # Deferred import

FileNotFoundError: [Errno 2] No such file or directory: '../outputs/6_glm/fsl_z_map.jpg'

7.7.1.4. Nilearn FSL comparison#

Hide code cell source
Image(filename='../outputs/6_glm/nilearn_fsl_comp.jpg')
---------------------------------------------------------------------------
FileNotFoundError                         Traceback (most recent call last)
Cell In[18], line 1
----> 1 Image(filename='../outputs/6_glm/nilearn_fsl_comp.jpg')

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1053, in Image.__init__(self, data, url, filename, format, embed, width, height, retina, unconfined, metadata, alt)
   1051 self.unconfined = unconfined
   1052 self.alt = alt
-> 1053 super(Image, self).__init__(data=data, url=url, filename=filename,
   1054         metadata=metadata)
   1056 if self.width is None and self.metadata.get('width', {}):
   1057     self.width = metadata['width']

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:371, in DisplayObject.__init__(self, data, url, filename, metadata)
    368 elif self.metadata is None:
    369     self.metadata = {}
--> 371 self.reload()
    372 self._check_data()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:1088, in Image.reload(self)
   1086 """Reload the raw data from file or URL."""
   1087 if self.embed:
-> 1088     super(Image,self).reload()
   1089     if self.retina:
   1090         self._retina_shape()

File /usr/share/miniconda/envs/pydra-tutorial/lib/python3.13/site-packages/IPython/core/display.py:397, in DisplayObject.reload(self)
    395 if self.filename is not None:
    396     encoding = None if "b" in self._read_flags else "utf-8"
--> 397     with open(self.filename, self._read_flags, encoding=encoding) as f:
    398         self.data = f.read()
    399 elif self.url is not None:
    400     # Deferred import

FileNotFoundError: [Errno 2] No such file or directory: '../outputs/6_glm/nilearn_fsl_comp.jpg'

7.8. Exercise#

What if we need to run the first-level GLM on multiple subject? We will need the splitter.

So, where should we add .split?