[Eeglablist] Advice on filtering strategy before ICA (EEGLAB pipeline; FAA, beta/alpha ratio/ PSD)

Kathryn Bolton kathryn.bolton at torontomu.ca
Tue Apr 28 15:21:07 PDT 2026


Hi EEGLAB mailing list,

I hope you're having a good week so far! I’m a PhD student working on an
EEG study examining affective responses to music in older adults.
Participants listen to multiple 1-minute music samples, and I plan to
compute frontal alpha asymmetry (FAA) and beta/alpha ratio (PSD-based
measures) and relate these to valence/arousal ratings.

I’ve structured my pipeline into three stages, partly to distribute work
across research assistants:

   - *Script 1:* preprocessing + ICA (run by RAs; outputs ICA-ready
   datasets)
   - *Script 2:* manual ICA inspection/cleaning (topography, spectrum,
   timecourse), followed by channel interpolation
   - *Script 3:* epoching, PSD computation, and extraction of FAA and
   beta/alpha ratio

In my original pipeline, I noticed that the component maps used for
inspection in Script 2 appeared a bit off (e.g., nearly uniform colour
across components). I’ve since revised Script 1 to the following workflow:

   - Re-reference to linked mastoids prior to ICA (BioSemi recommendations)
   - High-pass filter at 0.5 Hz only, removing a 50 Hz low-pass filter at
   this stage
   - Downsample to 512 Hz
   - Automated detection and removal of bad channels (logged), prior to ICA
   - Run ICA using rank-adjusted PCA

This seems to have resolved the issue with the component maps. But my
question about filtering is whether you recommend applying only a high-pass
filter prior to ICA (as above), and then applying the full bandpass (e.g.,
including a low-pass) later, prior to PSD analysis?

I want to ensure that the filtering approach is appropriate both for ICA
decomposition and for downstream frequency-based analyses (FAA and
beta/alpha ratio).

Thank you very much for your time and advice, I really appreciate it!

Best regards,
Kathryn


*Kathryn Bolton, MA*

Pronouns: she/her

PhD Candidate | Clinical Psychology

Department of Psychology | Cognitive Aging Lab (CAL)

Toronto Metropolitan University

*E-mail:* *kathryn.bolton at t <kathryn.bolton at ryerson.ca>orontomu.ca
<https://urldefense.com/v3/__http://orontomu.ca__;!!Mih3wA!HEf3RTkd7gvWzdftBOsrCvgP_ahawX4XGp6D3OMOOK2ck_zqZyrcOpLjBJdpl7-leggZto82bqKspbCJ49hb7OlEv45uNvcNR5M$ >*

*Website*: *https://urldefense.com/v3/__https://psychlabs.torontomu.ca/cal/portfolio/kathryn-bolton/__;!!Mih3wA!HEf3RTkd7gvWzdftBOsrCvgP_ahawX4XGp6D3OMOOK2ck_zqZyrcOpLjBJdpl7-leggZto82bqKspbCJ49hb7OlEv45uwX-37m8$ 
<https://urldefense.com/v3/__https://psychlabs.torontomu.ca/cal/portfolio/kathryn-bolton/__;!!Mih3wA!HEf3RTkd7gvWzdftBOsrCvgP_ahawX4XGp6D3OMOOK2ck_zqZyrcOpLjBJdpl7-leggZto82bqKspbCJ49hb7OlEv45uwX-37m8$ >*


*Due to the volume of emails I receive, I will do my best to respond within
2 business days. If you require an immediate response or if it is an
emergency, please put "urgent" in the subject line. Thank you for your
understanding. *
-------------- next part --------------
% ===== SCRIPT 1: PREPROCESSING + ICA =====

% 1. Load the BDF file
filename = 'A5_001_pre';
EEG = pop_biosig('A5_001_pre_10jan2023.bdf');


% 2. Load electrode locations
EEG = pop_chanedit(EEG, 'lookup', 'standard-10-5-cap385.elp');


% 3. Re-reference to channels 65 and 66
EEG = pop_reref(EEG, [65 66]);


% 4. Keep only scalp channels 1 through 64
% This removes the reference channels and any other extra sensors
EEG = pop_select(EEG, 'channel', 1:64);

% Save channel locations for later interpolation
original_chanlocs = EEG.chanlocs;
save([filename '_chanlocs.mat'], 'original_chanlocs');


% 5. High-pass filter  - improves ICA stability
EEG = pop_eegfiltnew(EEG, 'locutoff', 0.5); 


% 6. Downsample to 512 Hz (faster ICA)
EEG = pop_resample(EEG, 512);


% 7. Detect bad channels (automated)
[~, badChans] = pop_rejchan(EEG, ...
    'threshold',5, ...
    'norm','on', ...
    'measure','kurt');

disp('Automatically removing channels:');
disp(badChans);

if numel(badChans) > 10
    warning('More than 10 channels flagged. Consider reviewing this dataset.');
end

% OPTIONAL (PI use only): Visual inspection of channels before removal
% pop_eegplot(EEG, 1, 1, 1);


% 8. Remove bad channels automatically
EEG = pop_select(EEG, 'nochannel', badChans);
EEG = eeg_checkset(EEG);
save([filename '_badChans.mat'], 'badChans');


% 9. Compute data rank (important after channel removal)
rankEEG = min(rank(double(EEG.data')), size(EEG.data,1)-1);

% Save preprocessing log
save([filename '_preprocLog.mat'], 'badChans', 'rankEEG');


% 10. Run ICA
EEG = pop_runica(EEG, ...
    'extended', 1, ...
    'stop', 1e-7, ...
    'pca', rankEEG);


% 11. Classify components (ICLabel)
EEG = pop_iclabel(EEG, 'default');


% 12. Flag likely artifact ICs
EEG = pop_icflag(EEG, [...
    NaN NaN;  % Brain (keep)
    0.9 1;    % Muscle
    0.9 1;    % Eye
    NaN NaN;  % Heart
    NaN NaN;  % Line Noise
    0.9 1;    % Channel Noise
    0.9 1]);  % Other


% 13. Save flagged ICs (for Script 2)
flagged_components = find(EEG.reject.gcompreject);
disp('Flagged ICs:');
disp(flagged_components);


% 14. Final consistency check
EEG = eeg_checkset(EEG);


% 15. Save dataset for ICA inspection (Script 2)
pop_saveset(EEG, 'filename', [filename '_ICAready.set']);
save([filename '_flaggedICs.mat'], 'flagged_components');

disp('Script 1 complete');

% ------------------------------------


More information about the eeglablist mailing list