From 53f1b859d2bea2a432e153a02c386b428c71d566 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20H=C3=B6chenberger?= Date: Thu, 18 Feb 2021 16:28:42 +0100 Subject: [PATCH] =?UTF-8?q?MRG:=20selection.read=5Fselection()=20=E2=9E=A1?= =?UTF-8?q?=EF=B8=8F=20channels.read=5Fvectorview=5Fselection()=20(#8870)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move read_selection() to channels.read_neuromag_selection() Fixes #8865 * Fix Sphinx warning * read_neuromag_selection -> read_vectorview_selection * Use pytest context manager * Add API reference * Fix changelog * Update docstring * Update mne/channels/channels.py Co-authored-by: Eric Larson * Docstring, import * fix changelog [skip azp][skip github] Co-authored-by: Eric Larson --- doc/changes/0.12.inc | 2 +- doc/changes/latest.inc | 5 +- doc/sensor_space.rst | 2 +- examples/inverse/plot_tf_dics.py | 2 +- mne/__init__.py | 8 +- mne/beamformer/tests/test_lcmv.py | 4 +- mne/channels/__init__.py | 5 +- mne/channels/channels.py | 168 +++++++++++++++- mne/selection.py | 189 ------------------ ...n.py => test_read_vectorview_selection.py} | 25 +-- mne/utils/docs.py | 2 +- mne/utils/tests/test_check.py | 4 +- mne/viz/raw.py | 6 +- mne/viz/utils.py | 10 +- tutorials/simulation/plot_dics.py | 3 +- ...stats_cluster_1samp_test_time_frequency.py | 2 +- 16 files changed, 213 insertions(+), 224 deletions(-) delete mode 100644 mne/selection.py rename mne/tests/{test_selection.py => test_read_vectorview_selection.py} (63%) diff --git a/doc/changes/0.12.inc b/doc/changes/0.12.inc index b788cbc4a6d..39b48393d92 100644 --- a/doc/changes/0.12.inc +++ b/doc/changes/0.12.inc @@ -50,7 +50,7 @@ Changelog - Add epoch rejection based on annotated segments by `Jaakko Leppakangas`_ -- Add option to use new-style MEG channel names in :func:`mne.read_selection` by `Eric Larson`_ +- Add option to use new-style MEG channel names in ``mne.read_selection`` by `Eric Larson`_ - Add option for ``proj`` in :class:`mne.EpochsArray` by `Eric Larson`_ diff --git a/doc/changes/latest.inc b/doc/changes/latest.inc index 77dfa5ba04c..51d8ce002ec 100644 --- a/doc/changes/latest.inc +++ b/doc/changes/latest.inc @@ -32,6 +32,8 @@ Enhancements - Add :func:`mne.chpi.extract_chpi_locs_kit` to read cHPI coil locations from KIT/Yokogawa data (:gh:`` **by new contributor** |Matt Sanderson|_, `Robert Seymour`_, and `Eric Larson`_) +- Add ``match_alias`` parameter to :meth:`mne.io.Raw.set_montage` and related functions to match unrecognized channel location names to known aliases (:gh`8799` **by new contributor** |Zhi Zhang|_) + - Update the ``notebook`` 3d backend to use ``ipyvtk_simple`` for a better integration within ``Jupyter`` (:gh:`8503` by `Guillaume Favelier`_) - Add toggle-all button to :class:`mne.Report` HTML and ``width`` argument to :meth:`mne.Report.add_bem_to_section` (:gh:`8723` by `Eric Larson`_) @@ -144,5 +146,4 @@ Bugs API changes ~~~~~~~~~~~ - -- Add ``match_alias`` parameter to :meth:`mne.io.Raw.set_montage` and related functions to match unrecognized channel location names to known aliases (:gh`8799` **by new contributor** |Zhi Zhang|_) +- ``mne.read_selection`` has been deprecated in favor of `mne.read_vectorview_selection`. ``mne.read_selection`` will be removed in MNE-Python 0.24 (:gh:`8870` by `Richard Höchenberger`_) diff --git a/doc/sensor_space.rst b/doc/sensor_space.rst index 90550b99841..a1c72b3aa59 100644 --- a/doc/sensor_space.rst +++ b/doc/sensor_space.rst @@ -20,7 +20,7 @@ Sensor Space Data pick_info read_epochs read_reject_parameters - read_selection + read_vectorview_selection rename_channels :py:mod:`mne.baseline`: diff --git a/examples/inverse/plot_tf_dics.py b/examples/inverse/plot_tf_dics.py index c9d84624cf6..532bfeeaee5 100644 --- a/examples/inverse/plot_tf_dics.py +++ b/examples/inverse/plot_tf_dics.py @@ -36,7 +36,7 @@ # Pick a selection of magnetometer channels. A subset of all channels was used # to speed up the example. For a solution based on all MEG channels use # meg=True, selection=None and add mag=4e-12 to the reject dictionary. -left_temporal_channels = mne.read_selection('Left-temporal') +left_temporal_channels = mne.read_vectorview_selection('Left-temporal') picks = mne.pick_types(raw.info, meg='mag', eeg=False, eog=False, stim=False, exclude='bads', selection=left_temporal_channels) diff --git a/mne/__init__.py b/mne/__init__.py index 839517c8ee2..85b49a5f7dd 100644 --- a/mne/__init__.py +++ b/mne/__init__.py @@ -89,9 +89,9 @@ transform_surface_to, Transform) from .proj import (read_proj, write_proj, compute_proj_epochs, compute_proj_evoked, compute_proj_raw, sensitivity_map) -from .selection import read_selection from .dipole import read_dipole, Dipole, DipoleFixed, fit_dipole -from .channels import equalize_channels, rename_channels, find_layout +from .channels import (equalize_channels, rename_channels, find_layout, + read_vectorview_selection) from .report import Report, open_report from .io import read_epochs_fieldtrip, read_evoked_fieldtrip, read_evokeds_mff @@ -122,6 +122,10 @@ from . import viz from . import decoding +# deprecations +from .utils import deprecated_alias +deprecated_alias('read_selection', read_vectorview_selection) + # initialize logging set_log_level(None, False) set_log_file() diff --git a/mne/beamformer/tests/test_lcmv.py b/mne/beamformer/tests/test_lcmv.py index 774bcda04e5..942f77be362 100644 --- a/mne/beamformer/tests/test_lcmv.py +++ b/mne/beamformer/tests/test_lcmv.py @@ -12,7 +12,7 @@ from mne.transforms import apply_trans, invert_transform from mne import (convert_forward_solution, read_forward_solution, compute_rank, VolVectorSourceEstimate, VolSourceEstimate, EvokedArray, - pick_channels_cov) + pick_channels_cov, read_vectorview_selection) from mne.beamformer import (make_lcmv, apply_lcmv, apply_lcmv_epochs, apply_lcmv_raw, Beamformer, read_beamformer, apply_lcmv_cov, make_dics) @@ -70,7 +70,7 @@ def _get_data(tmin=-0.1, tmax=0.15, all_forward=True, epochs=True, # Setup for reading the raw data raw.info['bads'] = ['MEG 2443', 'EEG 053'] # 2 bad channels # Set up pick list: MEG - bad channels - left_temporal_channels = mne.read_selection('Left-temporal') + left_temporal_channels = read_vectorview_selection('Left-temporal') picks = mne.pick_types(raw.info, meg=True, selection=left_temporal_channels) picks = picks[::2] # decimate for speed diff --git a/mne/channels/__init__.py b/mne/channels/__init__.py index 6455e11801c..3593c52ed90 100644 --- a/mne/channels/__init__.py +++ b/mne/channels/__init__.py @@ -15,7 +15,9 @@ compute_native_head_t) from .channels import (equalize_channels, rename_channels, fix_mag_coil_types, read_ch_adjacency, _get_ch_type, find_ch_adjacency, - make_1020_channel_selections, combine_channels) + make_1020_channel_selections, combine_channels, + read_vectorview_selection, _SELECTIONS, _EEG_SELECTIONS, + _divide_to_regions) __all__ = [ # Data Structures @@ -35,6 +37,7 @@ 'rename_channels', 'make_1020_channel_selections', '_get_ch_type', 'equalize_channels', 'find_ch_adjacency', 'find_layout', 'fix_mag_coil_types', 'generate_2d_layout', 'get_builtin_montages', + 'combine_channels', 'read_vectorview_selection', # Other 'compute_dev_head_t', 'compute_native_head_t', diff --git a/mne/channels/channels.py b/mne/channels/channels.py index a98b5bc1585..8a25b868e3d 100644 --- a/mne/channels/channels.py +++ b/mne/channels/channels.py @@ -1,5 +1,6 @@ # Authors: Alexandre Gramfort # Matti Hämäläinen +# Martin Luessi # Denis Engemann # Andrew Dykstra # Teon Brooks @@ -7,6 +8,7 @@ # # License: BSD (3-clause) + import os import os.path as op import sys @@ -19,14 +21,16 @@ from ..defaults import HEAD_SIZE_DEFAULT, _handle_default from ..transforms import _frame_to_str from ..utils import (verbose, logger, warn, - _check_preload, _validate_type, fill_doc, _check_option) + _check_preload, _validate_type, fill_doc, _check_option, + _get_stim_channel, _check_fname) from ..io.compensator import get_current_comp from ..io.constants import FIFF from ..io.meas_info import anonymize_info, Info, MontageMixin, create_info from ..io.pick import (channel_type, pick_info, pick_types, _picks_by_type, _check_excludes_includes, _contains_ch_type, channel_indices_by_type, pick_channels, _picks_to_idx, - _get_channel_types, get_channel_type_constants) + _get_channel_types, get_channel_type_constants, + _pick_data_channels) from ..io.write import DATE_NONE from ..io._digitization import _get_data_as_dict_from_dig @@ -1762,3 +1766,163 @@ def combine_channels(inst, groups, method='mean', keep_stim=False, verbose=inst.verbose) return combined_inst + + +# NeuroMag channel groupings +_SELECTIONS = ['Vertex', 'Left-temporal', 'Right-temporal', 'Left-parietal', + 'Right-parietal', 'Left-occipital', 'Right-occipital', + 'Left-frontal', 'Right-frontal'] +_EEG_SELECTIONS = ['EEG 1-32', 'EEG 33-64', 'EEG 65-96', 'EEG 97-128'] + + +def _divide_to_regions(info, add_stim=True): + """Divide channels to regions by positions.""" + from scipy.stats import zscore + picks = _pick_data_channels(info, exclude=[]) + chs_in_lobe = len(picks) // 4 + pos = np.array([ch['loc'][:3] for ch in info['chs']]) + x, y, z = pos.T + + frontal = picks[np.argsort(y[picks])[-chs_in_lobe:]] + picks = np.setdiff1d(picks, frontal) + + occipital = picks[np.argsort(y[picks])[:chs_in_lobe]] + picks = np.setdiff1d(picks, occipital) + + temporal = picks[np.argsort(z[picks])[:chs_in_lobe]] + picks = np.setdiff1d(picks, temporal) + + lt, rt = _divide_side(temporal, x) + lf, rf = _divide_side(frontal, x) + lo, ro = _divide_side(occipital, x) + lp, rp = _divide_side(picks, x) # Parietal lobe from the remaining picks. + + # Because of the way the sides are divided, there may be outliers in the + # temporal lobes. Here we switch the sides for these outliers. For other + # lobes it is not a big problem because of the vicinity of the lobes. + with np.errstate(invalid='ignore'): # invalid division, greater compare + zs = np.abs(zscore(x[rt])) + outliers = np.array(rt)[np.where(zs > 2.)[0]] + rt = list(np.setdiff1d(rt, outliers)) + + with np.errstate(invalid='ignore'): # invalid division, greater compare + zs = np.abs(zscore(x[lt])) + outliers = np.append(outliers, (np.array(lt)[np.where(zs > 2.)[0]])) + lt = list(np.setdiff1d(lt, outliers)) + + l_mean = np.mean(x[lt]) + r_mean = np.mean(x[rt]) + for outlier in outliers: + if abs(l_mean - x[outlier]) < abs(r_mean - x[outlier]): + lt.append(outlier) + else: + rt.append(outlier) + + if add_stim: + stim_ch = _get_stim_channel(None, info, raise_error=False) + if len(stim_ch) > 0: + for region in [lf, rf, lo, ro, lp, rp, lt, rt]: + region.append(info['ch_names'].index(stim_ch[0])) + return OrderedDict([('Left-frontal', lf), ('Right-frontal', rf), + ('Left-parietal', lp), ('Right-parietal', rp), + ('Left-occipital', lo), ('Right-occipital', ro), + ('Left-temporal', lt), ('Right-temporal', rt)]) + + +def _divide_side(lobe, x): + """Make a separation between left and right lobe evenly.""" + lobe = np.asarray(lobe) + median = np.median(x[lobe]) + + left = lobe[np.where(x[lobe] < median)[0]] + right = lobe[np.where(x[lobe] > median)[0]] + medians = np.where(x[lobe] == median)[0] + + left = np.sort(np.concatenate([left, lobe[medians[1::2]]])) + right = np.sort(np.concatenate([right, lobe[medians[::2]]])) + return list(left), list(right) + + +@verbose +def read_vectorview_selection(name, fname=None, info=None, verbose=None): + """Read Neuromag Vector View channel selection from a file. + + Parameters + ---------- + name : str | list of str + Name of the selection. If a list, the selections are combined. + Supported selections are: ``'Vertex'``, ``'Left-temporal'``, + ``'Right-temporal'``, ``'Left-parietal'``, ``'Right-parietal'``, + ``'Left-occipital'``, ``'Right-occipital'``, ``'Left-frontal'`` and + ``'Right-frontal'``. Selections can also be matched and combined by + spcecifying common substrings. For example, ``name='temporal`` will + produce a combination of ``'Left-temporal'`` and ``'Right-temporal'``. + fname : str + Filename of the selection file (if ``None``, built-in selections are + used). + info : instance of Info + Measurement info file, which will be used to determine the spacing + of channel names to return, e.g. ``'MEG 0111'`` for old Neuromag + systems and ``'MEG0111'`` for new ones. + %(verbose)s + + Returns + ------- + sel : list of str + List with channel names in the selection. + """ + # convert name to list of string + if not isinstance(name, (list, tuple)): + name = [name] + if isinstance(info, Info): + picks = pick_types(info, meg=True, exclude=()) + if len(picks) > 0 and ' ' not in info['ch_names'][picks[0]]: + spacing = 'new' + else: + spacing = 'old' + elif info is not None: + raise TypeError('info must be an instance of Info or None, not %s' + % (type(info),)) + else: # info is None + spacing = 'old' + + # use built-in selections by default + if fname is None: + fname = op.join(op.dirname(__file__), '..', 'data', 'mne_analyze.sel') + + fname = _check_fname(fname, must_exist=True, overwrite='read') + + # use this to make sure we find at least one match for each name + name_found = {n: False for n in name} + with open(fname, 'r') as fid: + sel = [] + for line in fid: + line = line.strip() + # skip blank lines and comments + if len(line) == 0 or line[0] == '#': + continue + # get the name of the selection in the file + pos = line.find(':') + if pos < 0: + logger.info('":" delimiter not found in selections file, ' + 'skipping line') + continue + sel_name_file = line[:pos] + # search for substring match with name provided + for n in name: + if sel_name_file.find(n) >= 0: + sel.extend(line[pos + 1:].split('|')) + name_found[n] = True + break + + # make sure we found at least one match for each name + for n, found in name_found.items(): + if not found: + raise ValueError('No match for selection name "%s" found' % n) + + # make the selection a sorted list with unique elements + sel = list(set(sel)) + sel.sort() + if spacing == 'new': # "new" or "old" by now, "old" is default + sel = [s.replace('MEG ', 'MEG') for s in sel] + return sel diff --git a/mne/selection.py b/mne/selection.py deleted file mode 100644 index cf6dbafe431..00000000000 --- a/mne/selection.py +++ /dev/null @@ -1,189 +0,0 @@ -# Authors: Alexandre Gramfort -# Matti Hämäläinen -# Martin Luessi -# -# License: BSD (3-clause) - -from os import path -from collections import OrderedDict - -import numpy as np - -from .io.meas_info import Info -from .io.pick import _pick_data_channels, pick_types -from .utils import logger, verbose, _get_stim_channel - -_SELECTIONS = ['Vertex', 'Left-temporal', 'Right-temporal', 'Left-parietal', - 'Right-parietal', 'Left-occipital', 'Right-occipital', - 'Left-frontal', 'Right-frontal'] -_EEG_SELECTIONS = ['EEG 1-32', 'EEG 33-64', 'EEG 65-96', 'EEG 97-128'] - - -@verbose -def read_selection(name, fname=None, info=None, verbose=None): - """Read channel selection from file. - - By default, the selections used in ``mne_browse_raw`` are supported. - Additional selections can be added by specifying a selection file (e.g. - produced using ``mne_browse_raw``) using the ``fname`` parameter. - - The ``name`` parameter can be a string or a list of string. The returned - selection will be the combination of all selections in the file where - (at least) one element in name is a substring of the selection name in - the file. For example, ``name=['temporal', 'Right-frontal']`` will produce - a combination of ``'Left-temporal'``, ``'Right-temporal'``, and - ``'Right-frontal'``. - - The included selections are: - - * ``'Vertex'`` - * ``'Left-temporal'`` - * ``'Right-temporal'`` - * ``'Left-parietal'`` - * ``'Right-parietal'`` - * ``'Left-occipital'`` - * ``'Right-occipital'`` - * ``'Left-frontal'`` - * ``'Right-frontal'`` - - Parameters - ---------- - name : str or list of str - Name of the selection. If is a list, the selections are combined. - fname : str - Filename of the selection file (if None, built-in selections are used). - info : instance of Info - Measurement info file, which will be used to determine the spacing - of channel names to return, e.g. ``'MEG 0111'`` for old Neuromag - systems and ``'MEG0111'`` for new ones. - %(verbose)s - - Returns - ------- - sel : list of string - List with channel names in the selection. - """ - # convert name to list of string - if not isinstance(name, (list, tuple)): - name = [name] - if isinstance(info, Info): - picks = pick_types(info, meg=True, exclude=()) - if len(picks) > 0 and ' ' not in info['ch_names'][picks[0]]: - spacing = 'new' - else: - spacing = 'old' - elif info is not None: - raise TypeError('info must be an instance of Info or None, not %s' - % (type(info),)) - else: # info is None - spacing = 'old' - - # use built-in selections by default - if fname is None: - fname = path.join(path.dirname(__file__), 'data', 'mne_analyze.sel') - - if not path.isfile(fname): - raise ValueError('The file %s does not exist.' % fname) - - # use this to make sure we find at least one match for each name - name_found = {n: False for n in name} - with open(fname, 'r') as fid: - sel = [] - for line in fid: - line = line.strip() - # skip blank lines and comments - if len(line) == 0 or line[0] == '#': - continue - # get the name of the selection in the file - pos = line.find(':') - if pos < 0: - logger.info('":" delimiter not found in selections file, ' - 'skipping line') - continue - sel_name_file = line[:pos] - # search for substring match with name provided - for n in name: - if sel_name_file.find(n) >= 0: - sel.extend(line[pos + 1:].split('|')) - name_found[n] = True - break - - # make sure we found at least one match for each name - for n, found in name_found.items(): - if not found: - raise ValueError('No match for selection name "%s" found' % n) - - # make the selection a sorted list with unique elements - sel = list(set(sel)) - sel.sort() - if spacing == 'new': # "new" or "old" by now, "old" is default - sel = [s.replace('MEG ', 'MEG') for s in sel] - return sel - - -def _divide_to_regions(info, add_stim=True): - """Divide channels to regions by positions.""" - from scipy.stats import zscore - picks = _pick_data_channels(info, exclude=[]) - chs_in_lobe = len(picks) // 4 - pos = np.array([ch['loc'][:3] for ch in info['chs']]) - x, y, z = pos.T - - frontal = picks[np.argsort(y[picks])[-chs_in_lobe:]] - picks = np.setdiff1d(picks, frontal) - - occipital = picks[np.argsort(y[picks])[:chs_in_lobe]] - picks = np.setdiff1d(picks, occipital) - - temporal = picks[np.argsort(z[picks])[:chs_in_lobe]] - picks = np.setdiff1d(picks, temporal) - - lt, rt = _divide_side(temporal, x) - lf, rf = _divide_side(frontal, x) - lo, ro = _divide_side(occipital, x) - lp, rp = _divide_side(picks, x) # Parietal lobe from the remaining picks. - - # Because of the way the sides are divided, there may be outliers in the - # temporal lobes. Here we switch the sides for these outliers. For other - # lobes it is not a big problem because of the vicinity of the lobes. - with np.errstate(invalid='ignore'): # invalid division, greater compare - zs = np.abs(zscore(x[rt])) - outliers = np.array(rt)[np.where(zs > 2.)[0]] - rt = list(np.setdiff1d(rt, outliers)) - - with np.errstate(invalid='ignore'): # invalid division, greater compare - zs = np.abs(zscore(x[lt])) - outliers = np.append(outliers, (np.array(lt)[np.where(zs > 2.)[0]])) - lt = list(np.setdiff1d(lt, outliers)) - - l_mean = np.mean(x[lt]) - r_mean = np.mean(x[rt]) - for outlier in outliers: - if abs(l_mean - x[outlier]) < abs(r_mean - x[outlier]): - lt.append(outlier) - else: - rt.append(outlier) - - if add_stim: - stim_ch = _get_stim_channel(None, info, raise_error=False) - if len(stim_ch) > 0: - for region in [lf, rf, lo, ro, lp, rp, lt, rt]: - region.append(info['ch_names'].index(stim_ch[0])) - return OrderedDict([('Left-frontal', lf), ('Right-frontal', rf), - ('Left-parietal', lp), ('Right-parietal', rp), - ('Left-occipital', lo), ('Right-occipital', ro), - ('Left-temporal', lt), ('Right-temporal', rt)]) - - -def _divide_side(lobe, x): - """Make a separation between left and right lobe evenly.""" - lobe = np.asarray(lobe) - median = np.median(x[lobe]) - - left = lobe[np.where(x[lobe] < median)[0]] - right = lobe[np.where(x[lobe] > median)[0]] - medians = np.where(x[lobe] == median)[0] - - left = np.sort(np.concatenate([left, lobe[medians[1::2]]])) - right = np.sort(np.concatenate([right, lobe[medians[::2]]])) - return list(left), list(right) diff --git a/mne/tests/test_selection.py b/mne/tests/test_read_vectorview_selection.py similarity index 63% rename from mne/tests/test_selection.py rename to mne/tests/test_read_vectorview_selection.py index fef9a8e0bce..b122285bac8 100644 --- a/mne/tests/test_selection.py +++ b/mne/tests/test_read_vectorview_selection.py @@ -2,7 +2,7 @@ import pytest -from mne import read_selection +from mne import read_vectorview_selection from mne.io import read_raw_fif from mne.utils import run_tests_if_main @@ -11,8 +11,8 @@ raw_new_fname = op.join(test_path, 'test_chpi_raw_sss.fif') -def test_read_selection(): - """Test reading of selections.""" +def test_read_vectorview_selection(): + """Test reading of Neuromag Vector View channel selections.""" # test one channel for each selection ch_names = ['MEG 2211', 'MEG 0223', 'MEG 1312', 'MEG 0412', 'MEG 1043', 'MEG 2042', 'MEG 2032', 'MEG 0522', 'MEG 1031'] @@ -22,30 +22,31 @@ def test_read_selection(): raw = read_raw_fif(raw_fname) for i, name in enumerate(sel_names): - sel = read_selection(name) + sel = read_vectorview_selection(name) assert ch_names[i] in sel - sel_info = read_selection(name, info=raw.info) + sel_info = read_vectorview_selection(name, info=raw.info) assert sel == sel_info # test some combinations - all_ch = read_selection(['L', 'R']) - left = read_selection('L') - right = read_selection('R') + all_ch = read_vectorview_selection(['L', 'R']) + left = read_vectorview_selection('L') + right = read_vectorview_selection('R') assert len(all_ch) == len(left) + len(right) assert len(set(left).intersection(set(right))) == 0 - frontal = read_selection('frontal') - occipital = read_selection('Right-occipital') + frontal = read_vectorview_selection('frontal') + occipital = read_vectorview_selection('Right-occipital') assert len(set(frontal).intersection(set(occipital))) == 0 ch_names_new = [ch.replace(' ', '') for ch in ch_names] raw_new = read_raw_fif(raw_new_fname) for i, name in enumerate(sel_names): - sel = read_selection(name, info=raw_new.info) + sel = read_vectorview_selection(name, info=raw_new.info) assert ch_names_new[i] in sel - pytest.raises(TypeError, read_selection, name, info='foo') + with pytest.raises(TypeError, match='must be an instance of Info or None'): + read_vectorview_selection(name, info='foo') run_tests_if_main() diff --git a/mne/utils/docs.py b/mne/utils/docs.py index bfb8bb1df60..5baf4be42c1 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -2583,5 +2583,5 @@ def deprecated_alias(dep_name, func, removed_in=None): # Inject a deprecated version into the namespace inspect.currentframe().f_back.f_globals[dep_name] = deprecated( f'{dep_name} has been deprecated in favor of {func.__name__} and will ' - f'be removed in {removed_in}' + f'be removed in {removed_in}.' )(deepcopy(func)) diff --git a/mne/utils/tests/test_check.py b/mne/utils/tests/test_check.py index 9e6fa393ef8..225cb724a0d 100644 --- a/mne/utils/tests/test_check.py +++ b/mne/utils/tests/test_check.py @@ -13,6 +13,7 @@ from pathlib import Path import mne +from mne import read_vectorview_selection from mne.datasets import testing from mne.io.pick import pick_channels_cov from mne.utils import (check_random_state, _check_fname, check_fname, @@ -20,6 +21,7 @@ _check_mayavi_version, _check_info_inv, _check_option, check_version, _check_path_like, _validate_type, _suggest, _on_missing, requires_nibabel, _safe_input) + data_path = testing.data_path(download=False) base_dir = op.join(data_path, 'MEG', 'sample') fname_raw = op.join(data_path, 'MEG', 'sample', 'sample_audvis_trunc_raw.fif') @@ -83,7 +85,7 @@ def _get_data(): event_id, tmin, tmax = 1, -0.1, 0.15 # decimate for speed - left_temporal_channels = mne.read_selection('Left-temporal') + left_temporal_channels = read_vectorview_selection('Left-temporal') picks = mne.pick_types(raw.info, meg=True, selection=left_temporal_channels) picks = picks[::2] diff --git a/mne/viz/raw.py b/mne/viz/raw.py index 630a8629593..2fde2ab48bc 100644 --- a/mne/viz/raw.py +++ b/mne/viz/raw.py @@ -535,8 +535,8 @@ def plot_raw_psd_topo(raw, tmin=0., tmax=None, fmin=0., fmax=100., proj=False, def _setup_channel_selections(raw, kind, order): """Get dictionary of channel groupings.""" - from ..selection import (read_selection, _SELECTIONS, _EEG_SELECTIONS, - _divide_to_regions) + from ..channels import (read_vectorview_selection, _SELECTIONS, + _EEG_SELECTIONS, _divide_to_regions) from ..utils import _get_stim_channel _check_option('group_by', kind, ('position', 'selection')) if kind == 'position': @@ -557,7 +557,7 @@ def _setup_channel_selections(raw, kind, order): # loop over regions keys = np.concatenate([_SELECTIONS, _EEG_SELECTIONS]) for key in keys: - channels = read_selection(key, info=raw.info) + channels = read_vectorview_selection(key, info=raw.info) picks = pick_channels(raw.ch_names, channels) picks = np.intersect1d(picks, order) if not len(picks): diff --git a/mne/viz/utils.py b/mne/viz/utils.py index 1bb642f37bd..0157f41a4d8 100644 --- a/mne/viz/utils.py +++ b/mne/viz/utils.py @@ -38,9 +38,6 @@ from ..io.proj import setup_proj from ..utils import (verbose, get_config, warn, _check_ch_locs, _check_option, logger, fill_doc, _pl, _check_sphere, _ensure_int) - -from ..selection import (read_selection, _SELECTIONS, _EEG_SELECTIONS, - _divide_to_regions) from ..transforms import apply_trans @@ -926,6 +923,10 @@ def plot_sensors(info, kind='topomap', ch_type=None, title=None, for i, pick in enumerate(picks)] else: if ch_groups in ['position', 'selection']: + # Avoid circular import + from ..channels import (read_vectorview_selection, _SELECTIONS, + _EEG_SELECTIONS, _divide_to_regions) + if ch_groups == 'position': ch_groups = _divide_to_regions(info, add_stim=False) ch_groups = list(ch_groups.values()) @@ -933,7 +934,8 @@ def plot_sensors(info, kind='topomap', ch_type=None, title=None, ch_groups, color_vals = list(), list() for selection in _SELECTIONS + _EEG_SELECTIONS: channels = pick_channels( - info['ch_names'], read_selection(selection, info=info)) + info['ch_names'], + read_vectorview_selection(selection, info=info)) ch_groups.append(channels) color_vals = np.ones((len(ch_groups), 4)) for idx, ch_group in enumerate(ch_groups): diff --git a/tutorials/simulation/plot_dics.py b/tutorials/simulation/plot_dics.py index 387c8460a0c..330ccffae52 100644 --- a/tutorials/simulation/plot_dics.py +++ b/tutorials/simulation/plot_dics.py @@ -187,7 +187,8 @@ def coh_signal_gen(): # Plot some of the channels of the simulated data that are situated above one # of our simulated sources. -picks = mne.pick_channels(epochs.ch_names, mne.read_selection('Left-frontal')) +picks = mne.pick_channels(epochs.ch_names, + mne.read_vectorview_selection('Left-frontal')) epochs.plot(picks=picks) ############################################################################### diff --git a/tutorials/stats-sensor-space/plot_stats_cluster_1samp_test_time_frequency.py b/tutorials/stats-sensor-space/plot_stats_cluster_1samp_test_time_frequency.py index a82887101de..8b59f35687a 100644 --- a/tutorials/stats-sensor-space/plot_stats_cluster_1samp_test_time_frequency.py +++ b/tutorials/stats-sensor-space/plot_stats_cluster_1samp_test_time_frequency.py @@ -54,7 +54,7 @@ baseline=(None, 0), preload=True, reject=dict(grad=4000e-13, eog=150e-6)) # just use right temporal sensors for speed -epochs.pick_channels(mne.read_selection('Right-temporal')) +epochs.pick_channels(mne.read_vectorview_selection('Right-temporal')) evoked = epochs.average() # Factor to down-sample the temporal dimension of the TFR computed by