阶段五 · 前沿专题 · 模块 8 · 第 22 课 / 共 28 课

静息态 EEG 微状态分析(上)

换一个视角看脑电:不问"哪个频段多响",而问"头皮上的电位构型何时切换、 停留多久、怎么切换"。这一课从 GFP 一路走到转移概率矩阵,把微状态流水线完整走一遍。

🕐 60 分钟🎯 难度:高阶✅ 前置:模块 2、4

🎯 本节学习目标

  • 能用"电视频道切换"的类比说清微状态的准稳态含义,并说明它与频谱分析的互补关系
  • 独立完成 GFP 计算、峰值提取、极性无关 k-means 聚类与反向拟合全流程
  • 会计算并解读五大类指标:解释方差、duration、occurrence、coverage、转移概率
  • 理解从被试级走向组级统计的关键前提:所有被试拟合到同一套原型地图
  • 知道 K 值、平滑窗、最短持续时间、参考方式等参数如何选择并在论文中报告

一、微状态是什么:大脑的"电视频道"

到目前为止,我们观察脑电的方式无非两种:顺着电极看波形(ERP,模块 3), 或者顺着频段看能量(时频分析,模块 4)。微状态分析提供第三种视角: 看整个头皮的电位分布图(拓扑图)如何随时间演变

📖 概念:微状态(Microstate)

头皮的电位拓扑并不是连续缓慢地变化,而是在几十毫秒内保持一种构型("准稳态"), 然后在几毫秒内整体切换成另一种构型,如此往复—— 像电视机换台:每个频道画面稳定,换台在一瞬间完成。

经典研究(Lehmann 学派)发现:静息态脑电的拓扑图基本只在少数几张"原型地图"之间切换, 每张地图持续约 60–120ms。更有意思的是,最常出现的四张地图(惯称 A、B、C、D) 在不同人身上高度相似,且分别与 fMRI 的视觉网络、听觉网络、凸显网络和额顶注意网络存在对应。 于是这个领域流传着一句很有画面感的话:"思想的原子"—— 微状态被视为大规模脑网络短暂同步化的电生理表现。

📌 一句话抓住微状态

频谱分析问"哪个频段多响",微状态问"大脑此刻整体处于哪张地图、每张停留多久、怎么切换"。前者是幅值视角,后者是拓扑(空间构型)视角,两者互补而非替代。

二、互补的视角:拓扑分析 vs 频谱分析

维度频谱 / 时频分析微状态分析
观察对象各电极信号随频率的幅值分布全头皮电位的空间分布(拓扑图)
时间尺度数百毫秒到数秒的分析窗毫秒级(一段约 60–120ms)
回答的问题"哪些频段能量多大、何时变大""大脑整体处于哪种构型、如何切换"
对参考的依赖幅值指标相对不敏感强依赖参考(必须平均参考)
典型产出功率谱、时频图、ERD/ERS原型地图、duration、coverage、转移矩阵

三、分析流水线总览

① 预处理(平均参考是硬要求)+ 必要时降采样(如 250Hz)
② 计算全局场功率 GFP 曲线
③ 只在 GFP 峰值时刻提取拓扑图(信噪比最高的时刻)
④ 极性无关 k-means 聚类 → K 张原型地图
⑤ 反向拟合:给每个时间点贴上微状态标签(含平滑与最短持续时间)
⑥ 指标计算(duration / occurrence / coverage / 转移概率)
⑦ 被试级指标 → 组级统计

这条流水线看起来很长,其实每一步的代码都不超过三十行。下面逐步拆解。

四、第一步:GFP 曲线与峰值提取

📖 概念:GFP(全局场功率)

每个时间点上,所有电极电位相对于其平均值的空间离散度(空间标准差)。GFP 高 = 拓扑图"对比度"强、神经信号占优;GFP 低 = 拓扑主要由噪声决定。

为什么只在峰值处取样?因为微状态的切换几乎总是发生在 GFP 峰值附近—— 峰值时刻的拓扑最"清晰"。只在峰值处采样,既能大幅减少数据量, 又能去掉低信噪比的时刻,让聚类更稳。

Matlab
% ---- 第 1 步:GFP 曲线与峰值提取 ----
% 前提:数据已完成模块 2 的预处理,并使用平均参考(微状态对参考极其敏感)
fs   = EEG.srate;
data = EEG.data - mean(EEG.data, 1);   % 再做一次空间中心化,保证拓扑"相对平均"
gfp  = sqrt(mean(data.^2, 1));         % GFP(t):每个时刻的空间标准差

