EEG Montages and Referencing

Initialize NeuroAnalyzer
using NeuroAnalyzer
using Statistics
NeuroAnalyzer.progress_bar = false
eeg = import_edf("files/eeg/eeg.edf")
# filtered 0.5-35 Hz (removes the DC offsets and the line noise of the raw
# recording)
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :butterworth,
                      ftype = :hp,
                      cutoff = 0.5,
                      order = 4)
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :fir,
                      ftype = :lp,
                      cutoff = 35,
                      bw = 10)
load_locs!(eeg; file_name = "files/locs/standard-10-20-cap19-elmiko.locs")
# 60-second segment
eeg = crop(eeg; seg = (100, 160))

An EEG channel is always the voltage difference between two points: an electrode and a reference. The set of channels used to display or analyze the recording is a montage: e.g. all electrodes against one common reference (referential montage), or chains of neighboring electrode pairs (bipolar montage). Re-referencing computes a new montage from the recorded signals.

There Is No Reference-Free EEG

EEG amplifiers measure voltage differences; the absolute potential of a single electrode cannot be measured. Every electrode, including the reference, picks up brain and other body signals by volume conduction, so no point of the body is truly “silent”. The recorded signal of electrode \(i\) is

\[ V_i^{rec} = \phi_i - \phi_{ref} \]

where \(\phi\) is the (unknown) potential. Changing to another common reference subtracts the same signal from all channels:

\[ V_i^{new} = V_i^{rec} - V_{newref}^{rec} = \phi_i - \phi_{newref} \]

Consequently:

  • waveforms, amplitudes, spectra and connectivity depend on the reference: the same brain activity looks different with different references,
  • differences between channels do not depend on the common reference: \(V_i - V_j = \phi_i - \phi_j\) for any common reference,
  • the shape of a topographic map (the spatial pattern at a time point) does not depend on the common reference: the whole map is shifted by the same value, like a landscape described relative to a different sea level.

This does not hold for bipolar and Laplacian montages, which are spatial filters: each channel gets a different reference.

The recording used here is stored as recorded (physical reference: the reference electrode of the amplifier, not recorded as a channel):

println(eeg.header.recording[:reference])
physical

Alpha band power (8-12 Hz) in the same signal with different references, and the difference between F3 and P3:

refs = ["recorded" => eeg,
        "Cz" => reference_ce(eeg; ch = "Cz"),
        "linked ears" => reference_a(eeg; type = :l),
        "average" => reference_avg(eeg),
        "Laplacian" => reference_plap(eeg)]
println(rpad("reference", 13), rpad("alpha power [μV²]", 32), "SD(F3 - P3)")
println(rpad("", 13), "Fz     Cz     O1     T3")
for (name, obj) in refs
    # labels of the re-referenced channels get a suffix, e.g. "Fz-avg"
    ch = labels(obj)[get_channel(obj; ch = "eeg")]
    bp = vec(band_power(obj; ch = "eeg", flim = (8, 12)))
    idx(l) = findfirst(x -> x == l || startswith(x, l * "-"), ch)
    f3p3 = obj.data[get_channel(obj; ch = ch[idx("F3")]), :, 1] .-
           obj.data[get_channel(obj; ch = ch[idx("P3")]), :, 1]
    println(rpad(name, 13),
            join([rpad(round(bp[idx(l)]; digits = 1), 7)
                  for l in ["Fz", "Cz", "O1", "T3"]]),
            "    ", round(std(f3p3); digits = 2))
end
reference    alpha power [μV²]               SD(F3 - P3)
             Fz     Cz     O1     T3
recorded     0.4    2.7    5.4    7.0        10.67
Cz           2.1    0.0    2.4    5.1        10.67
linked ears  6.2    6.6    4.4    7.1        10.67
average      1.7    1.0    1.2    3.3        10.67
Laplacian    1.9    0.6    0.3    3.1        6.16

The power at each electrode depends on the reference: with the Cz reference, Cz itself is flat (power 0), and in the recorded signal Fz has almost no alpha activity, probably because the reference electrode of the amplifier was close to it. The difference F3 - P3 is the same for all common references (recorded, Cz, linked ears, average), but not for the Laplacian.

The topographic maps of the same time point (a large wave visible in all channels at 103.9 s), scaled to [-1, 1]: the maps for linked ears and the average reference are identical (as is the map of the recorded signal), the Laplacian map is different:

plot_topo(reference_a(eeg; type = :l);
          ch = "eeg",
          tpos = 3.9,
          title = "Linked ears",
          ps = :m,
          ch_labels = false)
