Skip to content

Commit 9e72b47

Browse files
committed
Fix bug with ica.plot_properties
1 parent 187e6c9 commit 9e72b47

3 files changed

Lines changed: 38 additions & 22 deletions

File tree

mne/viz/ica.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -238,13 +238,11 @@ def _plot_ica_properties(
238238

239239
# image and erp
240240
# we create a new epoch with dropped rows
241-
epoch_data = epochs_src.get_data(copy=False)
242-
epoch_data = np.insert(
243-
arr=epoch_data,
244-
obj=(dropped_indices - np.arange(len(dropped_indices))).astype(int),
245-
values=0.0,
246-
axis=0,
247-
)
241+
src_data = epochs_src.get_data(copy=False)
242+
n = len(src_data) + len(dropped_indices)
243+
epoch_data = np.zeros((n,) + (src_data.shape[1:]), dtype=src_data.dtype)
244+
use_idx = np.setdiff1d(np.arange(n), dropped_indices)
245+
epoch_data[use_idx] = src_data
248246
from ..epochs import EpochsArray
249247

250248
epochs_src = EpochsArray(
@@ -283,9 +281,11 @@ def _plot_ica_properties(
283281
range(len(epoch_var)), epoch_var, alpha=0.5, facecolor=[0, 0, 0], lw=0
284282
)
285283
# rejected epochs in red
284+
# TODO: This can't be right as the variance is computed on the good/remaining
285+
# epochs, so these are by necessity zero
286286
var_ax.scatter(
287287
dropped_indices,
288-
epoch_var[dropped_indices],
288+
0,
289289
alpha=1.0,
290290
facecolor=[1, 0, 0],
291291
lw=0,
@@ -610,7 +610,6 @@ def _fast_plot_ica_properties(
610610
)
611611
del reject
612612
ica_data = np.swapaxes(data[:, picks, :], 0, 1)
613-
dropped_src = ica_data
614613

615614
# spectrum
616615
Nyquist = inst.info["sfreq"] / 2.0
@@ -656,16 +655,6 @@ def set_title_and_labels(ax, title, xlab, ylab):
656655

657656
# we reconstruct an epoch_variance with 0 where indexes where dropped
658657
epoch_var = np.var(ica_data[idx], axis=1)
659-
drop_var = np.var(dropped_src[idx], axis=1)
660-
drop_indices_corrected = (
661-
dropped_indices - np.arange(len(dropped_indices))
662-
).astype(int)
663-
epoch_var = np.insert(
664-
arr=epoch_var,
665-
obj=drop_indices_corrected,
666-
values=drop_var[dropped_indices],
667-
axis=0,
668-
)
669658

670659
# the actual plot
671660
fig = _plot_ica_properties(
@@ -772,7 +761,7 @@ def _prepare_data_ica_properties(inst, ica, reject_by_annotation=True, reject="a
772761
)
773762
# getting dropped epochs indexes
774763
if drop_inds is not None:
775-
dropped_indices = [(d[0] // len(epochs_src.times)) + 1 for d in drop_inds]
764+
dropped_indices = [(d[0] // len(epochs_src.times)) for d in drop_inds]
776765
kind = "Segment"
777766
else:
778767
drop_inds = None

mne/viz/tests/test_ica.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from mne import (
1414
Annotations,
1515
Epochs,
16+
create_info,
1617
make_fixed_length_events,
1718
pick_types,
1819
read_cov,
@@ -143,7 +144,7 @@ def test_plot_ica_components():
143144

144145

145146
@pytest.mark.slowtest
146-
def test_plot_ica_properties():
147+
def test_plot_ica_properties_basic():
147148
"""Test plotting of ICA properties."""
148149
raw = _get_raw(preload=True).crop(0, 5)
149150
raw.add_proj([], remove_existing=True)
@@ -281,6 +282,33 @@ def test_plot_ica_properties():
281282
plt.close("all")
282283

283284

285+
@pytest.mark.parametrize("kind", ["first", "last"])
286+
def test_plot_ica_properties_reject(kind):
287+
"""Check for gh-13879."""
288+
sfreq, duration = 100.0, 10.0
289+
n_samples = int(sfreq * duration)
290+
rng = np.random.default_rng(0)
291+
n_channels = 3
292+
data = rng.uniform(-3e-6, 3e-6, size=(n_channels, n_samples))
293+
assert kind in ("first", "last")
294+
idx = 0 if kind == "first" else -1
295+
data[0, idx] = 1000e-6
296+
info = create_info(["Fz", "Cz", "C2"], sfreq, "eeg")
297+
raw = RawArray(data, info)
298+
raw.set_montage("standard_1020")
299+
ica = ICA(
300+
n_components=2,
301+
method="picard",
302+
random_state=0,
303+
fit_params=dict(ortho=False, extended=True),
304+
)
305+
with pytest.warns(RuntimeWarning, match="filtered"), catch_logging(True) as log:
306+
ica.fit(raw, reject=dict(eeg=500e-6))
307+
log = log.getvalue()
308+
assert log.count("Artifact detected") == 1 # dropped one epoch
309+
ica.plot_properties(raw, picks=[0], show=False)
310+
311+
284312
def test_plot_ica_sources(raw_orig, browser_backend, monkeypatch):
285313
"""Test plotting of ICA panel."""
286314
raw = raw_orig.copy().crop(0, 1)

tutorials/preprocessing/40_artifact_correction_ica.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,6 @@
320320

321321
# %%
322322
# .. note::
323-
#
324323
# `~mne.preprocessing.ICA.plot_components` (which plots the scalp
325324
# field topographies for each component) has an optional ``inst`` parameter
326325
# that takes an instance of `~mne.io.Raw` or `~mne.Epochs`.

0 commit comments

Comments
 (0)