1. 从“哪个脑区亮了”到“这个节点处在什么位置”:图论视角的切换
最早接触脑网络分析时,我有一个根深蒂固的误解:脑科学的所有问题,都可以归结为“某个区域负责什么事情”。看到情绪激活杏仁核,看到语言激活布洛卡区,看到记忆激活海马——这种定位式思维直到今天依然是主流。但图论给了我一个完全不同的答案:大脑里任何一个区域的功能,很大程度上取决于它和谁连接,以及它在整个网络中占据什么位置。一个被大量枢纽节点围绕的脑区,和一个只有稀疏连接的边缘脑区,哪怕激活程度一模一样,它们承担的计算角色也完全不同。这正是脑网络图论的价值:它不回答“哪个区域亮”,而回答“整个网络是怎么组织起来完成任务的”。
这个视角的切换,在工具层面体现得非常具体。你要处理的不是一张统计参数图,而是一张方方正正的连接矩阵。矩阵的行和列是脑区,矩阵里的数值是两个脑区之间的连接强度。图论里管这些脑区叫节点(node),管这些连接叫边(edge)。节点和边放在一起就是一张图,图论指标就是用来描述这张图各种统计特征的“压缩包”。很多人一听“聚类系数”“小世界特性”觉得数学味太重,其实它们本质上就是几个可解释的连接模式统计量。问题是,这些统计量对输入矩阵极其敏感,预处理稍有偏差,跑出来的指标就会变成一组既有数字又没有任何意义的漂亮结果。这也是我为什么坚持写这篇文章的原因:五个指标逐个讲清楚,BCT工具包的使用边界也讲清楚,剩下的坑,你自己踩过一遍就全明白了。
1.1 节点和边:把大脑写成一张邻接矩阵
在脑网络分析里,节点通常不是单个神经元,而是按照某种脑图谱划分出来的感兴趣区。比如最常用的AAL模板,把大脑皮层和皮下结构分成90个或更多区域;Schaefer模板可以按100、200、400甚至1000个节点划分。节点选得越多,网络分辨率越高,但同时每个节点的信号质量也更容易受到噪声干扰。把N个脑区两两配对,计算它们之间的连接强度,就能得到一个N×N的矩阵。矩阵第i行第j列的元素,表示第i个脑区和第j个脑区之间的连接强度。如果网络是无向的,这个矩阵是对称的;如果连接有方向性,就是非对称矩阵。
边的定义方式则决定了矩阵里数值的含义。结构连接一般来自弥散张量成像和纤维追踪,数值可以是纤维数量、平均各向异性分数或者概率追踪得到的连通概率;功能连接一般来自静息态fMRI的时间序列,两个脑区的BOLD信号做个皮尔逊相关,得到一个相关系数作为连接强度。注意,功能连接描述的是两个区域在时间上的统计共变,不代表两者之间一定存在直接的白质纤维;反过来,结构上直接相连的两个区域,功能活动也不一定同步。这两种网络回答的问题不一样,BCT里很多函数虽然都适用,但解释方式必须区分开来。
还有一类细节经常被忽略:矩阵对角线。理论上,脑区和自己的连接没有意义,BCT的绝大多数函数都默认对角线是0。如果你导入的矩阵对角线不为零,聚类系数、路径长度全部会被污染。我见过不止一次有人在Excel里把相关矩阵直接保存成CSV,忘了去掉对角线上的1.0,然后丢给BCT,最后得到的聚类系数看起来偏高,怎么排查都找不到原因。这个问题在后面的避坑部分我会再提。
1.2 结构网络和功能网络:两张图代表两个不同的“朋友圈”
结构网络更像一张真实的高铁线路图,告诉你两个城市之间有没有轨道相连;功能网络则更像同时段目的地相似的人群轨迹,两个城市之间即使没有直达高铁,只要很多人从A城市出发后最终出现在B城市附近,功能连接就会很强。理解这个区别非常重要,因为BCT工具包在设计时并不关心边的具体生物学含义,它只负责在数学上把网络指标算出来。同样的聚类系数,放在结构网络上可能反映局部白质纤维的密集程度,放在功能网络上可能反映一个功能模块内部的信息同步水平。
实际操作中,我建议在正式跑指标之前,先用一两段话把自己的网络定义写清楚。比如:“本研究使用Schaefer100模板定义节点,使用静息态fMRI时间序列的皮尔逊相关系数定义功能边,并采用20%稀疏度阈值构建二值网络。”这句话看起来短,但它决定了后面所有指标的生物学解释边界。没有这个前提,单独报告“聚类系数是0.53”没有任何意义——换一种模板、换一种阈值、换成加权网络,数字可能变成0.35或者0.68。
2. 五个指标逐个人话式拆解:从聚类系数到小世界特性
BCT工具包里有一百多个函数,但日常论文里出现频率最高的,其实集中在五类指标上。我把它们分成“局部—全局—系统”三个层次来讲:聚类系数管局部网络,特征路径长度和度/中心性管全局整合,模块度管中间尺度的子系统划分,小世界特性则把局部和全局两个维度合在一起。理解这个层次感,比死记公式有用得多。
2.1 聚类系数:你的邻居是不是也互相认识
聚类系数衡量的是网络的局部聚集程度,翻译成大白话就是:我和我的邻居们之间,相互连接有多紧密。一个节点的聚类系数,等于这个节点的邻居中实际存在的边数,除以这些邻居之间可能存在的最大边数。如果一个节点有k个邻居,那么这些邻居之间理论上最多有k(k-1)/2条边,实际观察到的边数除以这个理论最大值,就是这个节点的聚类系数。整个网络的聚类系数,通常对所有节点取平均。
为什么脑科学家关心这个指标?因为高聚类通常意味着局部信息处理效率高、网络存在冗余连接。一个区域的邻居们彼此紧密相连,就能在局部形成“小组讨论”,不需要每次都绕道中央枢纽去交换信息。这种特性在脑网络上有个专门术语叫“功能分离”(segregation)。默认网络、感觉皮层这些区域往往表现出相对较高的聚类系数,因为它们在局部承担着密集的信息加工任务。但聚类系数不是越高越好。如果整个网络都密不透风,信息只能在局部小圈子里打转,传不出去,全局整合效率反而会下降。所以聚类系数必须和下面的路径长度放在一起看。
BCT里对应的函数很直接:二值无向网络用clustering_coef_bu,加权无向网络用clustering_coef_wu。之所以区分加权和二值,是因为加权矩阵里的边不仅存在与否,还有强度信息;加权聚类系数会把边的强度纳进来,强连接对聚类的贡献更大。这里提醒一句:别以为加权一定比二值好。如果你的边权本身噪声很大,加权聚类系数可能引入更多虚假信号;但如果边权是经过严格质量控制的结构指标,加权结果往往更稳健。
2.2 特征路径长度:从A脑区到B脑区,最快需要经过几站
特征路径长度衡量的是网络的全局通信效率。它的定义是:网络中所有节点对之间最短路径长度的平均值。注意,是“最短路径”,不是“直接连接”。两个脑区之间哪怕没有直接边,只要中间经过几个节点能连起来,这条路也算数。在脑网络里,路径长度越短,信息从一个区域传到另一个区域所需的“中转”越少,全局整合效率越高。
图论里有个经典结论:最短路径只关心“能不能走通”和“最少经过几条边”,而加权网络里的路径长度要复杂一些。BCT在计算加权网络的距离矩阵时,会先把权重转换成代价——权重越大,代表两个节点越亲密,那么从这条边经过的“成本”就越低。具体来说,BCT把每一步的代价近似为1/W,然后寻找累计代价最小的路径。所以你在向distance_wei传入矩阵时,必须确保权重是“相似性”而不是“距离”。如果矩阵里存的是欧氏距离或者成本,就必须先做倒数或最大值相减的转换,否则算出来的路径长度彻底反了。这个问题很基础,但真有不少人在加权网络上算错过。
特征路径长度和聚类系数正好构成一对矛盾:聚类系数高,说明局部圈子紧密;路径长度短,说明全局传输高效。健康的大脑网络既要做到局部紧密,又不能牺牲全局效率,这就引出了后面要讲的小世界属性。
2.3 度、强度与介数中心性:哪些脑区是网络的关键枢纽
节点的度是最简单的图论指标:一个节点有多少条边和它相连。二值网络里直接数边数就行,加权网络里则习惯用“强度”代替“度”,也就是所有连接权重的总和。度高的节点是网络中的热门站,对信息传输非常关键。不过,度只描述一个节点有多活跃,不能描述它是不是处于信息传输的“咽喉要道”上。
这就要看介数中心性(betweenness centrality):在所有节点对的最短路径里,有多少比例必须经过某个节点。介数越高的节点,越像是连接不同子网络的必经之路。如果这个节点受损,整个网络的信息流通可能被严重切断。脑网络研究中,介数中心性高的区域常被称为枢纽(hub),比如楔前叶、后扣带回、颞顶联合区,这些区域在默认网络和跨模块整合中承担着关键角色。
这里有个实操上容易被忽视的点:介数中心性对单节点的噪声非常敏感,尤其当网络是稀疏的二值网络时,一条边的存在与否就可能改变大量最短路径的走向。因此,我一般不会只看单个被试的单次介数值,而是会结合度、强度、节点效率等多个指标一起判断一个区域是否为枢纽,甚至做多次随机置乱看稳定性。下一节的BCT实战里,这几个指标对应的函数会一起给出。
2.4 模块度:大脑怎么被划分成一个个“部门”
模块度衡量的是网络是否呈现出明显的社区结构。所谓社区,就是一组内部连接紧密、与外部连接相对稀疏的节点集合。你可以把整个大脑想象成一家公司,模块就是不同的部门;部门内部沟通频繁,跨部门沟通少一些,但少不等于没有,跨模块连接恰恰是实现全局整合的通道。
模块度Q的计算,本质上是拿真实网络和随机网络比较:如果真实网络里模块内部的边数明显多于随机网络的预期水平,Q值就高。Q值范围通常在-1到1之间,实际脑网络分析中,健康被试功能网络的Q值一般在0.3到0.6之间。BCT里最常用的社区检测算法是Louvain算法,对应函数community_louvain。它不需要预先指定社区个数,而是通过迭代优化模块度自动把所有节点分到不同社区里。
但社区检测有两个隐含风险。第一,Louvain算法是启发式的,每次运行可能得到不同的社区划分,尤其是模块度存在多个局部最优解时。第二,如果输入矩阵的符号比较复杂(比如同时存在正负连接),你需要在调用时考虑符号化处理。BCT提供了一个选项,community_louvain(W, [], 'negative_asym'),专门应对带符号权重的网络。对于绝大多数静息态功能连接研究,我更推荐在预处理阶段决定好如何处理负相关:要么取绝对值,要么只保留正相关,要么使用显式的负连接处理方式,不要默认让负相关混进模块度计算里。
2.5 小世界属性:高聚类和短路径的“最优折中”
小世界属性不是一个单独从图里直接读出来的原始指标,而是一个综合判断。它的经典定义来自Watts和Strogatz:如果网络的聚类系数明显高于随机网络,同时特征路径长度又和随机网络差不多,那么这个网络就是小世界网络。换句话说,小世界网络既有局部圈子的“抱团”,又有全局传输的“高效”,是介于规则网络和随机网络之间的中间形态。
衡量小世界通常用三个比值:γ = 实际聚类系数 / 随机网络聚类系数;λ = 实际特征路径长度 / 随机网络特征路径长度;σ = γ / λ。一般认为σ > 1时网络具有小世界属性。这里必须强调一句:健康的人类大脑不管是从结构网络还是功能网络来看,几乎都会表现出明显的小世界特征。所以当你发现自己的数据算出来σ远大于1时,不用太兴奋——它不一定是大脑的特殊优点,有时候只是因为你的随机网络选择不合理,或者你的二值网络密度设置得太低,导致聚类系数虚高。
小世界的真正价值在于比较,比如比较患者组和健康对照组。很多研究发现,精神分裂症、阿尔茨海默病患者的脑网络会朝“随机化”方向偏移,表现为聚类系数下降、路径长度相对持平或上升,σ值变小。但也有些研究得到的是反向结果。所以,小世界特性更适合作为整体网络状态的“温度计”,不适合单独作为某个疾病的生物标志物。
| 指标 | 衡量维度 | 高值意味着什么 | BCT常用函数 |
|---|---|---|---|
| 聚类系数 | 局部分离 | 局部连接紧密、冗余度高 | clustering_coef_bu / clustering_coef_wu |
| 特征路径长度 | 全局整合 | 路径长、传输效率可能低 | distance_bin / distance_wei + charpath |
| 度、强度、介数 | 节点重要性 | 节点连接多、处于信息通道关键位置 | degrees_und / strengths_und / betweenness_bin / betweenness_wei |
| 模块度 | 子系统划分 | 社区结构明显、模块内连接密集 | community_louvain |
| 小世界属性 | 局部-全局平衡 | 聚类高于随机、路径近似随机 | 自行计算,需要随机网络对照 |
3. 正式跑BCT前,先回答三个问题
我看过太多人拿到连接矩阵就兴冲冲地打开BCT,结果算出指标后被人一问“你这是结构网络还是功能网络?用的什么脑图谱?阈值怎么定的?”就愣住了。这些前置选择直接决定指标的含义,跑代码之前必须想清楚。
3.1 节点怎么定:脑图谱的分辨率不是越高越好
节点定义是整个分析中最容易被低估的一步。同样一批被试,用AAL90模板和用Schaefer1000模板,算出来的图论指标可能有明显差异。模板的脑区数目越多,每个脑区的体积越小,时间序列的空间平滑度越低,噪声也就越高;反过来,脑区数目太少,每个节点内部可能混入了几种不同功能的脑区,网络结构变得粗糙。目前比较常见的做法是在100到300个节点之间做选择,兼顾分辨率和稳定性。BCT本身不关心你用哪个图谱,但它要求所有节点对应关系一致——也就是每个被试的矩阵行列顺序必须完全一样。
3.2 边怎么定:相关矩阵、纤维追踪结果还是相位同步
功能网络最常见的边是皮尔逊相关系数。但相关矩阵有个天然问题:它是全连接矩阵,任何一个脑区和其他99个脑区之间都有非零相关。如果不做阈值化,整个网络会变成一个接近完全图的结构,聚类系数和路径长度都会失真。结构网络则相反,白质纤维追踪结果通常比较稀疏,有很多脑区对之间没有直接的纤维连接,矩阵里会存在大量零元素。这两种网络对后来阈值选择的策略要求不一样:功能网络更需要你主动设阈值去掉弱连接,结构网络则可以直接用二值化后的矩阵进入分析。
3.3 阈值怎么定:稀疏度扫描比“拍脑袋选一个”靠谱得多
阈值设定最常见的做法是固定稀疏度(sparsity)。稀疏度指的是网络中实际存在的边数占所有可能边数的比例,比如0.2就表示保留最强的前20%的边。用稀疏度而不是绝对相关阈值,可以保证不同被试之间的网络密度基本一致,避免因个体整体连接强度差异造成系统偏差。但单靠一个稀疏度值也存在问题:被试之间保留的绝对相关阈值不同,可能把不同连接强度的边混在一起。所以审稿人越来越倾向看到“多阈值扫描”的结果:在0.1到0.5之间按0.01或0.05步长扫多个稀疏度,分别计算指标,如果结论在大部分阈值下都稳定,再报告一个代表性结果或报告曲线下面积。这个做法我会在第五节具体展开。
如果你用的是加权网络,那么阈值化步骤就不是必须的。直接对加权矩阵算聚类系数、路径长度,可以避免二值化造成的信息损失。但有得必有失:加权结果对权重的分布形态敏感,如果个别连接出现极端大值,很容易主导整个指标。我的习惯是先在组平均值水平上做一次描述性统计,看看权重分布是否健康,再决定走二值化还是加权路线。
4. 用BCT把五个指标跑出来:安装到代码逐行拆解
BCT全称Brain Connectivity Toolbox,是一个纯MATLAB函数集,Olaf Sporns等人在Brain Connectivity官方页面上长期维护,也被广泛移植到Python、R等生态里。我这里讲的是最原始的MATLAB/Octave版本,因为大部分BCT教程和论文都基于它。别被它那个没有GUI的“朴素”界面劝退,命令行方式反而更适合批量处理。
4.1 安装与路径设置:一个addpath搞定
下载BCT压缩包后解压,你会看到一堆.m文件。把它们放在一个目录里,比如C:\Users\XXX\bct或者/home/xxx/bct。在MATLAB里运行:
addpath(genpath('/path/to/bct'));如果你希望每次启动MATLAB都自动生效,可以把这行写进startup.m,或者用MATLAB的“设置路径”对话框把BCT目录永久加入。检验是否安装成功,运行:
which clustering_coef_bu只要显示函数路径而不是“未找到”,就说明BCT已经可用。我建议尽量保持原始文件结构,不要随意改文件名;BCT内部有些函数相互调用,如果你改了名字或删了某个“看上去没用”的依赖文件,后面会跑出一堆莫名其妙的“未定义函数”错误。
4.2 聚类系数和特征路径长度:两个最基础的BCT调用
假设你已经准备好一个N×N的加权对称矩阵W,对角线为0,权重越大表示连接越强。首先算聚类系数:
C = clustering_coef_wu(W); C_mean = mean(C);C是每个节点的聚类系数,C_mean是整个网络的平均聚类系数。如果你拿到的是二值矩阵A,把函数换成clustering_coef_bu即可。BCT在二值网络里还会直接计算聚类系数的局部效率变体,但日常报告平均聚类系数已经够用。
接下来是特征路径长度。这里要注意,BCT没有直接把“特征路径长度”一步算出来的函数,而是分两步:先由权重矩阵得到距离矩阵,再从距离矩阵计算路径长度。
D = distance_wei(W); [lambda, efficiency] = charpath(D);distance_wei会把权重视作相似性,将每一段边的代价近似为1/W,再搜索任意两个节点之间的最短累计代价。charpath则负责统计这些最短路径的平均值,也就是特征路径长度。如果你的网络是二值的,直接用distance_bin(A),得到的距离矩阵里每一条边都是1,最短路径长度就是“最少经过几条边”。
这里有个常见的坑:加权网络如果阈值化后存在很多断开的脑区对,distance_wei计算出的距离矩阵里会出现无穷大,charpath默认会忽略这些无穷大路径。所以报告的lambda实际上只是“连通部分”的平均路径长度。网络越稀疏,断开的节点对越多,lambda的可靠性就越差。建议在跑路径长度前,用sum(isinf(D(:)))检查一下有多少无效路径。
4.3 度、强度、介数中心性和模块度:识别枢纽和子网络
后面三个指标可以直接在矩阵上算。二值网络用度数,加权网络用强度;介数中心性则分为二值和加权两个版本。
% 二值网络 deg = degrees_und(A); bc = betweenness_bin(A); % 加权网络 str = strengths_und(W); bc_w = betweenness_wei(W);degrees_und返回两个变量:总度和其他度,一般我们关心第一个。strengths_und对加权矩阵沿行求和,相当于把每个节点所有边的权重加在一起。betweenness_wei里的权重同样被视为相似性,权重越大,这条边在最短路径搜索中越容易被选中。
模块度用Louvain算法,一步到位:
[Ci, Q] = community_louvain(W);Ci是一个长度为N的向量,表示每个节点属于哪个社区;Q是整个网络的模块度标量。如果你只有二值矩阵,把W换成A就行。需要提醒的是,Louvain算法每次运行可能会有细微差别,尤其是网络模块结构本身不清晰时。为了稳定,我一般会设置随机种子后重复运行100次,保留模块度最高的一次结果,或者把多次划分结果做一致性聚类。简单起见,你可以在调用前用rng(2024)固定随机种子,至少保证这个分析的结果可以复现。
4.4 小世界属性的完整计算:必须加一组随机网络对照
BCT里没有一个叫small_world的万能函数,所以小世界属性需要你自己组合几个函数算出来。标准流程如下:
% 先算真实网络 C_real = mean(clustering_coef_bu(A)); D_real = distance_bin(A); L_real = charpath(D_real); % 生成100个保持度序列的随机网络 rng(42); R = 100; C_rand = zeros(R,1); L_rand = zeros(R,1); for r = 1:R Ar = randmio_und(A, 10); C_rand(r) = mean(clustering_coef_bu(Ar)); Dr = distance_bin(Ar); L_rand(r) = charpath(Dr); end gamma = C_real / mean(C_rand); lambda = L_real / mean(L_rand); sigma = gamma / lambda;randmio_und的第二个参数是重连次数,一般10到100之间。它的作用是随机交换边,同时保持每个节点的度不变,这样得到的随机网络和真实网络有相同的度序列,但局部聚集结构和模块结构被打散了。对比这样的随机网络,才能说明真实脑网络的聚类优势不是单纯靠度分布带来的。同样,加权网络可以用randmio_und函数传入加权矩阵,但结果解释要更小心,因为加权网络的强度分布也会影响路径长度。小世界的σ大于1是最常见的定性结论,正式报告时最好同时列出γ和λ,并检视二者相对随机的偏离方向。
5. 指标只算完一半:随机网络、多阈值和组间统计
算出五个指标只是第一步。如果这些指标不放进合适的统计框架里,我们很难判断它到底反映真实效应,还是一次特定参数下的偶然结果。这一节讲指标算完之后必须补上的三道工序。
5.1 为什么要和随机网络比:孤立的标量没有裁判
聚类系数0.55算高算低?路径长度3.2算长算短?如果不和参照系比较,这些问题没有答案。随机网络就是那面镜子。理论上,一个随机网络也会有聚类,只是远低于真实脑网络;随机网络的路径长度也会随网络规模和密度变化,所以lambda需要做同样的归一化。实际操作中,最好生成200到1000个随机网络,取它们指标分布的均值甚至整个分布。如果你只生成10个随机网络,随机波动很大,σ值高低可能只是噪声。我还见过有人直接用Erdos-Renyi随机图做对照,这种随机图不保持度分布,聚类和路径都会偏,结论很容易失真。所以BCT给的那几个保持度序列的随机网络函数,randmio_und和latmio_und,不是摆设。
5.2 多阈值扫描:别让结论赌在一个稀疏度上
很多初学者习惯选一个“看起来不错”的稀疏度,比如0.2,跑完全部指标收工。审稿人最不喜欢的就是这种单点结论,因为它完全没证明结果对阈值参数不敏感。常规做法是设定一个阈值范围,比如0.1到0.5,步长0.01或0.02,每个阈值都重新构建二值网络、计算同一套指标。最后看两个东西:一是关键指标随阈值变化的趋势,二是组间差异在这些阈值下是否稳定。如果组间差异只在某一个阈值下显著,其余阈值都不显著,那这个发现的可信度就要大打折扣。如果你想用一个数字代表全阈值结果,可以把指标曲线下面积算出来,比如对特征路径长度,在稀疏度0.1到0.5范围内算AUC,然后对这个AUC做组间比较。这种做法大大提高了结果的稳定性,也更容易通过审稿。
5.3 组间比较和置换检验:脑网络指标不是独立样本
当你比较患者和对照组的聚类系数或小世界σ时,会自然地想到对每个被试算出一个指标,然后做两样本t检验。但要注意,这些指标被算出来之前,已经隐含了对所有脑区的聚合,样本独立性比普通临床变量更脆弱,尤其在不同图谱、不同模板下,同一指标可能高度相关。更稳妥的做法是置换检验:把被试的组标签随机打乱成千上万次,每次重算组间差异统计量,得到零分布,然后把真实差异放在零分布里看显著程度。BCT虽然不直接提供置换检验函数,但置换检验本身只需要循环和随机数,用MATLAB写十几行就能实现。别忘了在多重阈值扫描时做多重比较校正,FDR已经算是最低要求了。
6. 我实际用BCT踩过的几个坑,一个个说
这些坑不是我一个人踩的,很多是帮学生改代码时看到的,但每个都真实到让人血压升高。写在这里,希望你能绕开。
6.1 矩阵方向和对角线:BCT函数不做“体检”
BCT不会自动帮你检查输入矩阵是否合理。你把上三角矩阵传给它,它不会报错;你把含NaN的矩阵传进去,它可能“智能地”把NaN当0处理,也可能干脆报一堆警告。我现在的习惯是写一行检查代码,三个东西一次查完:
assert(issymmetric(W), '矩阵必须对称'); assert(all(diag(W)==0), '对角线必须为0'); assert(~any(isnan(W(:))), '矩阵不能含NaN');这个习惯救了我很多次。尤其是从Python导出矩阵再导入MATLAB时,经常因为类型转换问题出现微小的非对称性,直接算加权聚类可能看不出问题,但介数中心性会对不对称极其敏感。
6.2 小图集的低密度问题:90个节点做稀疏二值网络很危险
用AAL90模板,如果稀疏度设成0.1,那么平均每个节点只有8.9条边,度很低,很多节点可能只有两三度。这时聚类系数的分母k(k-1)会很小,单个节点只要形成一个三角形,聚类系数就直接跳到1,网络平均聚类系数会大幅波动。我在早期一个研究里用0.15的稀疏度跑AAL90,结果发现同一批被试间隔两周重测,聚类系数的一致性差到没法看。后来换成Schaefer200,并把稀疏度下限提高到0.15,稳定性才明显改善。如果你的脑区数少于100,我建议优先选加权网络分析,至少不要用太低的稀疏度。
6.3 随机种子和重复运行:能复现的分析才算可靠
randmio_und、community_louvain等函数都包含随机过程,不设随机种子的话,每次跑结果都会少许不同。虽然这些差异通常不会翻转结论,但会让复核你代码的人很崩溃。我现在写脚本时,所有涉及随机性的步骤都在开头加一句rng(固定数值),并把随机网络数量、Louvain重复次数写进方法部分。审稿人问到“社区划分稳定性”时,你也能拿出重复运行的评估数据。另外,Louvain社区检测可以设置迭代次数,但BCT默认参数已经够用,不需要盲目调高。
最后再分享一个使用习惯:跑完指标后,先不要急着看p值,把每个被试的聚类系数、路径长度、模块度、σ值画成散点图,和头动参数、年龄、性别这些变量放在一起看相关性。如果聚类系数和你关心的临床量表高度相关,但同时也和头动位移强相关,那就要警惕结果是否被运动伪影污染。这个习惯帮我在投稿前挡掉了不少“看上去很美、根本上不可靠”的发现。
脑网络图论不是一套能自动输出真理的流水线,它更像一面放大镜。用得好,你能看见大脑在局部和全局之间精妙的平衡;用得不好,你会被一堆看似严谨的数字带进歧途。希望这篇从指标概念到BCT实操、再到统计避坑的记录,能让你的脑网络分析少走几段弯路。