Skip to content

Commit

Permalink
API: Deprecate ordered=False and legacy some functions (mne-tools#11665)
Browse files Browse the repository at this point in the history
  • Loading branch information
larsoner authored May 1, 2023
1 parent ccdbbdc commit daec3da
Show file tree
Hide file tree
Showing 41 changed files with 238 additions and 220 deletions.
37 changes: 6 additions & 31 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,30 +88,8 @@ jobs:
- run:
name: Set BASH_ENV
command: |
set -e
set -o pipefail
./tools/setup_xvfb.sh
sudo apt install -qq graphviz optipng python3.10-venv python3-venv libxft2 ffmpeg
python3.10 -m venv ~/python_env
echo "set -e" >> $BASH_ENV
echo "export OPENBLAS_NUM_THREADS=4" >> $BASH_ENV
echo "export XDG_RUNTIME_DIR=/tmp/runtime-circleci" >> $BASH_ENV
echo "export MNE_FULL_DATE=true" >> $BASH_ENV
source tools/get_minimal_commands.sh
echo "export MNE_3D_BACKEND=pyvistaqt" >> $BASH_ENV
echo "export MNE_3D_OPTION_MULTI_SAMPLES=1" >> $BASH_ENV
echo "export MNE_BROWSER_BACKEND=qt" >> $BASH_ENV
echo "export MNE_BROWSER_PRECOMPUTE=false" >> $BASH_ENV
echo "export PATH=~/.local/bin/:$PATH" >> $BASH_ENV
echo "export DISPLAY=:99" >> $BASH_ENV
echo "source ~/python_env/bin/activate" >> $BASH_ENV
mkdir -p ~/.local/bin
ln -s ~/python_env/bin/python ~/.local/bin/python
echo "BASH_ENV:"
cat $BASH_ENV
mkdir -p ~/mne_data
touch pattern.txt
command: ./tools/circleci_bash_env.sh

- run:
name: check neuromag2ft
command: |
Expand Down Expand Up @@ -398,18 +376,12 @@ jobs:
type: string
default: "false"
docker:
- image: circleci/python:3.9.2-buster
- image: cimg/base:current-22.04
steps:
- restore_cache:
keys:
- source-cache
- checkout
- run:
name: Set BASH_ENV
command: |
set -e
echo "set -e" >> $BASH_ENV
echo "export PATH=~/.local/bin/:$PATH" >> $BASH_ENV
- run:
name: Check-skip
command: |
Expand All @@ -418,6 +390,9 @@ jobs:
echo "Skip detected, exiting job ${CIRCLE_JOB}."
circleci-agent step halt;
fi
- run:
name: Set BASH_ENV
command: ./tools/circleci_bash_env.sh
- restore_cache:
keys:
- pip-cache
Expand Down
11 changes: 7 additions & 4 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Enhancements
- Add a video to :ref:`tut-freesurfer-mne` of a brain inflating from the pial surface to aid in understanding the inflated brain (:gh:`11440` by `Alex Rockhill`_)
- Add automatic projection of sEEG contact onto the inflated surface for :meth:`mne.viz.Brain.add_sensors` (:gh:`11436` by `Alex Rockhill`_)
- Allow an image with intracranial electrode contacts (e.g. computed tomography) to be used without the freesurfer recon-all surfaces to locate contacts so that it doesn't have to be downsampled to freesurfer dimensions (for microelectrodes) and show an example :ref:`ex-ieeg-micro` with :func:`mne.transforms.apply_volume_registration_points` added to aid this transform (:gh:`11567` by `Alex Rockhill`_)
- Use new :meth:`dipy.workflows.align.DiffeomorphicMap.transform_points` to transform a montage of intracranial contacts more efficiently (:gh:`11572` by `Alex Rockhill`_)
- Use new :meth:`dipy.align.imwarp.DiffeomorphicMap.transform_points` to transform a montage of intracranial contacts more efficiently (:gh:`11572` by `Alex Rockhill`_)
- Improve performance of raw data browsing with many annotations (:gh:`11614` by `Eric Larson`_)
- Add support for :func:`mne.preprocessing.maxwell_filter` with gradient-compensated CTF data, e.g., for tSSS-only mode (:gh:`10554` by `Eric Larson`_)
- Add support for eyetracking data using :func:`mne.io.read_raw_eyelink` (:gh:`11152` by `Dominik Welke`_ and `Scott Huberty`_)
Expand All @@ -50,9 +50,9 @@ Enhancements
Bugs
~~~~
- Improving compatibility with Qt6 by removing the use of deprecated ``AA_UseHighDpiPixmaps`` attribute for this Qt version (:gh:`11662` by :newcontrib:`Florin Pop`)
- Fix :func:`mne.time_frequency.psd_array_multitaper` docstring where argument ``bandwidth`` incorrectly reported argument as half-bandwidth and gave wrong explanation of default value (:gh:`11479` by :newcontrib: `Tom Stone`_)
- Fix bug where installation of a package depending on ``mne`` will error when done in an environment where ``setuptools`` is not present (:gh:`11454` by :newcontrib: `Arne Pelzer`_)
- Fix bug in :meth:`mne.Annotations.rename` where replacements were not done correctly (:gh:`11666` by :newcontrib:`Timur Sokhin`_ and `Eric Larson`_)
- Fix :func:`mne.time_frequency.psd_array_multitaper` docstring where argument ``bandwidth`` incorrectly reported argument as half-bandwidth and gave wrong explanation of default value (:gh:`11479` by :newcontrib:`Tom Stone`)
- Fix bug where installation of a package depending on ``mne`` will error when done in an environment where ``setuptools`` is not present (:gh:`11454` by :newcontrib:`Arne Pelzer`)
- Fix bug in :meth:`mne.Annotations.rename` where replacements were not done correctly (:gh:`11666` by :newcontrib:`Timur Sokhin` and `Eric Larson`_)
- Fix bug where :func:`mne.preprocessing.regress_artifact` and :class:`mne.preprocessing.EOGRegression` incorrectly tracked ``picks`` (:gh:`11366` by `Eric Larson`_)
- Fix bug where channel names were not properly sanitized in :func:`mne.write_evokeds` and related functions (:gh:`11399` by `Eric Larson`_)
- Fix bug where splash screen would not always disappear (:gh:`11398` by `Eric Larson`_)
Expand Down Expand Up @@ -83,3 +83,6 @@ API changes
- Deprecate arguments ``kind`` and ``path`` from :func:`mne.channels.read_layout` in favor of a common argument ``fname`` (:gh:`11500` by `Mathieu Scheltienne`_)
- Change ``aligned_ct`` positional argument in ``mne.gui.locate_ieeg`` to ``base_image`` to reflect that this can now be used with unaligned images (:gh:`11567` by `Alex Rockhill`_)
- ``mne.warp_montage_volume`` was deprecated in favor of :func:`mne.preprocessing.ieeg.warp_montage` (acts directly on points instead of using an intermediate volume) and :func:`mne.preprocessing.ieeg.make_montage_volume` (which makes a volume of ieeg contact locations which can still be useful) (:gh:`11572` by `Alex Rockhill`_)
- Deprecate ``mne.pick_channels_evoked`` in favor of ``evoked.copy().pick(...)`` (:gh:`11665` by `Eric Larson`_)
- Set instance methods ``inst.pick_types`` and ``inst.pick_channels`` as legacy in favor of ``inst.pick(...)`` (:gh:`11665` by `Eric Larson`_)
- The default of ``inst.pick_channels(..., ordered=False)`` will change to ``ordered=True`` in 1.5 to avoid silent bugs (:gh:`11665` by `Eric Larson`_)
3 changes: 1 addition & 2 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,7 @@
'picard': ('https://pierreablin.github.io/picard/', None),
'qdarkstyle': ('https://qdarkstylesheet.readthedocs.io/en/latest', None),
'eeglabio': ('https://eeglabio.readthedocs.io/en/latest', None),
'dipy': ('https://dipy.org/documentation/latest/',
'https://dipy.org/documentation/latest/objects.inv/'),
'dipy': ('https://dipy.org/documentation/latest/', None),
'pooch': ('https://www.fatiando.org/pooch/latest/', None),
'pybv': ('https://pybv.readthedocs.io/en/latest/', None),
'pyqtgraph': ('https://pyqtgraph.readthedocs.io/en/latest/', None),
Expand Down
8 changes: 4 additions & 4 deletions examples/inverse/multi_dipole_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,13 @@
# sensors on the right side of the helmet.
picks_left = read_vectorview_selection('Left', info=info)
evoked_fit_left = evoked_left.copy().crop(0.08, 0.08)
evoked_fit_left.pick_channels(picks_left)
cov_fit_left = cov.copy().pick_channels(picks_left)
evoked_fit_left.pick_channels(picks_left, ordered=False)
cov_fit_left = cov.copy().pick_channels(picks_left, ordered=False)

picks_right = read_vectorview_selection('Right', info=info)
evoked_fit_right = evoked_right.copy().crop(0.08, 0.08)
evoked_fit_right.pick_channels(picks_right)
cov_fit_right = cov.copy().pick_channels(picks_right)
evoked_fit_right.pick_channels(picks_right, ordered=False)
cov_fit_right = cov.copy().pick_channels(picks_right, ordered=False)

# Any SSS projections that are active on this data need to be re-normalized
# after picking channels.
Expand Down
1 change: 0 additions & 1 deletion examples/inverse/time_frequency_mixed_norm_inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
# Handling average file
condition = 'Left visual'
evoked = mne.read_evokeds(ave_fname, condition=condition, baseline=(None, 0))
evoked = mne.pick_channels_evoked(evoked)
# We make the window slightly larger than what you'll eventually be interested
# in ([-0.05, 0.3]) to avoid edge effects.
evoked.crop(tmin=-0.1, tmax=0.4)
Expand Down
4 changes: 2 additions & 2 deletions examples/preprocessing/eeg_bridging.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,14 @@
# pairs, meaning that it is unlikely that all four of these electrodes are
# bridged.

raw = raw_data[6].copy().pick_channels(['F2', 'F4', 'FC2', 'FC4'])
raw = raw_data[6].copy().pick_channels(['FC2', 'FC4', 'F2', 'F4'])
raw.add_channels([mne.io.RawArray(
raw.get_data(ch1) - raw.get_data(ch2),
mne.create_info([f'{ch1}-{ch2}'], raw.info['sfreq'], 'eeg'),
raw.first_samp) for ch1, ch2 in [('F2', 'F4'), ('FC2', 'FC4')]])
raw.plot(duration=20, scalings=dict(eeg=2e-4))

raw = raw_data[1].copy().pick_channels(['F2', 'F4', 'FC2', 'FC4'])
raw = raw_data[1].copy().pick_channels(['FC2', 'FC4', 'F2', 'F4'])
raw.add_channels([mne.io.RawArray(
raw.get_data(ch1) - raw.get_data(ch2),
mne.create_info([f'{ch1}-{ch2}'], raw.info['sfreq'], 'eeg'),
Expand Down
8 changes: 2 additions & 6 deletions mne/beamformer/_rap_music.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np

from ..forward import is_fixed_orient, convert_forward_solution
from ..io.pick import pick_channels_evoked, pick_info, pick_channels_forward
from ..io.pick import pick_info, pick_channels_forward
from ..inverse_sparse.mxne_inverse import _make_dipoles_sparse
from ..minimum_norm.inverse import _log_exp_var
from ..utils import logger, verbose, _check_info_inv, fill_doc
Expand Down Expand Up @@ -274,11 +274,7 @@ def rap_music(evoked, forward, noise_cov, n_dipoles=5, return_residual=False,
picks)

if return_residual:
residual = evoked.copy()
selection = [info['ch_names'][p] for p in picks]

residual = pick_channels_evoked(residual,
include=selection)
residual = evoked.copy().pick([info['ch_names'][p] for p in picks])
residual.data -= explained_data
active_projs = [p for p in residual.info['projs'] if p['active']]
for p in active_projs:
Expand Down
9 changes: 5 additions & 4 deletions mne/beamformer/tests/test_lcmv.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _get_data(tmin=-0.1, tmax=0.15, all_forward=True, epochs=True,
bads = [raw.ch_names[pick] for pick in bad_picks]
assert not any(pick in picks for pick in bad_picks)
picks = np.concatenate([picks, bad_picks])
raw.pick_channels([raw.ch_names[ii] for ii in picks])
raw.pick_channels([raw.ch_names[ii] for ii in picks], ordered=True)
del picks

raw.info['bads'] = bads # add more bads
Expand Down Expand Up @@ -429,7 +429,7 @@ def test_make_lcmv_bem(tmp_path, reg, proj, kind):
# (avoid "grad data rank (13) did not match the noise rank (None)")
data_cov_grad = pick_channels_cov(
data_cov, [ch_name for ch_name in epochs.info['ch_names']
if ch_name.endswith(('2', '3'))])
if ch_name.endswith(('2', '3'))], ordered=False)
assert len(data_cov_grad['names']) > 4
make_lcmv(epochs.info, forward_fixed, data_cov_grad, reg=0.01,
noise_cov=noise_cov)
Expand Down Expand Up @@ -499,8 +499,9 @@ def test_lcmv_cov(weight_norm, pick_ori):
filters = make_lcmv(evoked.info, forward, data_cov, noise_cov=noise_cov,
weight_norm=weight_norm, pick_ori=pick_ori)
for cov in (data_cov, noise_cov):
this_cov = pick_channels_cov(cov, evoked.ch_names)
this_evoked = evoked.copy().pick_channels(this_cov['names'])
this_cov = pick_channels_cov(cov, evoked.ch_names, ordered=False)
this_evoked = evoked.copy().pick_channels(
this_cov['names'], ordered=True)
this_cov['projs'] = this_evoked.info['projs']
assert this_evoked.ch_names == this_cov['names']
stc = apply_lcmv_cov(this_cov, filters)
Expand Down
12 changes: 5 additions & 7 deletions mne/channels/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from ..utils import (verbose, logger, warn,
_check_preload, _validate_type, fill_doc, _check_option,
_get_stim_channel, _check_fname, _check_dict_keys,
_on_missing)
_on_missing, legacy)
from ..io.constants import FIFF
from ..io.meas_info import (anonymize_info, Info, MontageMixin, create_info,
_rename_comps)
Expand Down Expand Up @@ -607,6 +607,7 @@ class UpdateChannelsMixin:
"""Mixin class for Raw, Evoked, Epochs, Spectrum, AverageTFR."""

@verbose
@legacy(alt='inst.pick(...)')
def pick_types(self, meg=False, eeg=False, stim=False, eog=False,
ecg=False, emg=False, ref_meg='auto', *, misc=False,
resp=False, chpi=False, exci=False, ias=False, syst=False,
Expand Down Expand Up @@ -660,18 +661,15 @@ def pick_types(self, meg=False, eeg=False, stim=False, eog=False,
return self

@verbose
def pick_channels(self, ch_names, ordered=False, *, verbose=None):
@legacy(alt='inst.pick(...)')
def pick_channels(self, ch_names, ordered=None, *, verbose=None):
"""Pick some channels.
Parameters
----------
ch_names : list
The list of channels to select.
ordered : bool
If True (default False), ensure that the order of the channels in
the modified instance matches the order of ``ch_names``.
.. versionadded:: 0.20.0
%(ordered)s
%(verbose)s
.. versionadded:: 1.1
Expand Down
6 changes: 3 additions & 3 deletions mne/channels/tests/test_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ def test_set_channel_types():
assert info['chs'][375]['kind'] == FIFF.FIFFV_SEEG_CH
assert info['chs'][375]['unit'] == FIFF.FIFF_UNIT_V
assert info['chs'][375]['coil_type'] == FIFF.FIFFV_COIL_EEG
for idx in pick_channels(raw.ch_names, ['MEG 2441', 'MEG 2443']):
for idx in pick_channels(raw.ch_names, ['MEG 2441', 'MEG 2443'],
ordered=False):
assert info['chs'][idx]['kind'] == FIFF.FIFFV_EEG_CH
assert info['chs'][idx]['unit'] == FIFF.FIFF_UNIT_V
assert info['chs'][idx]['coil_type'] == FIFF.FIFFV_COIL_EEG
Expand Down Expand Up @@ -480,9 +481,8 @@ def test_pick_channels():
assert len(raw.ch_names) == 3

# selected correctly 3 channels and ignored 'meg', and emit warning
with pytest.warns(RuntimeWarning, match='not present in the info'):
with pytest.raises(ValueError, match='not present in the info'):
raw.pick(['MEG 0113', "meg", 'MEG 0112', 'MEG 0111'])
assert len(raw.ch_names) == 3

names_len = len(raw.ch_names)
raw.pick(['all']) # selected correctly all channels
Expand Down
4 changes: 2 additions & 2 deletions mne/chpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@ def get_chpi_info(info, on_missing='raise', verbose=None):
hpi_pick = None # there is no pick!
if hpi_sub is not None:
if 'event_channel' in hpi_sub:
hpi_pick = pick_channels(info['ch_names'],
[hpi_sub['event_channel']])
hpi_pick = pick_channels(
info['ch_names'], [hpi_sub['event_channel']], ordered=False)
hpi_pick = hpi_pick[0] if len(hpi_pick) > 0 else None
# grab codes indicating a coil is active
hpi_on = [coil['event_bits'][0] for coil in hpi_sub['hpi_coils']]
Expand Down
17 changes: 10 additions & 7 deletions mne/cov.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,16 @@ def plot_topomap(
extrapolate=extrapolate, sphere=sphere, border=border,
time_format='')

def pick_channels(self, ch_names, ordered=False):
@verbose
def pick_channels(self, ch_names, ordered=None, *, verbose=None):
"""Pick channels from this covariance matrix.
Parameters
----------
ch_names : list of str
List of channels to keep. All other channels are dropped.
ordered : bool
If True (default False), ensure that the order of the channels
matches the order of ``ch_names``.
%(ordered)s
%(verbose)s
Returns
-------
Expand Down Expand Up @@ -1472,7 +1472,8 @@ def prepare_noise_cov(noise_cov, info, ch_names=None, rank=None,
raise RuntimeError('Not all channels present in noise covariance:\n%s'
% missing)
C = noise_cov._get_square()[np.ix_(noise_cov_idx, noise_cov_idx)]
info = pick_info(info, pick_channels(info['ch_names'], ch_names))
info = pick_info(
info, pick_channels(info['ch_names'], ch_names, ordered=False))
projs = info['projs'] + noise_cov['projs']
noise_cov = Covariance(
data=C, names=ch_names, bads=list(noise_cov['bads']),
Expand Down Expand Up @@ -1665,7 +1666,8 @@ def regularize(cov, info, mag=0.1, grad=0.1, eeg=0.1, exclude='bads',

# This actually removes bad channels from the cov, which is not backward
# compatible, so let's leave all channels in
cov_good = pick_channels_cov(cov, include=info_ch_names, exclude=exclude)
cov_good = pick_channels_cov(
cov, include=info_ch_names, exclude=exclude, ordered=False)
ch_names = cov_good.ch_names

# Now get the indices for each channel type in the cov
Expand Down Expand Up @@ -1723,7 +1725,8 @@ def regularize(cov, info, mag=0.1, grad=0.1, eeg=0.1, exclude='bads',
C[np.ix_(idx, idx)] = this_C

# Put data back in correct locations
idx = pick_channels(cov.ch_names, info_ch_names, exclude=exclude)
idx = pick_channels(
cov.ch_names, info_ch_names, exclude=exclude, ordered=False)
cov['data'][np.ix_(idx, idx)] = C

return cov
Expand Down
3 changes: 2 additions & 1 deletion mne/epochs.py
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,8 @@ def subtract_evoked(self, evoked=None):
evoked = self.average(picks)

# find the indices of the channels to use
picks = pick_channels(evoked.ch_names, include=self.ch_names)
picks = pick_channels(
evoked.ch_names, include=self.ch_names, ordered=False)

# make sure the omitted channels are not data channels
if len(picks) < len(self.ch_names):
Expand Down
3 changes: 2 additions & 1 deletion mne/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,8 @@ def find_stim_steps(raw, pad_start=None, pad_stop=None, merge=0,
# pull stim channel from config if necessary
stim_channel = _get_stim_channel(stim_channel, raw.info)

picks = pick_channels(raw.info['ch_names'], include=stim_channel)
picks = pick_channels(
raw.info['ch_names'], include=stim_channel, ordered=False)
if len(picks) == 0:
raise ValueError('No stim channel found to extract event triggers.')
data, _ = raw[picks, :]
Expand Down
9 changes: 6 additions & 3 deletions mne/forward/forward.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,8 @@ def _merge_fwds(fwds, *, verbose=None):


@verbose
def read_forward_solution(fname, include=(), exclude=(), verbose=None):
def read_forward_solution(fname, include=(), exclude=(), *, ordered=None,
verbose=None):
"""Read a forward solution a.k.a. lead field.
Parameters
Expand All @@ -487,6 +488,7 @@ def read_forward_solution(fname, include=(), exclude=(), verbose=None):
are included.
exclude : list, optional
List of names of channels to exclude. If empty include all channels.
%(ordered)s
%(verbose)s
Returns
Expand Down Expand Up @@ -665,7 +667,7 @@ def read_forward_solution(fname, include=(), exclude=(), verbose=None):

@verbose
def convert_forward_solution(fwd, surf_ori=False, force_fixed=False,
copy=True, use_cps=True, verbose=None):
copy=True, use_cps=True, *, verbose=None):
"""Convert forward solution between different source orientations.
Parameters
Expand Down Expand Up @@ -1401,7 +1403,8 @@ def _stc_src_sel(src, stc, on_missing='raise',

def _fill_measurement_info(info, fwd, sfreq, data):
"""Fill the measurement info of a Raw or Evoked object."""
sel = pick_channels(info['ch_names'], fwd['sol']['row_names'])
sel = pick_channels(
info['ch_names'], fwd['sol']['row_names'], ordered=False)
info = pick_info(info, sel)
info['bads'] = []

Expand Down
Loading

0 comments on commit daec3da

Please sign in to comment.