Skip to content

Commit 2c008ad

Browse files
authored
Improve handling of dropped epochs in ica.plot_properties (#13746)
1 parent 8b1529d commit 2c008ad

3 files changed

Lines changed: 44 additions & 15 deletions

File tree

doc/changes/dev/13746.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improved handling of dropped epochs in :func:`mne.viz.plot_ica_properties` and :meth:`mne.preprocessing.ICA.plot_properties`, allowing plots to be generated even with many bad annotations, by `Clemens Brunner`_.

mne/viz/ica.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -731,34 +731,47 @@ def _prepare_data_ica_properties(inst, ica, reject_by_annotation=True, reject="a
731731

732732
if reject == "auto":
733733
reject = ica.reject_
734+
drop_inds = None
735+
dropped_indices = []
734736
if reject is None:
735-
drop_inds = None
736-
dropped_indices = []
737-
# break up continuous signal into segments
738-
epochs_src = make_fixed_length_epochs(
739-
ica.get_sources(inst),
740-
duration=2,
741-
preload=True,
742-
reject_by_annotation=reject_by_annotation,
743-
proj=False,
744-
verbose=False,
745-
)
737+
inst_current = inst
746738
else:
747739
data = inst.get_data()
748740
data, drop_inds = _reject_data_segments(
749741
data, reject, flat=None, decim=None, info=inst.info, tstep=2.0
750742
)
751-
inst_rejected = RawArray(data, inst.info)
752-
# break up continuous signal into segments
743+
inst_current = RawArray(data, inst.info)
744+
# break up continuous signal into segments; suppress "All epochs were
745+
# dropped!" because we handle that case gracefully below
746+
with warnings.catch_warnings():
747+
warnings.filterwarnings(
748+
"ignore", "All epochs were dropped!", RuntimeWarning
749+
)
753750
epochs_src = make_fixed_length_epochs(
754-
ica.get_sources(inst_rejected),
751+
ica.get_sources(inst_current),
755752
duration=2,
756753
preload=True,
757754
reject_by_annotation=reject_by_annotation,
758755
proj=False,
759756
verbose=False,
760757
)
761-
# getting dropped epochs indexes
758+
# if all epochs were dropped by annotations, stitch the good segments
759+
# together so that the plot can still be generated
760+
if reject_by_annotation and len(epochs_src) == 0:
761+
good_data = inst_current.get_data(reject_by_annotation="omit")
762+
min_samples = int(2 * inst.info["sfreq"])
763+
if good_data.shape[1] >= min_samples:
764+
inst_good = RawArray(good_data, inst_current.info.copy(), verbose=False)
765+
epochs_src = make_fixed_length_epochs(
766+
ica.get_sources(inst_good),
767+
duration=2,
768+
preload=True,
769+
reject_by_annotation=False,
770+
proj=False,
771+
verbose=False,
772+
)
773+
# getting dropped epochs indexes
774+
if drop_inds is not None:
762775
dropped_indices = [(d[0] // len(epochs_src.times)) + 1 for d in drop_inds]
763776
kind = "Segment"
764777
else:

mne/viz/tests/test_ica.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,21 @@ def test_plot_ica_properties():
265265
# don't drop
266266
ica.plot_properties(raw_annot, reject_by_annotation=False, **topoargs)
267267

268+
# test fallback when ALL 2-second epoch windows are contaminated by annotations: one
269+
# bad segment per window so every epoch is dropped, but the inter-annotation gaps
270+
# stitch together to >= 2 s of clean data.
271+
annot_all_bad = Annotations(
272+
onset=[0.5, 2.5, 4.5, 6.5],
273+
duration=[1.0, 1.0, 1.0, 1.0],
274+
description=["BAD"] * 4,
275+
)
276+
raw_all_bad = _get_raw(preload=True).set_annotations(annot_all_bad).crop(0, 8)
277+
raw_all_bad.pick(np.arange(10))
278+
raw_all_bad.del_proj()
279+
fig = ica.plot_properties(raw_all_bad, picks=[0], **topoargs)
280+
assert_equal(len(fig), 1)
281+
plt.close("all")
282+
268283

269284
def test_plot_ica_sources(raw_orig, browser_backend, monkeypatch):
270285
"""Test plotting of ICA panel."""

0 commit comments

Comments
 (0)