Station 7 · Lesson 4

Publication-ready figures

Recommended12 minBefore this lesson: Scalp maps (topoplot), Plotting channel ERPs (coming soon)Print 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 correction, Rejecting bad epochsDownload the data (opens in a new tab)
Script to reach this state
MATLAB
% Shortcut: if you saved D1_epochs_clean.set in the epoch rejection lesson, keep
% the first line and the last two lines (pop_newset, eeglab redraw), and replace
% every line between them with:
% EEG = pop_loadset('D1_epochs_clean.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 eye and muscle components, then low-pass at 30 Hz
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
EEG = pop_eegfiltnew(EEG, 'hicutoff', 30);

% Epochs around 'square', baseline, and +/-100 uV rejection
EEG = pop_epoch(EEG, {'square'}, [-1 2], 'newname', 'D1 epochs', 'epochinfo', 'yes');
EEG = pop_rmbase(EEG, [-200 0]);
EEG = pop_eegthresh(EEG, 1, 1:EEG.nbchan, -100, 100, EEG.xmin, EEG.xmax, 0, 0);
badEpochs = find(EEG.reject.rejthresh);
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);
EEG.filename = ''; EEG.filepath = ''; EEG.datfile = ''; EEG.saved = 'no';
[ALLEEG, EEG, CURRENTSET] = pop_newset(ALLEEG, EEG, 0, 'setname', 'D1 epochs clean', 'gui', 'off');
eeglab redraw;

The idea in brief

A figure you understand on your screen is not a figure ready for a paper. The journal shrinks it to about one column wide, small text becomes unreadable, and a screenshot turns blurry when zoomed.

Before and after

Show
Pz plot for two conditions captured from the screen and shrunk: the text is unreadable, the lines are thin, the edges are jagged, and the vertical axis has no unit.
Before: a shrunk screen captureData: EEGLAB sample data (eeglab_data.set)
The same plot exported with exportgraphics: white background, two lines in two colours and two styles (solid and dashed), the vertical axis labelled Potential (µV), and trial counts in the legend.
After: a vector file, shown here as an imageData: EEGLAB sample data (eeglab_data.set)

Your next questions, each answered in its own section:

Why do I need this step?

The reasons in detail, and the terms you need

Honesty matters even more than looks. Two maps of two conditions drawn on different color scales suggest a difference that is not there, or hide one that is. Reviewers notice.

This lesson takes two figures from the previous lessons and prepares them for publication: a vector format, a fixed size, a readable font, one color scale, and a data file that backs up what the figure shows.

How do I do it in EEGLAB?

10 steps in the menus

Start with the D1 epochs clean datasetOne EEG data file loaded in EEGLAB together with its information. loaded, as in “Starting point”. For the maps, use D1 epochs clean scalp, the 30-channelThe signal recorded from one electrode, as shown in EEGLAB. copy without EOG1 and EOG2. If it is not open, make it as in step 0 of the scalp maps lesson (Edit then Select data). The GUIThe windows and menus used with the mouse instead of typed commands. is enough for maps of one dataset. Comparing two conditions in one figure needs the scriptA MATLAB file of commands that runs processing steps in order., as in the channel ERPThe brain's average response to a given event across many trials. lesson.

1. Find the scale limit

  1. With D1 epochs clean scalp selected, open Plot then ERP map series then In 2-D.
  2. Enter 100 200 300 400 in Plotting ERP scalp maps at these latencies and [1 4] in Plot geometry (rows,col.); [] -> near square. Click Ok.
  3. Read the top value on the color bar and round it up to a whole number. That is your limit.

2. Fix the scale and export

  1. Open the same window again. In the additional options field, enter 'electrodes', 'on', 'maplimits', [-5 5], replacing 5 with your limit. Use the same limit for every map you will compare with this one, even from another dataset.
  2. In the figure window, open File then Save As and choose PDF or SVG as the file type. This is MATLAB’s own menuA list of commands that opens from its name at the top of the window., not an EEGLAB menu.
  3. Open the file and zoom in: lines and letters stay sharp, and you can select the label text. That is what “vector” means.

