Your outline is close, but I would change a few points before automating it.
- Make the run explicit.
all_data["S001"][0]does not show which run index0represents. In EEGMMIDB, T1/T2 depend on the run: for runs 3, 4, 7, 8, 11 and 12 they represent left/right fist; in the other task runs they represent both fists/both feet. Please store and verify the run number before epoching.
Also, np.arange(8, 12) gives 8–11 Hz. Use np.arange(8, 13) if you want 8–12 Hz. Beta should be calculated separately.
- These are not the original event counts:
original_t1 = len(epochs["T1"])
original_t2 = len(epochs["T2"])
Epochs may already have removed events at recording boundaries or because of bad annotations. Count the original events directly:
original = {
label: int(np.sum(events[:, 2] == code))
for label, code in event_id.items()
}
Also, epochs.drop_bad() will not add meaningful amplitude rejection unless reject or flat criteria were configured. Use the same predefined criteria for every participant, and inspect epochs.drop_log and epochs.drop_log_stats() for the actual reasons.
-
Ten seconds is not enough for raw-data QC. Inspect the full run or several representative sections. Look for flat channels, clipping, abrupt steps, sustained drift, repeated large transients and persistent narrow-band noise. Compare C3/C4 with neighbouring channels and inspect their PSD. A channel should be marked bad because of signal quality, not because its ERD pattern is weak.
-
vlim=(None, None)scales each plot independently. Calculate one limit from both channels and reuse it:
motor_data = power_mu.copy().pick(["C3", "C4"]).data
limit = np.nanpercentile(np.abs(motor_data), 99)
for channel in ("C3", "C4"):
power_mu.plot(
picks=channel,
vlim=(-limit, limit),
title=f"S001 T1 {channel}",
)
This gives C3 and C4 the same robust colour scale. Use the same approach for T2.
- Minima and maxima are very sensitive to single artifacts. For the QC table, I would store retained epoch counts, drop percentage and reasons, bad-channel status, and robust percentiles calculated over the same time-frequency window.
Process both T1 and T2 within the same participant loop. This keeps their run mapping, preprocessing and QC paired. Save one row per participant and condition, then calculate participant-level contrasts and only afterwards perform the grand average.