Matlab 编程基础:从变量到矩阵
EEG 数据在计算机里就是一个"电极 × 时间"的数字矩阵。这一课把 Matlab 讲到 "够用且理解数据结构"的程度——不追求语法大全,每一步都对着脑电数据讲。
🎯 本节学习目标
- 熟悉 Matlab 的四个核心窗口,学会用
help和doc自助查文档 - 理解"一切皆矩阵",会用
[]、zeros、linspace创建数据 - 熟练使用索引与切片:
M(2,3)、M(:, 100:200)——这是读写 EEG 数据的基本功 - 掌握
for、if与自定义函数,能看懂别人的批处理脚本 - 会用
plot、imagesc画信号波形与数据热图
一、为什么脑电分析绕不开 Matlab
你可能听说过 Python 的 MNE,但现实是:国内绝大多数课题组的脑电流水线建立在 Matlab 生态上——EEGLAB、FieldTrip、ERPLAB、CARTOOL、sLORETA 全是 Matlab 工具包。 学会 Matlab,你就能直接使用这套学界积累了几十年的工具链,还能看懂师兄师姐留下的脚本。
好消息是:做脑电分析只需要 Matlab 的一小部分语法。这一课的目标不是"学会 Matlab",而是"让 Matlab 为脑电分析所用"。
二、认识 Matlab 的四个窗口
| 窗口 | 作用 | 你会在什么时候用到 |
|---|---|---|
| 命令行窗口 | 输入命令、立即看结果 | 试探性操作:看看变量、画张图 |
| 编辑器 | 写脚本(.m 文件) | 批处理、可复现的分析流程(模块 2.4 的主战场) |
| 工作区 | 查看当前内存里的变量 | 检查 EEG 结构里有什么字段 |
| 当前文件夹 | 文件浏览与路径 | 管理数据文件;路径含中文/空格是常见报错来源 |
忘记函数用法时:help plot(快速摘要)和 doc plot(完整文档页)。养成这个习惯,比搜索更快。
三、变量与矩阵:EEG 数据的本来面目
Matlab 的核心数据结构是矩阵。一段脑电数据采集下来,就是这样一个矩阵: 行 = 电极通道,列 = 时间点。例如 64 通道、采样率 500Hz、记录 10 分钟的数据, 就是一个 64 × 300000 的矩阵(500 × 60 × 10 = 300000 个时间点)。
% 创建变量:一行一个命令,分号结尾表示"执行但不显示结果"
a = 5; % 标量,其实是 1x1 矩阵
v = [10 20 30]; % 行向量:3 个时间点的信号
M = [1 2 3; 4 5 6]; % 2 行 3 列矩阵:想象成 2 个电极 x 3 个时间点
% 常用构造函数
z = zeros(64, 1000); % 64 通道 x 1000 时间点的全 0 矩阵
t = linspace(0, 1, 500); % 0 到 1 秒之间的 500 个时间点(采样率 500Hz)
% 模拟一段"脑电":10Hz 的 alpha 波 + 随机噪声
fs = 500; % 采样率
time = (0:1/fs:2)'; % 2 秒的时间轴(列向量)
eeg = 10*sin(2*pi*10*time) + 5*randn(size(time)); % alpha 波 + 噪声(微伏)
plot(time, eeg); % 画出来看看
xlabel('时间 (s)'); ylabel('幅度 (\muV)'); title('模拟 EEG 信号');
四、索引与切片:读写数据的基本功
假设 EEGdata 是 64 × 300000 的数据矩阵(行=通道,列=时间点):
| 写法 | 含义 | 脑电场景举例 |
|---|---|---|
EEGdata(2, 500) | 第 2 通道第 500 个时间点的值 | 查某个电极某一时刻的电位 |
EEGdata(2, :) | 第 2 通道的完整波形 | 取出一个电极画整段波形 |
EEGdata(:, 1000:2000) | 所有通道、第 1000–2000 时间点 | 截取 2 秒的数据段 |
EEGdata([1 2 3], :) | 指定多个通道 | 只取 Fp1、Fp2、Fz 三个电极 |
EEGdata(1:32, :) | 前 32 个通道 | 只用一半电极做快速测试 |
size(EEGdata) | 矩阵的尺寸 | 确认通道数和数据长度对不对 |
在 EEGLAB 里你点菜单,本质都是在操作这个矩阵:EEG.data 就是它。切片用熟了,后面写脚本会非常顺手。
五、矩阵运算:小心 * 与 .* 的区别
Matlab 里 * 是矩阵乘法,.* 才是"对应元素相乘"。对脑电分析,90% 场景用的是点运算:
a = [1 2 3];
b = [4 5 6];
a .* b % [4 10 18] 每个元素对应相乘 —— 常用!
a * b % 报错(维度不满足矩阵乘法规则)
a * b' % 32:向量内积
a ./ b % [0.25 0.4 0.5] 逐元素相除
a + 10 % [11 12 13] 广播运算:整体加 10(如单位换算 \muV 转 mV)
% 常见用途:把信号从微伏转成伏特、或做归一化
eeg_volts = eeg * 1e-6;
eeg_norm = eeg ./ max(abs(eeg)); % 除以最大绝对值,缩放到 [-1, 1]
六、流程控制:让重复劳动自动化
% for 循环:对每个通道求均值和标准差,标记"噪声过大"的通道
channels = 1:64;
bad_list = []; % 用来收集坏通道编号
for ch = channels
m = mean(EEGdata(ch, :)); % 该通道的平均电位
sd = std(EEGdata(ch, :)); % 该通道的波动大小
if sd > 100 % 波动超过 100 \muV 视为可疑
bad_list = [bad_list, ch]; % 记下来
end
end
disp(['可疑通道: ' mat2str(bad_list)]); % 打印结果
这段代码就是"坏通道检测"的最小雏形——模块 2.4 的批处理脚本里, 循环的外层从"通道"换成"被试",就是完整的批处理了。
七、画图三件套
% 1. plot:波形图(时间序列)
figure; plot(time, eeg);
% 2. imagesc:热图(矩阵可视化)—— 时频图、ERPimage 都靠它
figure; imagesc(randn(64, 500)); % 64 通道 x 500 时间点的伪彩色图
colorbar;
% 3. subplot:一页多图,对比不同条件
figure;
subplot(1, 2, 1); plot(time, eeg); title('原始信号');
subplot(1, 2, 2); plot(time, smooth(eeg, 25)); title('平滑后');
% 保存图片(print 比 saveas 控制更细)
print('-dpng', '-r300', 'my_first_eeg_figure.png');
投稿图件用矢量格式(-depsc 或 -dpdf)保存,放大不失真;给导师看预览用 PNG 即可。
八、五个必须养成的习惯
- 脚本开头写三连:
clear; clc; close all;——清空旧变量、清屏、关旧图,避免"上次运行残留"导致的诡异 bug。 - 写注释:一个月后的你等于陌生人。
%后面用一句话说明"为什么这么做"。 - 文件名只用英文:中文路径和空格是 EEGLAB/FieldTrip 报错的头号来源。
- 路径集中管理:数据、脚本、结果分三个文件夹,用
addpath添加路径,别让文件散落桌面。 - 随手保存:
save('result.mat', 'eeg')把中间结果存档;load('result.mat')随时恢复。
📌 本节小结
- EEG 数据 = 电极 × 时间的矩阵:
EEGdata(通道, 时间点) - 切片
M(:, 100:200)、构造zeros/linspace、尺寸size()是使用频率最高的四个操作 - 点运算
.* ./用于逐元素计算,*是矩阵乘法——两者不可混用 for + if组合可以实现坏通道检测等自动检查逻辑,也是批处理脚本的骨架- 三个习惯省半天调试:开头三连、英文路径、随手 save
✏️ 课后练习
- 生成一个 64 × 1000 的随机矩阵
X,用imagesc画出热图,加上colorbar并保存为 PNG。 - 对
X的每个通道求标准差,找出标准差最大的 3 个通道编号(提示:std+sort或maxk)。 - 用
linspace生成 2 秒时间轴,叠加 10Hz 和 40Hz 两个正弦波(幅度分别为 10 和 5),画图后观察你能"目测"出几个波峰结构。