Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions doc/changes/latest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ Enhancements

- :func:`mne.viz.plot_evoked_topomap` and :meth:`mne.Evoked.plot_topomap` now display the time range the map was averaged over if ``average`` was passed (:gh:`10606` by `Richard Höchenberger`_)

- :func:`mne.viz.plot_evoked` and :meth:`mne.Evoked.plot` gained a new parameter, ``highlight``, to visually highlight time periods of interest (:gh:`10614` by `Richard Höchenberger`_)

Bugs
~~~~
- Make ``color`` parameter check in in :func:`mne.viz.plot_evoked_topo` consistent (:gh:`10217` by :newcontrib:`T. Wang` and `Stefan Appelhoff`_)
Expand Down
6 changes: 4 additions & 2 deletions mne/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,16 @@ def plot(self, picks=None, exclude='bads', unit=True, show=True, ylim=None,
xlim='tight', proj=False, hline=None, units=None, scalings=None,
titles=None, axes=None, gfp=False, window_title=None,
spatial_colors=False, zorder='unsorted', selectable=True,
noise_cov=None, time_unit='s', sphere=None, verbose=None):
noise_cov=None, time_unit='s', sphere=None, *, highlight=None,
verbose=None):
return plot_evoked(
self, picks=picks, exclude=exclude, unit=unit, show=show,
ylim=ylim, proj=proj, xlim=xlim, hline=hline, units=units,
scalings=scalings, titles=titles, axes=axes, gfp=gfp,
window_title=window_title, spatial_colors=spatial_colors,
zorder=zorder, selectable=selectable, noise_cov=noise_cov,
time_unit=time_unit, sphere=sphere, verbose=verbose)
time_unit=time_unit, sphere=sphere, highlight=highlight,
verbose=verbose)

@copy_function_doc_to_method_doc(plot_evoked_image)
def plot_image(self, picks=None, exclude='bads', unit=True, show=True,
Expand Down
47 changes: 41 additions & 6 deletions mne/viz/evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ def _plot_evoked(evoked, picks, exclude, unit, show, ylim, proj, xlim, hline,
selectable=True, zorder='unsorted',
noise_cov=None, colorbar=True, mask=None, mask_style=None,
mask_cmap=None, mask_alpha=.25, time_unit='s',
show_names=False, group_by=None, sphere=None):
show_names=False, group_by=None, sphere=None, *,
highlight=None):
"""Aux function for plot_evoked and plot_evoked_image (cf. docstrings).

Extra param is:
Expand Down Expand Up @@ -273,6 +274,15 @@ def _plot_evoked(evoked, picks, exclude, unit, show, ylim, proj, xlim, hline,

_check_option('gfp', gfp, [True, False, 'only'])

if highlight is not None:
highlight = np.array(highlight, dtype=float)
highlight = np.atleast_2d(highlight)
if highlight.shape[1] != 2:
raise ValueError(
f'"highlight" must be reshapable into a 2D array with shape '
f'(n, 2). Got {highlight.shape}.'
)

scalings = _handle_default('scalings', scalings)
titles = _handle_default('titles', titles)
units = _handle_default('units', units)
Expand Down Expand Up @@ -351,7 +361,7 @@ def _plot_evoked(evoked, picks, exclude, unit, show, ylim, proj, xlim, hline,
units, scalings, hline, gfp, types, zorder, xlim, ylim,
times, bad_ch_idx, titles, ch_types_used, selectable,
False, line_alpha=1., nave=evoked.nave,
time_unit=time_unit, sphere=sphere)
time_unit=time_unit, sphere=sphere, highlight=highlight)
plt.setp(axes, xlabel='Time (%s)' % time_unit)

elif plot_type == 'image':
Expand Down Expand Up @@ -382,7 +392,7 @@ def _plot_evoked(evoked, picks, exclude, unit, show, ylim, proj, xlim, hline,
def _plot_lines(data, info, picks, fig, axes, spatial_colors, unit, units,
scalings, hline, gfp, types, zorder, xlim, ylim, times,
bad_ch_idx, titles, ch_types_used, selectable, psd,
line_alpha, nave, time_unit, sphere):
line_alpha, nave, time_unit, sphere, *, highlight):
"""Plot data as butterfly plot."""
from matplotlib import patheffects, pyplot as plt
from matplotlib.widgets import SpanSelector
Expand Down Expand Up @@ -478,6 +488,7 @@ def _plot_lines(data, info, picks, fig, axes, spatial_colors, unit, units,
linewidth=0.5)[0])
line_list[-1].set_pickradius(3.)

# Plot GFP / RMS
if gfp:
if gfp in [True, 'only']:
if this_type == 'eeg':
Expand Down Expand Up @@ -530,7 +541,19 @@ def _plot_lines(data, info, picks, fig, axes, spatial_colors, unit, units,
for h in hline:
c = ('grey' if spatial_colors is True else 'r')
ax.axhline(h, linestyle='--', linewidth=2, color=c)

# Plot highlights
if highlight is not None:
this_ylim = ax.get_ylim() if (ylim is None or this_type not in
ylim.keys()) else ylim[this_type]
for this_highlight in highlight:
ax.fill_betweenx(
this_ylim, this_highlight[0], this_highlight[1],
facecolor='orange', alpha=0.15, zorder=99
)

lines.append(line_list)

if selectable:
for ax in np.array(axes)[selectables]:
if len(ax.lines) == 1:
Expand Down Expand Up @@ -643,7 +666,7 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,
scalings=None, titles=None, axes=None, gfp=False,
window_title=None, spatial_colors=False, zorder='unsorted',
selectable=True, noise_cov=None, time_unit='s', sphere=None,
verbose=None):
*, highlight=None, verbose=None):
"""Plot evoked data using butterfly plots.

