2. EEG数据预处理实战:数据导入与格式转换

好,咱们直接进入正题。预处理这一步,说白了就是给原始EEG数据「洗澡」。你想想看,从放大器里直接捞出来的数据,那叫一个脏——工频干扰、眼动伪迹、肌肉活动,啥都有。我刚开始做ERP研究那会儿,就吃过没好好预处理的亏,结果统计结果全是假的,白忙活两周。

今天这一章,咱们就把预处理流程走一遍。从数据导入到坏段剔除,每一步我都会告诉你:为什么要做、怎么做、以及我踩过的坑。

2.1 数据导入与格式转换

EEG数据格式五花八门,EDF、BDF、CNT、SET……不同厂商各玩各的。我个人习惯用MNE库来处理,它几乎通吃所有格式。

核心原则:不管原始格式是什么,最终都要转成统一的内部数据结构(比如MNE的Raw对象),方便后续处理。

2.1.1 常见格式一览

格式 扩展名 特点 典型来源
EDF .edf 欧洲数据格式,16位整数 Brain Products、Neuroscan
BDF .bdf EDF的扩展版,24位整数 BioSemi系统
CNT .cnt Neuroscan专有格式 Neuroscan放大器
SET .set + .fdt EEGLAB格式 EEGLAB用户

2.1.2 实战代码:用MNE导入数据

import mne

# 导入EDF文件
raw = mne.io.read_raw_edf('subject01.edf', preload=True)

# 导入BDF文件(BioSemi系统)
raw = mne.io.read_raw_bdf('subject01.bdf', preload=True)

# 导入CNT文件(Neuroscan)
raw = mne.io.read_raw_cnt('subject01.cnt', preload=True)

# 查看基本信息
print(raw.info)
print(raw.ch_names)

我的经验:preload=True这个参数很重要。如果不加,MNE会采用懒加载模式,后续操作会变慢。我一般直接preload,省心。

2.2 电极定位与通道选择

电极定位,说白了就是告诉软件:每个通道的电极在头皮上哪个位置。没有这个信息,后续的拓扑图、源定位都做不了。

2.2.1 标准电极位置系统

最常用的是10-20系统。我建议你直接用MNE自带的模板,省得自己手动配。

# 设置标准10-20电极位置
montage = mne.channels.make_standard_montage('standard_1020')
raw.set_montage(montage)

# 验证电极位置
raw.plot_sensors(show_names=True)

注意:不同厂商的电极命名可能不一样。比如「Cz」有的叫「Cz」,有的叫「CZ」。我曾经因为大小写问题,折腾了半小时才发现是命名不匹配。

2.2.2 通道选择策略

不是所有通道都要保留。我个人习惯:

  • 保留EEG通道:Fz、Cz、Pz、Oz等关键位置
  • 剔除EOG通道:眼电通道单独处理
  • 剔除参考通道:比如M1、M2(乳突参考)
  • 剔除坏通道:阻抗过高的通道
# 选择特定通道
eeg_channels = ['Fz', 'Cz', 'Pz', 'Oz', 'F3', 'F4', 'C3', 'C4', 'P3', 'P4']
raw.pick_channels(eeg_channels)

# 或者按类型选择
raw.pick_types(eeg=True, eog=False, stim=False)

2.3 参考电极设置

参考电极的选择,直接影响ERP波形的形态。这里我重点讲三种常用方式。

2.3.1 平均参考

把全脑所有电极的平均值作为参考。这是目前最常用的方法,尤其适合高密度EEG。

# 设置平均参考
raw.set_eeg_reference('average', projection=False)

为什么用平均参考?因为它能减少单点参考带来的偏差。我做过对比实验,平均参考下的N170波形比单侧乳突参考更干净。

2.3.2 双极参考

两个相邻电极相减,得到局部电位差。适合分析特定脑区。

# 创建双极参考(Cz - Fz)
bipolar_ref = mne.set_bipolar_reference(raw, anode='Cz', cathode='Fz')

