Skip to content
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

MRG: Add "description" entity to BIDSPath #1057

Merged
merged 20 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion mne_bids/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,13 @@
# allowed BIDSPath entities
ALLOWED_PATH_ENTITIES = ('subject', 'session', 'task', 'run',
'processing', 'recording', 'space',
'acquisition', 'split',
'acquisition', 'description', 'split',
'suffix', 'extension')
ALLOWED_PATH_ENTITIES_SHORT = {'sub': 'subject', 'ses': 'session',
'task': 'task', 'acq': 'acquisition',
'run': 'run', 'proc': 'processing',
'space': 'space', 'rec': 'recording',
'desc': 'description',
'split': 'split'}

# Annotations to never remove during reading or writing
Expand Down Expand Up @@ -245,6 +246,7 @@
'recording': 'label',
'space': 'label',
'acquisition': 'label',
'description': 'label',
'split': 'index',
'suffix': 'label',
'extension': 'label'
Expand Down
80 changes: 51 additions & 29 deletions mne_bids/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import re
from io import StringIO
import shutil as sh
from collections import OrderedDict
from copy import deepcopy
from os import path as op
from pathlib import Path
Expand Down Expand Up @@ -179,6 +178,10 @@ class BIDSPath(object):
Corresponds to "space".
Note that valid values for ``space`` must come from a list
of BIDS keywords as described in the BIDS specification.
description : str | None
This corresponds to the BIDS entity ``desc``. It is used to provide
additional information for derivative data, e.g., preprocessed data
hoechenberger marked this conversation as resolved.
Show resolved Hide resolved
may be assigned ``description='cleaned'``.
split : int | None
The split of the continuous recording file for ``.fif`` data.
Corresponds to "split".
Expand All @@ -203,10 +206,10 @@ class BIDSPath(object):
Attributes
----------
entities : dict
The dictionary of the BIDS entities and their values:
A dictionary of the BIDS entities and their values:
``subject``, ``session``, ``task``, ``acquisition``,
``run``, ``processing``, ``space``, ``recording``, ``split``,
``suffix``, and ``extension``.
``run``, ``processing``, ``space``, ``recording``, ``description``,
``split``, ``suffix``, and ``extension``.
datatype : str | None
The data type, i.e., one of ``'meg'``, ``'eeg'``, ``'ieeg'``,
``'anat'``.
Expand Down Expand Up @@ -288,36 +291,38 @@ class BIDSPath(object):

def __init__(self, subject=None, session=None,
task=None, acquisition=None, run=None, processing=None,
recording=None, space=None, split=None, root=None,
suffix=None, extension=None, datatype=None, check=True):
recording=None, space=None, description=None,
split=None, root=None, suffix=None, extension=None,
datatype=None, check=True):
if all(ii is None for ii in [subject, session, task,
acquisition, run, processing,
recording, space, root, suffix,
extension]):
recording, space, description,
root, suffix, extension]):
raise ValueError("At least one parameter must be given.")

self.check = check

self.update(subject=subject, session=session, task=task,
acquisition=acquisition, run=run, processing=processing,
recording=recording, space=space, split=split,
root=root, datatype=datatype,
suffix=suffix, extension=extension)
recording=recording, space=space, description=description,
split=split, root=root, datatype=datatype, suffix=suffix,
extension=extension)

@property
def entities(self):
"""Return dictionary of the BIDS entities."""
return OrderedDict([
('subject', self.subject),
('session', self.session),
('task', self.task),
('acquisition', self.acquisition),
('run', self.run),
('processing', self.processing),
('space', self.space),
('recording', self.recording),
('split', self.split)
])
return {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dict insertion order is preserved in Python 3.7+, so no need to have an OrderedDict anymore

'subject': self.subject,
'session': self.session,
'task': self.task,
'acquisition': self.acquisition,
'run': self.run,
'processing': self.processing,
'space': self.space,
'recording': self.recording,
'description': self.description,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a loose thought...

Is it worth differentiating "raw" entities and "derivative" entities? E.g. via a parameter passed to entities?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this too at least for writing, but I'd say we can always add this later on... for now we had a specific use case where @agramfort couldnt read some OpenNeuro data with MNE-BIDS, and this PR fixes this :)

'split': self.split,
}

