一、什么是分段Epoching1.1 通俗理解到目前为止我们处理的是连续的脑电数据一整段录音。但脑电分析通常关注的是刺激出现前后的大脑反应连续数据 ├─────────────────────────────────────────────┤ 0秒 100秒 200秒 300秒 ↑ 刺激1 ↑ 刺激2 ↑ 刺激3 分段 把每个刺激前后的一小段时间切出来 刺激1周围 [刺激前0.2秒]──[刺激]──[刺激后0.5秒] 刺激2周围 [刺激前0.2秒]──[刺激]──[刺激后0.5秒] 刺激3周围 [刺激前0.2秒]──[刺激]──[刺激后0.5秒]1.2 形象比喻你正在录一整天的声音 ......早上好......吃了吗......再见...... 分段就是 把每个早上好出现的前后各 1 秒切出来 然后你就能分析 - 早上好这个声音的平均波形是什么 - 每次说早上好有什么变化1.3 Epochs 数据结构Epochs 对象 三维数组 维度1事件数量比如 72 个听觉刺激 × 4 种条件 288 个分段 维度2通道数量60 个 EEG 通道 维度3时间点数比如 -0.2s 到 0.5s共 421 个采样点 形状(288, 60, 421) ↑ ↑ ↑ 事件 通道 时间二、环境准备与数据加载2.1 导入库和基础设置# 环境设置 # matplotlib.use(TkAgg) 的作用 # 指定 matplotlib 的渲染后端 # TkAgg 是 Windows 系统自带的 Tkinter 后端 # 必须在 import pyplot 之前设置否则可能会报错 # 常见后端对比 # TkAggWindows 自带无需额外安装 # Qt5Agg功能更丰富需要安装 PyQt5 # Agg不需要显示窗口只保存图片适合服务器 import matplotlib matplotlib.use(TkAgg) # pyplotmatplotlib 的主要绘图接口 # 提供类似 MATLAB 的绘图函数plot, subplot, title 等 # plt 是约定俗成的简写 import matplotlib.pyplot as plt # mne脑电分析核心库 # 包含了数据加载、预处理、分析、可视化的所有功能 import mne # numpy数值计算库 # np 是约定俗成的简写 # 提供高效的数组操作、数学运算 import numpy as np # os操作系统路径处理模块 # 用于安全地拼接文件路径 import os # Counter统计工具 # 可以快速统计列表中各元素出现的次数 # 例如 Counter([a,a,b]) → {a:2, b:1} from collections import Counter # warnings控制警告信息的显示 import warnings # filterwarnings(ignore) # 忽略所有警告信息让输出更干净 # 之前遇到的 Blowfish 警告、event missing 警告都会被隐藏 warnings.filterwarnings(ignore) # 中文字体设置 # rcParams 是 matplotlib 的全局配置字典 # 类似于软件的设置或偏好设置菜单 # font.sans-serif设置无衬线字体列表 # matplotlib 会按列表顺序尝试使用第一个可用的字体 # Microsoft YaHei 微软雅黑Windows 常见美观 # SimHei 黑体Windows 自带备用方案 plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei] # axes.unicode_minus控制负号的显示方式 # True默认使用 Unicode 减号 −更美观 # False使用 ASCII 连字符 - # 为什么设为 False # 有些中文字体不支持 Unicode 减号 # 设为 False 可以避免负号显示为方块 □ plt.rcParams[axes.unicode_minus] False # 打印标题分隔线 # *60 表示将 重复 60 次 print(*60) print(MNE-Python 第6天分段Epoching与基线校正) print(*60)2.2 加载原始数据# ---------- 1. 加载数据 ---------- # mne.datasets.sample.data_path() 的作用 # 返回 MNE 示例数据集的存储路径 # 第一次运行时 # 1. 自动从网上下载数据集约 2GB # 2. 保存到 ~/mne_data/ 目录 # 之后运行时直接返回本地路径不会重新下载 sample_data_folder mne.datasets.sample.data_path() # os.path.join()安全地拼接文件路径 # 为什么不用 号直接拼接 # Windows 用反斜杠 \Linux/Mac 用正斜杠 / # os.path.join() 会自动使用正确的分隔符 # 例如 # Windows: MEG\sample\file.fif # Linux: MEG/sample/file.fif raw_fname os.path.join( sample_data_folder, # 数据集根目录 MEG, # MEG 子目录 sample, # sample 子目录 sample_audvis_raw.fif # 原始数据文件名 ) # mne.io.read_raw_fif()读取 .fif 格式的原始脑电数据 # .fif Functional Image FileMNE 的原生数据格式 # # preloadFalse 的含义 # 数据暂存在硬盘上不加载到内存RAM # 优点节省内存适合大文件 # 缺点操作时需要从硬盘读取速度慢 # # preloadTrue 的含义 # 数据立即加载到内存 # 优点操作速度快 # 缺点占用内存但这个数据集不大可以接受 raw mne.io.read_raw_fif(raw_fname, preloadFalse) print(✅ 原始数据加载完成).fif文件内部结构.fif 文件就像一个大箱子里面装有 ┌─────────────────────────────┐ │ 信号数据 │ │ - 每个通道每个时间点的数值 │ │ │ │ 通道信息 │ │ - 通道名称EEG 001 等 │ │ - 通道类型eeg/eog/stim │ │ - 电极位置 │ │ │ │ ⚙️ 元数据 │ │ - 采样率600.61 Hz │ │ - 采集日期 │ │ - 设备信息 │ └─────────────────────────────┘三、数据预处理3.1 提取通道# ---------- 2. 快速预处理 ---------- print(\n快速预处理...) # raw.copy()创建数据的独立副本 # 为什么要 copy # 保持原始 raw 对象不变 # 这样如果后续操作出错可以回到原始状态重新开始 # 类似于另存为而不是保存 # pick_types()根据通道类型筛选通道 # eegTrue → 保留 EEG脑电通道 # eogTrue → 保留 EOG眼电通道后续 ICA 需要 # stimTrue → 保留 STIM刺激标记通道提取事件需要 # # 注意这里没有 ecgTrue因为这个数据集没有 ECG 通道 raw_eeg raw.copy().pick_types(eegTrue, eogTrue, stimTrue) print(f 提取了 {len(raw_eeg.ch_names)} 个通道)通道类型说明3.2 重命名通道并设置 Montage# 找出所有以 EEG 开头的通道名称 # 列表推导式的语法[表达式 for 变量 in 列表 if 条件] # 遍历 raw_eeg.ch_names 中的每个通道名 # 只保留以 EEG 开头的 eeg_names [ch for ch in raw_eeg.ch_names if ch.startswith(EEG)] print(f EEG 通道数: {len(eeg_names)}) print(f EEG 通道示例: {eeg_names[:3]}) # 前3个如 [EEG 001, EEG 002, EEG 003] # 创建标准 10-20 系统的蒙太奇第2天学的内容 # make_standard_montage() 返回一个 Montage 对象 # 包含 94 个标准电极的 3D 坐标 montage mne.channels.make_standard_montage(standard_1020) # 从标准蒙太奇中取相同数量的电极名称 # montage.ch_names 包含所有标准电极名如 Fz, Cz, Pz, ... # [:len(eeg_names)] 取前 N 个N EEG 通道数量 standard_names montage.ch_names[:len(eeg_names)] print(f 标准名称示例: {standard_names[:3]}) # 如 [Fz, Cz, Pz] # dict(zip(A, B)) 详解 # 步骤1zip(eeg_names, standard_names) # 将两个列表逐对配对生成迭代器 # → [(EEG 001, Fz), (EEG 002, Cz), (EEG 003, Pz), ...] # # 步骤2dict(...) # 将配对列表转换为字典 # → {EEG 001: Fz, EEG 002: Cz, EEG 003: Pz, ...} # # 这个字典就是重命名映射表 rename_dict dict(zip(eeg_names, standard_names)) # rename_channels()根据字典批量重命名通道 # 旧名称键 → 新名称值 raw_eeg.rename_channels(rename_dict) print(f 重命名后通道示例: {raw_eeg.ch_names[:3]}) # [Fz, Cz, Pz] # set_montage()设置电极在头皮上的位置 # MNE 会根据通道名称在 montage 中查找对应的 3D 坐标 # 这样才能画地形图 raw_eeg.set_montage(montage) print( 通道重命名和蒙太奇设置完成)3.3 关键步骤先加载数据再滤波# ⚠️ 重要滤波等操作必须在数据加载到内存之后 # load_data() 的作用 # 将数据从硬盘文件读到计算机的内存RAM中 # 内存中的读写速度比硬盘快几百倍 # # 为什么不能在 preloadFalse 时做滤波 # 滤波需要对每个数据点做数学运算 # 如果数据在硬盘上每次读取都非常慢 # MNE 为了防止这种情况直接报错提醒你 print( 加载数据到内存...) raw_eeg.load_data() print( ✅ 数据已加载到内存)preload 与 load_data 的关系# 方式1加载时直接预加载 raw mne.io.read_raw_fif(file.fif, preloadTrue) # 此时数据已在内存中 # 方式2先不加载需要时再加载本次的方式 raw mne.io.read_raw_fif(file.fif, preloadFalse) # ... 做一些不需要内存的操作 ... raw.load_data() # 现在加载到内存 # ... 做需要内存的操作滤波等...3.4 陷波滤波# notch_filter()陷波滤波器 # # 陷波滤波器在特定频率挖一个非常窄的坑 # 只去除这一个频率不影响其他频率 # # 50Hz中国/欧洲或 60Hz美国是交流电的频率 # 电线、电器产生的电磁场会被电极拾取 # 在脑电信号中表现为持续的嗡嗡声 print( 陷波滤波去除 60Hz...) raw_eeg.notch_filter( freqs60, # 要去除的频率 pickseeg, # 只对 EEG 通道滤波 verboseFalse # 不打印详细信息 ) print( ✅ 去除了 60Hz 工频干扰)陷波滤波原理图信号幅度 │ │ ┌─┐ │ │ │ ← 60Hz 的尖峰工频干扰 │ ╱ ╲ │ ╱ ╲___ ← 正常的脑电信号 │ ╱ └────────────────────→ 频率 ↑ 60Hz被挖掉3.5 带通滤波# filter()带通滤波器 # # 带通滤波 高通滤波 低通滤波 # 高通部分去除低于 l_freq 的频率 # 低通部分去除高于 h_freq 的频率 # # 本次的设置 # l_freq1去除 1Hz 的缓慢漂移出汗、电极移动 # h_freq40去除 40Hz 的高频噪声肌肉活动、电磁干扰 # 保留 1-40Hz包含了脑电的主要频段 print( 带通滤波1-40 Hz...) raw_eeg.filter( l_freq1, # 低截止频率高通部分 h_freq40, # 高截止频率低通部分 pickseeg, # 只对 EEG 通道滤波 verboseFalse ) print( ✅ 保留了 1-40 Hz 的信号)脑电频段与滤波的关系频段 频率范围 本次是否保留 ───────────────────────────────────── Delta δ 0.5-4 Hz ✅ 保留1Hz 部分 Theta θ 4-8 Hz ✅ 保留 Alpha α 8-13 Hz ✅ 保留 Beta β 13-30 Hz ✅ 保留 Gamma γ 30-100 Hz ⚠️ 部分保留30-40Hz 高频噪声 40 Hz ❌ 去除 基线漂移 1 Hz ❌ 去除3.6 重参考# set_eeg_reference()设置 EEG 参考 # # 脑电测量的是电位差 # 记录值 电极电位 - 参考电位 # # 采集时可能以 Cz头顶或耳垂为参考 # 但这些位置本身也有脑电活动不是中性的 # # 平均参考average # 新参考 所有 EEG 通道的平均值 # 每个通道的新值 原值 - 所有通道的平均值 # 假设全头平均电位 ≈ 0 # 优点标准、可重复、适合高密度电极 print( 重参考平均参考...) raw_eeg.set_eeg_reference( average, # 参考类型所有通道的平均值 verboseFalse ) print( ✅ 重参考完成平均参考)重参考的数学原理# 假设有 3 个通道 ch1 10 # μV ch2 20 # μV ch3 -5 # μV # 平均参考 average (10 20 (-5)) / 3 8.33 μV # 新的值 ch1_new 10 - 8.33 1.67 μV ch2_new 20 - 8.33 11.67 μV ch3_new -5 - 8.33 -13.33 μV # 验证新值的平均值 0 (1.67 11.67 (-13.33)) / 3 ≈ 0 ✅四、提取事件4.1 从刺激通道提取事件# ---------- 3. 提取事件 ---------- print(\n提取事件...) # find_events()从刺激通道自动检测事件 # # 工作原理 # 1. 监控刺激通道STI 014的值 # 2. 平时值是 0刺激出现时值变为非 0 # 3. 检测到变化时记录为事件 # # 返回值events 数组 # 形状(N, 3) # N 事件总数 # 每行 [采样点编号, 持续采样点数, 事件编号] events mne.find_events( raw_eeg, # 包含刺激通道的数据 stim_channelSTI 014 # 刺激通道的名称 ) print(f 提取到 {len(events)} 个事件) # 查看事件数组的前5行 print(f\n 前5个事件) print(f 采样点 持续时间 事件编号) for i in range(min(5, len(events))): sample events[i, 0] # 第0列采样点位置 duration events[i, 1] # 第1列持续时间 event_code events[i, 2] # 第2列事件编号 time_sec sample / raw_eeg.info[sfreq] # 转换为秒 print(f {sample:6d} {duration:4d} {event_code} ({time_sec:.1f}秒))事件数组的结构# events 是一个 N×3 的二维数组 # 可以理解为一张表格 # ┌─────────┬─────────┬─────────┐ # │ 采样点 │ 持续时间│ 事件编号│ # ├─────────┼─────────┼─────────┤ # │ 15000 │ 0 │ 1 │ ← 第1个事件编号1瞬时 # │ 23000 │ 0 │ 2 │ ← 第2个事件编号2瞬时 # │ 31000 │ 0 │ 1 │ ← 第3个事件编号1瞬时 # │ ... │ ... │ ... │ # └─────────┴─────────┴─────────┘4.2 创建事件 ID 字典# event_id 字典将数字编号映射为有意义的事件名称 # 键 条件名称字符串 # 值 事件编号整数 event_id { 听觉/左耳: 1, # 编号 1 左耳听到纯音 听觉/右耳: 2, # 编号 2 右耳听到纯音 视觉/左眼: 3, # 编号 3 左眼看到棋盘格 视觉/右眼: 4 # 编号 4 右眼看到棋盘格 } # 统计每种事件的次数 print(\n事件统计) for event_name, event_code in event_id.items(): # .items() 返回 (键, 值) 对 # np.sum(events[:, 2] event_code) # 统计第2列中等于 event_code 的行数 count np.sum(events[:, 2] event_code) print(f {event_name} (编号{event_code}): {count} 次)五、创建 Epochs分段5.1 理解时间窗口print( 分段的时间窗口设计 刺激前基线期 刺激点 刺激后反应期 ←──────────|──────────★──────────|──────────→ -0.2秒 0秒 0.5秒 tmin -0.2从刺激前 0.2 秒开始 tmax 0.5到刺激后 0.5 秒结束 为什么这样设置 - 基线期-0.2 ~ 0秒观察刺激前的大脑状态 - 反应期0 ~ 0.5秒观察刺激引起的大脑反应 - 总长 0.7 秒足够看到早期和中期脑电成分 )5.2 定义拒绝标准# reject 参数定义坏段的判断标准 # # 什么是坏段 # 幅度异常大的分段通常是由残留伪迹引起的 # - 被试突然咳嗽 # - 电极瞬间接触不良 # - 眨眼没有被 ICA 完全去除 # # reject_criteria 是一个字典 # 键 通道类型 # 值 幅度阈值单位伏特 # # 150e-6 的含义 # 150 × 10⁻⁶ 0.000150 伏特 150 微伏 # 如果分段中任何 EEG 通道的幅度超过 ±150μV # 这个分段就会被丢弃 reject_criteria dict( eeg150e-6 # 150 微伏 ) print(f 拒绝标准: 幅度超过 {reject_criteria[eeg]*1e6:.0f} 微伏的分段将被丢弃)不同伪迹的幅度参考5.3 创建 Epochs 对象# ---------- 4. 创建 Epochs ---------- print(\n创建 Epochs...) # mne.Epochs()从连续数据创建分段 epochs mne.Epochs( # ── 数据源 ── raw_eeg, # 连续 EEG 数据 events, # 事件数组告诉 MNE 在哪切 event_idevent_id, # 事件 ID 字典告诉 MNE 每个编号叫什么 # ── 时间窗口 ── tmin-0.2, # 分段起始时间刺激前 0.2 秒 tmax0.5, # 分段结束时间刺激后 0.5 秒 # 总时长 tmax - tmin 0.5 - (-0.2) 0.7 秒 # ── 基线校正 ── baseline(-0.2, 0), # 用刺激前 0.2 秒做基线 # 原理每个分段减去基线期的平均值 # 效果让所有分段从同一起跑线开始 # ── 质量控制 ── rejectreject_criteria, # 拒绝幅度异常的分段 # ── 内存管理 ── preloadTrue, # 加载到内存加速后续操作 # ── 输出控制 ── verboseFalse # 不打印详细信息 ) # 输出统计信息 print(f✅ Epochs 创建完成) print(f 总事件数: {len(events)}) print(f 保留分段: {len(epochs)} ({len(epochs)/len(events)*100:.1f}%)) print(f 丢弃分段: {len(events) - len(epochs)} ({(len(events)-len(epochs))/len(events)*100:.1f}%)) # 查看各条件的分段数 print(f\n各条件分段统计) for cond_name in event_id.keys(): n_epochs len(epochs[cond_name]) n_events np.sum(events[:, 2] event_id[cond_name]) print(f {cond_name}: {n_epochs}/{n_events} 个 (保留率 {n_epochs/n_events*100:.1f}%))Epochs 参数详解表六、基线校正详解6.1 为什么需要基线校正问题不同分段可能在不同的高度 分段1 ~~~~~/‾‾‾‾‾~~~~ ← 整体偏上20 μV 偏移 分段2 ___/‾‾‾‾‾‾___ ← 整体偏下-10 μV 偏移 分段3 ~~~~~/‾‾‾‾‾~~~~ ← 整体居中0 μV 如果不校正叠加平均会被偏移污染 就好像把不同亮度的照片叠在一起结果一片模糊 基线校正后 分段1 ___/‾‾‾‾‾‾___ ← 都从 0 开始 分段2 ___/‾‾‾‾‾‾___ ← 都从 0 开始 分段3 ___/‾‾‾‾‾‾___ ← 都从 0 开始 现在可以公平地比较和叠加了6.2 基线校正的数学原理# 基线校正的数学过程 # 对于每个分段、每个通道 # 1. 找到基线期的时间范围-0.2 到 0 秒 # 2. 计算基线期内的平均值 # baseline_mean mean(data[基线期内]) # 3. 用整个分段减去这个平均值 # data_corrected data - baseline_mean # 4. 结果基线期的平均值为 0 # 简单例子 原始分段 [2.1, 2.2, 2.0, 2.5, 2.8, 3.1, 3.5] # ↑── 基线期 ──↑ ↑── 反应期 ──↑ 基线均值 (2.1 2.2 2.0) / 3 2.1 校正后 [-0.0, 0.1, -0.1, 0.4, 0.7, 1.0, 1.4] # ↑── 基线期 ≈ 0 ──↑七、Epochs 可视化7.1 蝴蝶图# ---------- 5. 可视化 ---------- print(\n可视化...) # epochs.plot()绘制分段的蝴蝶图 # # 为什么叫蝴蝶图 # 所有分段画在同一张图上 # 像蝴蝶翅膀一样展开 # # 怎么看 # 每条半透明的线 一个分段 # 线条的分布 不同分段之间的差异 # 线条密集处 稳定的脑电反应 # 线条离散处 变化较大的脑电反应 print(1. 绘制前 20 个分段的蝴蝶图...) epochs[:20].plot( n_epochs20, # 显示的分段数量 n_channels10, # 显示的通道数量 scalingsauto, # 自动调整纵轴比例 showTrue # 显示图形 ) plt.suptitle(前 20 个分段蝴蝶图, fontsize14, fontweightbold) plt.show(blockTrue) # blockTrue保持窗口打开蝴蝶图解读指南振幅 ↑ │ ╱‾‾╲ ← 所有分段在刺激后的反应 │ ╱ ╲___ │╱ ╲___ ────┼──────────────→ 时间 │ ↑ │ 刺激点(0秒) 看什么 1. 基线期-0.2~0秒应该接近 0 2. 刺激后各分段的变化方向是否一致 3. 有没有特别离谱的分离可能是坏段7.2 丢弃日志# plot_drop_log()绘制分段丢弃日志 # # 这个图显示 # - 每个条件有多少分段被丢弃 # - 丢弃的原因是什么 # - 被丢弃分段之间的关联 print(\n2. 绘制分段丢弃日志...) fig epochs.plot_drop_log(showFalse) plt.suptitle(分段丢弃日志, fontsize14, fontweightbold) plt.show(blockTrue)八、保存结果# ---------- 6. 保存 ---------- # save()将 Epochs 对象保存为 .fif 文件 # 保存了什么 # - 所有分段的数据 # - 通道信息 # - 事件信息 # - 时间信息 # 下次可以直接加载无需重新预处理和分段 epochs.save(day6_epochs.fif, overwriteTrue) print(\n✅ 数据已保存为 day6_epochs.fif) print(\n *60) print(第6天学习完成) print(*60)九、第6天完整代码# 环境设置 import matplotlib matplotlib.use(TkAgg) import matplotlib.pyplot as plt import mne import numpy as np import os from collections import Counter import warnings warnings.filterwarnings(ignore) plt.rcParams[font.sans-serif] [Microsoft YaHei, SimHei] plt.rcParams[axes.unicode_minus] False print(*60) print(MNE-Python 第6天分段Epoching与基线校正) print(*60) # ---------- 1. 加载数据 ---------- sample_data_folder mne.datasets.sample.data_path() raw_fname os.path.join(sample_data_folder, MEG, sample, sample_audvis_raw.fif) raw mne.io.read_raw_fif(raw_fname, preloadFalse) print(✅ 原始数据加载完成) # ---------- 2. 快速预处理 ---------- print(\n快速预处理...) raw_eeg raw.copy().pick_types(eegTrue, eogTrue, stimTrue) eeg_names [ch for ch in raw_eeg.ch_names if ch.startswith(EEG)] montage mne.channels.make_standard_montage(standard_1020) standard_names montage.ch_names[:len(eeg_names)] raw_eeg.rename_channels(dict(zip(eeg_names, standard_names))) raw_eeg.set_montage(montage) print( 加载数据到内存...) raw_eeg.load_data() print( 陷波滤波去除 60Hz...) raw_eeg.notch_filter(freqs60, pickseeg, verboseFalse) print( 带通滤波1-40 Hz...) raw_eeg.filter(l_freq1, h_freq40, pickseeg, verboseFalse) print( 重参考平均参考...) raw_eeg.set_eeg_reference(average, verboseFalse) print(✅ 快速预处理完成) # ---------- 3. 提取事件 ---------- print(\n提取事件...) events mne.find_events(raw_eeg, stim_channelSTI 014) print(f 提取到 {len(events)} 个事件) event_id { 听觉/左耳: 1, 听觉/右耳: 2, 视觉/左眼: 3, 视觉/右眼: 4 } for event_name, event_code in event_id.items(): count np.sum(events[:, 2] event_code) print(f {event_name}: {count} 次) # ---------- 4. 创建 Epochs ---------- print(\n创建 Epochs...) reject_criteria dict(eeg150e-6) epochs mne.Epochs( raw_eeg, events, event_idevent_id, tmin-0.2, tmax0.5, baseline(-0.2, 0), rejectreject_criteria, preloadTrue, verboseFalse ) print(f✅ Epochs 创建完成) print(f 总事件: {len(events)}, 保留: {len(epochs)}, 丢弃: {len(events)-len(epochs)}) # ---------- 5. 可视化 ---------- print(\n可视化...) epochs[:20].plot(n_epochs20, n_channels10, scalingsauto, showTrue) plt.suptitle(前 20 个分段蝴蝶图, fontsize14) plt.show(blockTrue) fig epochs.plot_drop_log(showFalse) plt.suptitle(分段丢弃日志, fontsize14) plt.show(blockTrue) # ---------- 6. 保存 ---------- epochs.save(day6_epochs.fif, overwriteTrue) print(\n✅ 数据已保存为 day6_epochs.fif) print(\n *60) print(第6天学习完成) print(*60)十、今日总结 核心概念️ 掌握的技能 操作顺序铁律1. 提取通道 pick_types() 2. 重命名通道 rename_channels() 3. 设置蒙太奇 set_montage() 4. 加载到内存 load_data() ← 关键必须在这之后 5. 陷波滤波 notch_filter() 6. 带通滤波 filter() 7. 重参考 set_eeg_reference() 8. 提取事件 find_events() 9. 创建分段 mne.Epochs()