Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changes/dev/13943.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix channel ordering bug in :func:`mne.viz.plot_compare_evokeds` when ``axes="topo"``, by `Lifeng Qiu Lin`_.
5 changes: 3 additions & 2 deletions mne/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,7 @@ def _get_evoked_node(fname):
return evoked_node


def _check_evokeds_ch_names_times(all_evoked):
def _check_evokeds_ch_names_times(all_evoked, inplace=False):
evoked = all_evoked[0]
ch_names = evoked.ch_names
for ii, ev in enumerate(all_evoked[1:]):
Expand All @@ -1618,7 +1618,8 @@ def _check_evokeds_ch_names_times(all_evoked):
raise ValueError(f"{evoked} and {ev} do not contain the same channels.")
else:
warn("Order of channels differs, reordering channels ...")
ev = ev.copy()
if not inplace:
ev = ev.copy()
ev.reorder_channels(ch_names)
all_evoked[ii + 1] = ev
if not np.max(np.abs(ev.times - evoked.times)) < 1e-7:
Expand Down
10 changes: 5 additions & 5 deletions mne/viz/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -2858,9 +2858,7 @@ def plot_compare_evokeds(
for evk in evokeds[cond]:
_validate_type(evk, Evoked, "All evokeds entries ", "Evoked")
# ensure same channels and times across all evokeds
all_evoked = sum(evokeds.values(), [])
_check_evokeds_ch_names_times(all_evoked)
del all_evoked
_check_evokeds_ch_names_times(sum(evokeds.values(), []), inplace=True)

# get some representative info
conditions = list(evokeds)
Expand Down Expand Up @@ -3019,6 +3017,7 @@ def plot_compare_evokeds(
if not do_topo:
# add vacuous "index" (needed for topo) so same code works for both
axes = [(ax, 0) for ax in axes]
assert len(axes) == 1
if np.array(picks).ndim < 2:
picks = [picks] # enables zipping w/ axes
else:
Expand Down Expand Up @@ -3130,7 +3129,8 @@ def click_func(
c_func = None if do_topo else combine_func
all_data = list()
all_cis = list()
for _picks, (ax, idx) in zip(picks, axes):
# We need to truncate axes because of a possible additional ax for the legend
for ax, idx in axes[: len(picks)]:
data_dict = dict()
ci_dict = dict()
for cond in conditions:
Expand All @@ -3145,7 +3145,7 @@ def click_func(
combine,
c_func,
ch_type=ch_type,
picks=_picks,
picks=picks[idx],
Comment thread
wmvanvliet marked this conversation as resolved.
scaling=scalings,
ci_fun=ci_fun,
)
Expand Down
23 changes: 23 additions & 0 deletions mne/viz/tests/test_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,16 @@ def test_plot_compare_evokeds(evoked):
# test defaults
figs = plot_compare_evokeds(evoked)
assert len(figs) == 3
# test arbitrary ordering of channels is handled correctly, in topo mode
evoked_subset = evoked.copy().pick(["MEG 0113", "MEG 0112"])
fig_ordered = plot_compare_evokeds(evoked_subset, axes="topo")
evoked_reordered = evoked_subset.copy()
evoked_reordered.reorder_channels(["MEG 0112", "MEG 0113"])
figs_reordered = plot_compare_evokeds(evoked_reordered, axes="topo")
assert_allclose(
fig_ordered[0].axes[0].lines[0].get_ydata(),
figs_reordered[0].axes[0].lines[0].get_ydata(),
)
Comment thread
wmvanvliet marked this conversation as resolved.
# test passing more than one evoked
red, blue = evoked.copy(), evoked.copy()
red.comment = red.comment + "*" * 100
Expand All @@ -463,6 +473,19 @@ def test_plot_compare_evokeds(evoked):
yvals = line.get_ydata()
assert (yvals < ylim[1]).all()
assert (yvals > ylim[0]).all()
# test that the channels are aligned when many evoked
# are passed in different orders, in topo mode
evoked_subset = evoked.copy().pick(["MEG 0113", "MEG 0112"])
evoked_reordered = evoked_subset.copy()
evoked_reordered.reorder_channels(["MEG 0112", "MEG 0113"])
# catch warnings when testing misalignment on purpose
with pytest.warns(RuntimeWarning, match="Order of channels differs"):
figs = plot_compare_evokeds(
dict(orig=evoked_subset, reordered=evoked_reordered), axes="topo"
)
assert_allclose(
figs[0].axes[0].lines[0].get_ydata(), figs[0].axes[0].lines[1].get_ydata()
)
# test plotting eyetracking data
plt.close("all") # close the previous figures as to avoid a too many figs warning
info_tmp = mne.create_info(["pupil_left"], evoked.info["sfreq"], ["pupil"])
Expand Down
Loading