@property
def basename(self):
Expand Down Expand Up @@ -442,6 +447,15 @@ def space(self) -> Optional[str]:
def space(self, value):
self.update(space=value)

@property
def description(self) -> Optional[str]:
"""The description entity."""
return self._description

@description.setter
def description(self, value):
self.update(description=value)

@property
def suffix(self) -> Optional[str]:
"""The filename suffix."""
Expand Down Expand Up @@ -617,9 +631,8 @@ def fpath(self):
def update(self, *, check=None, **kwargs):
"""Update inplace BIDS entity key/value pairs in object.

``run`` and ``split`` are auto-parsed to have two
numbers when passed in. For example, if ``run=1``, then it will
become ``run='01'``.
``run`` and ``split`` are auto-converted to have two
digits. For example, if ``run=1``, then it will nbecome ``run='01'``.

Also performs error checks on various entities to
adhere to the BIDS specification. Specifically:
Expand Down Expand Up @@ -1539,8 +1552,9 @@ def get_datatypes(root, verbose=None):
def get_entity_vals(root, entity_key, *, ignore_subjects='emptyroom',
ignore_sessions=None, ignore_tasks=None, ignore_runs=None,
ignore_processings=None, ignore_spaces=None,
ignore_acquisitions=None, ignore_splits=None,
ignore_modalities=None, ignore_datatypes=None,
ignore_acquisitions=None, ignore_descriptions=None,
ignore_splits=None, ignore_modalities=None,
ignore_datatypes=None,
ignore_dirs=('derivatives', 'sourcedata'), with_key=False,
verbose=None):
"""Get list of values associated with an `entity_key` in a BIDS dataset.
Expand Down Expand Up @@ -1583,8 +1597,12 @@ def get_entity_vals(root, entity_key, *, ignore_subjects='emptyroom',
Space(s) to ignore. If ``None``, include all spaces.
ignore_acquisitions : str | array-like of str | None
Acquisition(s) to ignore. If ``None``, include all acquisitions.
ignore_descriptions : str | array-like of str | None
Description(s) to ignore. If ``None``, include all descriptions.
ignore_splits : str | array-like of str | None
Split(s) to ignore. If ``None``, include all splits.

.. versionadded:: 0.11
ignore_modalities : str | array-like of str | None
Modalities(s) to ignore. If ``None``, include all modalities.
ignore_datatypes : str | array-like of str | None
Expand Down Expand Up @@ -1637,9 +1655,9 @@ def get_entity_vals(root, entity_key, *, ignore_subjects='emptyroom',
root = Path(root).expanduser()

entities = ('subject', 'task', 'session', 'run', 'processing', 'space',
'acquisition', 'split', 'suffix')
'acquisition', 'description', 'split', 'suffix')
entities_abbr = ('sub', 'task', 'ses', 'run', 'proc', 'space', 'acq',
'split', 'suffix')
'desc', 'split', 'suffix')
entity_long_abbr_map = dict(zip(entities, entities_abbr))

if entity_key not in entities:
Expand All @@ -1654,6 +1672,7 @@ def get_entity_vals(root, entity_key, *, ignore_subjects='emptyroom',
ignore_spaces = _ensure_tuple(ignore_spaces)
ignore_acquisitions = _ensure_tuple(ignore_acquisitions)
ignore_splits = _ensure_tuple(ignore_splits)
ignore_splits = _ensure_tuple(ignore_splits)
hoechenberger marked this conversation as resolved.
Show resolved Hide resolved
ignore_modalities = _ensure_tuple(ignore_modalities)

ignore_dirs = _ensure_tuple(ignore_dirs)
Expand Down Expand Up @@ -1699,6 +1718,9 @@ def get_entity_vals(root, entity_key, *, ignore_subjects='emptyroom',
if ignore_acquisitions and any([f'_acq-{a}_' in filename.stem
for a in ignore_acquisitions]):
continue
if ignore_descriptions and any([f'_desc-{a}_' in filename.stem
for a in ignore_acquisitions]):
continue
if ignore_splits and any([f'_split-{s}_' in filename.stem
for s in ignore_splits]):
continue
Expand Down