Station 5 · Lesson 3

Rejecting bad epochs

Core15 minBefore this lesson: Baseline correctionPrint the card
Starts from: eeglab_data.set (D1) · Already applied: High-pass & low-pass filtering, Removing line noise (50/60 Hz), Bad channels, Channel interpolation, Re-referencing, Running ICA, 1-Hz filtering for ICA & copying weights, ICLabel & removing components, Extracting epochs, Baseline correctionDownload the data (opens in a new tab)
Script to reach this state
MATLAB
% Shortcut: if you saved D1_main_clean_lp30.set in the ICLabel lesson, keep
% the first line and the last three lines, and replace every line between them with:
% EEG = pop_loadset('D1_main_clean_lp30.set', pwd);
[ALLEEG, EEG, CURRENTSET, ALLCOM] = eeglab;
eeglabRoot = fileparts(which('eeglab'));
EEG = pop_loadset('eeglab_data.set', fullfile(eeglabRoot, 'sample_data'));
fullLocs = EEG.chanlocs;

% Main dataset: high-pass at 0.1 Hz, then 60 Hz line-noise removal
EEG = pop_eegfiltnew(EEG, 'locutoff', 0.1);
EEG = pop_eegfiltnew(EEG, 'locutoff', 58, 'hicutoff', 62, 'revfilt', 1);

% Bad channels you removed earlier, e.g. {'T7'}; leave empty if none
badChans = {};
if ~isempty(badChans)
    EEG = pop_select(EEG, 'rmchannel', badChans);
    EEG = pop_interp(EEG, fullLocs, 'spherical');
end

% Average reference, EOG channels excluded
eogIdx = find(ismember({EEG.chanlocs.labels}, {'EOG1', 'EOG2'}));
EEG = pop_reref(EEG, [], 'exclude', eogIdx);

% ICA on a 1-Hz copy, then copy the weights back
EEG_ica = pop_eegfiltnew(EEG, 'locutoff', 1);
EEG_ica = pop_runica(EEG_ica, 'icatype', 'runica', 'extended', 1);
EEG.icaweights  = EEG_ica.icaweights;
EEG.icasphere   = EEG_ica.icasphere;
EEG.icachansind = EEG_ica.icachansind;
EEG.icawinv     = [];
EEG.icaact      = [];
EEG = eeg_checkset(EEG);

% Remove components ICLabel rates above 90% Eye or Muscle
EEG = pop_iclabel(EEG, 'default');
EEG = pop_icflag(EEG, [NaN NaN; 0.9 1; 0.9 1; NaN NaN; NaN NaN; NaN NaN; NaN NaN]);
artifactComps = find(EEG.reject.gcompreject);
if ~isempty(artifactComps)
    EEG = pop_subcomp(EEG, artifactComps, 0);
end

% Low-pass at 30 Hz
EEG = pop_eegfiltnew(EEG, 'hicutoff', 30);
EEG.setname = 'D1 main clean lp30';

% Epochs around 'square', baseline -200 to 0 ms
EEG = pop_epoch(EEG, {'square'}, [-1 2], 'newname', 'D1 epochs', 'epochinfo', 'yes');
EEG = pop_rmbase(EEG, [-200 0]);
EEG.setname = 'D1 epochs bl';
EEG = eeg_checkset(EEG);
EEG.filename = ''; EEG.filepath = ''; EEG.datfile = ''; EEG.saved = 'no';
[ALLEEG, EEG, CURRENTSET] = pop_newset(ALLEEG, EEG, 0, 'setname', 'D1 epochs bl', 'gui', 'off');
eeglab redraw;

The idea in brief

ICAA method that separates the recording into independent sources, some brain and some artifact. removed most blinks, but a few epochA short time segment cut out around an event. may still have a problem: a sudden movement, an electrodeThe physical sensor placed on the scalp to pick up the signal. that shifted for a moment, or a large blinkClosing and opening the eye; seen as large peaks on frontal channels. that ICA did not fully remove. One epoch with a peak of hundreds of microvolts is enough to distort the whole average.

