Waveform plots

EEGPhasePy provides a helper function to plot the pre/post-trigger waveform average with a standard deviation highlight. The function returns a matplotlib.figure.Figure object, so you can continue customizing it after creation.

Basic usage

If you already have an EEGPhasePy estimator fitted to your data, the simplest path is plot_mean_std_waveform_from_triggers(). Pass your raw EEG array, the sample indices where each trigger fired, and the time window (in seconds) to include on either side of t = 0:

Plotting with an existing estimator
waveform_fig = etp.plot_mean_std_waveform_from_triggers(
    testing_signal, triggers, tmin=0.1, tmax=0.1
)

You can also plot directly from a 2-D array of waveform segments using EEGPhasePy.viz.plot_waveform_average() — useful when you have already extracted epochs or want to skip the estimator entirely:

import numpy as np
import scipy.signal as signal
import EEGPhasePy.viz as viz

fs = 2000
time_data = np.arange(0, 30, 1 / fs)
raw = np.sin(2 * np.pi * 10 * time_data) + 0.3 * np.random.default_rng(0).standard_normal(len(time_data))

peaks = signal.find_peaks(np.sin(2 * np.pi * 10 * time_data))[0]

win_samples = int(0.1 * fs)
segments = [
    raw[p - win_samples: p + win_samples]
    for p in peaks
    if p - win_samples >= 0 and p + win_samples < len(raw)
]
waveforms = np.array(segments)

fig = viz.plot_waveform_average(waveforms, fs, t_trigger=win_samples)

(Source code, png, hires.png, pdf)

../_images/waveform_plots-1.png

Basic waveform average

Hiding the standard deviation highlight

Pass show_std=False to suppress the shaded region and show only the mean waveform:

import numpy as np
import scipy.signal as signal
import EEGPhasePy.viz as viz

fs = 2000
time_data = np.arange(0, 30, 1 / fs)
raw = np.sin(2 * np.pi * 10 * time_data) + 0.3 * np.random.default_rng(0).standard_normal(len(time_data))

peaks = signal.find_peaks(np.sin(2 * np.pi * 10 * time_data))[0]
win_samples = int(0.1 * fs)
segments = [
    raw[p - win_samples: p + win_samples]
    for p in peaks
    if p - win_samples >= 0 and p + win_samples < len(raw)
]
waveforms = np.array(segments)

fig = viz.plot_waveform_average(waveforms, fs, t_trigger=win_samples, show_std=False)

(Source code, png, hires.png, pdf)

../_images/waveform_plots-2.png

Mean waveform without standard deviation

Color customization

plot_waveform_average() accepts color for the waveform line and std_color for the shaded region. Both accept any matplotlib color: a named string, a hex code, or an RGB tuple. When std_color is not set it inherits from color.

import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
import EEGPhasePy.viz as viz

fs = 2000
time_data = np.arange(0, 30, 1 / fs)
raw = np.sin(2 * np.pi * 10 * time_data) + 0.3 * np.random.default_rng(0).standard_normal(len(time_data))

peaks = signal.find_peaks(np.sin(2 * np.pi * 10 * time_data))[0]
win_samples = int(0.1 * fs)
segments = [
    raw[p - win_samples: p + win_samples]
    for p in peaks
    if p - win_samples >= 0 and p + win_samples < len(raw)
]
waveforms = np.array(segments)

# Purple waveform with a separate orange std shading
fig = viz.plot_waveform_average(
    waveforms, fs, t_trigger=win_samples,
    color="#7B2D8B",
    std_color="#F4A300",
)

(Source code, png, hires.png, pdf)

../_images/waveform_plots-3.png

Custom line and shading colors

Advanced matplotlib customization

The function returns a standard matplotlib.figure.Figure, so you can reach into its axes and apply any matplotlib customization after the fact.

Post-hoc axes edits

import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
import EEGPhasePy.viz as viz

fs = 2000
time_data = np.arange(0, 30, 1 / fs)
raw = np.sin(2 * np.pi * 10 * time_data) + 0.3 * np.random.default_rng(0).standard_normal(len(time_data))

peaks = signal.find_peaks(np.sin(2 * np.pi * 10 * time_data))[0]
win_samples = int(0.1 * fs)
segments = [
    raw[p - win_samples: p + win_samples]
    for p in peaks
    if p - win_samples >= 0 and p + win_samples < len(raw)
]
waveforms = np.array(segments)

fig = viz.plot_waveform_average(waveforms, fs, t_trigger=win_samples)

ax = fig.axes[0]
ax.set_title("Mu-band waveform average — C3")
ax.axvline(0, color="gray", linestyle="--", linewidth=0.8, label="Trigger")
ax.legend()
ax.spines[["top", "right"]].set_visible(False)

(Source code, png, hires.png, pdf)

../_images/waveform_plots-4.png

Customizing the returned figure

Global style sheets

Apply a matplotlib.pyplot.style.use() call before plotting and the style will take effect inside the function, since the figure is created on the call:

import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
import EEGPhasePy.viz as viz

fs = 2000
time_data = np.arange(0, 30, 1 / fs)
raw = np.sin(2 * np.pi * 10 * time_data) + 0.3 * np.random.default_rng(0).standard_normal(len(time_data))

peaks = signal.find_peaks(np.sin(2 * np.pi * 10 * time_data))[0]
win_samples = int(0.1 * fs)
segments = [
    raw[p - win_samples: p + win_samples]
    for p in peaks
    if p - win_samples >= 0 and p + win_samples < len(raw)
]
waveforms = np.array(segments)

plt.style.use("ggplot")
fig = viz.plot_waveform_average(waveforms, fs, t_trigger=win_samples)
plt.style.use("default")  # reset so other plots in the session are unaffected

(Source code, png, hires.png, pdf)

../_images/waveform_plots-5.png

Using a matplotlib style sheet

Note

Because the figure is created inside the function, you cannot pass a pre-existing Axes to embed the waveform plot in a larger subplot grid. To work around this, create your subplot grid first, call plot_waveform_average() to get the figure, copy its axes content, and paste it into your target axes — or simply save the returned figure and assemble a composite image afterwards.