第 5 层 · 分段拒绝层 · 第 21 步 / 共 25 步

步骤 21:事件分段

按事件切分 Epochs

🕐 22 分钟🎯 难度:进阶🔗 核心 API:mne.Epochs⬅️ 前置:步骤 20

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.