Note
Go to the end to download the full example code.
Setting the EEG reference#
This tutorial describes how to set or change the EEG reference in MNE-Python.
As usual we’ll start by importing the modules we need, loading some example data, and cropping it to save memory. Since this tutorial deals specifically with EEG, we’ll also restrict the dataset to just a few EEG channels so the plots are easier to see:
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
import os
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(
sample_data_folder, "MEG", "sample", "sample_audvis_raw.fif"
)
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
raw.crop(tmax=60).load_data()
raw.pick([f"EEG 0{n:02}" for n in range(41, 60)])
Reading 0 ... 36037 = 0.000 ... 60.000 secs...
Background#
EEG measures a voltage (difference in electric potential) between each electrode and a reference electrode. This means that whatever signal is present at the reference electrode is effectively subtracted from all the measurement electrodes. Therefore, an ideal reference signal is one that captures none of the brain-specific fluctuations in electric potential, while capturing all of the environmental noise/interference that is being picked up by the measurement electrodes.
In practice, this means that the reference electrode is often placed in a location on the subject’s body and close to their head (so that any environmental interference affects the reference and measurement electrodes similarly) but as far away from the neural sources as possible (so that the reference signal doesn’t pick up brain-based fluctuations). Typical reference locations are the subject’s earlobe, nose, mastoid process, or collarbone. Each of these has advantages and disadvantages regarding how much brain signal it picks up (e.g., the mastoids pick up a fair amount compared to the others), and regarding the environmental noise it picks up (e.g., earlobe electrodes may shift easily, and have signals more similar to electrodes on the same side of the head).
Even in cases where no electrode is specifically designated as the reference, EEG recording hardware will still treat one of the scalp electrodes as the reference, and the recording software may or may not display it to you (it might appear as a completely flat channel, or the software might subtract out the average of all signals before displaying, making it look like there is no reference).
Setting or changing the reference channel#
If you want to recompute your data with a different reference than was used
when the raw data were recorded and/or saved, MNE-Python provides the
set_eeg_reference()
method on Raw
objects
as well as the mne.add_reference_channels()
function. To use an
existing channel as the new reference, use the
set_eeg_reference()
method; you can also designate multiple
existing electrodes as reference channels, as is sometimes done with mastoid
references:
# code lines below are commented out because the sample data doesn't have
# earlobe or mastoid channels, so this is just for demonstration purposes:
# use a single channel reference (left earlobe)
# raw.set_eeg_reference(ref_channels=['A1'])
# use average of mastoid channels as reference
# raw.set_eeg_reference(ref_channels=['M1', 'M2'])
# use a bipolar reference (contralateral)
# raw.set_bipolar_reference(anode='[F3'], cathode=['F4'])
If a scalp electrode was used as reference but was not saved alongside the
raw data (reference channels often aren’t), you may wish to add it back to
the dataset before re-referencing. For example, if your EEG system recorded
with channel Fp1
as the reference but did not include Fp1
in the data
file, using set_eeg_reference()
to set (say) Cz
as the
new reference will then subtract out the signal at Cz
without restoring
the signal at Fp1
. In this situation, you can add back Fp1
as a flat
channel prior to re-referencing using add_reference_channels()
.
(Since our example data doesn’t use the 10-20 electrode naming system, the
example below adds EEG 999
as the missing reference, then sets the
reference to EEG 050
.) Here’s how the data looks in its original state:
raw.plot()
By default, add_reference_channels()
returns a copy, so we can go
back to our original raw
object later. If you wanted to alter the
existing Raw
object in-place you could specify
copy=False
.
# add new reference channel (all zero)
raw_new_ref = mne.add_reference_channels(raw, ref_channels=["EEG 999"])
raw_new_ref.plot()
# set reference to `EEG 050`
raw_new_ref.set_eeg_reference(ref_channels=["EEG 050"])
raw_new_ref.plot()
EEG channel type selected for re-referencing
Applying a custom ('EEG',) reference.
Notice that the new reference (EEG 050
) is now flat, while the original
reference channel that we added back to the data (EEG 999
) has a non-zero
signal. Notice also that EEG 053
(which is marked as “bad” in
raw.info['bads']
) is not affected by the re-referencing.
Setting average reference#
To set a “virtual reference” that is the average of all channels, you can use
set_eeg_reference()
with ref_channels='average'
. Just
as above, this will not affect any channels marked as “bad”, nor will it
include bad channels when computing the average. However, it does modify the
Raw
object in-place, so we’ll make a copy first, so we can
still go back to the unmodified Raw
object later:
# use the average of all channels as reference
raw_avg_ref = raw.copy().set_eeg_reference(ref_channels="average")
raw_avg_ref.plot()
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Creating the average reference as a projector#
If using an average reference, it is possible to create the reference as a
projector rather than subtracting the reference from the data
immediately by specifying projection=True
:
raw.set_eeg_reference("average", projection=True)
print(raw.info["projs"])
EEG channel type selected for re-referencing
Adding average EEG reference projection.
1 projection items deactivated
Average reference projection was added, but has not been applied yet. Use the apply_proj method to apply it.
[<Projection | Average EEG reference, active : False, n_channels : 18>]
Creating the average reference as a projector has a few advantages:
It is possible to turn projectors on or off when plotting, so it is easy to visualize the effect that the average reference has on the data.
If additional channels are marked as “bad” or if a subset of channels are later selected, the projector will be re-computed to take these changes into account (thus guaranteeing that the signal is zero-mean).
If there are other unapplied projectors affecting the EEG channels (such as SSP projectors for removing heartbeat or blink artifacts), EEG re-referencing cannot be performed until those projectors are either applied or removed; adding the EEG reference as a projector is not subject to that constraint. (The reason this wasn’t a problem when we applied the non-projector average reference to
raw_avg_ref
above is that the empty-room projectors included in the sample data.fif
file were only computed for the magnetometers.)
for title, proj in zip(["Original", "Average"], [False, True]):
with mne.viz.use_browser_backend("matplotlib"):
fig = raw.plot(proj=proj, n_channels=len(raw))
# make room for title
fig.subplots_adjust(top=0.9)
fig.suptitle(f"{title} reference", size="xx-large", weight="bold")
Using matplotlib as 2D backend.
Using qt as 2D backend.
Using matplotlib as 2D backend.
Using qt as 2D backend.
Using an infinite reference (REST)#
To use the “point at infinity” reference technique described in
[1] requires a forward model, which we can create in a few
steps. Here we use a fairly large spacing of vertices (pos
= 15 mm) to
reduce computation time; a 5 mm spacing is more typical for real data
analysis:
raw.del_proj() # remove our average reference projector first
sphere = mne.make_sphere_model("auto", "auto", raw.info)
src = mne.setup_volume_source_space(sphere=sphere, exclude=30.0, pos=15.0)
forward = mne.make_forward_solution(raw.info, trans=None, src=src, bem=sphere)
raw_rest = raw.copy().set_eeg_reference("REST", forward=forward)
for title, _raw in zip(["Original", "REST (∞)"], [raw, raw_rest]):
with mne.viz.use_browser_backend("matplotlib"):
fig = _raw.plot(n_channels=len(raw), scalings=dict(eeg=5e-5))
# make room for title
fig.subplots_adjust(top=0.9)
fig.suptitle(f"{title} reference", size="xx-large", weight="bold")
Fitted sphere radius: 91.0 mm
Origin head coordinates: -4.1 16.0 51.8 mm
Origin device coordinates: 1.4 17.8 -10.3 mm
Equiv. model fitting -> RV = 0.00349096 %%
mu1 = 0.944696 lambda1 = 0.1372
mu2 = 0.667439 lambda2 = 0.683766
mu3 = -0.265718 lambda3 = -0.0105969
Set up EEG sphere model with scalp radius 91.0 mm
Sphere : origin at (-4.1 16.0 51.8) mm
radius : 81.9 mm
grid : 15.0 mm
mindist : 5.0 mm
Exclude : 30.0 mm
Setting up the sphere...
Surface CM = ( -4.1 16.0 51.8) mm
Surface fits inside a sphere with radius 81.9 mm
Surface extent:
x = -86.0 ... 77.8 mm
y = -65.9 ... 97.9 mm
z = -30.1 ... 133.7 mm
Grid extent:
x = -90.0 ... 90.0 mm
y = -75.0 ... 105.0 mm
z = -45.0 ... 135.0 mm
2197 sources before omitting any.
655 sources after omitting infeasible sources not within 30.0 - 81.9 mm.
532 sources remaining after excluding the sources outside the surface and less than 5.0 mm inside.
Adjusting the neighborhood info.
Source space : MRI voxel -> MRI (surface RAS)
0.015000 0.000000 0.000000 -90.00 mm
0.000000 0.015000 0.000000 -75.00 mm
0.000000 0.000000 0.015000 -45.00 mm
0.000000 0.000000 0.000000 1.00
Source space : <SourceSpaces: [<discrete, n_used=532>] MRI (surface RAS) coords, ~559 KiB>
MRI -> head transform : identity
Measurement data : instance of Info
Sphere model : origin at [-0.00413508 0.01598787 0.05175598] mm
Standard field computations
Do computations in head coordinates
Free source orientations
Read 1 source spaces a total of 532 active source locations
Coordinate transformation: MRI (surface RAS) -> head
1.000000 0.000000 0.000000 0.00 mm
0.000000 1.000000 0.000000 0.00 mm
0.000000 0.000000 1.000000 0.00 mm
0.000000 0.000000 0.000000 1.00
Read 19 EEG channels from info
Head coordinate coil definitions created.
Source spaces are now in head coordinates.
Using the sphere model.
Using the equivalent source approach in the homogeneous sphere for EEG
Computing EEG at 532 source locations (free orientations)...
Finished.
EEG channel type selected for re-referencing
Applying REST reference.
Applying a custom ('EEG',) reference.
18 out of 19 channels remain after picking
Using matplotlib as 2D backend.
Using qt as 2D backend.
Using matplotlib as 2D backend.
Using qt as 2D backend.
Using a bipolar reference#
To create a bipolar reference, you can use set_bipolar_reference()
along with the respective channel names for anode
and cathode
which
creates a new virtual channel that takes the difference between two
specified channels (anode and cathode) and drops the original channels by
default. The new virtual channel will be annotated with the channel info of
the anode with location set to (0, 0, 0)
and coil type set to
EEG_BIPOLAR
by default. Here we use a contralateral/transverse bipolar
reference between channels EEG 054
and EEG 055
as described in
[2] which creates a new virtual channel
named EEG 054-EEG 055
.
raw_bip_ref = mne.set_bipolar_reference(raw, anode=["EEG 054"], cathode=["EEG 055"])
raw_bip_ref.plot()
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=36038
Range : 25800 ... 61837 = 42.956 ... 102.956 secs
Ready.
Added the following bipolar channels:
EEG 054-EEG 055
EEG reference and source modeling#
If you plan to perform source modeling (either with EEG or combined EEG/MEG data), it is strongly recommended to use the average-reference-as-projection approach. It is important to use an average reference because using a specific reference sensor (or even an average of a few sensors) spreads the forward model error from the reference sensor(s) into all sensors, effectively amplifying the importance of the reference sensor(s) when computing source estimates. In contrast, using the average of all EEG channels as reference spreads the forward modeling error evenly across channels, so no one channel is weighted more strongly during source estimation. See also this FieldTrip FAQ on average referencing for more information.
The main reason for specifying the average reference as a projector was mentioned in the previous section: an average reference projector adapts if channels are dropped, ensuring that the signal will always be zero-mean when the source modeling is performed. In contrast, applying an average reference by the traditional subtraction method offers no such guarantee.
Important
For these reasons, when performing inverse imaging, MNE-Python
will raise a ValueError
if there are EEG channels present
and something other than an average reference projector strategy
has been specified. To ensure correct functioning consider
calling set_eeg_reference(projection=True)
to add an average
reference as a projector.
References#
Total running time of the script: (0 minutes 16.799 seconds)