Notebook 2: EEG data: shapes, masks, and signals#

The UCI EEG Eye State dataset contains 14 EEG channels from one continuous 117-second measurement, with eye state labelled from video (0 open, 1 closed).

It contains one participant and is not an epochs × channels × time dataset.

Roesler, O. (2013), UCI Machine Learning Repository, CC BY 4.0, https://doi.org/10.24432/C57G7J.

Hide code cell source

from pathlib import Path
import sys

# Find the repository from the notebook folder, solutions folder, or project root.
for _candidate in (Path.cwd().resolve(), *Path.cwd().resolve().parents):
    if (_candidate / "notebooks" / "workshop_checks.py").is_file():
        PROJECT_ROOT = _candidate
        break
else:
    raise FileNotFoundError(
        "Open this notebook inside the cloned workshop repository; "
        "notebooks/workshop_checks.py is required."
    )

sys.path.insert(0, str(PROJECT_ROOT / "notebooks"))

from workshop_checks import Check, run_checks

check = Check()

1: Load the ARFF file#

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import arff

data_path = PROJECT_ROOT / "book" / "data" / "real" / "eeg_eye_state.arff"
if not data_path.is_file():
    raise FileNotFoundError(f"Missing workshop data: {data_path}")

raw_records, metadata = ...
eeg = ...
eeg["eyeDetection"] = ...
eeg.head()
Hint

Use arff.loadarff(data_path), pass the returned records to pd.DataFrame, and convert eyeDetection with .astype(int).

Hide code cell source

run_checks("02_eeg_arrays_cell_6", locals())

2: Predict the shape#

The dataset has 14 EEG channels and one label column. What is the shape of the complete DataFrame?

  • A: (14980, 14)

  • B: (14980, 15)

  • C: (15, 14980)

answer_shape = ""
Hint

Count the 14 EEG channels and the eyeDetection label column.

Hide code cell source

run_checks("02_eeg_arrays_cell_11", locals())

3: Separate features and target#

Fill the blanks so X contains the channels and y contains eye state.

X = eeg.drop(columns=[...])
y = eeg[...]
Hint

Remove eyeDetection from X and select that same column as y.

Hide code cell source

run_checks("02_eeg_arrays_cell_16", locals())

4: Move from pandas to NumPy#

Create signals as a NumPy array. Then select the first 100 samples from channel O1. The output should be one-dimensional.

signals = ...
o1_index = list(X.columns).index("O1")
o1_excerpt = ...
Hint

Use .to_numpy() for signals. Select rows :100 and column o1_index.

Hide code cell source

run_checks("02_eeg_arrays_cell_24", locals())

5: Boolean masks#

Use y to make two arrays: samples recorded with eyes open and samples recorded with eyes closed.

eyes_open = ...
eyes_closed = ...
Hint

y.eq(0) and y.eq(1) create Boolean row masks.

Hide code cell source

run_checks("02_eeg_arrays_cell_29", locals())

6: Aggregate along the correct axis#

Calculate one mean value per channel for each eye state. The result should have shape (14,).

open_channel_means = ...
closed_channel_means = ...
Hint

Rows contain measurements and columns contain channels. Average the row axis.

Hide code cell source

run_checks("02_eeg_arrays_cell_34", locals())

7: Visual comparison#

Make a grouped or paired plot comparing the 14 channel means. Label the axes and states. Then answer: why would this plot alone be insufficient evidence that closing the eyes caused the observed differences?

# Create a grouped bar chart with one pair of bars per channel.


# Why this does not establish causation:
Hint

Use np.arange for channel positions and offset the open/closed bars by half a bar width. Consider the number of participants, chronological dependence, artefacts, and experimental control.

Bonus: Build pseudo-epochs#

Take the first 14,000 samples and reshape them into 100 pseudo-epochs × 140 time samples × 14 channels, then transpose to the ACN convention epochs × channels × time.

These fixed-width chunks are not experimentally defined epochs.

pseudo_epochs = ...
Hint

First reshape the first 14,000 rows to (100, 140, 14), then transpose axes 1 and 2.

Hide code cell source

run_checks("02_eeg_arrays_cell_46", locals())