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.
In [1]:
from bids.grabbids import BIDSLayout
layout = BIDSLayout("/data/ds102/")
In [2]:
!tree /data/ds102/
Let's figure out what are the subject labels in this dataset
In [3]:
layout.get_subjects()
Out[3]:
What modalities are included in this dataset?
In [4]:
layout.get_modalities()
Out[4]:
What different data types are included in this dataset?
In [5]:
layout.get_types()
Out[5]:
In [6]:
layout.get_types(modality='func')
Out[6]:
What are the different tasks included in this dataset?
In [7]:
layout.get_tasks()
Out[7]:
We can also ask for all of the data for a particular subject.
In [8]:
layout.get(subject='01')
Out[8]:
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).
In [9]:
layout.get(subject='01', type='bold', extensions=['nii', 'nii.gz'])
Out[9]:
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.
In [10]:
[f.filename for f in layout.get(subject='01', type='T1w', extensions=['nii', 'nii.gz'])]
Out[10]:
pybids
in your nipype
workflowThis 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 and T1w files.
In [11]:
def get_niftis(subject_id, data_dir):
# Remember that all the necesary imports need to be INSIDE the function for the Function Interface to work!
from bids.grabbids import BIDSLayout
layout = BIDSLayout(data_dir)
bolds = [f.filename for f in layout.get(subject=subject_id, type='bold', extensions=['nii', 'nii.gz'])]
return bolds
In [12]:
get_niftis('01', '/data/ds102')
Out[12]:
Ok we got our function. Now we need to wrap it inside a Node object.
In [13]:
from nipype.pipeline import Node, MapNode, Workflow
from nipype.interfaces.utility import IdentityInterface, Function
In [14]:
BIDSDataGrabber = Node(Function(function=get_niftis, input_names=["subject_id",
"data_dir"],
output_names=["bolds",
"T1ws"]), name="BIDSDataGrabber")
BIDSDataGrabber.inputs.data_dir = "/data/ds102"
In [15]:
BIDSDataGrabber.inputs.subject_id='01'
res = BIDSDataGrabber.run()
res.outputs
Out[15]:
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
In [16]:
def printMe(paths):
print("\n\nanalyzing " + str(paths) + "\n\n")
analyzeBOLD = Node(Function(function=printMe, input_names=["paths"],
output_names=[]), name="analyzeBOLD")
In [17]:
wf = Workflow(name="bids_demo")
wf.connect(BIDSDataGrabber, "bolds", analyzeBOLD, "paths")
wf.run()
Out[17]:
In [18]:
BIDSDataGrabber.iterables = ('subject_id', layout.get_subjects())
wf.run()
Out[18]:
In [19]:
layout.get_metadata('/data/ds102/sub-01/func/sub-01_task-flanker_run-1_bold.nii.gz')
Out[19]:
Can we incorporate this into our pipeline? Yes we can!
In [20]:
def printMetadata(path, data_dir):
from bids.grabbids import BIDSLayout
layout = BIDSLayout(data_dir)
print("\n\nanalyzing " + path + "\nTR: "+ str(layout.get_metadata(path)["RepetitionTime"]) + "\n\n")
analyzeBOLD2 = MapNode(Function(function=printMetadata, input_names=["path", "data_dir"],
output_names=[]), name="analyzeBOLD2", iterfield="path")
analyzeBOLD2.inputs.data_dir = "/data/ds102/"
In [21]:
wf = Workflow(name="bids_demo")
wf.connect(BIDSDataGrabber, "bolds", analyzeBOLD2, "path")
wf.run()
Out[21]: