Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix evoked topomap colorbars, closes #13050 #13063

Merged
merged 17 commits into from
Jan 16, 2025
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/devel/13063.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug in the colorbars created by :func:`mne.viz.plot_evoked_topomap` by `Santeri Ruuskanen`_.
4 changes: 2 additions & 2 deletions examples/visualization/evoked_topomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
Plotting topographic maps of evoked data
========================================

Load evoked data and plot topomaps for selected time points using multiple
additional options.
Load evoked data and plot topomaps for selected time points using
multiple additional options.
"""
# Authors: Christian Brodbeck <[email protected]>
# Tal Linzen <[email protected]>
Expand Down
13 changes: 11 additions & 2 deletions mne/viz/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
_clean_names,
_is_numeric,
_pl,
_time_mask,
_to_rgb,
_validate_type,
fill_doc,
Expand Down Expand Up @@ -1988,10 +1989,18 @@ def plot_evoked_joint(
contours = topomap_args.get("contours", 6)
ch_type = ch_types.pop() # set should only contain one element
# Since the data has all the ch_types, we get the limits from the plot.
vmin, vmax = ts_ax.get_ylim()
vmin, vmax = (None, None)
norm = ch_type == "grad"
vmin = 0 if norm else vmin
vmin, vmax = _setup_vmin_vmax(evoked.data, vmin, vmax, norm)
time_idx = [
np.where(
_time_mask(evoked.times, tmin=t, tmax=None, sfreq=evoked.info["sfreq"])
)[0][0]
for t in times_sec
]
scalings = topomap_args["scalings"] if "scalings" in topomap_args else None
scaling = _handle_default("scalings", scalings)[ch_type]
vmin, vmax = _setup_vmin_vmax(evoked.data[:, time_idx] * scaling, vmin, vmax, norm)
if not isinstance(contours, list | np.ndarray):
locator, contours = _set_contour_locator(vmin, vmax, contours)
else:
Expand Down
25 changes: 24 additions & 1 deletion mne/viz/topomap.py
Original file line number Diff line number Diff line change
Expand Up @@ -2116,6 +2116,22 @@ def plot_evoked_topomap(
:ref:`gridspec <matplotlib:arranging_axes>` interface to adjust the colorbar
size yourself.

The defaults for ``contours`` and ``vlim`` are handled as follows:

* When neither ``vlim`` nor a list of ``contours`` is passed, MNE sets
``vlim`` at ± the maximum absolute value of the data and then chooses
contours within those bounds.

* When ``vlim`` but not a list of ``contours`` is passed, MNE chooses
contours to be within the ``vlim``.

* When a list of ``contours`` but not ``vlim`` is passed, MNE chooses
``vlim`` to encompass the ``contours`` and the maximum absolute value of the
data.

* When both a list of ``contours`` and ``vlim`` are passed, MNE uses them
as-is.

When ``time=="interactive"``, the figure will publish and subscribe to the
following UI events:

Expand Down Expand Up @@ -2299,11 +2315,17 @@ def plot_evoked_topomap(
_vlim = [
_setup_vmin_vmax(data[:, i], *vlim, norm=merge_channels) for i in range(n_times)
]
_vlim = (np.min(_vlim), np.max(_vlim))
_vlim = [np.min(_vlim), np.max(_vlim)]
cmap = _setup_cmap(cmap, n_axes=n_times, norm=_vlim[0] >= 0)
# set up contours
if not isinstance(contours, list | np.ndarray):
_, contours = _set_contour_locator(*_vlim, contours)
else:
if vlim[0] is None and np.any(contours < _vlim[0]):
_vlim[0] = contours[0]
if vlim[1] is None and np.any(contours > _vlim[1]):
_vlim[1] = contours[-1]

# prepare for main loop over times
kwargs = dict(
sensors=sensors,
Expand Down Expand Up @@ -3352,6 +3374,7 @@ def _set_contour_locator(vmin, vmax, contours):
# correct number of bins is equal to contours + 1.
locator = ticker.MaxNLocator(nbins=contours + 1)
contours = locator.tick_values(vmin, vmax)
contours = contours[1:-1]
return locator, contours


Expand Down
Loading