Skip to content

Commit a1b903c

Browse files
committed
Added BIDSDataGrabber, test data and tests
1 parent 32e724c commit a1b903c

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

nipype/interfaces/bids.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# -*- coding: utf-8 -*-
2+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
3+
# vi: set ft=python sts=4 ts=4 sw=4 et:
4+
""" Set of interfaces that allow interaction with BIDS data. Currently
5+
available interfaces are:
6+
7+
BIDSDataGrabber: Query data from BIDS dataset using pybids grabbids.
8+
9+
Change directory to provide relative paths for doctests
10+
>>> import os
11+
>>> filepath = os.path.dirname( os.path.realpath( __file__ ) )
12+
>>> datadir = os.path.realpath(os.path.join(filepath, '../testing/data'))
13+
>>> os.chdir(datadir)
14+
15+
"""
16+
17+
from .base import (traits,
18+
DynamicTraitedSpec,
19+
BaseInterface,
20+
isdefined,
21+
Str,
22+
Undefined)
23+
24+
from bids.grabbids import BIDSLayout
25+
26+
27+
class BIDSDataGrabberInputSpec(DynamicTraitedSpec):
28+
base_dir = traits.Directory(exists=True,
29+
desc='Path to BIDS Directory.',
30+
mandatory=True)
31+
output_query = traits.Dict(key_trait=Str,
32+
value_trait=traits.Dict,
33+
desc='Queries for outfield outputs')
34+
return_type = traits.Enum('filename', 'namedtuple', usedefault=True)
35+
36+
37+
class BIDSDataGrabber(BaseInterface):
38+
39+
""" BIDS datagrabber module that wraps around pybids to allow arbitrary
40+
querying of BIDS datasets.
41+
42+
Examples
43+
--------
44+
45+
>>> from nipype.interfaces.bids import BIDSDataGrabber
46+
>>> from os.path import basename
47+
>>> import pprint
48+
49+
Select all files from a BIDS project
50+
51+
>>> bg = BIDSDataGrabber()
52+
>>> bg.inputs.base_dir = 'ds005/'
53+
>>> results = bg.run()
54+
>>> pprint.pprint(len(results.outputs.outfield)) # doctest: +ALLOW_UNICODE
55+
116
56+
57+
Using dynamically created, user-defined input fields,
58+
filter files based on BIDS entities.
59+
60+
>>> bg = BIDSDataGrabber(infields = ['subject', 'run'])
61+
>>> bg.inputs.base_dir = 'ds005/'
62+
>>> bg.inputs.subject = '01'
63+
>>> bg.inputs.run = '01'
64+
>>> results = bg.run()
65+
>>> basename(results.outputs.outfield[0]) # doctest: +ALLOW_UNICODE
66+
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz'
67+
68+
Using user-defined output fields, return different types of outputs,
69+
filtered on common entities
70+
filter files based on BIDS entities.
71+
72+
>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['func', 'anat'])
73+
>>> bg.inputs.base_dir = 'ds005/'
74+
>>> bg.inputs.subject = '01'
75+
>>> bg.inputs.output_query['func'] = dict(modality='func')
76+
>>> bg.inputs.output_query['anat'] = dict(modality='anat')
77+
>>> results = bg.run()
78+
>>> basename(results.outputs.func[0]) # doctest: +ALLOW_UNICODE
79+
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz'
80+
81+
>>> basename(results.outputs.anat[0]) # doctest: +ALLOW_UNICODE
82+
'sub-01_T1w.nii.gz'
83+
"""
84+
input_spec = BIDSDataGrabberInputSpec
85+
output_spec = DynamicTraitedSpec
86+
_always_run = True
87+
88+
def __init__(self, infields=None, outfields=None, **kwargs):
89+
"""
90+
Parameters
91+
----------
92+
infields : list of str
93+
Indicates the input fields to be dynamically created
94+
95+
outfields: list of str
96+
Indicates output fields to be dynamically created
97+
98+
"""
99+
if not outfields:
100+
outfields = []
101+
if not infields:
102+
infields = []
103+
104+
super(BIDSDataGrabber, self).__init__(**kwargs)
105+
undefined_traits = {}
106+
# used for mandatory inputs check
107+
self._infields = infields
108+
self._outfields = outfields
109+
for key in infields:
110+
self.inputs.add_trait(key, traits.Any)
111+
undefined_traits[key] = Undefined
112+
113+
if not isdefined(self.inputs.output_query):
114+
self.inputs.output_query = {}
115+
116+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
117+
118+
def _run_interface(self, runtime):
119+
return runtime
120+
121+
def _list_outputs(self):
122+
if not self._outfields:
123+
self._outfields = ['outfield']
124+
self.inputs.output_query = {'outfield' : {}}
125+
else:
126+
for key in self._outfields:
127+
if key not in self.inputs.output_query:
128+
raise ValueError("Define query for all outputs")
129+
130+
for key in self._infields:
131+
value = getattr(self.inputs, key)
132+
if not isdefined(value):
133+
msg = "%s requires a value for input '%s' because" \
134+
" it was listed in 'infields'" % \
135+
(self.__class__.__name__, key)
136+
raise ValueError(msg)
137+
138+
layout = BIDSLayout(self.inputs.base_dir)
139+
140+
filters = {i: getattr(self.inputs, i) for i in self._infields}
141+
142+
outputs = {}
143+
for key, query in self.inputs.output_query.items():
144+
outputs[key] = layout.get(
145+
**dict(query.items() | filters.items()),
146+
return_type='file')
147+
return outputs

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ configparser
1313
pytest>=3.0
1414
mock
1515
pydotplus
16+
pybids==0.3
1617
packaging

0 commit comments

Comments
 (0)