Viewing Signals

Initialize NeuroAnalyzer
using NeuroAnalyzer
NeuroAnalyzer.progress_bar = false
eeg = import_edf("files/eeg/eeg.edf")
# filtered 0.5-35 Hz (removes the DC offsets and the line noise of the raw
# recording)
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :butterworth,
                      ftype = :hp,
                      cutoff = 0.5,
                      order = 4)
NeuroAnalyzer.filter!(eeg;
                      ch = "all",
                      fprototype = :fir,
                      ftype = :lp,
                      cutoff = 35,
                      bw = 10)

plot() draws the signal of a NEURO object. It works in two modes:

  • static (gui = false): returns a figure, e.g. for a report or to save as an image,
  • interactive (gui = true, default): opens a window in which the signal can be scrolled, channels can be marked as bad and parts of the signal can be deleted.

The static mode is used in the examples below; the interactive mode is described in Interactive Mode.

Continuous Signals

By default, the first 10 seconds of all channels are drawn (at most n_channels = 20 channels at once); the time segment is set with seg (start and end in seconds). The beginning of this recording contains the transient of the high-pass filter, so the examples show a later segment:

NeuroAnalyzer.plot(eeg; seg = (100, 110), gui = false)

Channels are grouped by type (EEG, reference, EOG, ECG). All channels of a type share one amplitude scale, shown by the red bar: its height is the distance between two channels. The scale is computed from the whole recording, robustly (from the median absolute deviation of the channels), so that a single large artifact does not shrink the rest of the signal; such artifacts may extend into the neighboring channels. Each channel is centered at its median. Channels marked as bad are drawn in light gray.

Tip: Raw signals with DC offsets and slow drifts move away from their median and inflate the scale; filter them (at least with a high-pass filter) before viewing.

Channels are selected with ch (a label, a list of labels, a channel type or a regular expression):

NeuroAnalyzer.plot(eeg;
                   ch = ["Fp1", "Fp2", "EOG1", "EOG2"],
                   seg = (100, 105),
                   gui = false)

Butterfly plot

type = :butterfly overlays all channels of each type, with their average (avg = true, default) or with the mean and its 95% confidence interval (ci95 = true):

NeuroAnalyzer.plot(eeg;
                   ch = "eeg",
                   seg = (100, 110),
                   type = :butterfly,
                   gui = false)
NeuroAnalyzer.plot(eeg;
                   ch = "eeg",
                   seg = (100, 110),
                   type = :butterfly,
                   avg = false,
                   ci95 = true,
                   gui = false)

Other options

Argument Description
n_channels number of channels shown at once (default 20)
group_ch group channels by type (default true); false keeps their order
scale draw the scale bars (default true)
markers draw the markers (events), if there are any (default true)
mono monochrome palette (butterfly plots)
res draw every res-th sample: faster drawing of long segments
title, xlabel, ylabel plot labels

All 24 channels in the original order, without scale bars:

NeuroAnalyzer.plot(eeg;
                   seg = (100, 110),
                   n_channels = 24,
                   group_ch = false,
                   scale = false,
                   gui = false)

Markers

Markers (events) are drawn as dashed vertical lines with their ID and value:

faces = import_recording("files/eeg/s1_faces.vhdr")
# raw signal: filtered 0.5-35 Hz for viewing
NeuroAnalyzer.filter!(faces;
                      ch = "all",
                      fprototype = :butterworth,
                      ftype = :hp,
                      cutoff = 0.5,
                      order = 4)
NeuroAnalyzer.filter!(faces;
                      ch = "all",
                      fprototype = :fir,
                      ftype = :lp,
                      cutoff = 35,
                      bw = 10)
NeuroAnalyzer.plot(faces; ch = "eeg", seg = (20, 30), gui = false)

Epoched Signals

For epoched objects, plot() draws n_epochs consecutive epochs (default 5) starting from epoch ep; the epochs are separated by dotted vertical lines (the time axis is the time of the original recording):

e10 = epoch(eeg; ep_len = 10)
NeuroAnalyzer.plot(e10; ch = "eeg", ep = 3, n_epochs = 3, gui = false)

Comparing Two Signals

Two continuous objects with the same channels (e.g. before and after processing) can be overlaid; the first signal is drawn in blue, the second in black, both with the same amplitude scale:

eeg_alpha = NeuroAnalyzer.filter(eeg;
                                 ch = "all",
                                 fprototype = :butterworth,
                                 ftype = :bp,
                                 cutoff = (8, 12),
                                 order = 4)
NeuroAnalyzer.plot(eeg, eeg_alpha;
                   ch = ["O1", "O2", "P3", "P4"],
                   seg = (100, 105),
                   gui = false)

