A MATLAB pipeline for computing quantitative EEG features from multi-channel neonatal EEG recordings, designed around an 8-channel (4 monopolar + 4 bipolar) neonatal montage.
Maintained by: Saeed Montazeri, BabaCenter, 2026 (Original pipeline: BabaCenter, 2019 — individual algorithm credits are kept in the relevant files; see Credits below.)
For each subject and each epoch, the pipeline extracts:
| Feature | Description |
|---|---|
| PSD | Power Spectral Density, per channel and frequency band |
| cPSD | Cross Power Spectral Density, per channel pair and frequency band |
| ASI | Activation Synchrony Index (interhemispheric synchrony) |
| wPLI | weighted Phase Lag Index (connectivity), per channel pair and frequency band |
| NC | Nestedness Coefficient / Phase-Amplitude Coupling (PAC) |
| aEEG | amplitude-integrated EEG — mean and interquartile range |
| rEEG | range-EEG — mean, interquartile range, and lower 5th percentile |
| SC | Suppression Curve |
- MATLAB (built and tested on R2019a; later versions should work)
- Signal Processing Toolbox (
butter,filtfilt,pwelch,cpsd,envelope,hilbert,resample) - Statistics and Machine Learning Toolbox (
kmeans,prctile,iqr,pdist2) - No third-party downloads needed — all helper functions (including the
edfread-based EDF reader and themultiWaitbarprogress bar) are bundled in this repository.
Create the following folders in the same directory as
feature_analysis_pipeline.m (or update the paths at the top of that
script to point elsewhere):
your_project/
├── feature_analysis_pipeline.m
├── subfunctions/ <- put all the other .m files here
├── data/ <- your .edf recordings go here
│ ├── subject01.edf
│ ├── subject02.edf
│ └── ...
├── artifact_annotations/ <- optional, see 1.3
└── results/ <- created automatically if missing
Each .edf file must:
-
Have a sampling rate ≥ 250 Hz.
-
Contain (or allow derivation of) the following 8 channels, in this exact order, as they will be assembled by
get_montages.m:F3(monopolar/referential)F4(monopolar/referential)P3(monopolar/referential)P4(monopolar/referential)F3–P3("Left" bipolar)F4–P4("Right" bipolar)F3–F4("Frontal" bipolar)P3–P4("Parietal" bipolar)
Channel labels are matched case-insensitively against common variants (e.g.
F3,F3-Ref,EEGF3-REF,F3P3,F3-P3, etc.) — seeget_montages.mif your file uses a label not already recognized, and add it to the matching list there. -
If a required channel is missing from a file, that channel will simply be left as zeros for that subject — always sanity-check the console output and your channel labels beforehand.
The pipeline supports excluding artifact-contaminated samples from feature
calculation. By default (feature_analysis_pipeline.m, section "Construct
artifact masks"), it uses a dummy all-zero mask (i.e. no artifacts are
excluded). To use real artifact annotations:
- Produce, for each subject, a binary matrix of size
[time_samples x 8 channels], where1= artifact/missing data and0= clean data, in the same channel order as above and at the same sampling rate as the raw EDF (i.e. before resampling). - Store these matrices in a cell array named
artifact_masks, one cell per subject, in the same subject order asmontages(i.e. the same orderget_montages.mreads the.edffiles in — alphabetical by filename). - Replace the dummy-mask section in
feature_analysis_pipeline.mwith code that loads yourartifact_maskscell array (e.g. from a.matfile inartifact_annotations/) instead of generating zeros.
If you don't have an artifact detector, you can leave the dummy mask in place — no epochs will be excluded — but be aware this means artifacts in your raw data will affect the computed features.
- Open
feature_analysis_pipeline.min MATLAB. - Edit the "FOLDER & FILE PATHS, OTHER VARIABLES" section at the top:
data_path,results_path,annotation_path— adjust if your folder names differ from the structure in §1.1.epoch_length_seconds/epoch_overlap_seconds— epoch window and overlap (defaults: 120 s epochs, 60 s overlap).global_highpass,global_lowpass,global_gate,new_sampling_rate— global filtering/resampling settings applied to all channels before feature extraction (defaults: 0.4–30 Hz bandpass, ±1000 µV artifact gate, resampled to 250 Hz).artifact_percentage_thresholds— per-feature tolerance (0–1) for how much of an epoch may be marked as artifact before that feature is skipped for that epoch (see the comments in that section for details).
- Run the script section by section (recommended, since importing and
preprocessing large EDF datasets can take a while and it's useful to
inspect intermediate results):
- Import: reads all
.edffiles indata_pathand assembles the 8-channel montage per subject (get_montages.m). - Artifact masks: loads/generates the artifact mask per subject.
- Preprocessing: applies the global bandpass filter, amplitude gate,
and resampling (
preprocess.m). - Epoching: splits the continuous data (and artifact mask) into
overlapping epochs (
epoch_data.m). - Feature extraction: computes all features per subject/epoch
(
get_features.m) and saves them tofeatures.matin the current working directory.
- Import: reads all
- Progress bars (via
multiWaitbar) show import/preprocessing/epoching progress and per-subject/per-epoch progress during feature extraction, since this last step is the most time-consuming.
Running the pipeline saves a single file, features.mat, containing one
variable:
features % 1 x N_subjects struct array (N_subjects = number of .edf files)Each element features(i) corresponds to one subject (in the same order
the .edf files were read, i.e. alphabetical by filename in data_path),
and has the following fields:
| Field | Size | Meaning |
|---|---|---|
ASI |
[8 x 8 x epochs] |
ASI between every channel pair, per epoch |
wPLI |
[8 x 8 x 4 x epochs] |
wPLI per channel pair, in 4 frequency bands (0.4–3, 3–8, 8–13, 13–22 Hz), per epoch |
NC |
[2 x 3 x epochs] |
Nestedness/PAC: row 1 = Frontal (avg. F3/F4), row 2 = Parietal (avg. P3/P4); 3 frequency bands (3–8, 8–15, 15–30 Hz) |
aEEG |
struct with .mean, .iqr, each [8 x epochs] |
amplitude-integrated EEG per monopolar/bipolar channel |
rEEG |
struct with .mean, .iqr, .li, each [8 x epochs] |
range-EEG mean, IQR, and lower 5th percentile per channel |
PSD |
[8 x 4 x epochs] |
Power spectral density per channel, in 4 bands (1–3, 3–8, 8–15, 15–30 Hz) |
cPSD |
[8 x 8 x 4 x epochs] |
Cross power spectral density per channel pair, same 4 bands as PSD |
SC |
[1 x epochs] |
Suppression Curve value per epoch |
Notes:
-
The 8 channels are always in the fixed order:
[F3, F4, P3, P4, Left(F3-P3), Right(F4-P4), Frontal(F3-F4), Parietal(P3-P4)]. -
NaNvalues indicate that an epoch (or channel/channel-pair, for cross-channel features) was skipped because its artifact percentage exceeded the threshold set inartifact_percentage_thresholdsfor that feature. -
If a subject's
.edffile could not be read, that subject's fields will simply beNaNinstead of arrays. -
To load and inspect results in a new MATLAB session:
load('features.mat') features(1).PSD % PSD for subject 1 squeeze(features(1).ASI(1,2,:)) % ASI between F3 and F4 across all epochs
- Pipeline maintained by Saeed Montazeri, BabaCenter, 2026.
- Original pipeline structure: BabaCenter, 2019.
get_artifact_prcnt.m,get_line_length.m: original algorithms by Timo Vehviläinen, BabaCenter, 2019.my_aeeg.m: original algorithm by Nathan Stevenson, University of Helsinki, Finland, 2017.getASI.m/getFFT.m/ETDF.m: Activation Synchrony Index method by Okko Räsänen & Sampsa Vanhatalo (see Räsänen & Vanhatalo, NeuroImage, 2013).edfreadUntilDone.m: adapted fromedfread.mby Brett Shoelson, PhD, © 2009–2012 The MathWorks, Inc.multiWaitbar.m: by Ben Tordoff, © 2007–2014 The MathWorks, Inc.
Please retain the above attributions in any redistribution of this code.