Skip to content

Plot ERP ​

This demo demonstrates plotting event-related potentials (ERPs) with comprehensive customization options.

What is an ERP Plot? ​

ERP plots visualize averaged brain responses time-locked to events:

  • Time course: Amplitude changes over time

  • Waveforms: Characteristic positive and negative deflections

  • Condition comparison: Overlay multiple experimental conditions

  • Channel-specific: View individual electrodes or averages

Layout Options ​

The demo shows three layout modes:

LayoutDescription
:singleOne plot with overlaid conditions
:gridMultiple subplots (one per channel)
:topoChannels arranged by scalp location

Single Layout ​

Average across channels:

julia
plot_erp(erps, average_channels = true)

Shows grand average waveform across all selected channels.

Individual channels:

julia
plot_erp(erps, average_channels = false, colormap = :viridis)

Overlays all channels with color-coding.

Selected channels:

julia
plot_erp(erps, 
    channel_selection = channels([:Cz, :PO7, :PO8]),
    average_channels = false
)

Shows only specified channels.

Grid Layout ​

Displays multiple channels as subplots:

julia
plot_erp(erps, layout = :grid)

Custom grid dimensions:

julia
plot_erp(erps,
    channel_selection = channels([:F3, :Cz, :PO7, :PO8, :Fp1, :Fp2]),
    layout = :grid,
    layout_kwargs = (; grid_dims = (3, 2))  # 3 rows × 2 columns
)

Skip positions:

julia
plot_erp(erps,
    layout_kwargs = (; grid_dims = (3, 4), grid_skip_positions = [(2, 1), (2, 3)])  # Leave empty
)

Creates custom layouts with empty spaces.

Adjust spacing:

julia
plot_erp(erps,
    layout = :grid,
    layout_kwargs = (; grid_rowgap = 0, grid_colgap = 0)
)

Topographic Layout ​

Arranges channels by scalp position:

julia
plot_erp(erps, layout = :topo)

Each channel plotted at its actual spatial location for intuitive interpretation.

Customization Options ​

Y-axis orientation:

julia
plot_erp(erps, yreversed = true)  # Negative up (common convention)

Legend placement:

julia
plot_erp(erps,
    legend_channel = [:Fp1, :M2],  # Channels for legend
    legend_nbanks = 3              # Number of legend columns
)

Figure padding:

julia
plot_erp(erps,
    figure_padding = (150, 150, 150, 150)  # left, right, bottom, top
)

Combining with Topography ​

Create publication-quality figures with embedded topographies:

julia
using GLMakie
fig = Figure(size = (800, 800))
ax1 = Axis(fig[1, 1])
ax2 = Axis(fig[1, 1], width = Relative(0.2), height = Relative(0.2))

plot_erp!(fig, ax1, erps, average_channels = true)
plot_topography!(fig, ax2, erps[1],
    point_plot = false,
    label_plot = false,
    colorbar_plot = true
)

Shows ERP waveform with scalp distribution at a specific time point.

Common Use Cases ​

Condition comparison:

  • Overlay multiple experimental conditions

  • Identify differences in amplitude or latency

  • Statistical intervals highlighted

Component identification:

  • Classic ERP components (N1, P1, N170, P3, etc.)

  • Measure peak amplitudes and latencies

  • Compare across channels

Publication figures:

  • High-quality vector graphics

  • Customizable colors and styles

  • Grid layouts for multiple channels

Interpretation ​

Positive/negative deflections:

  • P1, P2, P3: Positive peaks (sometimes plotted downward with yreversed = true)

  • N1, N2, N4: Negative peaks (plotted upward)

Typical components:

  • P1/N1 (~100-200 ms): Early sensory processing

  • N170 (~170 ms): Face perception (occipito-temporal)

  • P3 (~300-600 ms): Attention, memory updating

  • N400 (~400 ms): Semantic processing

Workflow Summary ​

This demo shows:

  1. Basic plotting: All three layouts

  2. Channel averaging: Grand average vs individual channels

  3. Custom grids: Flexible subplot arrangements

  4. Grid customization: Gaps, skip positions, padding

  5. Legend control: Placement and formatting

  6. Combined plots: ERP + topography insets

ERP plots are the foundation of event-related brain potential analysis!

Code Examples ​

Show Code
julia
# Demo: ERP Plotting
# Shows ERP waveform visualization with various customization options.

# Note: EegFun.example_path() resolves bundled example data paths.
# When using your own data, simply pass the file path directly, e.g.:
# dat = EegFun.read_raw_data("/path/to/your/data.bdf")

using EegFun
using GLMakie
using JLD2

# read raw data
dat = EegFun.read_raw_data(EegFun.example_path("data/bdf/example1.bdf"));

# read and prepare layout file
layout = EegFun.read_layout(EegFun.example_path("layouts/biosemi/biosemi72.csv"));
EegFun.polar_to_cartesian_xy!(layout)