figure; plot(EEG.times/1000, gfp);     % 目检:GFP 应是快速起伏的曲线
xlabel('时间 (s)'); ylabel('GFP (\muV)'); title('全局场功率');

% 只保留 GFP 局部极大值处的拓扑:间隔至少 10ms、高度超过均值
[pk, idx] = findpeaks(gfp, 'MinPeakDistance', round(0.01*fs), ...
                      'MinPeakHeight', mean(gfp));
maps = data(:, idx);                   % 通道 x 峰值时刻:候选拓扑集合
fprintf('采样拓扑数 %d(占全部时间点 %.1f%%)\n', ...
        size(maps,2), 100*size(maps,2)/size(data,2));
% findpeaks 来自 Signal Processing Toolbox(后面的 filtfilt 也是)

五、第二步:聚类成原型地图

现在手里有几千张候选拓扑图,但它们其实只是少数几种构型的反复出现。 聚类的任务就是把它们归并成 K 张"原型地图"。为什么不能直接用 Matlab 自带的 kmeans因为微状态的聚类有两处特殊改造:

  1. 极性无关:一张拓扑图和它整体变号后的图(红蓝互换)在生理上是同一个状态,相似度必须取空间相关的绝对值;
  2. 中心更新用空间主成分:同一类里可能混着正负两种朝向的图,直接取均值会互相抵消,用类的空间第一主成分(SVD 的第一奇异向量)才站得住。