plot_topo(reference_avg(eeg);
          ch = "eeg",
          tpos = 3.9,
          title = "Average reference",
          ps = :m,
          ch_labels = false)
plot_topo(reference_plap(eeg);
          ch = "eeg",
          tpos = 3.9,
          title = "Planar Laplacian",
          ps = :m,
          ch_labels = false)

Types of References

Reference Each channel is referenced to Emphasizes Main drawback
common electrode one electrode (e.g. Cz) or the mean of a few (A1, A2) widespread activity activity and artifacts of the reference are in all channels
average reference the mean of all electrodes widespread activity needs many, evenly distributed, clean channels
Laplacian the (weighted) mean of the nearest neighbors local activity removes widespread activity; edge electrodes
bipolar the neighboring electrode in a chain local activity, gradients removes activity common to both electrodes

Montages as spatial filters

Bipolar and Laplacian montages are spatial high-pass filters: activity common to neighboring electrodes (widespread rhythms, distant sources, common artifacts) is removed, local activity is enhanced. Common and average references keep the widespread activity.

Common Electrode Reference

All channels are referenced to one electrode or to the mean of several electrodes. Typical choices:

Reference Use Notes
vertex (Cz) recording reference of many systems Cz is close to many sources; central channels have low amplitude
ears (A1, A2) clinical EEG close to the temporal lobes (temporal activity is reduced); ECG artifacts, often larger on the left
mastoids (M1, M2) ERP research as ears; poor contact of a single mastoid electrode contaminates all channels
nose, Fpz some research systems eye movement artifacts

reference_ce() references the EEG channels to one channel or to the mean (or median, med = true) of several channels:

eeg_cz = reference_ce(eeg; ch = "Cz")
eeg_fp = reference_ce(eeg; ch = ["Fp1", "Fp2"])
println(labels(eeg_cz)[1:4])
println(eeg_fp.header.recording[:reference])
["Fp1-Cz", "Fp2-Cz", "F3-Cz", "F4-Cz"]
common (Fp1, Fp2 averaged)

The labels of the re-referenced channels get a suffix with the reference, and the reference is stored in the header. Only EEG channels are re-referenced (EOG, ECG and the reference channels A1, A2 are not changed). With a single reference electrode that is an EEG channel, this channel becomes flat.

Ears and mastoids

reference_a() (auricular electrodes A1, A2) and reference_m() (mastoid electrodes M1, M2) use the two electrodes in three ways (type):

  • :l (default): linked - the mean of both electrodes, for all channels,
  • :i: ipsilateral - A1 (M1) for the channels over the left hemisphere, A2 (M2) for the right,
  • :c: contralateral - A2 (M2) for the left channels, A1 (M1) for the right.

With :i and :c, the midline channels (Fz, Cz, Pz) use the linked reference:

eeg_ai = reference_a(eeg; type = :i)
println(labels(eeg_ai)[[1, 2, 3, 4, 19, 20]])
["Fp1-A1", "Fp2-A2", "F3-A1", "F4-A2", "Fz-A1A2", "Cz-A1A2"]

This recording has no mastoid electrodes; to show reference_m(), the ear electrodes are renamed:

eeg_m = rename_channel(eeg; ch = "A1", name = "M1")
rename_channel!(eeg_m; ch = "A2", name = "M2")
eeg_mc = reference_m(eeg_m; type = :c)

Tip: “Linked” ears or mastoids should be computed (as here), not wired together during the recording: physically linked electrodes form a low-resistance path that distorts the scalp potentials.

Average Reference

The common average reference (CAR) subtracts the mean of all EEG channels from each channel. It is based on the fact that the potentials over a closed surface surrounding all sources sum to zero; the mean of the electrodes then approximates zero potential. The approximation is good only with many electrodes covering the head evenly (64 or more, including the lower parts of the head); with 19 electrodes of the 10-20 system it is rough.

After average referencing, the channels sum to zero at each time point:

eeg_avg = reference_avg(eeg)
ch = get_channel(eeg_avg; ch = "eeg")
s = vec(sum(eeg_avg.data[ch, :, :]; dims = 1))
println("maximum |sum of channels|: ", round(maximum(abs.(s)); digits = 10))
maximum |sum of channels|: 0.0

Options of reference_avg():

Argument Effect
exclude_fpo = true Fp1, Fp2 (eye blinks) and O1, O2 (head movements) are not used to compute the reference
exclude_current = true each channel is excluded from its own reference
med = true median instead of mean: less sensitive to single noisy channels
weighted = true channels weighted by inverse distance (requires channel locations): close channels count more
eeg_avg2 = reference_avg(eeg; exclude_fpo = true, med = true)

