[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