diff --git a/doc/changes/dev/14013.bugfix.rst b/doc/changes/dev/14013.bugfix.rst new file mode 100644 index 00000000000..e004e0a703d --- /dev/null +++ b/doc/changes/dev/14013.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`, and clarify the ``picks`` docstring to describe this conditional bad-channel behavior, by :newcontrib:`Eva Safi`. 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..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. @@ -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..4df67b49b5d 100644 --- a/mne/tests/test_evoked.py +++ b/mne/tests/test_evoked.py @@ -71,6 +71,22 @@ 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) diff --git a/mne/utils/docs.py b/mne/utils/docs.py index 74b54e62b21..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}" )