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 ieeg tests #118

Merged
merged 9 commits into from
Nov 12, 2018
23 changes: 18 additions & 5 deletions mne_bids/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,16 @@
'.cnt', # Neuroscan
]

ALLOWED_EXTENSIONS = allowed_extensions_meg + allowed_extensions_eeg
allowed_extensions_ieeg = ['.vhdr', # BrainVision, accompanied by .vmrk, .eeg
'.edf', # European Data Format
'.set', # EEGLAB, potentially accompanied by .fdt
'.mef', # MEF: Multiscale Electrophysiology File
'.nwb', # Neurodata without borders
]

ALLOWED_EXTENSIONS = (allowed_extensions_meg +
allowed_extensions_eeg +
allowed_extensions_ieeg)

reader = {'.con': io.read_raw_kit, '.sqd': io.read_raw_kit,
'.fif': io.read_raw_fif, '.pdf': io.read_raw_bti,
Expand All @@ -43,8 +52,6 @@ def _read_raw(raw_fname, electrode=None, hsp=None, hpi=None, config=None,
"""Read a raw file into MNE, making inferences based on extension."""
fname, ext = _parse_ext(raw_fname)

# MEG File Types
# --------------
# KIT systems
if ext in ['.con', '.sqd']:
raw = io.read_raw_kit(raw_fname, elp=electrode, hsp=hsp,
Expand All @@ -65,9 +72,15 @@ def _read_raw(raw_fname, electrode=None, hsp=None, hpi=None, config=None,
elif ext in ['.edf', '.bdf']:
raw = reader[ext](raw_fname, preload=True)

# MEF and NWB are allowed, but not yet implemented
elif ext in ['.mef', '.nwb']:
raise ValueError('Got "{}" as extension. This is an allowed extension '
'but there is no IO support for this file format yet.'
.format(ext))

# No supported data found ...
# ---------------------------
else:
raise ValueError("Raw file name extension must be one of %\n"
"Got %" % (ALLOWED_EXTENSIONS, ext))
raise ValueError('Raw file name extension must be one of {}\n'
'Got {}'.format(ALLOWED_EXTENSIONS, ext))
return raw
22 changes: 12 additions & 10 deletions mne_bids/mne_bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,19 +52,24 @@
'.meg4': 'CTF'}

eeg_manufacturers = {'.vhdr': 'BrainProducts', '.eeg': 'BrainProducts',
'.edf': 'Mixed', '.bdf': 'Biosemi', '.set': 'Mixed',
'.fdt': 'Mixed', '.cnt': 'Neuroscan'}
'.edf': 'n/a', '.bdf': 'Biosemi', '.set': 'n/a',
'.fdt': 'n/a', '.cnt': 'Neuroscan'}

ieeg_manufacturers = {'.vhdr': 'BrainProducts', '.eeg': 'BrainProducts',
'.edf': 'n/a', '.set': 'n/a', '.fdt': 'n/a',
'.mef': 'n/a', '.nwb': 'n/a'}

# Merge the manufacturer dictionaries in a python2 / python3 compatible way
MANUFACTURERS = dict()
MANUFACTURERS.update(meg_manufacturers)
MANUFACTURERS.update(eeg_manufacturers)
MANUFACTURERS.update(ieeg_manufacturers)

# List of synthetic channels by manufacturer that are to be excluded from the
# channel list. Currently this is only for stimulus channels.
IGNORED_CHANNELS = {'KIT/Yokogawa': ['STI 014'],
'BrainProducts': ['STI 014'],
'Mixed': ['STI 014'],
'n/a': ['STI 014'], # for unknown manufacturers, ignore it
'Biosemi': ['STI 014'],
'Neuroscan': ['STI 014']}

Expand Down Expand Up @@ -135,10 +140,10 @@ def _channels_tsv(raw, fname, overwrite=False, verbose=True):
('name', raw.info['ch_names']),
('type', ch_type),
('units', units),
('description', description),
('sampling_frequency', np.full((n_channels), sfreq)),
('low_cutoff', np.full((n_channels), low_cutoff)),
('high_cutoff', np.full((n_channels), high_cutoff)),
('description', description),
('sampling_frequency', np.full((n_channels), sfreq)),
('status', status)]))
df.drop(ignored_indexes, inplace=True)