Advantages: no single reference electrode dominates; the reference is not attached to one region of the head; recommended for high-density recordings and for source analysis.

Drawbacks:

  • a noisy or bad channel contaminates all channels: bad channels must be removed or interpolated before computing the average reference (see Bad channels and epochs, Interpolating bad channels),
  • widespread activity (e.g. a generalized discharge) appears with reversed sign in the channels where it is absent,
  • with few electrodes, the reference is biased towards the regions with most electrodes.

Laplacian Reference

The Laplacian references each electrode to the (weighted) mean of its neighbors, e.g. \(Cz - \frac{1}{4}(Fz + C3 + C4 + Pz)\):

\[ V_i^{LAP} = V_i - \sum_{j \in S_i} g_{ij} V_j, \qquad g_{ij} = \frac{1/d_{ij}}{\sum_{k \in S_i} 1/d_{ik}} \]

where \(S_i\) are the neighbors of electrode \(i\) and \(d_{ij}\) the distances (for the unweighted Laplacian, \(g_{ij} = 1/|S_i|\)). The result approximates the second spatial derivative of the scalp potential, which is related to the current flowing through the skull at the electrode (current source density). It enhances activity generated directly under the electrode and removes widespread activity and volume-conducted activity from distant sources; it does not depend on the recording reference.

Small Laplacian uses the nearest neighbors, large Laplacian the next-nearest neighbors (at a larger distance), which is less sensitive to noise and to small sources:

NeuroAnalyzer computes the nearest-neighbor Laplacian in two ways (both require channel locations):

  • reference_plap(): planar - neighbors and distances in the 2-D projection of the electrode positions (as in topographic maps),
  • reference_slap(): spherical - great-circle distances on the sphere, which accounts for the curvature of the head.

nn is the number of neighbors (default 4), weighted = true weights them by inverse distance, and med = true uses the (weighted) median:

eeg_plap = reference_plap(eeg; nn = 4)
eeg_wplap = reference_plap(eeg; nn = 6, weighted = true)
eeg_slap = reference_slap(eeg; nn = 4, weighted = true)
println(eeg_wplap.header.recording[:reference])
weighted planar Laplacian (6)

Tip: Electrodes at the edge of the montage (e.g. Fp1, T3, O2) do not have neighbors on all sides, so their Laplacian is less accurate. The spline-based surface Laplacian (current source density, below), which uses all electrodes, is more accurate but requires a dense montage.

Current Source Density (CSD)

The current source density (CSD) is the surface Laplacian of the scalp potential, computed with spherical splines (Perrin et al. 1989): a smooth function on a sphere is fitted to the potentials of all electrodes, and its second spatial derivative is calculated analytically at each electrode. It is proportional to the current flowing radially through the skull into (sink) or out of (source) the scalp at the electrode. Compared with the nearest-neighbor Laplacian, it uses all electrodes and the curvature of the head, and is more accurate, especially at the edge of the montage.

Like the Laplacian, CSD:

  • is reference-free: the Laplacian of a constant (a change of the reference adds the same value to all channels) is zero,
  • enhances local activity (sources close to the electrode) and removes widespread, volume-conducted activity: it sharpens topographic maps,
  • needs channel locations and a sufficient number of electrodes (≥ 32 is recommended; with 19 electrodes it is a rough estimate).

csd() transforms the EEG channels (channel type and data type "csd"):

eeg_csd = csd(eeg)

The result does not depend on the reference of the data:

ch_csd = get_channel(eeg_csd; ch = get_channel(eeg_csd; type = "csd"))
c_cz = csd(reference_ce(eeg; ch = "Cz"))
c_avg = csd(reference_avg(eeg))
println("maximum |CSD|:                         ",
        round(maximum(abs.(eeg_csd.data[ch_csd, :, :])); digits = 1))
println("maximum difference, Cz reference:      ",
        round(maximum(abs.(eeg_csd.data[ch_csd, :, :] .-
                           c_cz.data[ch_csd, :, :])); sigdigits = 2))
println("maximum difference, average reference: ",
        round(maximum(abs.(eeg_csd.data[ch_csd, :, :] .-
                           c_avg.data[ch_csd, :, :])); sigdigits = 2))
maximum |CSD|:                         1267.7
maximum difference, Cz reference:      8.7e-11
maximum difference, average reference: 9.9e-11

