步骤 21:事件分段
按事件切分 Epochs
1. 目标
把连续的 Raw 数据切分为以触发事件为中心的小段(Epochs)。每个 epoch 包含 tmin 到 tmax 的时间窗口,是 ERP 时域分析的基本单位。
2. 何时使用 / 何时跳过
- 必须使用:任何 ERP 研究
- 可跳过:连续数据研究(如 resting-state PSD、functional connectivity)
3. 前置条件
- 已执行步骤 20:events 已成功提取
- 已执行步骤 19:ICA 已施加(推荐)或即将施加(在 Epochs 上 apply)
4. 核心代码
Python
import mne
# ====== 方式 A:标准 ERP 创建 ======
epochs = mne.Epochs(
raw,
events=events,
event_id=event_id, # 之前提取的事件字典
tmin=-0.2, # 刺激前 200 ms(基线)
tmax=0.8, # 刺激后 800 ms
baseline=(-0.2, 0), # 基线窗口(用于步骤 24)
picks=['eeg', 'eog'], # 仅 EEG 和 EOG 通道(不含 stim)
reject=None, # 不在此处拒绝(步骤 22 单独做)
preload=True, # 必须 True 才能后续操作
proj=True, # 自动施加已记录的 SSP 投影
on_missing='warn', # 缺失事件如何处理
reject_by_annotation=True,
)
# ====== 方式 B:固定拒绝阈值(一步创建+拒绝) ======
epochs = mne.Epochs(
raw,
events=events,
event_id=event_id,
tmin=-0.2,
tmax=0.8,
baseline=(-0.2, 0),
reject=dict(eeg=150e-6, eog=250e-6), # 单位:V
preload=True,
)
# ====== 验证 ======
print(epochs)
print(f"总 epoch 数: {len(epochs)}")
# 注意:epochs.event_id 是 {条件名: 触发码} 字典,不是计数
# 正确的计数方式:
for condition, event_code in epochs.event_id.items():
n = len(epochs[condition])
print(f"{condition} (code={event_code}): {n} 个 epoch")
print(f"数据形状: {epochs.get_data().shape}")
# 输出: (n_epochs, n_channels, n_times)
5. 参数详解
| 参数 | 含义 | 推荐值 |
|---|---|---|
tmin |
起始时间(秒,相对事件) | -0.2(含基线)/ 0.0(无基线) |
tmax |
结束时间 | 0.5–1.0(依研究) |
baseline |
基线校正区间 | (tmin, 0) / None(跳过 24 步) |
picks |
参与通道 | ['eeg'] / ['eeg', 'eog'] |
reject |
振幅拒绝阈值 | 详见步骤 22 |
proj |
是否施加 SSP 投影 | True(推荐) |
preload |
加载到内存 | True |
on_missing |
缺失事件处理 | 'warn' / 'error' / 'ignore' |
时间窗口选择建议
| ERP 成分 | 建议 tmin | 建议 tmax |
|---|---|---|
| N170 | -0.2 | 0.4 |
| MMN | -0.2 | 0.5 |
| N400 | -0.3 | 1.0 |
| P300 | -0.2 | 0.8 |
| P100 / N100 | -0.1 | 0.4 |
| 准备电位 | -1.0 | 2.0 |
6. 验证方法
Python
# 验证 1:基本属性
print(epochs)
# 输出示例:<Epochs | 240 events (all good) | tmin: -0.2, tmax: 0.8, baseline: (-0.2, 0)>
# 验证 2:数据形状
data = epochs.get_data()
print(f"Shape: {data.shape}") # (n_epochs, n_channels, n_times)
assert data.shape[1] == len(epochs.ch_names)
# 验证 3:时间轴
times = epochs.times
print(f"时间轴: [{times[0]:.2f}, {times[-1]:.2f}] 秒, 步长 {1/(times[1]-times[0]):.0f} Hz")
# 验证 4:可视化
epochs.plot(n_epochs=10, n_channels=20)
# 期望:能看到清晰的事件相关成分
# 验证 5:平均 ERP
evoked = epochs.average()
evoked.plot()
# 验证 6:每个条件的 epoch 数(正确写法)
for condition in epochs.event_id:
n = len(epochs[condition])
print(f"{condition}: {n}")
7. 常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 找不到触发事件 | events 为空 | 回到步骤 20 |
baseline=None 后漂移严重 |
未做基线校正 | 立即执行步骤 24 |
| epoch 被全部拒绝 | reject 阈值过严 |
步骤 22 调高阈值或改用 autoreject |
| epoch 与事件不对齐 | 触发延迟未补偿 | 用 epochs.shift_time(tshift) |
| epoch 总数异常(如只有 10 个) | events 不完整 | 检查 raw.ch_names 中 stim 通道类型 |
preload=False 时后续报错 |
未加载到内存 | 重新创建并设 preload=True |
print(epochs.event_id) 当成 epoch 数 |
它是 {条件名: 触发码} 字典 |
改用 len(epochs[condition]) 计数 |
8. 官方来源
| 资源类型 | 链接 |
|---|---|
mne.Epochs |
https://mne.tools/stable/generated/mne.Epochs.html |
| Epochs 教程 | https://mne.tools/stable/auto_tutorials/epochs/plot_10_epochs_overview.html |
shift_time |
https://mne.tools/stable/generated/mne.Epochs.html#mne.Epochs.shift_time |
9. 学术引用
Luck, S. J. (2014). An Introduction to the Event-Related Potential Technique (2nd ed.), Chapter 5. MIT Press.