Expand Down Expand Up @@ -486,6 +491,7 @@ def _sidecar_json(raw, task, manufacturer, fname, kind, overwrite=False,
('EEGPlacementScheme', _infer_eeg_placement_scheme(raw)),
('Manufacturer', manufacturer)]
ch_info_json_ieeg = [
('iEEGReference', 'n/a'),
('ECOGChannelCount', n_ecogchan),
('SEEGChannelCount', n_seegchan)]
ch_info_ch_counts = [
Expand Down Expand Up @@ -646,8 +652,6 @@ def write_raw_bids(raw, bids_basename, output_path, events_data=None,
orient = ORIENTATION.get(ext, 'n/a')
unit = UNITS.get(ext, 'n/a')
manufacturer = MANUFACTURERS.get(ext, 'n/a')
if manufacturer == 'Mixed':
manufacturer = 'n/a'

# save all meta data
_participants_tsv(raw, subject_id, "n/a", participants_fname, overwrite,
Expand Down Expand Up @@ -700,9 +704,7 @@ def write_raw_bids(raw, bids_basename, output_path, events_data=None,
'Please contact MNE developers if you have '
'any questions.')
else:
# TODO insert arg `split_naming=split_naming`
# when MNE releases 0.17
raw.save(bids_fname, overwrite=True)
raw.save(bids_fname, overwrite=True, split_naming='bids')

# CTF data is saved in a directory
elif ext == '.ds':
Expand Down
17 changes: 16 additions & 1 deletion mne_bids/tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# Authors: Stefan Appelhoff <stefan.appelhoff@mailbox.org>
#
# License: BSD (3-clause)
import os.path as op
import pytest

from mne.utils import _TempDir

from mne_bids.io import _parse_ext, _read_raw


Expand All @@ -25,5 +28,17 @@ def test_read_raw():
"""Test the raw reading."""
# Use a file ending that does not exist
f = 'file.bogus'
with pytest.raises(ValueError):
with pytest.raises(ValueError, match='file name extension must be one of'):
_read_raw(f)


def test_not_implemented():
"""Test the not yet implemented data formats raise an adequate error."""
for not_implemented_ext in ['.mef', '.nwb']:
data_path = _TempDir()
raw_fname = op.join(data_path, 'test' + not_implemented_ext)
with open(raw_fname, 'w'):
pass
with pytest.raises(ValueError, match=('there is no IO support for '
'this file format yet')):
_read_raw(raw_fname)
34 changes: 30 additions & 4 deletions mne_bids/tests/test_mne_bids.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@
shell = True


# MEG Tests
# ---------
def test_fif():
"""Test functionality of the write_raw_bids conversion for fif."""
output_path = _TempDir()
Expand Down Expand Up @@ -184,8 +182,6 @@ def test_bti():
run_subprocess(cmd, shell=shell)


# EEG Tests
# ---------
def test_vhdr():
"""Test write_raw_bids conversion for BrainVision data."""
output_path = _TempDir()
Expand All @@ -205,6 +201,16 @@ def test_vhdr():
overwrite=True)
assert len([f for f in os.listdir(data_path) if op.isfile(f)]) == 0

# Also cover iEEG
# We use the same data and pretend that eeg channels are ecog
raw.set_channel_types({raw.ch_names[i]: 'ecog'
for i in mne.pick_types(raw.info, eeg=True)})
output_path = _TempDir()
write_raw_bids(raw, bids_basename, output_path, overwrite=False)

cmd = ['bids-validator', '--bep010', output_path]
run_subprocess(cmd, shell=shell)


def test_edf():
"""Test write_raw_bids conversion for European Data Format data."""
Expand Down Expand Up @@ -244,6 +250,16 @@ def test_edf():
df = pd.read_csv(scans_tsv, sep='\t')
assert df.shape[0] == 2

# Also cover iEEG
# We use the same data and pretend that eeg channels are ecog
raw.set_channel_types({raw.ch_names[i]: 'ecog'
for i in mne.pick_types(raw.info, eeg=True)})
output_path = _TempDir()
write_raw_bids(raw, bids_basename, output_path)

cmd = ['bids-validator', '--bep010', output_path]
run_subprocess(cmd, shell=shell)


def test_bdf():
"""Test write_raw_bids conversion for Biosemi data."""
Expand Down Expand Up @@ -283,6 +299,16 @@ def test_set():
cmd = ['bids-validator', '--bep006', output_path]
run_subprocess(cmd, shell=shell)

# Also cover iEEG
# We use the same data and pretend that eeg channels are ecog
raw.set_channel_types({raw.ch_names[i]: 'ecog'
for i in mne.pick_types(raw.info, eeg=True)})
output_path = _TempDir()
write_raw_bids(raw, bids_basename, output_path)

cmd = ['bids-validator', '--bep010', output_path]
run_subprocess(cmd, shell=shell)
sappelhoff marked this conversation as resolved.
Show resolved Hide resolved


def test_cnt():
"""Test write_raw_bids conversion for Neuroscan data."""
Expand Down
32 changes: 31 additions & 1 deletion mne_bids/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,48 @@
from datetime import datetime

from scipy.io import savemat
from numpy.random import random
import mne
from mne.datasets import testing
from mne.utils import _TempDir

from mne_bids.utils import (make_bids_folders, make_bids_basename,
_check_types, print_dir_tree, _age_on_date,
_get_brainvision_paths, copyfile_brainvision,
copyfile_eeglab, _infer_eeg_placement_scheme)
copyfile_eeglab, _infer_eeg_placement_scheme,
_handle_kind)

base_path = op.join(op.dirname(mne.__file__), 'io')


def test_handle_kind():
jasmainak marked this conversation as resolved.
Show resolved Hide resolved
"""Test the automatic extraction of kind from the data."""
# Create a dummy raw
n_channels = 1
sampling_rate = 100
data = random((n_channels, sampling_rate))
channel_types = ['grad', 'eeg', 'ecog']
expected_kinds = ['meg', 'eeg', 'ieeg']
# do it once for each type ... and once for "no type"
for chtype, kind in zip(channel_types, expected_kinds):
info = mne.create_info(n_channels, sampling_rate, ch_types=[chtype])
raw = mne.io.RawArray(data, info)
assert _handle_kind(raw) == kind

# if the situation is ambiguous (EEG and iEEG channels both), raise error
with pytest.raises(ValueError, match='Both EEG and iEEG channels found'):
info = mne.create_info(2, sampling_rate,
ch_types=['eeg', 'ecog'])
raw = mne.io.RawArray(random((2, sampling_rate)), info)
_handle_kind(raw)

# if we cannot find a proper channel type, we raise an error
with pytest.raises(ValueError, match='Neither MEG/EEG/iEEG channels'):
info = mne.create_info(n_channels, sampling_rate, ch_types=['misc'])
raw = mne.io.RawArray(data, info)
_handle_kind(raw)


def test_print_dir_tree():
"""Test printing a dir tree."""
with pytest.raises(ValueError):
Expand Down
10 changes: 7 additions & 3 deletions mne_bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,14 @@ def _handle_kind(raw):
"""Get kind."""
if 'meg' in raw:
kind = 'meg'
elif 'eeg' in raw:
kind = 'eeg'
elif 'ecog' in raw:
elif 'ecog' in raw and 'eeg' not in raw:
sappelhoff marked this conversation as resolved.
Show resolved Hide resolved
kind = 'ieeg'
elif 'eeg' in raw and 'ecog' not in raw:
sappelhoff marked this conversation as resolved.
Show resolved Hide resolved
kind = 'eeg'
elif 'eeg' in raw and 'ecog' in raw:
raise ValueError('Both EEG and iEEG channels found in data.'
'There is currently no specification on how'
'to handle this data. Please proceed manually.')
else:
raise ValueError('Neither MEG/EEG/iEEG channels found in data.'
'Please use raw.set_channel_types to set the '
Expand Down