EEG Topoplots
The eeg_topoplot
recipe adds a bit of convenience for plotting Topoplots from EEG data, like drawing a head shape and automatically looking up default positions for known sensors. Otherwise, it supports the same attributes as topoplot
.
TopoPlots.eeg_topoplot
— Functioneeg_topoplot(data::Vector{<: Real}, labels::Vector{<: AbstractString})
Attributes:
positions::Vector{<: Point} = Makie.automatic
: Can be calculated from label (channel) names. Currently, only 10/20 montage has default coordinates provided.labels::AbstractVector{<:AbstractString} = Makie.automatic
: Add custom labels, whenlabel_text
is set to true. Ifpositions
is not specified,labels
are used to look up the 10/20 coordinates.head = (color=:black, linewidth=3)
: draw the outline of the head. Set to nothing to not draw the head outline, otherwise set to a namedtuple that get passed down to theline!
call that draws the shape.
Some attributes from topoplot are set to different defaults:
label_scatter = true
contours = true
enlarge = 1
`
Otherwise the recipe just uses the topoplot
defaults and passes through the attributes.
The 10-05 channel locations are "perfect" spherical locations based on https://github.com/sappelhoff/eegpositions/ - the mne-default 10-20 locations are _not, they were warped to a fsaverage head. Which makes the locations provided here good for visualizations, but not good for source localisation.
You MUST set label_text=true
for labels to display.
For the standard 10/20 (or 10/05) montage, one can drop the positions
attribute:
using TopoPlots, CairoMakie
labels = TopoPlots.CHANNELS_10_05 # TopoPlots.CHANNELS_10_20 contains the 10/20 subset
f,ax,h = TopoPlots.eeg_topoplot(rand(348); labels=labels, axis=(aspect=DataAspect(),), label_text=true, label_scatter=(markersize=2, strokewidth=2,),colorrange=[-5,5])
If the channels aren't 10/05, one can still plot them, but then the positions need to be passed as well:
data, positions = TopoPlots.example_data()
labels = ["s$i" for i in 1:size(data, 1)]
TopoPlots.eeg_topoplot(data[:, 340, 1]; labels, label_text = true, positions=positions, axis=(aspect=DataAspect(),))
Subset of channels
If you only ask to plot a subset of channels, we highly recommend to define your bounding geometry yourself. We follow MNE functionality and normalize the positions prior to interpolation / plotting. If you only use a subset of channels, the positions will be relative to each other, not at absolute coordinates.
f = Figure()
ax1 = f[1,1] = Axis(f;aspect=DataAspect())
ax2 = f[1,2] = Axis(f;aspect=DataAspect())
kwlist = (;label_text=true,label_scatter=(markersize=10, strokewidth=2,color=:white))
TopoPlots.eeg_topoplot!(ax1,[1,0.5,0]; labels=["Cz","Fz","Fp1"],kwlist...)
TopoPlots.eeg_topoplot!(ax2,[1,0.5,05]; labels=["Cz","Fz","Fp1"], bounding_geometry=Circle(Point2f(0.5,0.5), 0.5),kwlist...)
f
As visible in the left plot, the positions are normalized to the bounding geometry. The right plot shows the same data, but with Cz correctly centered.
Example data
TopoPlots.example_data
— Functionexample_data()
Load EEG example data.
Returns a two-tuple:
- data: a (64, 400, 3) Float32 array of channel x timepoint x stat array. Timepoints corresponds to samples at 500Hz from -0.3s to 0.5s relative to stimulus onset. Stats are mean over subjects, standard errors over subjects, and associated p-value from a t-test. For demonstration purposes, the first stat dimension is generally the most applicable.
- positions: a length-64 Point2f vector of positions for each channel in data.
Data source
Ehinger, B. V., König, P., & Ossandón, J. P. (2015). Predictions of Visual Content across Eye Movements and Their Modulation by Inferred Information. The Journal of Neuroscience, 35(19), 7403–7413. https://doi.org/10.1523/JNEUROSCI.5114-14.2015