第 8 章:应用到你自己的数据¶
把第 1–7 章的所有内容,应用到你自己的 .edf 记录上。这个 notebook 完全在你自己的电脑上运行 —— 只需要修改下面的 EDF_PATH;这里的任何内容都不会把你的数据上传或发送到任何地方。
你的文件已知的一些特殊之处(下面已经考虑进去了)(来自之前的检查):
- 32 个 EEG 通道,以
CPz为参考,命名类似EEG Fp1-CPz—— 需要先改名,标准 montage 才能套用(第 3 章)。 - 包含乳突电极
M1/M2。 - 采样率 1000 Hz,时长约 944 秒。
raw.annotations中包含了类似Impedance ...的条目 —— 这些是放大器阻抗检查标记,不是任务事件。在切分 epoch 有意义之前,你需要先确认你真正的刺激/反应触发标记长什么样(用新的眼光再检查一遍raw.annotations,或者向记录这份数据的人询问他们使用的标记方案)。
import mne
import matplotlib.pyplot as plt
import re
%matplotlib inline
mne.set_log_level("WARNING")
1. 加载(第 3 章)¶
EDF_PATH = r"PUT_YOUR_FILE_PATH_HERE.edf" # <-- 修改这一行为你的文件路径
raw = mne.io.read_raw_edf(EDF_PATH, preload=True)
raw
2. 检查¶
print("通道名称:", raw.ch_names)
print("采样率 (Hz):", raw.info["sfreq"])
print("时长 (s):", raw.times[-1])
print("标注:", raw.annotations)
3. 重命名通道并应用 montage(第 3 章)¶
去掉 EEG 前缀和 -CPz 参考后缀,使通道名称与标准 10-20 命名对应(EEG Fp1-CPz → Fp1)。这只是标签层面的修正 —— 不会改变底层信号或它的参考方案。
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. 可视化并标记坏通道(第 3 章)¶
raw.plot(n_channels=20, duration=10, scalings="auto")
plt.show()
raw.info["bads"] = [] # <-- 把你在上面注意到的坏通道名称填在这里
5. 滤波(第 4 章)¶
LINE_FREQ = 50 # <-- 如果你所在地区市电是 60 Hz,改成 60
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. 用 ICA 去除伪迹(第 5 章)¶
你的文件没有专门的 EOG 通道,所以我们退回到肉眼检查 —— 寻找一个具有强烈、对称、额部地形图的成分(典型的眨眼特征)。
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 = [] # <-- 填入要去除的成分索引,例如 [0, 3]
raw_clean = raw_filt.copy()
ica.apply(raw_clean)
raw_clean.plot(n_channels=20, duration=10, scalings="auto")
plt.show()
7. 查找事件(第 6 章)¶
继续之前,先停下来仔细检查这一步。 前面的检查显示,你的 raw.annotations 中包含的是阻抗检查标记,而不是真正的任务事件。运行下面的单元格,看看 event_id 里实际有什么 —— 如果里面只是阻抗/设置相关的标记,那么你真正的触发信号存放在别处(一个状态/触发通道,或者你实验软件生成的一份单独日志文件),你需要先调整这一步,切分 epoch 才有意义。
events, event_id = mne.events_from_annotations(raw_clean)
print("找到的事件编码:", event_id)
print("事件数量:", len(events))
8. 切分 Epoch(第 6 章)¶
等你知道了真正的事件标签之后,把下面的 event_id 调整为你真正关心的那个(些)条件(例如 event_id={"stimulus/target": 1}),并根据你的实验设计调整 tmin/tmax。
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, # 等你看过典型的幅度大小之后,可以加上峰峰值阈值,例如 dict(eeg=150e-6)
)
epochs
9. 平均成 ERP(第 7 章)¶
evoked = epochs.average()
evoked.plot()
plt.show()
# 等你有了真实、有意义的条件标签之后,可以这样比较它们:
# evoked_condA = epochs["real_label_a"].average()
# evoked_condB = epochs["real_label_b"].average()
# mne.viz.plot_compare_evokeds({"条件 A": evoked_condA, "条件 B": evoked_condB})
接下来做什么¶
在运行这个 notebook 的过程中,把你在每一步观察到的情况描述出来(通道名称、事件标签、epoch 数量、图看起来是什么样)—— 只需要文字/打印出来的输出,绝不要把数据文件本身发出来 —— 我们会一起调试、完善。一旦真实的事件接上了,并且开始得到看起来合理的 ERP,接下来自然的步骤是:测量具体的成分峰值(第 7 章的 get_peak()),规范地比较条件,最终再进入正式的统计检验(mne.stats,第 7 章简单提到过)。