Left click to a line shows the channel name. Selecting an area by clicking
Expand Down Expand Up @@ -749,6 +772,18 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,

.. versionadded:: 0.16
%(sphere_topomap_auto)s
highlight : array-like of float, shape(2,) | array-like of tuples of float, shape (n, 2) | None
Comment thread
hoechenberger marked this conversation as resolved.
Outdated
Segments of the data to highlight by means of a light-yellow
background color. Can be used to put visual emphasis on certain
time periods. The time periods must be specified as ``array-like``
objects in the form of ``(t_start, t_end)`` in the unit given by the
``time_unit`` parameter.
Multiple time periods can be specified by passing an ``array-like``
object of individual time periods (e.g., for 3 time periods, the shape
of the passed object would be ``(3, 2)``. If ``None``, no highlighting
is applied.

.. versionadded:: 1.1
%(verbose)s

Returns
Expand All @@ -759,14 +794,14 @@ def plot_evoked(evoked, picks=None, exclude='bads', unit=True, show=True,
See Also
--------
mne.viz.plot_evoked_white
"""
""" # noqa: E501
return _plot_evoked(
evoked=evoked, picks=picks, exclude=exclude, unit=unit, show=show,
ylim=ylim, proj=proj, xlim=xlim, hline=hline, units=units,
scalings=scalings, titles=titles, axes=axes, plot_type="butterfly",
gfp=gfp, window_title=window_title, spatial_colors=spatial_colors,
selectable=selectable, zorder=zorder, noise_cov=noise_cov,
time_unit=time_unit, sphere=sphere)
time_unit=time_unit, sphere=sphere, highlight=highlight)


def plot_evoked_topo(evoked, layout=None, layout_scale=0.945,
Expand Down
14 changes: 14 additions & 0 deletions mne/viz/tests/test_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,20 @@ def test_plot_evoked():
evoked.plot(verbose=True, time_unit='s')
assert 'Need more than one' in log_file.getvalue()

# Test highlight
for highlight in [
(0, 0.1),
[(0, 0.1), (0.1, 0.2)]
]:
fig = evoked.plot(time_unit='s', highlight=highlight)
for ax in fig.get_axes():
highlighted_areas = [child for child in ax.get_children()
if isinstance(child, PolyCollection)]
assert len(highlighted_areas) == len(np.atleast_2d(highlight))

with pytest.raises(ValueError, match='must be reshapable into a 2D array'):
fig = evoked.plot(time_unit='s', highlight=0.1)


def test_constrained_layout():
"""Test that we handle constrained layouts correctly."""
Expand Down
2 changes: 1 addition & 1 deletion mne/viz/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2233,7 +2233,7 @@ def _plot_psd(inst, fig, freqs, psd_list, picks_list, titles_list,
ylim=None, times=freqs, bad_ch_idx=[], titles=titles,
ch_types_used=ch_types_used, selectable=True, psd=True,
line_alpha=line_alpha, nave=None, time_unit='ms',
sphere=sphere)
sphere=sphere, highlight=None)

for ii, (ax, xlabel) in enumerate(zip(ax_list, xlabels_list)):
ax.grid(True, linestyle=':')
Expand Down
12 changes: 12 additions & 0 deletions tutorials/evoked/20_visualize_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@

evks['aud/left'].plot(picks='mag', spatial_colors=True, gfp=True)

# %%
# Interesting time periods can be highlighted via the ``highlight`` parameter.

time_ranges_of_interest = [
(0.05, 0.14),
(0.22, 0.27)
]
evks['aud/left'].plot(
picks='mag', spatial_colors=True, gfp=True,
highlight=time_ranges_of_interest
)

# %%
# Plotting scalp topographies
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down