Preprocess Data: Filtering, Re-referencing, Resampling#

Mirrors the EEGLAB tutorial section “Preprocess data” on the checked-in tutorial dataset. The script runs headless: no dialog, no figure window.

Load the tutorial dataset.

import copy
from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import eegprep
from eegprep import pop_eegfiltnew, pop_loadset, pop_reref, pop_resample, pop_spectopo

REPO_ROOT = Path(eegprep.__file__).resolve().parents[2]  # sphinx-gallery defines no __file__
EEG = pop_loadset(REPO_ROOT / "sample_data" / "eeglab_data.set")

print(f"loaded    : {EEG['nbchan']} chan, {EEG['pnts']} pnts, {EEG['srate']:g} Hz")
print(f"reference : {EEG.get('ref')!r}")
print(f"events    : {len(EEG['event'])}, first latency {EEG['event'][0]['latency']:g}")
loaded    : 32 chan, 30504 pnts, 128 Hz
reference : 'common'
events    : 154, first latency 129.009

Filtering. Apply the high-pass and the low-pass as separate passes so each transition band is designed for its own cutoff.

EEG, hp_com = pop_eegfiltnew(EEG, locutoff=1.0, plotfreqz=False, return_com=True)
EEG, lp_com = pop_eegfiltnew(EEG, hicutoff=40.0, plotfreqz=False, return_com=True)
print(f"highpass  : {hp_com}")
print(f"lowpass   : {lp_com}")
highpass  : EEG = pop_eegfiltnew(EEG, 'locutoff', 1);
lowpass   : EEG = pop_eegfiltnew(EEG, 'hicutoff', 40);

Re-referencing. An empty reference means the average of all channels.

filtered = copy.deepcopy(EEG)
EEG, ref_com = pop_reref(EEG, [], return_com=True)
print(f"reref     : {ref_com}")
print(f"reference : {EEG.get('ref')!r}")
print(f"max abs channel sum per sample: {abs(EEG['data'].sum(axis=0)).max():.3e}")
reref     : EEG = pop_reref( EEG, []);
reference : 'average'
max abs channel sum per sample: 3.872e-13

Re-referencing to one channel, keeping that channel in the data. The retained reference channel becomes flat, which is the expected single-channel result. Channel labels avoid any index-base ambiguity; numeric ref values are 0-based in EEGPrep.

single = pop_reref(filtered, "Cz", keepref="on")
cz = [chan["labels"] for chan in single["chanlocs"]].index("Cz")
print(f"kept nbchan: {single['nbchan']}, Cz std {single['data'][cz].std():.3e}")
dropped = pop_reref(filtered, "Cz")
print(f"dropped nbchan: {dropped['nbchan']}")
kept nbchan: 32, Cz std 0.000e+00
dropped nbchan: 31

Resampling. The anti-alias low-pass, the time vector, and the event latencies are updated together.

first_latency_before = EEG["event"][0]["latency"]
EEG, rs_com = pop_resample(EEG, 64, return_com=True)
print(f"resample  : {rs_com}")
print(f"after     : {EEG['pnts']} pnts, {EEG['srate']:g} Hz, xmax {EEG['xmax']:.3f} s")
print(f"first event latency {first_latency_before:g} -> {EEG['event'][0]['latency']:g} samples")
resample  : EEG = pop_resample( EEG, 64);
after     : 15252 pnts, 64 Hz, xmax 238.297 s
first event latency 129.009 -> 65.0044 samples

Check the result in the frequency domain without opening a window.

spectra = pop_spectopo(EEG, 1, [], freqs=[10], plot="off")
print(f"spectra shape {spectra['spectra'].shape}, freqs up to {spectra['freqs'].max():g} Hz")
spectra shape (32, 33), freqs up to 32 Hz

Total running time of the script: (0 minutes 0.277 seconds)

Gallery generated by Sphinx-Gallery