A bad channel (see Bad channels and epochs) can be deleted or interpolated: its signal is replaced by an estimate computed from the good channels. Interpolation keeps the full set of channels, which is needed e.g. to average data across subjects, for topographic maps and for montages that use the channel. Because the EEG is spatially smooth (each source is recorded by many neighboring electrodes, see volume conduction), the signal at an electrode can be estimated from its neighbors.
Interpolation can also repair a part of a channel: one bad epoch, or a short segment of a continuous recording (e.g. an electrode pop or a movement of one electrode).
Interpolation cannot recover activity that is recorded only by the bad electrode (a very focal source), and it fails if the neighbors of the bad channel are also bad. An interpolated channel contains no new information: it is a combination of the other channels (see Limitations).
Methods
Function
Method
Needs
sphinterpolate_channel()
spherical splines (Perrin et al. 1989): a smooth function on a sphere is fitted to the good channels and evaluated at the positions of the bad channels
channel locations
plinterpolate_channel()
planar interpolation: at each sample, the values of the good channels at their (2-D) positions are interpolated at the position of the bad channel
channel locations
lrinterpolate_channel()
linear regression: the channel is predicted as a linear combination of the good channels, with weights fitted on clean data (other epochs or the rest of the epoch)
clean training data
mlinterpolate_channel()
machine learning: as linear regression, with any regression model of MLJ.jl
clean training data, a model
The spherical spline and planar interpolation need the positions of the electrodes but no clean data of the bad channel, so they can repair a channel that is bad for the whole recording. The regression methods learn the relationship between the channel and the other channels from data in which the channel is good, so they need no locations, but can be used only when the channel is bad in part of the recording.
Spherical spline interpolation is the standard method of most EEG software (EEGLAB, MNE, FieldTrip).
A Bad Channel
To check the interpolation, C3 is replaced by noise, and the interpolated signal is compared with the original C3:
c3 =get_channel(eeg; ch ="C3")[1]eeg_bad =deepcopy(eeg)eeg_bad.data[c3, :, 1] =50.*randn(signal_len(eeg))# mark C3 as bad: bad channels are not used to interpolate other channelseeg_bad.header.recording[:bad_channel][c3] =true
Spherical splines
sphinterpolate_channel() projects the electrode positions onto a sphere and fits a spherical spline (a smooth function on the sphere) to the good EEG channels; the spline is evaluated at the positions of the interpolated channels (ch, one or several). The interpolation is a fixed weighted sum of the good channels, so it is fast. Channels marked as bad are not used.
flexibility of the spline: higher values give a smoother (stiffer) spline
n
50
number of terms (Legendre polynomials) of the spline
lambda
1e-5
regularization: 0 fits the good channels exactly, larger values smooth the fit
Planar interpolation
plinterpolate_channel() interpolates the channel ch in the epochs ep (default: all epochs). At each sample, a 2-D interpolator is built from the values of the good signal channels at their positions (loc_x, loc_y) and evaluated at the position of ch. The interpolation method is set with imethod:
nearest neighbor (the value of the closest channel)
:ga
Gaussian radial basis functions
t = eeg.time_ptsidx =round(Int, 20*sr(eeg)):round(Int, 23*sr(eeg))fig =Figure(size = fig_size)ax =Axis(fig[1, 1]; ax_style..., title ="C3", xlabel ="Time [s]", ylabel ="Amplitude [μV]")lines!(ax, t[idx], x0[idx]; color =:black, label ="original")lines!(ax, t[idx], eeg_sph.data[c3, idx, 1]; color =:blue, label ="spherical spline")lines!(ax, t[idx], eeg_pl.data[c3, idx, 1]; color =:red, label ="planar (:sh)")axislegend(ax; position =:rb)fig
The interpolated signals follow the original most of the time, but miss activity recorded mainly by C3 itself (e.g. the wave at 21.9-22.3 s): local activity cannot be recovered from the neighbors. For C3, the spherical spline is the most accurate (r ≈ 0.87), followed by the planar Shepard and thin plate methods (r ≈ 0.8); nearest neighbor (the signal of one other channel) is the worst.
Tip: Spherical splines are most accurate for channels surrounded by good channels. For channels at the edge of a sparse montage (e.g. Fp1, T6, O2 in the 10-20 system), the spline extrapolates and can be less accurate than the planar Shepard method; with noisy data, a larger lambda (e.g. 1e-3) makes the spline less sensitive to noise in the good channels.
Repairing a Segment
With seg (start and end in seconds), only a part of the channel is replaced, e.g. a short artifact in a continuous recording. A 2-s artifact in C3:
eeg_art =deepcopy(eeg)# samples of 10-12 s (sample = t × fs + 1)r = (round(Int, 10*sr(eeg)) +1):(round(Int, 12*sr(eeg)) +1)eeg_art.data[c3, r, 1] .+=100.*randn(length(r))eeg_seg =plinterpolate_channel(eeg_art; ch ="C3", seg = (10, 12))
lrinterpolate_channel() can also repair a segment of a continuous recording: the regression model is fitted on the samples outside the segment (where C3 is good) and predicts C3 inside it. ep = 1 is the only epoch of a continuous signal:
eeg_seg_lr =lrinterpolate_channel(eeg_art; ch ="C3", ep =1, seg = (10, 12))
idx =round(Int, 9*sr(eeg)):round(Int, 13*sr(eeg))fig =Figure(size = fig_size)ax =Axis(fig[1, 1]; ax_style..., title ="C3: repairing a segment (10-12 s)", xlabel ="Time [s]", ylabel ="Amplitude [μV]")lines!(ax, t[idx], eeg_art.data[c3, idx, 1]; color = (:gray, 0.5), label ="with artifact")lines!(ax, t[idx], x0[idx]; color =:black, label ="original")lines!(ax, t[idx], eeg_seg.data[c3, idx, 1]; color =:red, label ="planar")lines!(ax, t[idx], eeg_seg_lr.data[c3, idx, 1]; color =:blue, label ="linear regression")ylims!(ax, -40, 40)axislegend(ax; position =:rb)fig
for (name, o) in [("planar", eeg_seg), ("linear regression", eeg_seg_lr)] x = o.data[c3, :, 1]println(rpad(name, 18), ": r = ", round(cor(x[r], x0[r]); digits =3),", samples changed outside the segment: ",count(x[setdiff(eachindex(x), r)] .!= eeg_art.data[c3,setdiff(eachindex(x), r), 1]))end
planar : r = 0.949, samples changed outside the segment: 0
linear regression : r = 0.925, samples changed outside the segment: 0
Only the samples inside the segment are replaced.
Repairing an Epoch
In epoched data, a channel that is bad in some epochs can be repaired in these epochs only. lrinterpolate_channel() fits the regression on reference epochs (ep_ref, default: all other epochs; they must be clean) and predicts the channel in epoch ep:
e10 =epoch(eeg; ep_len =10)e10_bad =deepcopy(e10)e10_bad.data[c3, :, 3] =50.*randn(epoch_len(e10))e10_lr =lrinterpolate_channel(e10_bad; ch ="C3", ep =3)e10_pl =plinterpolate_channel(e10_bad; ch ="C3", ep =3)x0 = e10.data[c3, :, 3]for (name, o) in [("linear regression", e10_lr), ("planar", e10_pl)]println(rpad(name, 18), ": r = ",round(cor(o.data[c3, :, 3], x0); digits =3))end
linear regression : r = 0.925
planar : r = 0.93
The fit is reported (R², RMSE …) when verbose is on.
Machine learning models
mlinterpolate_channel() works like lrinterpolate_channel(), but uses any regression model of MLJ.jl (the model package must be installed), e.g. a random forest:
usingMLJRandomForestRegressor =@load RandomForestRegressor pkg = DecisionTreemodel =RandomForestRegressor()e10_ml =mlinterpolate_channel(e10_bad; ch ="C3", ep =3, model = model)
Non-linear models can fit the training data better, but are slower and do not necessarily predict new data better; compare them with linear regression on clean data first.
All interpolation functions have in-place versions (sphinterpolate_channel!(), plinterpolate_channel!(), lrinterpolate_channel!(), mlinterpolate_channel!()).
Bad channel marks after interpolation
By default, an interpolated channel stays marked as bad, so that it is recorded which channels were interpolated (and, with the exclude_bads preference on, they are still left out of analyses). With reset_bads = true, the interpolated channels are unmarked:
Neighbors must be good: the interpolation uses the other good channels; channels marked as bad are not used (planar and linear regression). If several neighboring channels are bad, the estimate is poor; with many bad channels, deleting them or rejecting the recording is better.
Edge channels (e.g. Fp1, O2, T3) have neighbors on one side only, so planar interpolation is less accurate for them.
Rank: an interpolated channel is a combination of the other channels and adds no information. The data have one dimension less than the number of channels per interpolated channel, which matters for ICA (the number of components must be reduced) and for the average reference.
Reference: interpolate before computing the average reference, otherwise the bad channel contaminates all channels through the reference (see Referencing).
Regression needs clean training data: if the channel is also bad in the reference epochs (or outside the segment), the model learns the artifact.