From 192e837560f3e83f2d8bb19fe4f448d3d61666b9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 2 Jul 2026 02:17:06 +0300 Subject: [PATCH 1/6] BUG: Evoked.get_data() should exclude bad channels for explicit picks --- doc/changes/dev/PLACEHOLDER.bugfix.rst | 1 + doc/changes/names.inc | 1 + mne/evoked.py | 9 ++++++++- mne/tests/test_evoked.py | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 doc/changes/dev/PLACEHOLDER.bugfix.rst diff --git a/doc/changes/dev/PLACEHOLDER.bugfix.rst b/doc/changes/dev/PLACEHOLDER.bugfix.rst new file mode 100644 index 00000000000..98fe0d748d6 --- /dev/null +++ b/doc/changes/dev/PLACEHOLDER.bugfix.rst @@ -0,0 +1 @@ +Fix :meth:`mne.Evoked.get_data` so that explicit ``picks`` (e.g. ``picks="eeg"``) exclude channels marked as bad by default, matching the existing behavior of :meth:`mne.Epochs.get_data`, by :newcontrib:`Eva Safi`. \ No newline at end of file diff --git a/doc/changes/names.inc b/doc/changes/names.inc index 53635a40864..ac71677d148 100644 --- a/doc/changes/names.inc +++ b/doc/changes/names.inc @@ -100,6 +100,7 @@ .. _Erica Peterson: https://github.com/nordme .. _Erkka Heinila: https://github.com/Teekuningas .. _Etienne de Montalivet: https://github.com/etiennedemontalivet +.. _Eva Safi: https://github.com/evasafi .. _Evan Hathaway: https://github.com/ephathaway .. _Evgenii Kalenkovich: https://github.com/kalenkovich .. _Evgeny Goldstein: https://github.com/evgenygoldstein diff --git a/mne/evoked.py b/mne/evoked.py index c1d62a1df52..cbc6ac19203 100644 --- a/mne/evoked.py +++ b/mne/evoked.py @@ -252,7 +252,14 @@ def get_data(self, picks=None, units=None, tmin=None, tmax=None): # Avoid circular import from .io.base import _get_ch_factors - picks = _picks_to_idx(self.info, picks, "all", exclude=()) + # When no picks are given, keep prior behavior of including bads. + # When explicit picks are given (e.g. picks="eeg"), exclude bads by + # default, matching the behavior of Epochs.get_data(). + orig_picks = picks + if orig_picks is None: + picks = _picks_to_idx(self.info, picks, "all", exclude=()) + else: + picks = _picks_to_idx(self.info, picks) start, stop = self._handle_tmin_tmax(tmin, tmax) diff --git a/mne/tests/test_evoked.py b/mne/tests/test_evoked.py index a5ac783f802..4111af8d49f 100644 --- a/mne/tests/test_evoked.py +++ b/mne/tests/test_evoked.py @@ -71,6 +71,21 @@ def test_get_data(): d2 = evoked.get_data(picks="eeg", units="V") assert_array_equal(d1, d2) + # Regression test for gh-12577: explicit picks should exclude bad + # channels in Evoked.get_data(), matching Epochs.get_data(). + evoked_bads = evoked.copy() + eeg_ch_names = [ + ch for ch, kind in zip(evoked_bads.ch_names, evoked_bads.get_channel_types()) + if kind == "eeg" + ] + evoked_bads.info["bads"] = [eeg_ch_names[0]] + n_eeg_with_bads = len(pick_types(evoked_bads.info, eeg=True, exclude=())) + n_eeg_without_bads = len(pick_types(evoked_bads.info, eeg=True, exclude="bads")) + assert n_eeg_without_bads < n_eeg_with_bads # sanity check the fixture + assert evoked_bads.get_data(picks="eeg").shape[0] == n_eeg_without_bads + # picks=None should still keep prior behavior (bads included) + assert evoked_bads.get_data().shape[0] == len(evoked_bads.ch_names) + # Convert to µV d3 = evoked.get_data(picks="eeg", units="µV") assert_array_equal(d1 * 1e6, d3) From 2eddcd3b7b58eed1c5fc829d1c9a836a2cc7e246 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 23:21:55 +0000 Subject: [PATCH 2/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mne/tests/test_evoked.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mne/tests/test_evoked.py b/mne/tests/test_evoked.py index 4111af8d49f..4df67b49b5d 100644 --- a/mne/tests/test_evoked.py +++ b/mne/tests/test_evoked.py @@ -75,7 +75,8 @@ def test_get_data(): # channels in Evoked.get_data(), matching Epochs.get_data(). evoked_bads = evoked.copy() eeg_ch_names = [ - ch for ch, kind in zip(evoked_bads.ch_names, evoked_bads.get_channel_types()) + ch + for ch, kind in zip(evoked_bads.ch_names, evoked_bads.get_channel_types()) if kind == "eeg" ] evoked_bads.info["bads"] = [eeg_ch_names[0]] From 4b8ad44057e06444ddf57a026bfab90e4af4d654 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 2 Jul 2026 02:22:37 +0300 Subject: [PATCH 3/6] Rename changelog fragment to match PR number --- doc/changes/dev/{PLACEHOLDER.bugfix.rst => 14013.bugfix.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename doc/changes/dev/{PLACEHOLDER.bugfix.rst => 14013.bugfix.rst} (100%) diff --git a/doc/changes/dev/PLACEHOLDER.bugfix.rst b/doc/changes/dev/14013.bugfix.rst similarity index 100% rename from doc/changes/dev/PLACEHOLDER.bugfix.rst rename to doc/changes/dev/14013.bugfix.rst From d7dd3ce603357bbc2242052fd126dfb6899a6626 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 3 Jul 2026 05:48:15 +0300 Subject: [PATCH 4/6] DOC: clarify bad-channel handling in Evoked.get_data() picks docstring --- mne/evoked.py | 2 +- mne/utils/docs.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mne/evoked.py b/mne/evoked.py index cbc6ac19203..15f79a45806 100644 --- a/mne/evoked.py +++ b/mne/evoked.py @@ -233,7 +233,7 @@ def get_data(self, picks=None, units=None, tmin=None, tmax=None): Parameters ---------- - %(picks_all)s + %(picks_all_get_data)s %(units)s tmin : float | None Start time of data to get in seconds. diff --git a/mne/utils/docs.py b/mne/utils/docs.py index 74b54e62b21..b129521fc0f 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -3557,6 +3557,12 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): docdict["picks_good_data_noref"] = _reflow_param_docstring( f"{picks_base} good data channels {noref}" ) +docdict["picks_all_get_data"] = _reflow_param_docstring( + f"{picks_base} all channels, and bad channels are included by default " + f"when picks is None. If picks are given as channel type(s) (e.g., " + f"``'eeg'``), bad channels of those types are excluded by default. " + f"{reminder}" +) docdict["picks_header"] = _picks_header docdict["picks_ica"] = """ picks : int | list of int | slice | None From bfbb4636ff72346e01c263f62f5b0a3209ab888c Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 3 Jul 2026 05:57:52 +0300 Subject: [PATCH 5/6] DOC: update changelog to mention docstring clarification --- doc/changes/dev/14013.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changes/dev/14013.bugfix.rst b/doc/changes/dev/14013.bugfix.rst index 98fe0d748d6..e004e0a703d 100644 --- a/doc/changes/dev/14013.bugfix.rst +++ b/doc/changes/dev/14013.bugfix.rst @@ -1 +1 @@ -Fix :meth:`mne.Evoked.get_data` so that explicit ``picks`` (e.g. ``picks="eeg"``) exclude channels marked as bad by default, matching the existing behavior of :meth:`mne.Epochs.get_data`, by :newcontrib:`Eva Safi`. \ No newline at end of file +Fix :meth:`mne.Evoked.get_data` so that explicit ``picks`` (e.g. ``picks="eeg"``) exclude channels marked as bad by default, matching the existing behavior of :meth:`mne.Epochs.get_data`, and clarify the ``picks`` docstring to describe this conditional bad-channel behavior, by :newcontrib:`Eva Safi`. From 23588ab31d443862f63c4c7bb050928752fc3f06 Mon Sep 17 00:00:00 2001 From: Your Name Date: Fri, 3 Jul 2026 06:11:57 +0300 Subject: [PATCH 6/6] DOC: fix alphabetical ordering of picks_all_get_data in docdict --- mne/utils/docs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/mne/utils/docs.py b/mne/utils/docs.py index b129521fc0f..a9025eb3321 100644 --- a/mne/utils/docs.py +++ b/mne/utils/docs.py @@ -3547,6 +3547,12 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): docdict["picks_all_data_noref"] = _reflow_param_docstring( f"{picks_base} all data channels {noref}" ) +docdict["picks_all_get_data"] = _reflow_param_docstring( + f"{picks_base} all channels, and bad channels are included by default " + f"when picks is None. If picks are given as channel type(s) (e.g., " + f"``'eeg'``), bad channels of those types are excluded by default. " + f"{reminder}" +) docdict["picks_all_notypes"] = _reflow_param_docstring( f"{picks_base_notypes} all channels. {reminder}" ) @@ -3557,12 +3563,6 @@ def _reflow_param_docstring(docstring, has_first_line=True, width=75): docdict["picks_good_data_noref"] = _reflow_param_docstring( f"{picks_base} good data channels {noref}" ) -docdict["picks_all_get_data"] = _reflow_param_docstring( - f"{picks_base} all channels, and bad channels are included by default " - f"when picks is None. If picks are given as channel type(s) (e.g., " - f"``'eeg'``), bad channels of those types are excluded by default. " - f"{reminder}" -) docdict["picks_header"] = _picks_header docdict["picks_ica"] = """ picks : int | list of int | slice | None