From 1f64d0f6bdf921bd1695a5667b8c831bcff663d9 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 11:00:02 +0100 Subject: [PATCH 1/8] rm from documentation --- doc/_includes/memory.rst | 1 + doc/api/sensor_space.rst | 6 ------ doc/changes/v1.1.rst | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/doc/_includes/memory.rst b/doc/_includes/memory.rst index 0e668b54525..185090a1d0b 100644 --- a/doc/_includes/memory.rst +++ b/doc/_includes/memory.rst @@ -35,6 +35,7 @@ Similarly, epochs can also be be read from disk on-demand. For example:: import mne events = mne.find_events(raw) event_id, tmin, tmax = 1, -0.2, 0.5 + # TODO: https://github.com/mne-tools/mne-python/issues/11913 picks = mne.pick_types(raw.info, meg=True, eeg=True, stim=False, eog=True) epochs = mne.Epochs(raw, events, event_id, tmin, tmax, picks=picks, baseline=(None, 0), reject=dict(eeg=80e-6, eog=150e-6), diff --git a/doc/api/sensor_space.rst b/doc/api/sensor_space.rst index 8121f63f3d5..d44ec911b50 100644 --- a/doc/api/sensor_space.rst +++ b/doc/api/sensor_space.rst @@ -12,12 +12,6 @@ Sensor Space Data equalize_channels grand_average match_channel_orders - pick_channels - pick_channels_cov - pick_channels_forward - pick_channels_regexp - pick_types - pick_types_forward pick_info read_epochs read_reject_parameters diff --git a/doc/changes/v1.1.rst b/doc/changes/v1.1.rst index de0f597c0ee..14d39625aeb 100644 --- a/doc/changes/v1.1.rst +++ b/doc/changes/v1.1.rst @@ -117,7 +117,7 @@ Bugs - Fix bug in :func:`mne.io.read_raw_brainvision` when BrainVision data are acquired with the Brain Products "V-Amp" amplifier and disabled lowpass filter is marked with value ``0`` (:gh:`10517` by :newcontrib:`Alessandro Tonin`) -- Fix bug in :func:`mne.pick_types` and related methods where ``csd=True`` was not passed handled properly (:gh:`10470` by :newcontrib:`Matthias Dold`) +- Fix bug in ``mne.pick_types`` and related methods where ``csd=True`` was not passed handled properly (:gh:`10470` by :newcontrib:`Matthias Dold`) - Fix bug where plots produced using the ``'qt'`` / ``mne_qt_browser`` backend could not be added using :meth:`mne.Report.add_figure` (:gh:`10485` by `Eric Larson`_) @@ -145,7 +145,7 @@ Bugs - Fix bug in coregistration GUI that prevented it from starting up if only a high-resolution head model was available (:gh:`10543` by `Richard Höchenberger`_) -- Fix bug with :class:`mne.Epochs.add_reference_channels` where attributes were not updated properly so subsequent `~mne.Epochs.pick_types` calls were broken (:gh:`10912` by `Eric Larson`_) +- Fix bug with :class:`mne.Epochs.add_reference_channels` where attributes were not updated properly so subsequent ``mne.Epochs.pick_types`` calls were broken (:gh:`10912` by `Eric Larson`_) - - Fix bug in the :class:`mne.viz.Brain` tool bar that prevented the buttons to call the corresponding feature (:gh:`10560` by `Guillaume Favelier`_) From 77b6fbac97a04abd1d2da2fe04a633c29cb3fd82 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 11:02:06 +0100 Subject: [PATCH 2/8] mark legacy functions --- mne/_fiff/pick.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/mne/_fiff/pick.py b/mne/_fiff/pick.py index 4c5854f36fe..92abf58aaa1 100644 --- a/mne/_fiff/pick.py +++ b/mne/_fiff/pick.py @@ -15,6 +15,7 @@ _ensure_int, _validate_type, fill_doc, + legacy, logger, verbose, warn, @@ -258,6 +259,7 @@ def channel_type(info, idx): return first_kind +@legacy @verbose def pick_channels(ch_names, include, exclude=[], ordered=None, *, verbose=None): """Pick channels by names. @@ -339,6 +341,7 @@ def pick_channels(ch_names, include, exclude=[], ordered=None, *, verbose=None): return np.array(sel, int) +@legacy def pick_channels_regexp(ch_names, regexp): """Pick channels using regular expression. @@ -456,6 +459,7 @@ def _check_info_exclude(info, exclude): return exclude +@legacy @fill_doc def pick_types( info, @@ -705,6 +709,7 @@ def _has_kit_refs(info, picks): return False +@legacy @verbose def pick_channels_forward( orig, include=[], exclude=[], ordered=None, copy=True, *, verbose=None @@ -790,6 +795,7 @@ def pick_channels_forward( return fwd +@legacy def pick_types_forward( orig, meg=False, @@ -892,6 +898,7 @@ def channel_indices_by_type(info, picks=None): return idx_by_type +@legacy @verbose def pick_channels_cov( orig, include=[], exclude="bads", ordered=None, copy=True, *, verbose=None From d5e1594eb5aa7e2f8160f27dff8bdb39898b2dc6 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 11:47:48 +0100 Subject: [PATCH 3/8] add structure --- mne/_fiff/_pick.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 mne/_fiff/_pick.py diff --git a/mne/_fiff/_pick.py b/mne/_fiff/_pick.py new file mode 100644 index 00000000000..a43dde68257 --- /dev/null +++ b/mne/_fiff/_pick.py @@ -0,0 +1,86 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from mne.utils import _validate_type + +if TYPE_CHECKING: + from re import Pattern + from typing import Optional + + import numpy as np + from numpy.typing import DTypeLike, NDArray + + from .. import Info + + ScalarIntType: tuple[DTypeLike, ...] = (np.int8, np.int16, np.int32, np.int64) + + +# fmt: off +def pick_ch_names_to_idx( + ch_names: list[str] | tuple[str] | set[str], + picks: Optional[list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice], # noqa: E501 + exclude: list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice, # noqa: E501 +) -> NDArray[np.int32]: + """Pick on a list-like of channels with validation. + + Replaces: + - pick_channels + - pick_channel_regexp + """ + _validate_type(ch_names, (list, tuple, set), "ch_names") + ch_names = list(ch_names) if isinstance(ch_names, (set, tuple)) else ch_names + exclude = _ensure_int_array_pick_exclude_with_ch_names(ch_names, exclude, "exclude") + if picks is None or picks == "all": + picks = np.arange(len(ch_names)) + else: + picks = _ensure_int_array_pick_exclude_with_ch_names(ch_names, picks, "picks") + return np.setdiff1d(picks, exclude, assume_unique=True).astype(np.int32) + + +def _ensure_int_array_pick_exclude_with_ch_names( + ch_names: list[str], + var: list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice, # noqa: E501 + var_name: str +) -> NDArray[np.int32]: + pass + + +def pick_info_to_idx( + info: Info, + picks: Optional[list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice], # noqa: E501 + exclude: list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice, # noqa: E501 +) -> NDArray[np.int32]: + """Pick on an info with validation. + + Replaces: + - pick_channels + - pick_channels_regexp + - pick_types + """ + _validate_type(info, Info, "info") + if exclude == "bads": + exclude = info["bads"] + else: + exclude = _ensure_int_array_pick_exclude_with_info(info, exclude, "exclude") + if picks is None or picks == "all": + picks = np.arange(len(info["ch_names"])) + elif picks == "data": + return _pick_data_to_idx(info, exclude) + else: + picks = _ensure_int_array_pick_exclude_with_info(info, picks, "picks") + return np.setdiff1d(picks, exclude, assume_unique=True).astype(np.int32) + + +def _pick_data_to_idx(info: Info, exclude: NDArray[np.int32]): + """Pick all data channels without validation.""" + pass + + +def _ensure_int_array_pick_exclude_with_info( + info: Info, + var: list[str | int] | tuple[str | int] | set[str | int] | NDArray[+ScalarIntType] | str | int | Pattern | slice, # noqa: E501 + var_name: str +) -> NDArray[np.int32]: + pass +# fmt: on From 2486ebc0a9aecbf2cd2e165a58715bba07779121 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 12:18:10 +0100 Subject: [PATCH 4/8] add legacy for cov and forward --- mne/cov.py | 10 ++++++++++ mne/forward/forward.py | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/mne/cov.py b/mne/cov.py index 1b2d4cd8ebe..7c5842039f5 100644 --- a/mne/cov.py +++ b/mne/cov.py @@ -452,6 +452,7 @@ def plot_topomap( time_format="", ) + @legacy @verbose def pick_channels(self, ch_names, ordered=None, *, verbose=None): """Pick channels from this covariance matrix. @@ -478,6 +479,15 @@ def pick_channels(self, ch_names, ordered=None, *, verbose=None): self, ch_names, exclude=[], ordered=ordered, copy=False ) + def pick(self, picks, exclude): + """Pick channels from the covariance matrix. + + Replaces: + - Covariance.pick_channels + - pick_channels_cov + """ + pass + ############################################################################### # IO diff --git a/mne/forward/forward.py b/mne/forward/forward.py index dc39a58bd8f..63c250a78cd 100644 --- a/mne/forward/forward.py +++ b/mne/forward/forward.py @@ -245,6 +245,7 @@ def _repr_html_(self): def ch_names(self): return self["info"]["ch_names"] + @legacy def pick_channels(self, ch_names, ordered=False): """Pick channels from this forward operator. @@ -271,6 +272,16 @@ def pick_channels(self, ch_names, ordered=False): self, ch_names, exclude=[], ordered=ordered, copy=False, verbose=False ) + def pick(self, picks, exclude): + """Pick channels from the forward operator. + + Replaces: + - Forward.pick_channels + - pick_channels_forward + - pick_types_forward + """ + pass + def _block_diag(A, n): """Construct a block diagonal from a packed structure. From eef65b1e97b76b2c7885b3e947c06b7a4e71d57b Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 12:18:36 +0100 Subject: [PATCH 5/8] don't trigger CIs [skip ci] From 391b8b58b93210121ee821f5a8540ae1e58326c1 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 12:25:20 +0100 Subject: [PATCH 6/8] add missing import [skip ci] --- mne/cov.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mne/cov.py b/mne/cov.py index 7c5842039f5..7b8b08de938 100644 --- a/mne/cov.py +++ b/mne/cov.py @@ -77,6 +77,7 @@ copy_function_doc_to_method_doc, eigh, fill_doc, + legacy, logger, verbose, warn, From 4dee8710fe6b952c391900adfe7ac05e5e02c77c Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 12:28:10 +0100 Subject: [PATCH 7/8] fix for 'bads' [skip ci] --- mne/_fiff/_pick.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mne/_fiff/_pick.py b/mne/_fiff/_pick.py index a43dde68257..cdc42ca1e24 100644 --- a/mne/_fiff/_pick.py +++ b/mne/_fiff/_pick.py @@ -60,11 +60,13 @@ def pick_info_to_idx( """ _validate_type(info, Info, "info") if exclude == "bads": - exclude = info["bads"] + exclude = np.array([info["ch_names"].index(ch) for ch in info["bads"]], dtype=np.int32) # noqa: E501 else: exclude = _ensure_int_array_pick_exclude_with_info(info, exclude, "exclude") if picks is None or picks == "all": picks = np.arange(len(info["ch_names"])) + elif picks == "bads": + exclude = np.array([info["ch_names"].index(ch) for ch in info["bads"]], dtype=np.int32) # noqa: E501 elif picks == "data": return _pick_data_to_idx(info, exclude) else: From 6a9a5b4b07b5b717993e10897babd54a14c34cf2 Mon Sep 17 00:00:00 2001 From: Mathieu Scheltienne Date: Tue, 9 Jan 2024 12:29:13 +0100 Subject: [PATCH 8/8] add dtype for picks='all' [skip ci] --- mne/_fiff/_pick.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mne/_fiff/_pick.py b/mne/_fiff/_pick.py index cdc42ca1e24..4c0879b1bef 100644 --- a/mne/_fiff/_pick.py +++ b/mne/_fiff/_pick.py @@ -32,7 +32,7 @@ def pick_ch_names_to_idx( ch_names = list(ch_names) if isinstance(ch_names, (set, tuple)) else ch_names exclude = _ensure_int_array_pick_exclude_with_ch_names(ch_names, exclude, "exclude") if picks is None or picks == "all": - picks = np.arange(len(ch_names)) + picks = np.arange(len(ch_names), dtype=np.int32) else: picks = _ensure_int_array_pick_exclude_with_ch_names(ch_names, picks, "picks") return np.setdiff1d(picks, exclude, assume_unique=True).astype(np.int32) @@ -64,7 +64,7 @@ def pick_info_to_idx( else: exclude = _ensure_int_array_pick_exclude_with_info(info, exclude, "exclude") if picks is None or picks == "all": - picks = np.arange(len(info["ch_names"])) + picks = np.arange(len(info["ch_names"]), dtype=np.int32) elif picks == "bads": exclude = np.array([info["ch_names"].index(ch) for ch in info["bads"]], dtype=np.int32) # noqa: E501 elif picks == "data":