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] ENH: func to extract list of instances of a certain entity from BIDS ds #252

Merged
merged 11 commits into from
Aug 7, 2019
22 changes: 11 additions & 11 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
node_js:
- "10.0.0"
- "10.16.1"
language: python

# Specify version of BIDS-validator to be used, 'master', or 'stable'
env:
global:
- VALIDATOR_VERSION="master"
- MNE_VERSION="master" # can be "maint/0.18" ... or "master"
# can be "stable", or anything that can be used with git checkout
- VALIDATOR_VERSION="a7e2fddeec489a8d5db81c561a5adc89363a306e"
# can be "maint/0.18" ... or "master"
- MNE_VERSION="master"


before_install:
Expand All @@ -16,26 +18,24 @@ before_install:
- export PATH=/home/travis/miniconda/bin:$PATH
- conda update --yes --quiet conda
- npm install -g npm stable
- npm install -g node@10.0.0
- npm install -g node@10.16.1
- npm --version
- node --version
- yarn --version

install:
- echo $PATH
- |
if [ $VALIDATOR_VERSION == 'master' ];then
if [ $VALIDATOR_VERSION == 'stable' ];then
npm install -g bids-validator
else
pushd ~
git clone --depth 1 https://github.com/bids-standard/bids-validator
git clone https://github.com/bids-standard/bids-validator
cd bids-validator
git checkout $VALIDATOR_VERSION
yarn
export PATH=~/bids-validator/bids-validator/bin:$PATH
popd
elif [ $VALIDATOR_VERSION == 'stable' ];then
npm install -g bids-validator
else
echo "VALIDATOR_VERSION should be set to master or stable"
echo "but VALIDATOR_VERSION=$VALIDATOR_VERSION"
fi
- echo $PATH
- bids-validator --version
Expand Down
27 changes: 15 additions & 12 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,38 @@
environment:
# Specify version of BIDS-validator to be used, "master", or "stable"
# Specify version of BIDS-validator to be used, "stable", or anything that can be used with git checkout
# NOTE: For "master" you MUST adjust VALIDATOR_EXECUTABLE to the following value:
# "C:\\projects\\mne-bids\\bids-validator\\bids-validator\\bin\\bids-validator"
# ... whereas for "stable", VALIDATOR_EXECUTABLE MUST be set to "n/a"
global:
VALIDATOR_VERSION: "master"
MNE_VERSION: "master" # can be "maint/0.18" ... or "master"
# can be
VALIDATOR_VERSION: "a7e2fddeec489a8d5db81c561a5adc89363a306e"
# can be "maint/0.18" ... or "master"
MNE_VERSION: "master"
VALIDATOR_EXECUTABLE: "C:\\projects\\mne-bids\\bids-validator\\bids-validator\\bin\\bids-validator"
NODEJS_VERSION: "10.0.0"
NODEJS_VERSION: "10.16.1"
PYTHON: "C:\\conda"
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
PYTHON_VERSION: "3.6"
PYTHON_ARCH: "64"

install:
- ps: Install-Product node $env:NODEJS_VERSION
# https://www.appveyor.com/docs/lang/nodejs-iojs/
- ps: Update-NodeJsInstallation $env:NODEJS_VERSION
- node --version
- npm --version
- yarn --version
- cmd: if [%VALIDATOR_VERSION%]==[master] (
git clone --depth 1 https://github.com/bids-standard/bids-validator &&
cd bids-validator &&
yarn &&
cd .. &&
set PATH="%PATH%;C:\projects\mne-bids\bids-validator\bids-validator\bin\" )
- cmd: if [%VALIDATOR_VERSION%]==[stable] (
npm install -g bids-validator
bids-validator --version
which bids-validator
)
) else (
git clone https://github.com/bids-standard/bids-validator &&
cd bids-validator &&
git checkout %VALIDATOR_VERSION% &&
yarn &&
cd .. &&
set PATH="%PATH%;C:\projects\mne-bids\bids-validator\bids-validator\bin\" )
- "git clone --depth 1 git://github.com/astropy/ci-helpers.git"
- "powershell ci-helpers/appveyor/install-miniconda.ps1"
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
Expand Down
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Utils (:py:mod:`mne_bids.utils`)
:toctree: generated/

print_dir_tree
get_values_for_key

Copyfiles (:py:mod:`mne_bids.copyfiles`)
========================================
Expand Down
1 change: 1 addition & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Current
Changelog
~~~~~~~~~

- New function :func:`mne_bids.utils.get_values_for_key` allows to get a list of instances of a certain entity in a BIDS directory, by `Mainak Jas`_ and `Stefan Appelhoff`_ (`#252 <https://github.com/mne-tools/mne-bids/pull/252>`_)
- :func:`mne_bids.utils.print_dir_tree` now accepts an argument :code:`max_depth` which can limit the depth until which the directory tree is printed, by `Stefan Appelhoff`_ (`#245 <https://github.com/mne-tools/mne-bids/pull/245>`_)
- New command line function exposed :code:`cp` for renaming/copying files including automatic doc generation "CLI", by `Stefan Appelhoff`_ (`#225 <https://github.com/mne-tools/mne-bids/pull/225>`_)
- :func:`read_raw_bids` now also reads channels.tsv files accompanying a raw BIDS file and sets the channel types accordingly, by `Stefan Appelhoff`_ (`#219 <https://github.com/mne-tools/mne-bids/pull/219>`_)
Expand Down
64 changes: 44 additions & 20 deletions mne_bids/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,60 @@
_infer_eeg_placement_scheme, _handle_kind,
_find_matching_sidecar, _parse_ext,
_get_ch_type_mapping, _parse_bids_filename,
_find_best_candidates)
_find_best_candidates, get_values_for_key)