3. Export the numbers behind the figure

  1. Open File then Export then Data and ICA activity to text file.
  2. In Output file name, enter erp_all.txt.
  3. Check Export ERP average instead of trials:, and leave Transpose matrix (elec -> cols):, Export channel labels/component numbers: and Export time values: checked as they are. Click Ok.
  4. You get a text file: the first row holds channel names, the first column holds time in milliseconds, and each following row is one time point. Put it in the paper’s supplementary material, or plot from it in another program.

How do I write it as code?

The same steps in 46 lines of MATLAB
MATLAB
EEG = pop_loadset('D1_epochs_clean.set', pwd);
pos = eeg_getepochevent(EEG, 'type', {'square'}, 'timewin', [-5 5], 'fieldname', 'position');
EEG_pos1 = pop_select(EEG, 'trial', find(pos == 1));
EEG_pos2 = pop_select(EEG, 'trial', find(pos == 2));
EEG_pos1.setname = 'D1 epochs clean pos1';
EEG_pos2.setname = 'D1 epochs clean pos2';
chan = find(strcmp({EEG.chanlocs.labels}, 'Pz'));
scalp = find(~ismember({EEG.chanlocs.labels}, {'EOG1', 'EOG2'}));   % EOG is not on the average reference
erp1 = mean(EEG_pos1.data, 3);
erp2 = mean(EEG_pos2.data, 3);

% Figure 1: Pz waveforms at single-column size, 9-pt font, white background
fig1 = figure('Color', 'w', 'Units', 'centimeters', 'Position', [2 2 9 7]);
hold on;
plot(EEG.times, erp1(chan, :), 'Color', [0 0.45 0.74], 'LineWidth', 1.2);
plot(EEG.times, erp2(chan, :), 'Color', [0.85 0.33 0.10], 'LineWidth', 1.2, 'LineStyle', '--');
xline(0, 'k:'); yline(0, 'k-');
xlim([-200 800]);
set(gca, 'YDir', 'normal', 'FontName', 'Arial', 'FontSize', 9, 'TickDir', 'out', 'Box', 'off');
xlabel('Time (ms)'); ylabel('Potential (\muV)');
legend({sprintf('Position 1 (n = %d)', EEG_pos1.trials), ...
        sprintf('Position 2 (n = %d)', EEG_pos2.trials)}, 'Location', 'northeast', 'Box', 'off');
title('Pz', 'FontWeight', 'normal');
exportgraphics(fig1, 'fig_pz_erp.pdf', 'ContentType', 'vector', 'BackgroundColor', 'white');

% Figure 2: 300 to 500 ms maps for both conditions on one shared symmetric scale
win = EEG.times >= 300 & EEG.times <= 500;
map1 = mean(erp1(scalp, win), 2);
map2 = mean(erp2(scalp, win), 2);
lim = ceil(max(abs([map1; map2])));
fprintf('Shared color scale: -%d to %d uV\n', lim, lim);
fig2 = figure('Color', 'w', 'Units', 'centimeters', 'Position', [2 2 12 6]);
subplot(1, 2, 1);
topoplot(map1, EEG.chanlocs(scalp), 'maplimits', [-lim lim], 'electrodes', 'on');
title('Position 1', 'FontName', 'Arial', 'FontSize', 9, 'FontWeight', 'normal');
ax = subplot(1, 2, 2);
topoplot(map2, EEG.chanlocs(scalp), 'maplimits', [-lim lim], 'electrodes', 'on');
title('Position 2', 'FontName', 'Arial', 'FontSize', 9, 'FontWeight', 'normal');
axPos = ax.Position;
cb = colorbar('FontName', 'Arial', 'FontSize', 9);
ax.Position = axPos;   % keep both maps the same size
cb.Position = [0.92 0.25 0.02 0.5];
ylabel(cb, '\muV');
exportgraphics(fig2, 'fig_maps_300_500.pdf', 'ContentType', 'vector', 'BackgroundColor', 'white');
exportgraphics(fig2, 'fig_maps_300_500.png', 'Resolution', 600, 'BackgroundColor', 'white');