The topography of the same time point as in the maps above (compare with the average reference and the planar Laplacian):

plot_topo(eeg_csd;
          ch = "csd",
          tpos = 3.9,
          title = "CSD",
          ps = :m,
          ch_labels = false)

Parameters

Argument Default Meaning
m 4 stiffness of the spline (2-10): higher values give a smoother surface
n 8 number of Legendre polynomial terms
lambda 1e-5 regularization: larger values smooth the fit (less sensitive to noise of single channels)

The second derivative amplifies noise and small differences between neighboring channels; stiffer splines and more regularization give smoother (and smaller) CSD:

for (m, l) in [(3, 1e-5), (4, 1e-5), (5, 1e-5), (4, 1e-3)]
    x = csd(eeg; m = m, lambda = l).data[ch_csd, :, :]
    println("m = ", m, ", lambda = ", l, ": SD ", round(std(x); digits = 1))
end
m = 3, lambda = 1.0e-5: SD 97.7
m = 4, lambda = 1.0e-5: SD 61.6
m = 5, lambda = 1.0e-5: SD 33.9
m = 4, lambda = 0.001: SD 19.7

Tip: The electrode positions are projected onto a sphere of radius 1, so the values (labeled μV/m²) correspond to a head of radius 1 m, as in MNE with sphere = (0, 0, 0, 1); they are proportional to the true current density, and their scale depends on m and lambda. Compare CSD values only between data computed with the same parameters.

CSD is used e.g. to separate overlapping ERP components and their generators (Kayser and Tenke 2006) and before connectivity analyses, where it reduces spurious connectivity caused by volume conduction.

Bipolar Montages

In a bipolar montage, each channel is the difference between two neighboring electrodes, usually arranged in chains:

  • longitudinal (A, “double banana”): chains from front to back, e.g. Fp1-F7, F7-T3, T3-T5, T5-O1,
  • transverse (B): chains from left to right, e.g. F7-F3, F3-Fz, Fz-F4, F4-F8,
  • circumferential (C): a closed loop around the head (with the central chains inside).

Common chains of the longitudinal montage (10-20 system):

Chain Channels
left temporal Fp1-F7, F7-T3, T3-T5, T5-O1
left parasagittal Fp1-F3, F3-C3, C3-P3, P3-O1
central Fz-Cz, Cz-Pz
right parasagittal Fp2-F4, F4-C4, C4-P4, P4-O2
right temporal Fp2-F8, F8-T4, T4-T6, T6-O2

Bipolar montages are standard in clinical EEG: they enhance local activity, cancel activity and artifacts common to neighboring electrodes, and localize focal activity by phase reversal.

Phase reversal

The electrode closest to a focal source has the most extreme potential. In a chain, it is the second electrode of one channel and the first electrode of the next, so the two channels have opposite signs. A negative focus at T4, with the potentials:

Electrode Fp2 F8 T4 T6 O2
μV -5 -20 -40 -10 0

gives the channels of the right temporal chain:

Channel Value
Fp2-F8 -5 - (-20) = +15 μV
F8-T4 -20 - (-40) = +20 μV
T4-T6 -40 - (-10) = -30 μV
T6-O2 -10 - 0 = -10 μV

The sign changes between F8-T4 and T4-T6: the source is at their common electrode, T4. In the clinical display convention (negative up), the deflections of F8-T4 and T4-T6 point towards each other (negative phase reversal).

In a referential montage, there is no phase reversal: the focus is the electrode with the largest amplitude (provided the reference is not close to the source).

End of chain

If the source is at the first or last electrode of a chain (e.g. Fp2 or O2), there is no channel on the other side and no phase reversal. A circumferential montage, which has no ends, or a referential montage, helps in such cases.

Bipolar montages in NeuroAnalyzer

reference_custom() creates any montage from a list of channels ("Fz") and channel pairs ("Fz-Cz" = Fz − Cz); by default, it creates the longitudinal bipolar montage:

eeg_bip = reference_custom(eeg)
println(eeg_bip.header.recording[:reference])
println(labels(eeg_bip))
longitudinal-BIP
["Fz-Cz", "Cz-Pz", "Fp1-F7", "Fp1-F3", "F7-T3", "T3-T5", "T5-O1", "F3-C3", "C3-P3", "P3-O1", "Fp2-F8", "Fp2-F4", "F8-T4", "T4-T6", "T6-O2", "F4-C4", "C4-P4", "P4-O2", "A1", "A2", "EOG1", "EOG2", "ECG"]

