Skip to content

Commit f460eaa

Browse files
committed
Moved to bids utils
1 parent dd03863 commit f460eaa

File tree

1 file changed

+169
-0
lines changed

1 file changed

+169
-0
lines changed

nipype/interfaces/bids_utils.py

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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+
>>> import bids
12+
>>> filepath = os.path.realpath(os.path.dirname(bids.__file__))
13+
>>> datadir = os.path.realpath(os.path.join(filepath, 'grabbids/tests/data/'))
14+
>>> os.chdir(datadir)
15+
16+
"""
17+
from os.path import join, dirname
18+
from .. import logging
19+
from .base import (traits,
20+
DynamicTraitedSpec,
21+
Directory,
22+
BaseInterface,
23+
isdefined,
24+
Str,
25+
Undefined)
26+
27+
try:
28+
from bids import grabbids as gb
29+
import json
30+
except ImportError:
31+
have_pybids = False
32+
else:
33+
have_pybids = True
34+
35+
LOGGER = logging.getLogger('workflows')
36+
37+
class BIDSDataGrabberInputSpec(DynamicTraitedSpec):
38+
base_dir = Directory(exists=True,
39+
desc='Path to BIDS Directory.',
40+
mandatory=True)
41+
output_query = traits.Dict(key_trait=Str,
42+
value_trait=traits.Dict,
43+
desc='Queries for outfield outputs')
44+
raise_on_empty = traits.Bool(True, usedefault=True,
45+
desc='Generate exception if list is empty '
46+
'for a given field')
47+
return_type = traits.Enum('filename', 'namedtuple', usedefault=True)
48+
49+
50+
class BIDSDataGrabber(BaseInterface):
51+
52+
""" BIDS datagrabber module that wraps around pybids to allow arbitrary
53+
querying of BIDS datasets.
54+
55+
Examples
56+
--------
57+
58+
>>> from nipype.interfaces.bids_utils import BIDSDataGrabber
59+
>>> from os.path import basename
60+
61+
By default, the BIDSDataGrabber fetches anatomical and functional images
62+
from a project, and makes BIDS entities (e.g. subject) available for
63+
filtering outputs.
64+
65+
>>> bg = BIDSDataGrabber()
66+
>>> bg.inputs.base_dir = 'ds005/'
67+
>>> bg.inputs.subject = '01'
68+
>>> results = bg.run()
69+
>>> basename(results.outputs.anat[0]) # doctest: +ALLOW_UNICODE
70+
'sub-01_T1w.nii.gz'
71+
72+
>>> basename(results.outputs.func[0]) # doctest: +ALLOW_UNICODE
73+
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz'
74+
75+
76+
Dynamically created, user-defined output fields can also be defined to
77+
return different types of outputs from the same project. All outputs
78+
are filtered on common entities, which can be explicitly defined as
79+
infields.
80+
81+
>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi'])
82+
>>> bg.inputs.base_dir = 'ds005/'
83+
>>> bg.inputs.subject = '01'
84+
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi')
85+
>>> results = bg.run()
86+
>>> basename(results.outputs.dwi[0]) # doctest: +ALLOW_UNICODE
87+
'sub-01_dwi.nii.gz'
88+
89+
"""
90+
input_spec = BIDSDataGrabberInputSpec
91+
output_spec = DynamicTraitedSpec
92+
_always_run = True
93+
94+
def __init__(self, infields=None, outfields=None, **kwargs):
95+
"""
96+
Parameters
97+
----------
98+
infields : list of str
99+
Indicates the input fields to be dynamically created
100+
101+
outfields: list of str
102+
Indicates output fields to be dynamically created.
103+
If no matching items, returns Undefined.
104+
"""
105+
super(BIDSDataGrabber, self).__init__(**kwargs)
106+
if not have_pybids:
107+
raise ImportError("The BIDSEventsGrabber interface requires pybids."
108+
" Please make sure it is installed.")
109+
110+
# If outfields is None use anat and func as default
111+
if outfields is None:
112+
outfields = ['func', 'anat']
113+
self.inputs.output_query = {
114+
"func": {"modality": "func"},
115+
"anat": {"modality": "anat"}}
116+
else:
117+
self.inputs.output_query = {}
118+
119+
# If infields is None, use all BIDS entities
120+
if infields is None:
121+
bids_config = join(dirname(gb.__file__), 'config', 'bids.json')
122+
bids_config = json.load(open(bids_config, 'r'))
123+
infields = [i['name'] for i in bids_config['entities']]
124+
125+
self._infields = infields
126+
self._outfields = outfields
127+
128+
# used for mandatory inputs check
129+
undefined_traits = {}
130+
for key in infields:
131+
self.inputs.add_trait(key, traits.Any)
132+
undefined_traits[key] = Undefined
133+
134+
self.inputs.trait_set(trait_change_notify=False, **undefined_traits)
135+
136+
def _run_interface(self, runtime):
137+
return runtime
138+
139+
def _list_outputs(self):
140+
layout = gb.BIDSLayout(self.inputs.base_dir)
141+
142+
for key in self._outfields:
143+
if key not in self.inputs.output_query:
144+
raise ValueError("Define query for all outputs")
145+
146+
# If infield is not given nm input value, silently ignore
147+
filters = {}
148+
for key in self._infields:
149+
value = getattr(self.inputs, key)
150+
if isdefined(value):
151+
filters[key] = value
152+
153+
outputs = {}
154+
for key, query in self.inputs.output_query.items():
155+
args = query.copy()
156+
args.update(filters)
157+
filelist = layout.get(return_type='file',
158+
**args)
159+
if len(filelist) == 0:
160+
msg = 'Output key: %s returned no files' % (
161+
key)
162+
if self.inputs.raise_on_empty:
163+
raise IOError(msg)
164+
else:
165+
LOGGER.warning(msg)
166+
filelist = Undefined
167+
else:
168+
outputs[key] = filelist
169+
return outputs

0 commit comments

Comments
 (0)