Saving Plots

plot_save() saves a figure as PNG or PDF:

p = NeuroAnalyzer.plot(eeg; gui = false)
plot_save(p; file_name = "eeg.png")

Interactive Mode

With gui = true (default), plot() opens a window with the signal:

NeuroAnalyzer.plot(eeg)
NeuroAnalyzer.plot(eeg; ch = "eeg", seg = (0, 20))

The call returns when the window is closed. Besides the signal, the window has:

  • a time bar (below the signal): the gray rectangle shows the displayed segment (for epoched signals: the displayed epochs),
  • a channel bar (right of the signal, type = :normal only): the gray rectangle shows the displayed channels.

The length of the displayed segment is set by seg (e.g. seg = (0, 20) shows 20 seconds) and stays the same while scrolling.

Mouse

Action Effect
left click on the time bar go to that time (epoch)
left click on the channel bar scroll to that channel
left click on a channel label print the channel details (channel_info())
right click on a channel label mark / unmark the channel as bad
left click on the signal continuous: place a marker; epoched: select an epoch
right click on the signal continuous: clear the selected segment

Keyboard

Key Continuous signal Epoched signal
← / → back / forward by 1 second back / forward by 1 epoch
Shift + ← / → back / forward by the segment length back / forward by n_epochs - 1
Home / End go to the start / end go to the first / last epochs
↑ / ↓ scroll channels up / down scroll channels up / down
Page Up / Page Down show more / fewer channels show more / fewer channels
s toggle snapping of markers -
d delete the selected segment delete the selected epochs

Channels marked as bad are drawn in light gray, and the mark is saved in the object header (obj.header.recording[:bad_channel]), so it is kept after the window is closed; see Bad channels and epochs.

Tip: Zooming and panning with the mouse are disabled, so that clicks are used only for the actions above; to show a longer or shorter segment, open the plot again with a different seg, and to show more channels use Page Up or n_channels.

Deleting Segments and Epochs

Bad parts of the signal (e.g. movements, electrode artifacts) can be removed while viewing the signal in the interactive mode, or with trim() and delete_epoch().

Important: the interactive mode modifies the object passed to plot() in place. To keep the original signal, view a copy:

eeg_edit = deepcopy(eeg)
NeuroAnalyzer.plot(eeg_edit)

Deleting a Segment of a Continuous Signal

In the interactive mode (type = :normal):

  1. left click on the signal at the start of the segment: the first marker (blue line) is placed,
  2. left click at the end of the segment: the second marker is placed and the segment between them is highlighted; further clicks move the second marker (the markers are swapped if needed, so the first is always the earlier one),
  3. press d to delete the segment, or right click on the signal to clear the selection.

By default, markers snap to 0.1 s (snap = true); press s (or use snap = false) to place them at the exact mouse position. After the deletion, the signal is redrawn and the following part moves back.

The same without the interactive mode, with trim() (segment in seconds):

eeg_tr = trim(eeg; seg = (60, 70))
println("duration: ", signal_duration(eeg), " s → ",
        round(signal_duration(eeg_tr); digits = 2), " s")
duration: 1205.0 s → 1195.0 s

The deleted segment is cut out and the signal after it is joined to the signal before it. A DELETED marker is added at the junction (the cut creates a discontinuity, which should not be analyzed as a signal feature), markers inside the segment are removed and the later markers are moved back:

eeg_tr.markers
1×5 DataFrame
Row id start length value channel
String Float64 Float64 String Int64
1 NA 60.0 0.0 DELETED 0

Several segments are deleted starting from the last one, so that the times of the earlier segments do not change:

bad_seg = [(30, 32), (60, 70), (100, 101.5)]
eeg_tr = deepcopy(eeg)
for seg in sort(bad_seg; rev = true)
    trim!(eeg_tr; seg = seg)
end
println("duration: ", round(signal_duration(eeg_tr); digits = 2), " s")
duration: 1191.49 s

Tip: Deleting segments shortens the recording and creates discontinuities. For analyses based on epochs, it is often better to split the signal into epochs first and delete the bad epochs.

Deleting Epochs

In the interactive mode, a left click on an epoch selects it (highlighted in orange; a second click deselects it); d deletes all selected epochs. All epochs cannot be deleted.

e10_edit = deepcopy(e10)
NeuroAnalyzer.plot(e10_edit)

The same with delete_epoch() (or keep_epoch() to keep the listed epochs):

e10_del = delete_epoch(e10; ep = [2, 5, 9])
println("epochs: ", nepochs(e10), " → ", nepochs(e10_del))
epochs: 120 → 117

Bad epochs can also be detected automatically with epoch_reject(); see Bad channels and epochs.