简介:GRETNA 2.0.0 Release 是北京师范大学研发、面向 MATLAB 环境的脑成像数据分析插件,主要服务于脑电(EEG)与脑磁图(MEG)方向的神经科学研究人员及具备一定 MATLAB 编程基础的研究生。它集成滤波去噪、源定位、功率谱与相干性分析、独立成分分析等算法,可处理视觉诱发电位、听觉诱发电位及事件相关电位等实验数据,并支持头皮电位图与源定位图像的可视化,帮助研究者从复杂信号中提取大脑网络的动态连接模式。资源包共 845 个文件,约 27.51MB,以 387 个 .m 脚本、51 个 .hpp 与 24 个 .c 源码构成核心算法,配合 41 个 .mat 数据、24 个 .nii 影像及多平台 mex 二进制文件,另有 png、fig、html 等文档与示例资源,目录结构完整,便于按模块查阅与二次开发。目前已有 524 人学习下载,适合需要系统开展脑电分析、批量处理实验数据并深入理解算法实现的研究者参考使用。
1. 从一份 MATLAB 脑网络分析包说起:GRETNA-2.0.0 能解决什么
如果你在做静息态功能磁共振(rs-fMRI)的脑网络分析,大概率绕不开一个现实问题:从原始影像到“图论指标”之间,横着预处理、脑区划分、时间序列提取、功能连接矩阵构建、网络属性计算这一长串步骤,每一步都有各自的工具和格式要求。GRETNA-2.0.0_release.zip 就是冲着这条链路来的——它是一套基于 MATLAB 的图论网络分析工具箱,把功能连接矩阵的构建和后续的图论指标计算打包在一起,让研究者不用在多个脚本之间来回倒腾数据。这套东西适合两类人:一是刚进脑影像组、需要快速跑通一套标准流程的新手;二是已经有预处理管线、只想找个稳定模块算网络属性的熟手。它不负责最前端的 DICOM 转换和头动校正,那是 SPM、DPABI 那类工具的活,GRETNA 的定位是“拿到时间序列之后的事”。
2. 环境准备与目录结构:把 MATLAB 路径这关先过了
2.1 版本选择与依赖确认
GRETNA 是纯 MATLAB 实现,不依赖编译型 mex 文件(部分图论指标有内置函数),所以对操作系统不挑,Windows、Linux、macOS 都能跑。真正要确认的是 MATLAB 版本。2.0.0 这个版本号对应的代码里用了一些较新的语法特性,我一般建议 R2016b 及以上,R2018a 之后更稳。低于 R2014b 的版本在图形句柄和部分函数签名上会报错,别硬试。
依赖方面,GRETNA 自身是自包含的,但它读数据、写结果会用到 MATLAB 基础工具箱里的统计和矩阵函数,这些默认都有。唯一需要额外留意的是,如果你的预处理在 DPABI 或 SPM 里做,确保它们的路径也在 MATLAB 搜索路径中,因为 GRETNA 的某些批处理入口会调用它们生成的中间文件(比如以.nii或.mat形式存的时间序列)。不需要额外装 Python 或 R,所有计算都在 MATLAB 内完成。
2.2 解压后的目录该看哪几个
把 zip 解压到一个不含中文和空格的路径下,这是血泪经验——MATLAB 对含空格路径的处理在某些load、save调用里会出玄学问题。解压后你会看到类似这样的结构:
GRETNA-2.0.0/ ├── GRETNA.m % 主入口,运行后弹出 GUI ├── gretna_*.m % 各功能模块的顶层函数 ├── GUI/ % 图形界面相关 ├── Functions/ % 核心计算函数 ├── Templates/ % 脑区模板、坐标文件 ├── Examples/ % 示例数据 └── ...关键入口是GRETNA.m。在 MATLAB 命令窗口里cd到解压目录,然后直接运行:
% 切换到 GRETNA 根目录(替换成你自己的路径) cd('D:/Tools/GRETNA-2.0.0'); % 把 GRETNA 及其子目录加入搜索路径 addpath(genpath(pwd)); % 启动主界面 GRETNA;addpath(genpath(pwd))这一步不能省,否则 GUI 里点任何按钮都会提示函数未定义。运行GRETNA后弹出的界面是后续所有操作的起点,它把功能分成了几个大块:数据准备、网络构建、网络分析、统计比较。第一次打开建议先点Examples里的示例数据跑一遍,确认环境没问题再上自己的数据。
提示:如果你在 Linux 服务器上没有图形界面,GRETNA 也支持纯命令行调用,直接调
gretna_*.m里的顶层函数并传参数即可,不必强开 GUI。
3. 从时间序列到功能连接矩阵:核心参数怎么设
3.1 输入数据的组织方式
GRETNA 对输入数据的组织有比较明确的要求。它期望的输入通常是每个被试一个文件夹,里面存放各脑区的时间序列文件,或者是一个已经提取好的.mat/.txt矩阵。常见做法是:先用 DPABI 或 SPM 完成预处理,然后用 AAL、Brainnetome 等模板做脑区划分,把每个脑区平均后的时间序列导出来。GRETNA 的Templates目录里自带了一些常用模板,可以直接用来做 ROI 提取。
假设你有 30 个被试,每个被试一个文件夹,命名如Sub001、Sub002,每个文件夹里有一个timeseries.mat,里面是一个T × N的矩阵(T 是时间点数,N 是脑区数)。在 GUI 里选“Network Construction”模块,指定被试文件夹的父目录,工具会自动遍历。
3.2 功能连接的计算与阈值选择
功能连接矩阵的计算本质上是脑区时间序列两两之间的相关性。GRETNA 默认用 Pearson 相关,也支持偏相关和部分相干。对大多数静息态研究,Pearson 相关是起点。计算完得到一个 N×N 的相关矩阵,值在 -1 到 1 之间。
接下来是阈值化,这一步最容易翻车。GRETNA 提供两种主流策略:
| 阈值策略 | 含义 | 适用场景 |
|---|---|---|
| 绝对阈值 | 保留相关系数大于某值的边 | 组间连接强度差异明显时 |
| 比例阈值(密度) | 保留最强的前 x% 的边 | 组间比较,控制网络密度一致 |
| 加权/二值 | 保留权重或转成 0/1 | 图论指标对二值/加权敏感 |
我一般推荐比例阈值,因为不同被试的绝对相关值分布受头动、噪声影响大,用密度能保证每个被试的网络边数一致,组间比较更公平。常见密度范围是 0.1 到 0.3,步长 0.01 或 0.05。GRETNA 允许你设一个密度区间,它会在这个区间内对每个密度分别算图论指标,最后得到指标随密度变化的曲线,这样比单点阈值稳健得多。
% 示例:命令行方式构建功能连接并做比例阈值 % 假设时间序列已存为 subj_ts(T×N 矩阵) % 计算 Pearson 相关矩阵 fc = corrcoef(subj_ts); % 设定密度阈值序列,从 0.1 到 0.3,步长 0.05 densities = 0.10:0.05:0.30; % 对每个密度做阈值化并计算图论指标 for d = 1:length(densities) % 按密度保留最强的边,其余置零 thr = prctile(fc(:), 100*(1 - densities(d))); fc_thr = fc; fc_thr(fc_thr < thr) = 0; % 对角线置零,避免自连接 fc_thr(1:N+1:end) = 0; % 此处可调用 GRETNA 的图论函数计算指标 % metrics = gretna_graph_metrics(fc_thr, ...); end这段代码的逻辑是:prctile找到对应密度下的相关系数分位点,低于该值的边全部砍掉,对角线清零防止自环。参数densities的选择要看你的网络规模——N 越大,可用的密度下限越低,因为边数多。如果 N 只有 90(AAL 模板),密度低于 0.1 时网络可能不连通,算出来的路径长度会变成 Inf,这是常见报错来源。
3.3 图论指标的选择与含义
GRETNA 能算的图论指标覆盖了大多数临床和认知研究的需求,常用的有:
- 全局效率(Eg):衡量网络信息传递的整体效率,对网络断连敏感。
- 局部效率(Eloc):衡量局部集群的信息传递能力。
- 聚类系数(Cp):反映网络的局部聚集程度。
- 特征路径长度(Lp):网络平均最短路径,越小越“小世界”。
- 小世界属性(Sigma):Cp/Cp_random 与 Lp/Lp_random 的比值,大于 1 说明有小世界特性。
- 节点度、节点效率、介数中心性:节点层面的指标,用于定位关键脑区。
选哪些指标取决于你的假设。如果是探索性研究,建议全局和节点层面都算一遍,GRETNA 支持批量输出。注意,二值网络和加权网络算出来的同一指标不可直接比较,论文里要写清楚用的是哪种。GRETNA 的 GUI 里每个指标旁边有勾选框,勾多了计算时间会明显增加,30 个被试、90 个脑区、10 个密度点,全指标跑下来可能要十几分钟到半小时,取决于机器。
4. 批处理与统计比较:把 30 个被试一次跑完
4.1 批处理的目录规范与脚本化
GUI 适合试跑,但真正处理几十上百个被试时,手动点不现实。GRETNA 的批处理依赖一套约定:父目录下每个被试一个子文件夹,子文件夹里放时间序列文件,文件名要一致。比如:
D:/Data/ ├── Sub001/ │ └── ts.mat ├── Sub002/ │ └── ts.mat └── ...然后在 GUI 里选父目录,指定文件名模式,工具会自动生成一个批处理脚本并执行。更稳的做法是自己写一个循环调用底层函数,这样出错了能定位到具体被试:
% 批量构建功能连接矩阵并保存 data_dir = 'D:/Data/'; subjects = dir(fullfile(data_dir, 'Sub*')); output_dir = 'D:/Results/'; if ~exist(output_dir, 'dir'); mkdir(output_dir); end for i = 1:length(subjects) subj_name = subjects(i).name; ts_file = fullfile(data_dir, subj_name, 'ts.mat'); if ~exist(ts_file, 'file') fprintf('跳过 %s:找不到时间序列文件\n', subj_name); continue; end load(ts_file); % 假设变量名为 ts fc = corrcoef(ts); % 保存功能连接矩阵,后续统一做阈值化和指标计算 save(fullfile(output_dir, [subj_name '_fc.mat']), 'fc'); fprintf('完成 %s\n', subj_name); end这段脚本的关键点是:先检查文件是否存在再加载,避免一个被试缺文件导致整个循环中断;每个被试的 FC 矩阵单独保存,方便后续换阈值策略时不用重算。corrcoef对含 NaN 的时间序列会返回 NaN 矩阵,如果预处理没做好,这里会静默产生一堆 NaN,所以跑完最好抽查几个矩阵的sum(isnan(fc(:)))。
4.2 组间统计比较的注意事项
GRETNA 自带了一些统计比较功能,比如两组被试的全局指标 t 检验、节点指标的 FDR 校正等。但实际研究中,组间比较往往还要控制头动、年龄、性别等协变量,这时候 GRETNA 的内置统计可能不够灵活,常见做法是把指标导出到 SPSS、R 或 Python 里做一般线性模型。
如果直接用 GRETNA 做统计,要注意两点:一是多重比较校正,节点层面有几十上百个脑区,不做校正的 p<0.05 基本不可信,GRETNA 提供 FDR 和 Bonferroni,选 FDR 更常用;二是效应量,光看 p 值不够,报告 Cohen's d 或 η² 能让结果更有说服力。GRETNA 的输出里一般会包含均值、标准差,效应量可以自己算。
注意:组间比较前先检查两组的基本信息(年龄、性别、头动参数)是否匹配,如果头动差异大,网络指标的组间差异可能只是头动的副产品,这是审稿人常问的点。
5. 避坑与排查:那些让我重跑过整批数据的坑
5.1 路径含中文或空格导致读取失败
现象:GUI 里选完目录,点运行后报错Invalid file identifier或直接闪退。原因:MATLAB 的fopen、load在部分版本对含中文或空格的路径处理不一致,GRETNA 内部拼接路径时没有做转义。解决:把所有数据、工具、输出路径都改成纯英文、无空格,比如D:/Data/而不是D:/我的数据/。这是最省事的办法,别跟路径较劲。
5.2 密度阈值过低导致网络不连通
现象:算路径长度时结果全是Inf或NaN,或者小世界属性算不出来。原因:比例阈值设得太低,保留的边太少,网络分裂成多个不连通的子图,最短路径不存在。解决:提高密度下限,或者先检查每个密度下网络的连通性。GRETNA 有些版本会输出连通性警告,看到警告就往上调密度。一般 N=90 时密度不低于 0.1,N=200 以上可以到 0.05。
5.3 时间序列含 NaN 或常数段
现象:功能连接矩阵出现整行整列 NaN,或者相关系数全是 0。原因:预处理时某些脑区的时间序列被全零填充,或者某个时间点有 NaN 没插值。解决:在构建 FC 之前加一步检查,sum(isnan(ts(:)))和std(ts)逐列看,标准差为 0 的脑区说明信号是常数,要么剔除该脑区,要么回预处理检查配准是否失败。
5.4 内存不足导致批处理中断
现象:跑到一半 MATLAB 卡死或报Out of memory。原因:一次性把所有被试的 FC 矩阵读进内存做统计,被试多、脑区多时内存爆炸。解决:分步走——先逐个被试算 FC 并保存到磁盘,再逐个读取做阈值化和指标计算,最后只把指标汇总成小矩阵做统计。别在内存里囤大矩阵。
5.5 指标含义混淆导致结论错误
现象:论文里写“全局效率降低”,但实际算的是局部效率,或者二值网络的指标当成加权网络解释。原因:GRETNA 输出文件命名有时用缩写,不熟悉的人容易看错列。解决:跑完先看输出文件的表头或 README,确认每个指标的全称和计算方式。二值和加权网络的同一指标不可比,方法部分必须写明阈值策略和网络类型。
6. 进阶技巧:用脚本复现整条流水线并做结果验证
GUI 点一遍能出结果,但课题组的现实是:审稿人让你换一个密度区间重算,或者换一个模板重跑,你不可能每次从头点。我的习惯是把整条流水线写成一个可配置的脚本,参数集中放在开头,换参数只改几行。下面是一个简化框架:
%% 配置区 data_dir = 'D:/Data/'; % 被试父目录 out_dir = 'D:/Results/'; % 输出目录 template = 'AAL90'; % 模板名称,对应 Templates 里的文件 densities = 0.10:0.05:0.30; % 密度序列 metrics = {'Eg','Eloc','Cp','Lp','Sigma'}; % 要算的指标 %% 主循环 subjects = dir(fullfile(data_dir, 'Sub*')); all_metrics = cell(length(subjects), 1); for i = 1:length(subjects) subj = subjects(i).name; ts = load(fullfile(data_dir, subj, 'ts.mat')); fc = corrcoef(ts.ts); fc(1:size(fc,1)+1:end) = 0; % 对角线清零 subj_metrics = struct(); for d = 1:length(densities) fc_thr = threshold_fc(fc, densities(d)); % 自定义阈值函数 for m = 1:length(metrics) val = compute_metric(fc_thr, metrics{m}); % 自定义指标函数 subj_metrics.(metrics{m})(d) = val; end end all_metrics{i} = subj_metrics; fprintf('完成 %s\n', subj); end %% 汇总与验证 % 把 cell 转成矩阵,检查是否有 NaN 或 Inf for m = 1:length(metrics) mat = cellfun(@(x) x.(metrics{m}), all_metrics, 'UniformOutput', false); mat = cell2mat(mat); if any(isnan(mat(:))) || any(isinf(mat(:))) warning('指标 %s 存在 NaN 或 Inf,检查密度下限', metrics{m}); end % 保存供统计使用 save(fullfile(out_dir, [metrics{m} '.mat']), 'mat'); end这个框架的价值在于:threshold_fc和compute_metric可以替换成 GRETNA 的底层函数,也可以自己实现,方便做方法学对比。汇总阶段主动检查 NaN 和 Inf,能在跑统计之前就发现问题,省得后面返工。
验证结果是否合理,我一般做三件事:一是拿Examples里的示例数据跑一遍,看指标数值是否在文献常见范围内(比如小世界属性 Sigma 通常在 1.5 到 3 之间);二是随机抽一个被试,手动用corrcoef和简单循环算一遍聚类系数,跟 GRETNA 输出对一下;三是把密度-指标曲线画出来,正常的小世界网络,Eg 和 Eloc 随密度上升而上升,Lp 随密度上升而下降,如果曲线走势反了,多半是阈值化或指标计算哪里搞错了。
从那以后我每次跑新数据集,都强制先用示例数据走一遍全流程,确认环境、路径、参数都没问题再上真实数据。这个习惯帮我省下了至少两次整批重跑的时间。希望帮到你。
本文还有配套的精品资源,点击获取