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.
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).
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.
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.
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.
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.
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.
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.