Before and after

Try different thresholds and watch the number of rejected epochs and the shape of the average.

The simulation needs JavaScript. Here is a still image of the same idea.

Your next questions, each answered in its own section:

Why do I need this step?

The reasons in detail, and the terms you need

The simple fix: reject any epoch in which the amplitudeThe size of a signal, measured in microvolts (µV) in EEG. crosses a set limit in any channelThe signal recorded from one electrode, as shown in EEGLAB..

But rejectionExcluding epochs or data that are unfit for analysis. has a cost. Every epoch you reject is one trialOne repetition of the task, usually around a single stimulus. less in the average, and an average of fewer trials is noisier. So the goal is not to reject everything that looks odd, but to reject what actually harms the average.

How do I do it in EEGLAB?

16 steps in the menus

Start with the datasetOne EEG data file loaded in EEGLAB together with its information. D1 epochs bl open, as in “Starting point”.

Why legacy?

EEGLAB calls these menus legacy, because it prefers cleaning the continuous data before epoching (for example with clean_rawdata). But rejecting epochs with an amplitude thresholdA value beyond which the signal is treated as a problem. after ICA is still a common step in ERPThe brain's average response to a given event across many trials. analysis, and it is the one Luck’s book teaches.

First: mark the extreme epochs and reject them with the scriptA MATLAB file of commands that runs processing steps in order.

The epoch rejection menu is hidden by default in EEGLAB 2026.1, so we start with the script.

  1. Copy the whole block from “The equivalent script” below into the command window.
  2. The command window prints the number of marked epochs, for example Marked 1 of 80 epochs. The script removes the marked epochs, names the dataset D1 epochs clean, saves it as D1_epochs_clean.set, and adds it to the Datasets menu. The lessons in stations 6 and 7 start from it.
  3. If it prints Marked 0 of 80 epochs, there is nothing to reject. The new dataset is a copy of the previous one with the new name, and that is correct.

Second (optional): the same step from the menu

This is an alternative to the script, not an extra step. The menu is Tools then Reject data epochs then Reject extreme values, but it is hidden by default: under the Tools menu you will see a gray line that reads (Expand tool choices via “File > Preferences”). To show it:

  1. Select D1 epochs bl from the Datasets menu, and save it with File then Save current dataset as, as D1_epochs_bl.set.
  2. Open File then Preferences, check If set, show all menu items from previous EEGLAB versions. You must restart EEGLAB for this to take effect., then press Ok.
  3. Restart EEGLAB: type eeglab in the command window, then open the file with File then Load existing dataset.

This option stays saved, and it changes how all the menus look compared with the pictures in these lessons, so turn it off after this step. Once the menu shows:

  1. Open Tools then Reject data epochs then Reject extreme values.
  2. The Rejection abnormal elec. values — pop_eegthresh() window opens. Keep Electrode (indices(s), Ex: 2 4 5): at 1:32, that is, all channels.
  3. In Minimum rejection threshold(s) (uV, Ex:-20 -10 -15):, type -100 instead of the default -10.
  4. In Maximum rejection threshold(s) (uV, Ex: 20 10 15):, type 100 instead of the default 10.
  5. Keep Start time limit(s) (seconds, Ex -0.1 0.3): and End time limit(s) (seconds, Ex 0.2): at their values, that is, the whole epoch.
  6. Check Reject marked trial(s) and press Ok.
  7. A window showing the epochs opens, with the marked epochs highlighted. Review them by eye: you can click an epoch to unmark or mark it.
    • If the command window prints 0/80 trials marked for rejection, there is nothing to reject, and the REJECT button will do nothing. Close the viewing window, open Edit then Dataset info, type D1 epochs clean in Dataset name, press Ok, and go to step 10.
  8. Press REJECT, then confirm with YES.
  9. In the new-dataset window, type D1 epochs clean in Name it:, then press Ok.
  10. Save it with File then Save current dataset as, as D1_epochs_clean.set. The lessons in stations 6 and 7 start from it.
