-
Notifications
You must be signed in to change notification settings - Fork 532
ENH: BIDS Data Grabber #2174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
ENH: BIDS Data Grabber #2174
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
a1b903c
Added BIDSDataGrabber, test data and tests
adelavega da44acf
Edited contributors
adelavega 8ed1c44
Removed data, using test data from pybids package
adelavega 52cf9e4
Check if pybids is installed when importing
adelavega 6a4ce64
Tried to fix reqs and travis
adelavega 72bd8e2
Added mandatory option for outfieds
adelavega 4bfa177
Merge branch 'master' into bids_grabber
effigies dc64c72
Install pybids from github in travis
adelavega 406c729
Fix docstring, and Directory import
adelavega 0915a9d
Use logger
adelavega 651bc85
Smart defaults
adelavega cfc2ed6
Merge branch 'bids_grabber' of github.com:adelavega/nipype into bids_…
adelavega dd03863
Try fix CI
adelavega f460eaa
Moved to bids utils
adelavega 3cc3a23
Try fix Circle
adelavega e2e78c4
Return type argument
adelavega 60dce3f
Undefined output if no match
adelavega 97d832b
Fix return type
adelavega ce148eb
Install pybids for tesing in Dockerfile
adelavega fc6dd01
Update circle.yml
adelavega 03243d8
Added reference to io
adelavega 451e20c
Merge branch 'bids_grabber' of github.com:adelavega/nipype into bids_…
adelavega 0d877ee
Merge branch 'master' into bids_grabber
adelavega File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,4 +149,3 @@ ENV MATLABCMD="/opt/mcr/v85/toolbox/matlab" \ | |
FORCE_SPMMCR=1 | ||
|
||
WORKDIR /work | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
# -*- coding: utf-8 -*- | ||
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*- | ||
# vi: set ft=python sts=4 ts=4 sw=4 et: | ||
""" Set of interfaces that allow interaction with BIDS data. Currently | ||
available interfaces are: | ||
|
||
BIDSDataGrabber: Query data from BIDS dataset using pybids grabbids. | ||
|
||
Change directory to provide relative paths for doctests | ||
>>> import os | ||
>>> import bids | ||
>>> filepath = os.path.realpath(os.path.dirname(bids.__file__)) | ||
>>> datadir = os.path.realpath(os.path.join(filepath, 'grabbids/tests/data/')) | ||
>>> os.chdir(datadir) | ||
|
||
""" | ||
from os.path import join, dirname | ||
from .. import logging | ||
from .base import (traits, | ||
DynamicTraitedSpec, | ||
Directory, | ||
BaseInterface, | ||
isdefined, | ||
Str, | ||
Undefined) | ||
|
||
try: | ||
from bids import grabbids as gb | ||
import json | ||
except ImportError: | ||
have_pybids = False | ||
else: | ||
have_pybids = True | ||
|
||
LOGGER = logging.getLogger('workflows') | ||
|
||
class BIDSDataGrabberInputSpec(DynamicTraitedSpec): | ||
base_dir = Directory(exists=True, | ||
desc='Path to BIDS Directory.', | ||
mandatory=True) | ||
output_query = traits.Dict(key_trait=Str, | ||
value_trait=traits.Dict, | ||
desc='Queries for outfield outputs') | ||
raise_on_empty = traits.Bool(True, usedefault=True, | ||
desc='Generate exception if list is empty ' | ||
'for a given field') | ||
return_type = traits.Enum('file', 'namedtuple', usedefault=True) | ||
|
||
|
||
class BIDSDataGrabber(BaseInterface): | ||
|
||
""" BIDS datagrabber module that wraps around pybids to allow arbitrary | ||
querying of BIDS datasets. | ||
|
||
Examples | ||
-------- | ||
|
||
>>> from nipype.interfaces.bids_utils import BIDSDataGrabber | ||
>>> from os.path import basename | ||
|
||
By default, the BIDSDataGrabber fetches anatomical and functional images | ||
from a project, and makes BIDS entities (e.g. subject) available for | ||
filtering outputs. | ||
|
||
>>> bg = BIDSDataGrabber() | ||
>>> bg.inputs.base_dir = 'ds005/' | ||
>>> bg.inputs.subject = '01' | ||
>>> results = bg.run() | ||
>>> basename(results.outputs.anat[0]) # doctest: +ALLOW_UNICODE | ||
'sub-01_T1w.nii.gz' | ||
|
||
>>> basename(results.outputs.func[0]) # doctest: +ALLOW_UNICODE | ||
'sub-01_task-mixedgamblestask_run-01_bold.nii.gz' | ||
|
||
|
||
Dynamically created, user-defined output fields can also be defined to | ||
return different types of outputs from the same project. All outputs | ||
are filtered on common entities, which can be explicitly defined as | ||
infields. | ||
|
||
>>> bg = BIDSDataGrabber(infields = ['subject'], outfields = ['dwi']) | ||
>>> bg.inputs.base_dir = 'ds005/' | ||
>>> bg.inputs.subject = '01' | ||
>>> bg.inputs.output_query['dwi'] = dict(modality='dwi') | ||
>>> results = bg.run() | ||
>>> basename(results.outputs.dwi[0]) # doctest: +ALLOW_UNICODE | ||
'sub-01_dwi.nii.gz' | ||
|
||
""" | ||
input_spec = BIDSDataGrabberInputSpec | ||
output_spec = DynamicTraitedSpec | ||
_always_run = True | ||
|
||
def __init__(self, infields=None, outfields=None, **kwargs): | ||
""" | ||
Parameters | ||
---------- | ||
infields : list of str | ||
Indicates the input fields to be dynamically created | ||
|
||
outfields: list of str | ||
Indicates output fields to be dynamically created. | ||
If no matching items, returns Undefined. | ||
""" | ||
super(BIDSDataGrabber, self).__init__(**kwargs) | ||
if not have_pybids: | ||
raise ImportError("The BIDSEventsGrabber interface requires pybids." | ||
" Please make sure it is installed.") | ||
|
||
# If outfields is None use anat and func as default | ||
if outfields is None: | ||
outfields = ['func', 'anat'] | ||
self.inputs.output_query = { | ||
"func": {"modality": "func"}, | ||
"anat": {"modality": "anat"}} | ||
else: | ||
self.inputs.output_query = {} | ||
|
||
# If infields is None, use all BIDS entities | ||
if infields is None: | ||
bids_config = join(dirname(gb.__file__), 'config', 'bids.json') | ||
bids_config = json.load(open(bids_config, 'r')) | ||
infields = [i['name'] for i in bids_config['entities']] | ||
|
||
self._infields = infields | ||
self._outfields = outfields | ||
|
||
# used for mandatory inputs check | ||
undefined_traits = {} | ||
for key in infields: | ||
self.inputs.add_trait(key, traits.Any) | ||
undefined_traits[key] = Undefined | ||
|
||
self.inputs.trait_set(trait_change_notify=False, **undefined_traits) | ||
|
||
def _run_interface(self, runtime): | ||
return runtime | ||
|
||
def _list_outputs(self): | ||
layout = gb.BIDSLayout(self.inputs.base_dir) | ||
|
||
for key in self._outfields: | ||
if key not in self.inputs.output_query: | ||
raise ValueError("Define query for all outputs") | ||
|
||
# If infield is not given nm input value, silently ignore | ||
filters = {} | ||
for key in self._infields: | ||
value = getattr(self.inputs, key) | ||
if isdefined(value): | ||
filters[key] = value | ||
|
||
outputs = {} | ||
for key, query in self.inputs.output_query.items(): | ||
args = query.copy() | ||
args.update(filters) | ||
filelist = layout.get(return_type=self.inputs.return_type, | ||
**args) | ||
if len(filelist) == 0: | ||
msg = 'Output key: %s returned no files' % ( | ||
key) | ||
if self.inputs.raise_on_empty: | ||
raise IOError(msg) | ||
else: | ||
LOGGER.warning(msg) | ||
filelist = Undefined | ||
|
||
outputs[key] = filelist | ||
return outputs |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as a future improvement to a GrabbitIT interface that could serve as a base class, could this be passed along as a parameter?
this would also allow arbitrary and newer layouts to be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. The issue here is that I cannot get the entities of a project (or BIDS entities in general), without initializing a BIDSLayout with a specific dataset. So this was the only way I could think of to access the entities definition.