2.3.3 常见参考方式对比

参考方式 优点 缺点 适用场景
平均参考 全局无偏 高密度时计算量大 大多数ERP研究
双极参考 局部精准 丢失全局信息 特定脑区分析
乳突参考 传统标准 可能引入偏侧化 经典ERP范式

2.4 滤波:让信号变干净

滤波是预处理的重头戏。说白了,就是把不需要的频率成分滤掉,留下我们关心的频段。

2.4.1 四种滤波类型

  • 高通滤波:滤掉低频漂移(0.1-1 Hz)
  • 低通滤波:滤掉高频噪声(30-100 Hz)
  • 带通滤波:同时滤掉高低频(比如0.1-30 Hz)
  • 陷波滤波:专门滤掉50/60 Hz工频干扰
# 带通滤波:0.1 - 30 Hz(ERP常用)
raw.filter(0.1, 30, fir_design='firwin')

# 陷波滤波:滤掉50 Hz工频
raw.notch_filter(50, fir_design='firwin')

我的建议:先做高通再做低通,顺序别搞反。另外,陷波滤波要谨慎使用,它可能会扭曲附近的频率成分。我一般先看频谱,确认有工频干扰才用。

2.4.2 滤波参数选择

ERP成分 推荐滤波范围 说明
N170、P100 0.1 - 30 Hz 视觉ERP标准设置
MMN 0.5 - 20 Hz 低频成分为主
P300 0.1 - 10 Hz P300是慢波

2.5 坏段剔除与插值

数据里总有一些「脏段」——眨眼、肌肉活动、电极脱落。这些段必须处理掉,否则会污染ERP平均结果。

2.5.1 手动剔除坏段

我习惯先肉眼扫一遍数据,标记明显的坏段。

# 手动标记坏段(交互式)
raw.plot(duration=10, n_channels=30)

# 用代码剔除
raw.crop(tmin=10, tmax=100)  # 只保留10-100秒

2.5.2 自动检测坏段

用阈值法自动标记。我一般设置±100 μV为阈值。

# 自动标记坏段
annotations = mne.preprocessing.annotate_amplitude(raw, 
                                                    peak=100e-6,  # 100 μV
                                                    min_duration=0.01)
raw.set_annotations(annotations)

避坑指南:我曾经遇到过一个问题——自动检测把正常的ERP成分也标记成了坏段。后来发现是阈值设得太低了。建议先看数据分布,再设阈值。

2.5.3 坏通道插值

如果某个通道全程都是坏的,别直接删掉,用周围通道插值补全。

# 标记坏通道
raw.info['bads'] = ['Fz']  # 假设Fz坏了

# 插值修复
raw.interpolate_bads(reset_bads=True)

插值的原理:用周围3-4个通道的加权平均来估计坏通道的值。权重由电极间的距离决定。这个方法对单个坏通道效果很好,但如果一片区域都坏了,那就别插了。

知识体系总览

下面这张图,把整个预处理流程串起来了。你可以把它当作检查清单。

EEG预处理流程总览 数据导入 EDF/BDF/CNT → MNE 电极定位 10-20系统模板 通道选择 保留EEG,剔除EOG 参考电极设置 平均/双极/乳突 滤波 高通/低通/带通/陷波 坏段剔除与插值 手动/自动 + 插值 预处理完成 → 进入ERP分析 💡 我的经验总结 • 顺序很重要:先定位 → 再选通道 → 再参考 → 再滤波 → 最后去坏段 • 每一步都要可视化检查,别偷懒 • 保存中间结果,方便回溯

好了,这一章的内容就到这儿。预处理虽然繁琐,但它是ERP分析的基石。你想想看,如果数据本身就有问题,后面再花哨的分析也是白搭。我个人习惯把预处理流程写成脚本,每次跑数据直接调用,省时省力。

记住一句话:预处理做得好,ERP分析没烦恼。

专注资料整理