The default limits

The default limits in the window (-10 to 10 µV) are examples of the format, not a recommendation. With them, almost every epoch would be rejected.

How do I write it as code?

The same steps in 14 lines of MATLAB
MATLAB
% Mark epochs where any channel goes beyond +/-100 uV anywhere in the epoch
EEG = pop_eegthresh(EEG, 1, 1:EEG.nbchan, -100, 100, EEG.xmin, EEG.xmax, 0, 0);
badEpochs = find(EEG.reject.rejthresh);
fprintf('Marked %d of %d epochs\n', numel(badEpochs), EEG.trials);

% Remove the marked epochs
if ~isempty(badEpochs)
    EEG = pop_rejepoch(EEG, EEG.reject.rejthresh, 0);
end
EEG.setname = 'D1 epochs clean';
EEG = eeg_checkset(EEG);
EEG = pop_saveset(EEG, 'filename', 'D1_epochs_clean.set', 'filepath', pwd);

% Add the clean dataset to the EEGLAB window
[ALLEEG, EEG, CURRENTSET] = eeg_store(ALLEEG, EEG);
eeglab redraw

The last argument of pop_eegthresh is 0, meaning “mark only, do not reject”. The rejection happens on the next lines, so you can print the count first.

Which value should I choose?

Suitable values for your analysis goal
SettingValueWhy
Amplitude threshold after ICA cleaning±100 µVA common starting rule. Brain activity in EEG is usually tens of microvolts, so anything above 100 is most likely an artifact.
A strict threshold±50 µV or lessRejects clean epochs because of normal noise and slow waves. Try it in the exercise above.
ChannelsAll channels, including EOGAfter ICA the EOG channels are free of blinks, so if a large peak remains there in an epoch, that epoch deserves rejection.
Time windowThe whole epochThe simplest choice. You can narrow it to the period where you measure the ERP.
Minimum number of trialsNo general numberIt depends on the size of the ERP component. If more than about a quarter of the trials are rejected, check the data before you raise the threshold.

Rule: the same threshold for every participant and every condition, decided before you see the results. Report the number of rejected epochs per condition in your paper.

How do I check it worked?

4 quick checks
  • The command window prints the number marked, such as 1/80 trials marked for rejection. In D1 after ICA cleaning, expect a small number, about 0 to 3. It varies slightly from run to run because the ICA result varies.
  • Epochs in the main window equals 80 minus the number rejected.
  • max(abs(EEG.data(:))) is below 100.
  • The file D1_epochs_clean.set is in your working folder.

What mistakes do people make?

5 mistakes and how to avoid them
  • A threshold that is too strict. It throws away good data, so the average comes from few trials and is noisier. The best threshold is not the smallest possible number.
  • Rejecting before baseline correction. An epoch shifted away from zero as a whole may cross the limit with no artifact. Correct the baseline first, as in our path.
  • Rejecting instead of using ICA. If you reject every epoch with a blink without ICA, you may lose a large part of your data, as the interactive demo shows at ±50. ICA fixes blinks; rejection is for rare problems.
  • Very different counts between conditions. If 40% of one condition and 5% of another are rejected, the comparison is not fair. Check the count for each condition.
  • Forgetting to save after rejection. In the GUI, rejection creates a new dataset in memory only. Save it before you close EEGLAB.

How do I write it in my paper?

A Methods template: edit the data, settings and versions to match what you ran

A template: edit the data, settings and versions to match what you ran.

Epochs in which the voltage at any channel exceeded ±100 µV were rejected (pop_eegthresh, EEGLAB 2026.1), leaving [N] of 80 epochs.

Where can I read more?

3 sources, official docs first

Written for EEGLAB 2026.1 · 2026-10-28

Need help?

Ask, share feedback, or report an error