Skip to content

Commit

Permalink
MRG: Set channel type to misc if we cannot perform a proper conversio…
Browse files Browse the repository at this point in the history
…n from BIDS to MNE channel types (#1052)

* Set channel type to misc if we cannot perform a proper conversion from BIDS to MNE channel types

Previously, e.g. GSR and temperature channels could end up as "eeg" channels, for example.

Fixes #1048

* Fix test

* Fix NIRS
  • Loading branch information
hoechenberger committed Aug 19, 2022
1 parent 6ed57f5 commit d98128a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 9 deletions.
2 changes: 2 additions & 0 deletions doc/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ Detailed list of changes

- Internal helper function to :func:`~mne_bids.read_raw_bids` would reject BrainVision data if ``_scans.tsv`` listed a ``.eeg`` file instead of ``.vhdr``, by `Teon Brooks`_ (:gh:`1034`)

- Whenever :func:`~mne_bids.read_raw_bids` encounters a channel type that currently doesn't translate into an appropriate MNE channel type, the channel type will now be set to ``'misc``. Previously, seemingly arbitrary channel types would be applied, e.g. ``'eeg'`` for GSR and temperature channels, by `Richard Höchenberger`_ (:gh:`1052`)

:doc:`Find out what was new in previous releases <whats_new_previous_releases>`

.. include:: authors.rst
32 changes: 25 additions & 7 deletions mne_bids/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,12 +507,19 @@ def _handle_channels_reading(channels_fname, raw):
# Now we can do some work.
# The "type" column is mandatory in BIDS. We can use it to set channel
# types in the raw data using a mapping between channel types
channel_type_dict = dict()
channel_type_bids_mne_map = dict()

# Get the best mapping we currently have from BIDS to MNE nomenclature
bids_to_mne_ch_types = _get_ch_type_mapping(fro='bids', to='mne')
ch_types_json = channels_dict['type']
for ch_name, ch_type in zip(ch_names_tsv, ch_types_json):
# We don't map MEG channels for now, as there's no clear 1:1 mapping
# from BIDS to MNE coil types.
if ch_type.upper() in (
'MEGGRADAXIAL', 'MEGMAG', 'MEGREFGRADAXIAL', 'MEGGRADPLANAR',
'MEGREFMAG', 'MEGOTHER'
):
continue

# Try to map from BIDS nomenclature to MNE, leave channel type
# untouched if we are uncertain
Expand All @@ -530,8 +537,16 @@ def _handle_channels_reading(channels_fname, raw):
'will raise an error in the future.')
warn(msg)

if updated_ch_type is not None:
channel_type_dict[ch_name] = updated_ch_type
if updated_ch_type is None:
# We don't have an appropriate mapping, so make it a "misc" channel
channel_type_bids_mne_map[ch_name] = 'misc'
warn(
f'No BIDS -> MNE mapping found for channel type "{ch_type}". '
f'Type of channel "{ch_name}" will be set to "misc".'
)
else:
# We found a mapping, so use it
channel_type_bids_mne_map[ch_name] = updated_ch_type

# Special handling for (synthesized) stimulus channel
synthesized_stim_ch_name = 'STI 014'
Expand All @@ -556,16 +571,19 @@ def _handle_channels_reading(channels_fname, raw):
raw.rename_channels({raw_ch_name: bids_ch_name})

# Set the channel types in the raw data according to channels.tsv
ch_type_map_avail = {
channel_type_bids_mne_map_available_channels = {
ch_name: ch_type
for ch_name, ch_type in channel_type_dict.items()
for ch_name, ch_type in channel_type_bids_mne_map.items()
if ch_name in raw.ch_names
}
ch_diff = set(channel_type_dict.keys()) - set(ch_type_map_avail.keys())
ch_diff = (
set(channel_type_bids_mne_map.keys()) -
set(channel_type_bids_mne_map_available_channels.keys())
)
if ch_diff:
warn(f'Cannot set channel type for the following channels, as they '
f'are missing in the raw data: {", ".join(sorted(ch_diff))}')
raw.set_channel_types(ch_type_map_avail)
raw.set_channel_types(channel_type_bids_mne_map_available_channels)

# Set bad channels based on _channels.tsv sidecar
if 'status' in channels_dict:
Expand Down
30 changes: 30 additions & 0 deletions mne_bids/tests/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,36 @@ def test_handle_channel_type_casing(tmp_path):
read_raw_bids(bids_path)


@pytest.mark.filterwarnings(warning_str['channel_unit_changed'])
def test_handle_non_mne_channel_type(tmp_path):
"""Test that channel types not known to MNE will be read as 'misc'."""
bids_path = _bids_path.copy().update(root=tmp_path)
raw = _read_raw_fif(raw_fname, verbose=False)

write_raw_bids(raw, bids_path, overwrite=True,
verbose=False)

channels_tsv_path = bids_path.copy().update(
root=tmp_path,
datatype='meg',
suffix='channels',
extension='.tsv'
).fpath

channels_data = _from_tsv(channels_tsv_path)
# Violates BIDS, but ensures we won't have an appropriate
# BIDS -> MNE mapping.
ch_idx = -1
channels_data['type'][ch_idx] = 'FOOBAR'
_to_tsv(data=channels_data, fname=channels_tsv_path)

with pytest.warns(RuntimeWarning, match='will be set to \"misc\"'):
raw = read_raw_bids(bids_path)

# Should be a 'misc' channel.
assert raw.get_channel_types([channels_data['name'][ch_idx]]) == ['misc']


@pytest.mark.filterwarnings(warning_str['channel_unit_changed'])
def test_bads_reading(tmp_path):
bids_path = _bids_path.copy().update(root=tmp_path, datatype='meg')
Expand Down
8 changes: 6 additions & 2 deletions mne_bids/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def _get_ch_type_mapping(fro='mne', to='bids'):
mapping = dict(eeg='EEG', misc='MISC', stim='TRIG', emg='EMG',
ecog='ECOG', seeg='SEEG', eog='EOG', ecg='ECG',
resp='RESP', bio='MISC', dbs='DBS',
# NIRS
fnirs_cw_amplitude='NIRSCWAMPLITUDE',
# MEG channels
meggradaxial='MEGGRADAXIAL', megmag='MEGMAG',
Expand All @@ -77,8 +78,11 @@ def _get_ch_type_mapping(fro='mne', to='bids'):
elif fro == 'bids' and to == 'mne':
mapping = dict(EEG='eeg', MISC='misc', TRIG='stim', EMG='emg',
ECOG='ecog', SEEG='seeg', EOG='eog', ECG='ecg',
RESP='resp', NIRS='fnirs_cw_amplitude',
# No MEG channels for now
RESP='resp',
# NIRS
NIRSCWAMPLITUDE='fnirs_cw_amplitude',
NIRS='fnirs_cw_amplitude',
# No MEG channels for now (see Notes above)
# Many to one mapping
VEOG='eog', HEOG='eog', DBS='dbs')
else:
Expand Down

0 comments on commit d98128a

Please sign in to comment.