1. 环境搭建与安装
Python环境与MNE安装
虚拟环境 是确保脑电数据分析可重复性与依赖隔离的关键步骤,推荐使用 Conda 或 venv 创建一个独立的工作环境,避免与系统全局包冲突。
在Python环境就绪后,使用 MNE-Python 作为核心库,搭配 NumPy、SciPy、以及 pandas 等依赖库实现高效的数据处理与分析。下面给出常用的安装方式,直接参考即可快速落地。
# 使用 conda 创建并激活环境
conda create -n mne python=3.10
conda activate mne
# 安装 MNE-Python(主流方式)
pip install mne
# 也可通过 conda-forge 安装以获取最新依赖
conda install -c conda-forge mne
安装完成后,进行一次简单的验证,确保MNE-Python 已就绪并可调用,避免后续因环境问题中断分析流程。
import mne
print(mne.__version__)
2. 加载与浏览脑电数据
原始数据读取与检查
在脑科学研究与脑机接口应用中,脑电原始数据的格式多样,常见的有 EDF/BDF、EEG/EDF、以及 FIF 等。正确读取原始数据并快速查看通道信息,是后续分析的基础。
读取后应关注系统致敬的元数据,例如通道名称、采样率、阻抗信息等,这些都直接影响到后续的滤波和事件相关分析。
import mne
# 读取常见的 EDF/EDF+. 数据
raw = mne.io.read_raw_edf('subject01.edf', preload=True)
# 查看数据头信息,快速了解采样率、通道列表等
print(raw.info)
# 简单绘制前几秒钟数据,快速检查信号质量
raw.plot(n_channels=20, duration=5, scalings='auto')
3. 预处理与去伪迹
滤波与独立成分分析(ICA)
为了提升信噪比,需要对脑电信号进行 带通滤波、陷波滤波 等处理,并通过 ICA 去除眨眼、肌肉等伪迹的成分。1–40 Hz 的带通常用于鎏定典型的事件相关分析,若数据中存在 50/60 Hz 工频伪迹,可使用 notch_filter 进行频率抑制。
ICA 的关键在于组件的识别与排除,通常结合经验和\n顶板电极的相关性来挑选需要排除的成分,最后将清洗后的数据再进行应用。下方给出一个典型的流程示例。
# 带通滤波与陷波
raw_filtered = raw.copy().filter(1.0, 40.0, fir_design='firwin')
raw_filtered.notch_filter(freqs=[50], filter_length='auto', method='spectrum_fit')
# ICA 去伪迹(肌电、眨眼等)
from mne.preprocessing import ICA
ica = ICA(n_components=0.95, random_state=97, max_iter=500)
ica.fit(raw_filtered)
# 查看 ICA 成分,手动或自动选择需要排除的成分
ica.plot_components()
# 通过可视化或自动判定将不需要的成分排除
ica.exclude = [0, 1, 3]
raw_clean = raw_filtered.copy()
ica.apply(raw_clean)
完成上述处理后,建议再次检查数据质量,例如重新绘制时序、查看通道信号分布,以及对照原始数据确认伪迹的显著下降。
# 验证清洗后的数据
raw_clean.plot(n_channels=20, duration=5, scalings='auto')
4. 事件相关分析与ERP
事件标注、分段与ERP绘制
在脑电研究中,事件相关电位(ERP/ERF)分析依赖于对事件的准确标注与对齐。通过读取事件信息并按事件类型分段,可以生成时间锁定的分段数据(epochs),进而计算平均波形或进行统计比较。
典型的工作流包括:从原始数据提取事件、定义事件 ID、创建 Epochs、计算 Evoked(ERP/ERF)以及绘制拓扑图与时间-频率矩阵。下列示例展示了完整流程。
# 读取经过清洗的原始数据
# 事件信息通常来自刺激通道(如 STI 014)或注释
events = mne.find_events(raw_clean, stim_channel='STI 014')
# 定义事件标识,如左刺激、右刺激等
event_id = {'left': 1, 'right': 2}
# 生成事件相关的分段数据(Epochs)
epochs = mne.Epochs(raw_clean, events, event_id=event_id, tmin=-0.2, tmax=0.5, baseline=(None, 0))
# 计算并绘制左刺激的ERP
evoked_left = epochs['left'].average()
evoked_left.plot(show=True)
# 绘制头皮拓扑图以查看 ERP 的空间分布
evoked_left.plot_topomap(times=[0.08, 0.16, 0.25])
除了单个条件的 ERP 外,还可以进行两组条件的统计对比,或在不同脑区上对 ERP 成分进行聚焦分析。强烈推荐结合 顶帽分布、以及 时序对齐来提升解释力。
# 另一种常见的分析:绘制事件相关的平均波形并比较两组
evoked_right = epochs['right'].average()
evoked_right.plot()
# 直接对比两个条件的差分 ERP
difference = mne.combine_evoked([evoked_right, evoked_left], weights=[1, -1])
difference.plot()
5. 时频分析与脑机接口应用
时频分析与解码示例
除了时间域的 ERP 外,脑电信号在不同频带的振荡也承载丰富的信息。通过 时频分析,可获得频率-时间域的能量分布,进而用于解码任务、脑机接口(BCI)应用和认知状态推断。
常用的方法包括 Morlet 小波变换、多粒度的时频分析,以及对 epoch/单次试验进行群体层面的统计。下面给出一个简化的时频分析示例,以及一个基于提取特征的解码示例。
# 时频分析:对每个 Epoch 计算 Morlet 小波的时频表示
import numpy as np
from mne.time_frequency import tfr_morlet
freqs = np.arange(6, 30, 2)
n_cycles = freqs / 2.0
power = tfr_morlet(epochs, freqs=freqs, n_cycles=n_cycles, return_itc=False, average=True)
# 将结果绘制为频率-时间图
power.plot([0], mode='logratio', title='Average Morlet power')
在脑机接口应用中,常将时频特征用于分类模型,例如基于频带功率的简单线性分类器,或使用更复杂的时序模型。下面给出一个基于 Scikit-Learn 的解码流程,结合标准化、交叉验证与线性分类器实现口令识别或任务识别。
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score
# 将时频特征展平为样本-特征矩阵
X = power.data # 形状可能为 (n_epochs, n_channels, n_freqs, n_times)
X = X.reshape(len(epochs), -1)
# 标签:以事件类型为类别
y = epochs.events[:, -1]
clf = Pipeline([('scaler', StandardScaler()), ('clf', LogisticRegression(max_iter=1000))])
scores = cross_val_score(clf, X, y, cv=5)
print('Cross-validated accuracy:', scores.mean())
6. 数据导出与共享
保存结果与数据格式
为确保分析结果的可重现性与后续复用,建议将原始数据、处理后的数据以及结果以标准化格式保存到磁盘。FIF、EDF、以及 FIF 是常用的脑电数据封装格式,MNE 提供了丰富的保存接口。
同时,将处理后的 ERP、时频矩阵以及解码模型导出为通用格式,提升跨研究、跨平台的可重现性与协作效率。
# 保存处理后的原始数据与分段数据
raw_clean.save('subject01-raw-clean.fif', overwrite=True)
epochs.save('subject01-epo.fif')
evoked_left.save('subject01-ave-left-erp.fif')
evoked_right = epochs['right'].average()
evoked_right.save('subject01-ave-right-erp.fif')
# 导出一个简单的 CSV 版本的 ERP 数据,便于共享和再分析
import numpy as np
np.savetxt('erp-left.csv', evoked_left.data, delimiter=',')
np.savetxt('erp-right.csv', evoked_right.data, delimiter=',')