# create EegFun data structure (EegFun.ContinuousData)
dat = EegFun.create_eegfun_data(dat, layout);

# Some minimal preprocessing (average reference and highpass filter)
EegFun.rereference!(dat, :avg)
EegFun.highpass_filter!(dat, 0.1)

# EPOCHS -> ERPs
epoch_cfg = [
    EegFun.EpochCondition(name = "ExampleEpoch1", trigger_sequences = [[1]]),
    EegFun.EpochCondition(name = "ExampleEpoch2", trigger_sequences = [[2]]),
]
epochs = EegFun.extract_epochs(dat, epoch_cfg, (-2, 4))
erps = EegFun.average_epochs(epochs)

EegFun.plot_erp(erps, layout = :single)
EegFun.plot_erp(erps, layout = :grid)
EegFun.plot_erp(erps[1], layout = :topo)


with_theme(theme_ggplot2()) do

with_theme(Theme(colormap=:coolwarm)) do
EegFun.plot_erp(erps, layout = :grid, channel_selection = EegFun.channels([:Fp1, :M2]), 
legend_channel = [:Fp1, :M2], yreversed = true, theme_fontsize=30)
end

with_theme(theme_dark()) do
EegFun.plot_erp(erps, layout = :grid, legend_channel = [:Fp1, :M2], yreversed = true)
end

EegFun.plot_erp(
    erps,
    channel_selection = EegFun.channels([:F3, :Cz, :PO7, :PO8, :Fp1, :Fp2]),
    layout = :grid,
    layout_kwargs = (; grid_dims = (3, 2), grid_skip_positions = [(2, 1)]),
)

EegFun.plot_erp(
    erps,
    channel_selection = EegFun.channels([:Cz, :PO7, :PO8, :Fp1, :Fp2, :F3]),
    layout = :grid,
    layout_kwargs = (; grid_dims = (2, 3)),
    axis_type = :origin,
    scale_x_value = 0.5,
    scale_y_value = 5.0,
    xlabel = "s",
    ylabel = "μV",
)



EegFun.plot_erp(
    erps,
    channel_selection = EegFun.channels([:Cz, :PO7, :PO8, :Fp1, :Fp2, :F3, :T8, :F4]),
    layout = :grid,
    layout_kwargs = (; grid_dims = (3, 4), grid_skip_positions = [(2, 1), (2, 3)]),
)

EegFun.plot_erp(
    erps,
    channel_selection = EegFun.channels([:Cz, :PO7, :PO8, :Fp1, :Fp2, :F3]),
    layout = :grid,
    layout_kwargs = (; grid_dims = (2, 4), grid_skip_positions = [(2, 1), (2, 3)], grid_rowgap = 0, grid_colgap = 0),
    figure_padding = (150, 150, 150, 150),
)

# ERP Plots (:single)
EegFun.plot_erp(erps, average_channels = false, colormap = :viridis, legend_nbanks = 12)
EegFun.plot_erp(erps[1], average_channels = false, colormap = :viridis, legend_nbanks = 12)

EegFun.plot_erp(erps, average_channels = true)
EegFun.plot_erp(erps, channel_selection = EegFun.channels([:Cz, :PO7, :PO8]), average_channels = true)
EegFun.plot_erp(erps, channel_selection = EegFun.channels([:Cz, :PO7, :PO8]), average_channels = false)
EegFun.plot_erp(erps, channel_selection = EegFun.channels([:Cz, :PO7, :PO8]), average_channels = false, legend_nbanks = 3)
EegFun.plot_erp([erps[1], erps[1]], channel_selection = EegFun.channels([:PO8]))

# ERP Plots (:grid)
EegFun.plot_erp(erps, layout = :grid)
EegFun.plot_erp(erps, channel_selection = EegFun.channels([:Cz, :PO7, :PO8, :Fp1, :Fp2, :F3]), layout = :grid)

# EPR Plots (:topo)
EegFun.plot_erp(erps, layout = :topo)
EegFun.plot_erp([erps[1], erps[1]], layout = :topo)
EegFun.plot_erp(erps, layout = :topo, channel_selection = EegFun.channels([:Fp1, :Fp2, :PO8]))

# Combined plots
using GLMakie
fig = Figure(size = (800, 800))
ax1 = Axis(fig[1, 1])
ax2 = Axis(fig[1, 1], width = Relative(0.2), height = Relative(0.2), halign = 0, valign = 0)
EegFun.plot_erp!(fig, ax1, erps, average_channels = true)
EegFun.plot_topography!(
    fig,
    ax2,
    erps[1];
    point_plot = false,
    label_plot = false,
    colorbar_plot = true,
    colorbar_kwargs = (; width = Relative(0.03),
        height = Relative(0.2),
        tellheight = false,
        tellwidth = false,
        position = (1, 1),
        halign = 0.25,
        valign = 0,
        flipaxis = true
    ),
)
fig

GLMakie.closeall()

See Also ​