步骤 20:事件提取
从 annotations 提取 events 数组
1. 目标
从 Raw 对象中提取事件标记(trigger),用于后续 Epochs 创建。事件是 (sample_index, prev_value, trigger_id) 三元组数组。
2. 何时使用 / 何时跳过
- 必须使用:任何基于事件分段的 ERP 研究
- 可跳过:连续数据研究(如 resting-state)
3. 前置条件
- 已执行步骤 19:ICA 已施加(或决定先创建 epochs 后施加)
- 数据中存在
stim类型通道或 annotation
4. 核心代码
Python
import mne
# ====== 方式 A:从 annotation 提取事件(推荐) ======
# 适用于 .fif、.vhdr(已含 annotation)格式
events = mne.events_from_annotations(raw, event_id=None)
# 返回: events_array, event_id_dict
events, event_id = events
print(f"事件数: {len(events)}")
print(f"事件 ID 字典: {event_id}")
# ====== 方式 B:从 stim 通道直接检测 ======
# 适用于 .edf、.cnt、原始 .fif 等使用 stim 通道的格式
# 实测 MNE 1.12.1 源码(mne/event.py)L33:
# output : 'onset' | 'offset' | 'step'
# 默认即 'onset';'onset' 是合法值(不是 'red'/'on'/'off')
events = mne.find_events(
raw,
stim_channel='STI001', # stim 通道名
output='onset', # 'onset'(默认)/'offset'/'step'
consecutive='increasing', # 默认 'increasing'
min_duration=0, # 默认 0
shortest_event=2, # 默认 2 样本
verbose='INFO',
)
print(f"找到 {len(events)} 个事件")
print(f"前 5 个事件: {events[:5]}")
# ====== 方式 C:自定义事件字典映射 ======
# 仅保留你关心的 trigger
event_id = {
'target': 1,
'standard': 2,
}
events, _ = mne.events_from_annotations(raw, event_id=event_id)
print(f"过滤后事件数: {len(events)}")
# ====== 验证:每个 ID 的事件数 ======
from collections import Counter
event_counts = Counter(events[:, 2])
print(f"事件计数: {event_counts}")
# 期望:每个 ID 的事件数与实验设计一致
5. 参数详解
events_from_annotations
| 参数 | 含义 | 推荐值 |
|---|---|---|
event_id |
事件过滤字典 | None(保留所有)/ {'cond1': 1, 'cond2': 2} |
regexp |
正则匹配 | 默认匹配所有 |
find_events
| 参数 | 含义 | 推荐值 |
|---|---|---|
stim_channel |
stim 通道 | 'STI001' / 'STI 014' |
output |
输出模式 | 'onset'(默认,实测合法)/ 'offset' / 'step' |
consecutive |
是否合并相邻 | 'increasing'(默认);其他有意义取值:True / False |
min_duration |
最小事件间隔 | 0(默认;单位:秒) |
6. 验证方法
Python
# 验证 1:事件数与实验设计一致
n_target = (events[:, 2] == event_id['target']).sum()
n_standard = (events[:, 2] == event_id['standard']).sum()
print(f"target: {n_target}, standard: {n_standard}")
# 期望:与设计文件一致
# 验证 2:事件间隔合理
event_onsets = events[:, 0]
intervals = np.diff(event_onsets) / raw.info['sfreq'] # 秒
print(f"事件间隔: min={intervals.min():.2f}s, mean={intervals.mean():.2f}s")
# 期望:≥ 实验中两个 trial 的最小间隔
# 验证 3:可视化(在 stim 通道上加标记)
mne.viz.plot_events(events, raw.info['sfreq'])
# 验证 4:触发延迟验证(用 onset 时刻附近)
# 应有事件处出现 ERP 信号
7. 常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
| 找到 0 个事件 | stim 通道名错误 | 检查 raw.ch_names |
| 事件数过多(倍数关系) | 同时存在 onset + offset | 设 consecutive='increasing'(默认) |
| 事件 ID 字典不匹配 | 文件 trigger ID 与设计文件不一致 | 用 event_id=None 全部取出后核对 |
| 触发延迟 0(很精准)但 ERP 时序错 | 设备存在硬件延迟 | 用 mne.event.shift_time_events(tshift, ...) 或调整 tmin |
events_from_annotations 返回空 |
格式不含 annotation(如 EDF) | 改用 find_events |
8. 官方来源
| 资源类型 | 链接 |
|---|---|
events_from_annotations |
https://mne.tools/stable/generated/mne.events_from_annotations.html |
find_events |
https://mne.tools/stable/generated/mne.find_events.html |
plot_events |
https://mne.tools/stable/generated/mne.viz.plot_events.html |
| 事件教程 | https://mne.tools/stable/auto_tutorials/epochs/plot_10_epochs_overview.html |
9. 学术引用
无需单独引用。本步骤是事件相关的标准化操作。