Matlab
% ---- 第 2 步:极性无关 k-means 聚类(教学简化版)----
K = 4;  nInit = 50;                     % 经典微状态研究常取 K=4
nS = size(maps, 2);
mapsN = maps ./ vecnorm(maps);          % 样本归一化:只比较"形状",不比较强弱
gfp2  = sum(maps.^2, 1);                % 每个样本的 GFP^2(后面当权重)
bestGEV = -inf;
for init = 1:nInit                      % 随机重启:对抗 k-means 的初值敏感性
    C = maps(:, randperm(nS, K));       % 初始中心:随机挑 K 张真实拓扑
    for it = 1:50                       % 教学版固定迭代;工程版加收敛判断
        sim = abs((C ./ vecnorm(C))' * mapsN);   % 分配步:K x nS 的|相关|矩阵
        [~, seg] = max(sim, [], 1);
        for k = 1:K                     % 更新步:类内空间第一主成分当新中心
            Xk = mapsN(:, seg == k);
            if size(Xk, 2) < 2, continue; end
            [u, ~, ~] = svd(Xk * Xk', 'econ');
            C(:, k) = u(:, 1);
        end
    end
    % 用全局解释方差 GEV 给本次初始化打分(这 K 张地图解释了多少电位方差)
    gev = 0;
    for k = 1:K
        sel = (seg == k);
        cn = C(:, k) / norm(C(:, k));
        gev = gev + sum((cn' * mapsN(:, sel)).^2 .* gfp2(sel));
    end
    gev = gev / sum(gfp2);
    if gev > bestGEV
        bestGEV = gev;
        Cbest = C ./ vecnorm(C);        % 归一化的 K 张原型地图
    end
end
C = Cbest;
fprintf('K = %d, 全局解释方差 GEV = %.1f%%\n', K, 100*bestGEV);
% 直觉:GEV 是"这 K 张地图演了全剧的多大比例";K=4 时静息态典型值约 70-80%
💡 K 怎么选

让 K 从 2 取到 8,画一条 GEV 随 K 变化的曲线,看"肘部";更严谨的做法是交叉验证(留出一段数据看原型地图的泛化)。不要只因为"经典研究用 4"就盲从 4——你的数据、你的范式、你的研究问题都可能需要别的 K。工程上也不必每次手写:EEGLAB 的微状态插件或下一课的 CARTOOL 都有成熟实现;手写的价值在于你知道每个按钮背后在算什么。

六、第三步:反向拟合:给全数据贴标签

原型地图回答了"有哪几种状态",反向拟合(backfitting)回答"每一时刻处于哪种状态": 对每个时间点,计算它与 K 张地图的空间相关,贴上最像的那张的标签。 随后必须做两件清理工作:时间平滑(去掉几毫秒的闪烁标签) 和最短持续时间约束(真实微状态至少持续几十毫秒)。

Matlab
% ---- 第 3 步:反向拟合——给每个时间点贴标签 ----
dataN = data ./ max(vecnorm(data), eps);   % 逐点归一化:不让高 GFP 时刻主导
sim   = abs(C' * dataN);                   % K x 时间点 的|空间相关|
[~, labels] = max(sim, [], 1);             % 每个时刻最像哪张地图
labels(gfp < 0.5*mean(gfp)) = 0;           % GFP 太低的时刻不归类(0 = 噪声主导)

% 时间平滑 + 最短持续时间:真实微状态持续约 60ms 以上
labels = medfilt1(double(labels), 3);      % 3 点中值滤波去掉孤立标签(奇数窗不出小数)
% labels = enforce_min_duration(labels, round(0.03*fs));  % <30ms 并入邻段:课后练习 3

七、指标体系:给微状态"记账"

贴好标签后,整段数据变成了一串"频道号"序列。下面这些指标就是这串序列的账本:

指标定义直觉解释
解释方差 GEV原型地图解释的总电位方差比例这 K 张图"演了全剧的多大比例"
平均持续时间 duration每段微状态的平均时长一个"频道"平均播多久
出现频率 occurrence每秒进入该状态的次数换到这个频道多频繁
覆盖率 coverage该状态占总时间的比例这个频道全天播放占比
转移概率P(下一状态 j | 当前离开状态 i)观众从频道 i 换台时去哪
Matlab
% ---- 第 4 步:指标计算(游程编码思路:先切段,再按类汇总)----
d      = diff(labels);
edges  = [1, find(d ~= 0) + 1, numel(labels) + 1];   % 每一段的起止边界
durPts = diff(edges);                                % 每段持续的采样点数
segLab = labels(edges(1:end-1));                     % 每段的微状态编号
valid  = segLab > 0;                                 % 排除未归类(0)的段
recSec = numel(labels) / fs;

occurrence = accumarray(segLab(valid)', 1, [K 1]) / recSec;              % 次/秒
nSeg       = accumarray(segLab(valid)', 1, [K 1]);
coverage   = accumarray(segLab(valid)', durPts(valid)') / sum(durPts);   % 时间占比
duration   = accumarray(segLab(valid)', durPts(valid)', [K 1]) ./ max(nSeg, 1);
duration_ms = duration / fs * 1000;                  % 每段平均持续毫秒数

% 转移概率矩阵:行 = 当前状态,列 = 下一状态(行归一化的条件概率)
T = zeros(K);
seq = segLab(valid);
for s = 1:numel(seq) - 1
    if seq(s) ~= seq(s+1)
        T(seq(s), seq(s+1)) = T(seq(s), seq(s+1)) + 1;
    end
end
T = T ./ max(sum(T, 2), 1);
disp('转移概率矩阵:'); disp(round(T, 3));

八、从被试级到组级统计

单被试的指标算出来只是原材料。走向组级之前有一条铁律: 所有被试、所有条件必须拟合到同一套原型地图上。 这套地图通常来自组级总平均数据的峰值拓扑聚类(或用全体被试合并的数据), 然后逐被试反向拟合、逐被试算指标。指标在组间用置换检验或 bootstrap 置信区间比较, 多个状态 × 多个指标的比较要做 FDR 校正(模块 5 讲过)。

⚠️ 两个最容易踩的坑

每个被试各聚各的再比较:你的 A 类和别人的 A 类根本不是同一张图,"组间差异"可能只是地图不同造成的假象;② 忘记平均参考:微状态的拓扑完全由"相对参考的分布"定义,参考不统一,一切指标不可比。论文报告时写清:参考方式、K 及其确定方法、聚类算法、峰值采样间隔、平滑与最短持续时间、GEV。

📌 本节小结

  • 微状态 = 头皮拓扑的毫秒级准稳态;四张经典地图与大尺度脑网络存在对应
  • 流水线:GFP → 峰值拓扑 → 极性无关 k-means → 反向拟合 → 指标 → 组级统计
  • 两处"必须不同":相似度取绝对值(极性无关),中心更新用空间主成分(不是均值)
  • 五大指标 = 演多久(duration)、多频繁(occurrence)、占多少(coverage)、怎么切换(转移矩阵)、演了多少(GEV)
  • 组级比较的前提:所有被试拟合到同一套原型地图;平均参考不可省

✏️ 课后练习

  1. 对一段至少 60 秒的静息态数据计算 GFP 并画图,用 findpeaks 提取峰值拓扑,统计峰值数量与平均间隔(毫秒)。
  2. 令 K = 2…8 分别聚类,画出 GEV 随 K 变化的曲线,找"肘部",并写两句话论证你的研究问题需要几个状态。
  3. 实现 enforce_min_duration(labels, minPts):把短于阈值的段并入前后较长的邻段,比较处理前后 duration 的分布变化。