批量预处理实战:把流程变成脚本
30 个被试点鼠标点一遍 = 一整天的机械劳动 + 无法复现。这一课把 2.3 的流水线 改写成一份可以在一夜之间跑完全部被试的脚本——这也是科研规范(可复现)的起点。
🎯 本节学习目标
- 理解 EEGLAB "GUI 操作即命令"的设计,会用
eegh查菜单对应的命令 - 掌握核心
pop_*函数:loadset / eegfilt / runica / epoch / saveset - 能独立写出多被试批量预处理脚本,带参数区、循环体、日志与错误处理
- 学会给每个被试输出质控图,人工抽查代替人工操作
- 知道批处理最常见的 5 个坑及预防办法
一、从 GUI 到脚本:eegh 的秘密
EEGLAB 的菜单操作全部会转成命令执行,并在内部历史里记录下来。在命令行输入 eegh
可以回放你在 GUI 里做过的每一步。这意味着:你在 2.3 用鼠标完成的流程,脚本版本已经"自动生成"了,
只需要把它们抄进 .m 文件、包一层 for 循环。
Matlab
% 在你做完一遍 GUI 预处理后,命令行输入:
eegh
% 会输出类似:
% EEG = pop_eegfilt(EEG, 0.1, 45, [], []);
% EEG = pop_runica(EEG, 'extended', 1);
% EEG = pop_epoch(EEG, {'stimulus'}, [-0.2 0.8], 'newname', ...);
% 这就是你的脚本原料。
二、核心函数速查
| 函数 | 对应 GUI 操作 | 常用参数 |
|---|---|---|
pop_loadset | File > Load | 'filename','sub01.set','filepath',dir |
pop_eegfilt | 滤波 | EEG, 0.1, 45(高通, 低通) |
pop_eegfiltnew | 零相位滤波(推荐) | EEG, 'fcutoff',50,'ftype','stop' 陷波 |
pop_select | 选通道/删通道 | 'channel',[1:32] |
pop_runica | ICA 分解 | 'extended',1(默认算法 infomax) |
pop_rejcont | 连续坏段剔除(自动阈值) | 阈值设置见 help |
pop_epoch | 分段 | EEG, {'stim'}, [-0.2 0.8] |
pop_rmbase | 基线校正 | EEG, [-200 0](ms) |
pop_saveset | 保存 | 'filename', outname |
三、批量模板(可直接改造使用)
Matlab · batch_preprocess.m
%% ============ 参数区:只改这里 ============
clear; clc; close all;
dataDir = 'D:\project\raw\'; % 原始数据目录(英文路径!)
outDir = 'D:\project\processed\';% 输出目录
qcDir = 'D:\project\qc\'; % 质控图目录
subjects = {'sub01','sub02','sub03','sub04'}; % 被试列表
hp = 0.1; lp = 45; % 滤波参数
epochWin = [-0.2 0.8]; % 分段窗口 (s)
baseline = [-200 0]; % 基线 (ms)
if ~exist(outDir, 'dir'), mkdir(outDir); end
if ~exist(qcDir, 'dir'), mkdir(qcDir); end
%% ============ 循环区:所有被试同一流程 ============
for s = 1:length(subjects)
sub = subjects{s};
fprintf('==== [%d/%d] %s ====\n', s, length(subjects), sub);
try
% 1. 读入
EEG = pop_loadset('filename', [sub '_raw.set'], 'filepath', dataDir);
% 2. 滤波(零相位)
EEG = pop_eegfiltnew(EEG, 'fcutoff', hp, 'ftype', 'highpass');
EEG = pop_eegfiltnew(EEG, 'fcutoff', lp, 'ftype', 'lowpass');
% 3. ICA(长数据时耗时,可在此跳过调试)
EEG = pop_runica(EEG, 'extended', 1);
% TODO: ICA 成分剔除 —— 建议人工查看后把成分编号写进一个表
% 4. 分段 + 基线
EEG = pop_epoch(EEG, {'stimulus'}, epochWin, 'newname', [sub '_ep']);
EEG = pop_rmbase(EEG, baseline);
% 5. 试次级剔除(±100 uV)
EEG = pop_rejtrend(EEG, 1, EEG.nbchan, -100, 100);
% 6. 保存
pop_saveset(EEG, 'filename', [sub '_pp.set'], 'filepath', outDir);
% 7. 质控图:ERP 总平均 + 剩余试次数写进日志
figure('Visible','off');
plot(EEG.times, mean(EEG.data, 2)); % 通道平均的粗略 ERP
title(sprintf('%s trials=%d', sub, EEG.trials));
print('-dpng', '-r150', [qcDir sub '_erp.png']);
close;
fprintf(' OK, %d trials kept\n', EEG.trials);
catch err
% 单个被试失败不中断整批
fprintf(2, ' FAILED: %s\n', err.message);
fid = fopen([qcDir 'error_log.txt'], 'a');
fprintf(fid, '%s\t%s\n', sub, err.message);
fclose(fid);
end
end
disp('全部完成,请检查 qc 目录。');
四、质控:抽查代替操作
脚本跑完后不要直接进入分析——打开 qc 目录,逐个检查每张 ERP 预览图和剩余试次数。 质控清单:剩余试次 ≥ 设计的 70%?波形形态符合预期?有没有个别被试整段异常? 有问题的被试回到原始数据单独处理,不要让一个坏被试污染组平均。
五、常见坑清单
- 中文路径 / 空格路径:IO 报错十有八九来自它。数据目录全英文;
- 被试文件命名不统一(sub1、subject01、S1 混用):循环拼文件名失败。开工前先统一命名;
- EEG 变量被覆盖:循环里忘记每次重新 load,上一个被试的数据被重复处理;
- 内存不足:高密度通道 + 高采样率 + 多被试同时在内存。一次处理一个,及时
clear EEG; - 事件名不一致:有的被试标记叫
stim有的叫stimulus。pop_epoch匹配不到就 0 个试次——检查EEG.event.type。
💡 调试建议
先用 1 个被试跑通全流程,确认输出无误后,再把 subjects 列表换成全部被试整夜运行。永远不要第一次就用全量数据试跑。
📌 本节小结
eegh回放 GUI 操作历史——GUI 会用,脚本就会写- 批处理脚本三段式:参数区 / 循环区 / 质控输出,参数只改一处
try-catch保证单个被试失败不中断整批,错误写入日志- 跑完必抽查质控图:试次剩余率、波形形态、异常被试
- 批处理五大坑:中文路径、命名不统一、变量覆盖、内存、事件名不一致
✏️ 课后练习
- 把 2.3 的完整流程(含 ICA 成分剔除)改写成脚本,对你手头(或公开数据集里)的 2 个"被试"文件跑通。
- 故意把一个被试的文件名改错,验证 try-catch 是否正常记录日志并继续处理其他人。
- 在质控图上叠加显示每个被试剩余试次数,写一段 10 行以内的代码,自动把剩余试次 < 50% 的被试列入"需复查"列表。