% The numbers behind the figures: one row per time point, one column per channel
pop_export(EEG_pos1, 'erp_pos1.txt', 'erp', 'on', 'transpose', 'on', 'elec', 'on', 'time', 'on', 'precision', 4);
pop_export(EEG_pos2, 'erp_pos2.txt', 'erp', 'on', 'transpose', 'on', 'elec', 'on', 'time', 'on', 'precision', 4);

Which value should I choose?

Suitable values for your analysis goal
SettingValueWhy
FormatPDF, SVG or EPS (vector) for waveforms, and PNG at 600 dpi for maps if the vector file is heavyVector files stay sharp at any zoom, and journals usually accept them. A scalp mapA top-down picture showing how a value is spread over the head. holds thousands of colored polygons, so its vector file can get large. Never use JPEG, which adds noiseAny part of the recording that does not carry the information we want. around lines.
WidthAbout 9 cm for one column, about 18 cm for twoDraw at the final size from the start instead of shrinking later. Check the journal’s guide, as sizes differ slightly.
Font size8 to 10 pt at final sizeBelow 7 pt is unreadable in print. Because you draw at final size, what you choose here is what the reader sees.
Lines1 to 1.5 pt, with two different styles for two conditionsLine style (solid and dashed) separates the conditions even in black-and-white print, and for readers who have trouble telling colors apart.
Color scaleOne symmetric scale for all maps being compared, with a color bar and its unitThis is the single most important honesty check. Round the limit to a whole number and state it in the caption.
PolarityPositive up, and say soSome labs plot negative up. Whatever you choose, the reader needs to see it on the axis or in the caption.
exportgraphicsNeeds MATLAB R2020a or laterOn an older release, use print(fig1, 'fig_pz_erp', '-dpdf', '-painters').

How do I check it worked?

4 quick checks
  • Zoom the PDF to 400%: waveforms and letters stay sharp, and you can select the axis text with the cursor.
  • The page size of fig_pz_erp.pdf is about 9 × 7 cm in the file properties.
  • The two maps in fig_maps_300_500.pdf have 30 electrodeThe physical sensor placed on the scalp to pick up the signal. dots each, none below the eyes, are the same size and share one color bar whose limits match the number printed by the Shared color scale line.
  • erp_pos1.txt has a header row, then 384 rows (one point every 7.8 ms from -1000 to 1992 ms), and 33 columns: time, then 32 channels.

What mistakes do people make?

6 mistakes and how to avoid them
  • Screenshots. They have your screen’s resolution and degrade in print. Always export the figure from MATLAB.
  • Drawing large, then shrinking. A 10-pt font on a 25-cm figure becomes 3.6 pt once shrunk to 9 cm. Set the size before you plot.
  • One color scale per map. If you plot each conditionAn experimental situation compared with others, such as familiar vs unfamiliar. in its own window, EEGLAB picks each window’s limits from its own data. Set maplimits to the same value in every window.
  • EEGLAB’s light blue background. EEGLAB plot windows default to a very light blue. Make the background white on export, as the script does.
  • An incomplete caption. Under every ERP figure, state the channel, referenceThe point against which each channel's voltage is measured., filters, trials per condition and polarity. Without them the figure cannot be interpreted.
  • Polishing the data. Do not add smoothing or crop the axis to enlarge a difference. What you export must match the data file.

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.

Figures were exported as vector PDF files with MATLAB exportgraphics; scalp maps of both conditions (30 average-referenced scalp channels) share one symmetric color scale of ±[X] µV (topoplot, EEGLAB 2026.1).

Where can I read more?

2 sources, official docs first
  • EEGLAB TutorialDocumentation11. Write scripts > d. Advanced plots
  • EEGLAB TutorialDocumentationReference Topics > Exporting Data: Exporting data to an ASCII text file

Written for EEGLAB 2026.1 · 2026-10-28

Need help?

Ask, share feedback, or report an error