All EEG channels are replaced by the montage channels; other channels (here A1, A2, EOG, ECG) are kept. A montage channel is marked as bad if one of its electrodes is marked as bad.

Locations of montage channels

A single channel (e.g. "Fz") keeps its location. The location of a channel pair is set by the loc option:

  • loc = :mid (default): the midpoint between the two electrodes. A bipolar channel measures the potential difference between both electrodes, so it reflects activity at and between them. The midpoint of the Cartesian coordinates is projected back onto the head surface, and the spherical coordinates are recalculated from it; the polar coordinates are the midpoint between the two electrodes on the topographic plot.
  • loc = :first: the location of the first electrode of the pair (as in MNE). This moves each channel half the inter-electrode distance towards the beginning of its chain.
plot_locs(eeg_bip,
          ch = "eeg",
          gui = false)
eeg_bip_first = reference_custom(eeg; loc = :first)

Note: A bipolar channel measures the potential gradient along one direction, not the potential at a point. Topographic maps of bipolar montages are therefore only approximate and should not be interpreted as maps of referential data.

The right temporal chain; signal plots use the clinical convention (negative up):

NeuroAnalyzer.plot(eeg_bip;
                   ch = ["Fp2-F8", "F8-T4", "T4-T6", "T6-O2"],
                   seg = (3, 5),
                   gui = false)

Transverse bipolar montage, and a longitudinal montage with the midline channels kept referential:

eeg_trans = reference_custom(eeg;
                             ref_list = ["Fp2-Fp1", "F8-Fp2", "F8-F4",
                                         "F4-Fz", "Fz-F3", "F3-F7",
                                         "Fp1-F7", "T4-C4", "C4-Cz",
                                         "Cz-C3", "C3-T3", "T6-P4",
                                         "P4-Pz", "Pz-P3", "P3-T5",
                                         "O2-O1"],
                             ref_name = "transverse-BIP")
eeg_long = reference_custom(eeg;
                            ref_list = ["Fz", "Cz", "Pz",
                                        "Fp1-F7", "F7-T3", "T3-T5", "T5-O1",
                                        "Fp1-F3", "F3-C3", "C3-P3", "P3-O1",
                                        "Fp2-F4", "F4-C4", "C4-P4", "P4-O2",
                                        "Fp2-F8", "F8-T4", "T4-T6", "T6-O2"],
                            ref_name = "longitudinal-BIP + midline")

The labels in the list must match the channel labels of the recording (e.g. T3/T4/T5/T6 in the old 10-20 nomenclature, T7/T8/P7/P8 in the 10-10 nomenclature).

Montage files

Montages can be stored in text files: the first line is the name of the montage, each next line one channel or channel pair; empty lines and lines starting with # are ignored. Example files are in the montages/ folder of NeuroAnalyzer:

m = import_montage(joinpath(NeuroAnalyzer.PATH, "montages", "bip_trans.mnt"))
eeg_trans = reference_custom(eeg; ref_list = m.ref_list, ref_name = m.ref_name)
println(m.ref_name, ": ", join(m.ref_list[1:5], ", "), ", …")
transverse-BIP: Fp2-Fp1, F8-Fp2, F8-F4, F4-Fz, Fz-F3, …

edit_montage() opens a montage file in the text editor of the system (on Linux: the editor set in the EDITOR environment variable):

edit_montage("my_montage.mnt")

Choosing a Reference

Purpose Recommended Notes
focal activity (spikes, local sources) bipolar, Laplacian phase reversal (bipolar); local activity enhanced
widespread activity (generalized discharges) common electrode (ears, mastoids), average bipolar and Laplacian montages cancel it
ERPs average (dense montages), linked mastoids follow the convention of the field, so that results can be compared
hemispheric asymmetry average, linked ears/mastoids an ipsilateral or one-sided reference creates spurious asymmetry
finding bad electrodes referential and bipolar montages a bad electrode stands out in all channels that use it
source analysis, high-density EEG average the reference must be modelled; the average is standard

There is no “correct” reference: it should be chosen for the question asked, reported in publications, and, if possible, results should be checked with another reference.

Practical Workflow

  1. Filter the signal (at least a high-pass filter).
  2. Detect bad channels (Bad channels and epochs); interpolate or delete them (Interpolating bad channels).
  3. Re-reference: an average reference must be computed only from clean channels.
  4. Remove artifacts (e.g. ICA), split into epochs, reject bad epochs, analyze.

All referencing functions return a new object; their ! versions (reference_avg!(), reference_ce!(), …) modify the object in place. The reference is recorded in the header and in the processing history.