base_path = op.join(op.dirname(mne.__file__), 'io')
subject_id = '01'
session_id = '01'
run = '01'
acq = '01'
acq = None
task = 'testing'

bids_basename = make_bids_basename(
subject=subject_id, session=session_id, run=run, acquisition=acq,
task=task)


@pytest.fixture(scope='session')
def return_bids_test_dir(tmpdir_factory):
"""Return path to a written test BIDS dir."""
output_path = str(tmpdir_factory.mktemp('mnebids_utils_test_bids_ds'))
data_path = testing.data_path()
raw_fname = op.join(data_path, 'MEG', 'sample',
'sample_audvis_trunc_raw.fif')

event_id = {'Auditory/Left': 1, 'Auditory/Right': 2, 'Visual/Left': 3,
'Visual/Right': 4, 'Smiley': 5, 'Button': 32}
events_fname = op.join(data_path, 'MEG', 'sample',
'sample_audvis_trunc_raw-eve.fif')

raw = mne.io.read_raw_fif(raw_fname)
# Write multiple runs for test_purposes
bids_basename2 = bids_basename.replace('run-{}'.format(run), 'run-02')
for name in [bids_basename,
bids_basename2,
]:
with pytest.warns(UserWarning, match='No line frequency'):
write_raw_bids(raw, name, output_path,
events_data=events_fname, event_id=event_id,
overwrite=True)

return output_path


def test_get_values_for_key(return_bids_test_dir):
"""Test getting a list of entities."""
bids_root = return_bids_test_dir
with pytest.raises(ValueError, match='`key` must be one of'):
get_values_for_key(bids_root, key='bogus')

assert get_values_for_key(bids_root, 'sub') == [subject_id]
assert get_values_for_key(bids_root, 'ses') == [session_id]
assert get_values_for_key(bids_root, 'run') == [run, '02']
assert get_values_for_key(bids_root, 'acq') == []


def test_get_ch_type_mapping():
"""Test getting a correct channel mapping."""
with pytest.raises(ValueError, match='specified from "bogus" to "mne"'):
Expand Down Expand Up @@ -239,30 +278,15 @@ def test_find_best_candidates(candidate_list, best_candidates):
assert _find_best_candidates(params, candidate_list) == best_candidates


def test_find_matching_sidecar():
def test_find_matching_sidecar(return_bids_test_dir):
"""Test finding a sidecar file from a BIDS dir."""
# First write a BIDS dir
output_path = _TempDir()
data_path = testing.data_path()
raw_fname = op.join(data_path, 'MEG', 'sample',
'sample_audvis_trunc_raw.fif')

event_id = {'Auditory/Left': 1, 'Auditory/Right': 2, 'Visual/Left': 3,
'Visual/Right': 4, 'Smiley': 5, 'Button': 32}
events_fname = op.join(data_path, 'MEG', 'sample',
'sample_audvis_trunc_raw-eve.fif')

raw = mne.io.read_raw_fif(raw_fname)
with pytest.warns(UserWarning, match='No line frequency'):
write_raw_bids(raw, bids_basename, output_path,
events_data=events_fname, event_id=event_id,
overwrite=False)
output_path = return_bids_test_dir

# Now find a sidecar
sidecar_fname = _find_matching_sidecar(bids_basename, output_path,
'coordsystem.json')
expected_file = op.join('sub-01', 'ses-01', 'meg',
'sub-01_ses-01_acq-01_coordsystem.json')
'sub-01_ses-01_coordsystem.json')
assert sidecar_fname.endswith(expected_file)

# Find multiple sidecars, tied in score, triggering an error
Expand Down
40 changes: 40 additions & 0 deletions mne_bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import re
from datetime import datetime
from collections import defaultdict
from pathlib import Path

import numpy as np
from mne import read_events, find_events, events_from_annotations
Expand All @@ -28,6 +29,45 @@
from mne_bids.tsv_handler import _to_tsv, _tsv_to_str


def get_values_for_key(bids_root, key):
"""Get list of values for a key in a BIDS dataset.

BIDS file names are organized by key-value pairs called "entities" [1]_.

Parameters
----------
bids_root : str
Path to the root of the BIDS directory.
key : str
The name of the key to search for. Can be one of
['sub', 'ses', 'run', 'acq'].

Returns
-------
value_list : list of str
List of the values associated with a `key` in the BIDS dataset pointed
to by `bids_root`.

References
----------
.. [1] https://bids-specification.rtfd.io/en/latest/02-common-principles.html#file-name-structure # noqa: E501

"""
accepted_keys = ('sub', 'ses', 'run', 'acq')
if key not in accepted_keys:
raise ValueError('`key` must be one of "{}". Got "{}"'
.format(accepted_keys, key))

p = re.compile(r'{}-(.*?)_'.format(key))
value_list = list()
for filename in Path(bids_root).rglob('*{}-*_*'.format(key)):
match = p.search(filename.stem)
value = match.group(1)
if value not in value_list:
value_list.append(value)
return sorted(value_list)


def _get_ch_type_mapping(fro='mne', to='bids'):
"""Map between BIDS and MNE nomenclatures for channel types.

Expand Down