Chapter 8: Apply This to Your Own Data¶
Everything from Chapters 1–7, applied to your own .edf recording. This notebook runs entirely on your machine — only edit EDF_PATH below; nothing here uploads or transmits your data anywhere.
Your file's known quirks, already accounted for below (from earlier inspection):
- 32 EEG channels, referenced to
CPz, named likeEEG Fp1-CPz— needs renaming before a standard montage will apply (Chapter 3). - Includes mastoids
M1/M2. - Sampling rate 1000 Hz, ~944 s duration.
raw.annotationscontained entries likeImpedance ...— these are amplifier impedance checks, not task events. Before epoching means anything, you need to confirm what your real stimulus/response triggers look like (checkraw.annotationsagain with fresh eyes, or ask whoever ran the recording about the marker scheme used).
import mne
import matplotlib.pyplot as plt
import re
%matplotlib inline
mne.set_log_level("WARNING")
1. Load (Chapter 3)¶
EDF_PATH = r"PUT_YOUR_FILE_PATH_HERE.edf" # <-- edit this line
raw = mne.io.read_raw_edf(EDF_PATH, preload=True)
raw
2. Inspect¶
print("Channel names:", raw.ch_names)
print("Sampling rate (Hz):", raw.info["sfreq"])
print("Duration (s):", raw.times[-1])
print("Annotations:", raw.annotations)
3. Rename channels and apply montage (Chapter 3)¶
Stripping the EEG prefix and -CPz reference suffix so channel names match the standard 10-20 names (EEG Fp1-CPz → Fp1). This is a labeling fix only — it does not change the underlying signal or its reference.
raw.rename_channels(lambda name: re.sub(r"^EEG\s+", "", name).split("-")[0])
print(raw.ch_names)
montage = mne.channels.make_standard_montage("standard_1020")
raw.set_montage(montage, on_missing="warn")
4. Visualize and mark bad channels (Chapter 3)¶
raw.plot(n_channels=20, duration=10, scalings="auto")
plt.show()
raw.info["bads"] = [] # <-- fill in any bad channel names you noticed above
5. Filter (Chapter 4)¶
LINE_FREQ = 50 # <-- set to 60 if your mains power is 60 Hz
raw_filt = raw.copy().filter(l_freq=1.0, h_freq=40.0)
raw_filt.notch_filter(freqs=LINE_FREQ)
raw_filt.compute_psd(picks="eeg").plot()
plt.show()
6. Artifact removal with ICA (Chapter 5)¶
Your file has no dedicated EOG channel, so we fall back to visual inspection — look for a component with a strong, symmetric, frontal topography (classic eye-blink signature).
ica = mne.preprocessing.ICA(n_components=15, random_state=97, max_iter="auto")
ica.fit(raw_filt)
ica.plot_components()
plt.show()
ica.plot_sources(raw_filt)
plt.show()
ica.exclude = [] # <-- fill in with component indices to remove, e.g. [0, 3]
raw_clean = raw_filt.copy()
ica.apply(raw_clean)
raw_clean.plot(n_channels=20, duration=10, scalings="auto")
plt.show()
7. Find events (Chapter 6)¶
Stop and check this carefully before continuing. Earlier inspection showed your raw.annotations contained impedance-check markers, not real task events. Run the cell below and look at what event_id actually contains — if it's just impedance/setup markers, your real triggers live somewhere else (a status/trigger channel, or a separate log file from your experiment software), and you'll need to adapt this step before epoching means anything.
events, event_id = mne.events_from_annotations(raw_clean)
print("Event codes found:", event_id)
print("Number of events:", len(events))
8. Epoch (Chapter 6)¶
Adjust event_id below to only the real condition(s) you care about once you know your true event labels (e.g. event_id={"stimulus/target": 1}), and adjust tmin/tmax to match your experiment design.
epochs = mne.Epochs(
raw_clean,
events,
event_id=event_id,
tmin=-0.2,
tmax=0.8,
baseline=(-0.2, 0.0),
preload=True,
reject=None, # add peak-to-peak thresholds once you've seen typical amplitudes, e.g. dict(eeg=150e-6)
)
epochs
9. Average into an ERP (Chapter 7)¶
evoked = epochs.average()
evoked.plot()
plt.show()
# Once you have real, meaningful condition labels, compare them like this:
# evoked_condA = epochs["real_label_a"].average()
# evoked_condB = epochs["real_label_b"].average()
# mne.viz.plot_compare_evokeds({"condition A": evoked_condA, "condition B": evoked_condB})
Where to go from here¶
As you run this, describe what you observe at each step (channel names, event labels, number of epochs, what plots look like) — text/printed output only, never the data file itself — and we'll debug and refine together. Once real events are wired in and you're getting a plausible ERP, natural next steps: measuring specific component peaks (Chapter 7's get_peak()), comparing conditions properly, and eventually formal statistics (mne.stats, briefly mentioned in Chapter 7).