R语言网络分析实战:全球贸易网络的构建、指标与模型
2026/9/1 9:42:17 网站建设 项目流程

简介:针对2018年全球主要贸易关系,面向国际贸易网络研究者与经济学课程学习者,提供一套基于R语言的网络分析脚本,完整覆盖网络密度、平均路径长度、传递性、互易性、分类性、自旋玻璃社区检测、结构等效性及指数随机图模型等核心指标与算法。压缩包内含1个R脚本,仅7KB,轻量易用,适合有一定R和网络分析基础的读者阅读、调试或二次开发,也可作为经济地理或国际经济学课程的实验代码。目前已有233人学习,脚本按指标分段组织,函数与注释较完整,能够帮助使用者理解各网络指标的计算逻辑与结果含义,并据此复现2018年全球贸易格局的实证分析。通过研读这份脚本,还可掌握ERGM等复杂网络模型的R实现要点,为后续扩展研究提供参考。 做贸易研究的人,早晚会遇到一个问题:整个世界那么多国家,彼此之间买进卖出,这种千丝万缕的关系,能不能像社交网络一样被分析?答案是肯定的。TradeNetworkDistributionsAnalysis 这个项目,做的就是这件事——用 R 语言网络分析,把 2018 年主要国家之间的双边贸易关系整理成一张有向网络,然后从密度、平均路径长度、传递性、互易性、分类性这些基础指标,一直算到自旋玻璃社区检测算法、结构等效性和指数随机图模型(ERGM)。这篇文章会把每个指标的计算逻辑、R 代码和结果解读都展开讲,适合想用网络视角研究贸易数据,或者正在做类似 R 数据项目的读者参照。

在我自己抠这个项目的过程中,印象最深的是:R 里做网络分析,函数调用只是最简单的一层,真正花时间的是数据清洗、阈值设定和结果解释。下面我会按项目实操的顺序来说,尽量把容易踩的坑提前标出来。

1. 数据准备与网络构建

1.1 贸易数据长什么样

别急着写代码,先搞清楚数据格式。项目里用的数据源可以是 UN Comtrade 或者 CEPII-BACI,都是公开的国际贸易数据库。通常下载下来是一个长表,每一行代表一条双边贸易记录,关键字段包括出口国、进口国、年份、贸易额,可能还有产品类别。而且这类数据有几个共同毛病:国家代码不统一,有的用 ISO3,有的用英文全名;同一对国家的贸易额在不同表格里可能重复;部分年份缺失严重。

所以需要先做三步聚合和清洗。第一步,如果数据是 HS 编码或者 SITC 编码的产品级数据,要按 exporter-importer-year 聚合成国家层面的总贸易额;第二步,删除国家自己对自己的记录,也就是排除掉 exporter == importer 的行;第三步,把明显是缺失值或零值的记录统一处理成 0,但要注意有些 0 是真的没有贸易,有些是未报告。我自己的原则是单独保留一份“未清洗原始数据”,所有清洗步骤都在新表里操作,这样可以随时回溯哪一步出了问题。

清洗完之后,我们会得到一个 n×n 的贸易矩阵,行表示出口国,列表示进口国。矩阵里第 i 行第 j 列的数值就是 i 国出口到 j 国的贸易额。这个矩阵就是后续所有网络指标的基础。要注意,矩阵的行列顺序必须保持一致,否则后面算分类性、结构等效性时结果会出现莫名其妙的错位。

1.2 构建有向加权网络

R 里做网络分析,我推荐优先用 igraph。原因是它经过大量工程优化,像密度、互易性、社区检测这些计算都是底层 C 实现,处理几十个国家的数据非常快,图形输出也能跟 ggraph 无缝衔接。当然,也可以只装 tidygraph 走数据管道风格,但底层还是 igraph,所以先熟悉 igraph 是更稳妥的路线。

核心代码大概是这样的:

library(igraph) library(dplyr) library(tidyr) # 假设 trade_df 是清洗后的长表,包含 exporter, importer, value g <- graph_from_data_frame(trade_df, directed = TRUE) # 删除自环和重复边 g <- simplify(g, remove.loops = TRUE, remove.multiple = TRUE) # graph_from_data_frame 会自动把 value 作为边属性 E(g)$weight <- E(g)$value

如果你手里的数据是矩阵,也可以用graph_from_adjacency_matrix(as.matrix(trade_mat), mode = "directed", weighted = TRUE, diag = FALSE)。这里diag = FALSE可以直接忽略对角线,省得之后还要清自环。有一个细节我踩过坑:如果贸易额的单位差异很大,比如有的数据源报美元,有的报千美元,那后面算权重相关指标时会非常离谱。最好在构建网络前统一单位,并用log1p(value)取对数缓解长尾分布,直接log(value)会在 0 值上报-Inf

2. 核心网络指标的计算与解读

2.1 网络密度与平均路径长度

网络密度是最直观的指标,用来衡量这张网络有多“稠密”。在有向网络中,n 个节点之间的最大可能边数是 n*(n-1)(不考虑自环),密度就是实际存在边数除以这个数。在 2018 年全球贸易场景下,即使只取主要经济体,密度一般也不会太高,因为不是任意两个国家都有直接的大额贸易往来。

R 代码很简单:

edge_density(g) # 整体密度 edge_density(g, loops = FALSE) # 忽略自环,效果同上 mean_distance(g, directed = TRUE, unconnected = TRUE)

mean_distance算的是所有节点对之间的最短路径长度平均值。在贸易网络里,如果平均路径长度在 2 左右,说明任意两个国家平均只要通过一个中间国家就能建立贸易联系。这个“小世界”特征非常重要,它意味着某个国家的供给冲击或需求波动,很容易通过网络传导到其他国家,而不是只影响直接贸易伙伴。

顺带一提,unconnected = TRUE这个参数不能省。当网络里有孤立节点,或者有向图里存在不可达的节点对时,默认会计算距离为无穷大,不设置的话要么报错要么会得到 Inf,后面就全乱了。我在第一次跑的时候,就是因为有一批小岛国没进入任何主要贸易关系,导致结果直接变废。

2.2 传递性与互易性

传递性是三角关系的闭合程度。换成大白话:A 国出口到 B 国,B 国出口到 C 国,那么 A 和 C 之间是否也会发生贸易?如果这种“朋友的朋友也是朋友”的模式远高于随机水平,说明网络中存在明显的三角循环或传递结构。

transitivity(g, type = "global")

这里有个细节:transitivity默认处理的是无向图。如果你传的是有向图,它会把有向边按无向边处理,这其实是可以接受的,因为它关心的只是“连接是否存在”。但如果想严格区分方向,需要自己对邻接矩阵做逻辑运算,比如计算 A→B、B→C、A→C 同时发生的概率。大多数贸易网络分析里,无向的全局传递性已经够用。

互易性则专门针对有向网络,衡量双向贸易关系的比例:我出口给你,你是不是也出口给我?

reciprocity(g, ignore.loops = TRUE)

reciprocity()返回值在 0 到 1 之间,0.7 说明有 70% 的贸易关系是双向对等的。现实世界中,全球贸易网络的互易性通常比较高,因为大多数国家之间既有出口也有进口,只是金额不一定对等。这也是后面 ERGM 里mutual项往往显著的原因。要注意,reciprocity如果不设置ignore.loops = TRUE,网络里的自环会被当成互易边,结果虚高。我一开始就用默认值,跑了半天才发现数字不太对。

3. 分类性分析与结构等效性

3.1 分类性:强节点是否更倾向连接强节点

分类性(assortativity)在网络分析里常用来看节点之间的“门当户对”程度。贸易网络里,我们最关心两个版本:一个是基于度的分类性,另一个是基于属性的名义分类性。

基于度的分类性,看的是贸易规模大的国家是否更倾向于彼此连接。计算方法在 igraph 里是:

assortativity_degree(g, directed = TRUE)

返回值如果是正的,说明“强强联手”明显;如果是负的,说明大型经济体更多是向小型经济体延伸连接,比如典型的枢纽-辐条结构。现实中,全球贸易网络往往呈现正的度分类性,因为核心国家之间的贸易流量实在太大了。我跑 2018 年数据时得到正数,和预期一致。

如果想看区域属性,可以用assortativity_nominal(),例如按联合国区域分组:

V(g)$region <- countries$region assortativity_nominal(V(g)$region, directed = TRUE)

正值表示相同区域的国家之间更容易形成贸易连接,这就从网络层面验证了“区域化倾向”。这一步看起来很轻量,但能为后面社区检测的结果提供交叉验证。如果分类性为正,那么社区检测大概率会得到几个明显聚在一起的区域集团,两者可以相互印证。

3.2 结构等效性:找网络中的“角色替补”

结构等效性(structural equivalence)是一个和社区检测完全不同的分析。它不关心节点是否紧密连接,而是关心两个节点是否拥有相似的“关系画像”:如果我连接到的一堆国家和另外某个国家连接的几乎一样,那这两个国家在网络中的角色就可能是同类的。

实现思路是这样的:用邻接矩阵的每一行代表一个节点的“出向关系画像”,每一列代表“入向关系画像”,然后计算节点两两之间的相似度或距离。常见做法是把行列拼成一个向量,计算 Pearson 相关或欧氏距离。R 里可以这样跑:

adj <- as_adjacency_matrix(g, sparse = FALSE) profiles <- cbind(adj, t(adj)) # 同时考虑出边和入边 sim_matrix <- cor(t(profiles)) # 转换成距离并做层次聚类 dist_mat <- as.dist(1 - sim_matrix) hc <- hclust(dist_mat, method = "ward.D2") plot(hc, hang = -1, cex = 0.8)

聚类出来之后,可以按类别个数cutree(hc, k = 4)给每个国家打上“角色标签”。这些标签和社区标签不一样:一个小组里的国家可能分布在全球不同区域,但因为它们都和同样的中间国家贸易,所以被视为结构等效。这一步在实际项目中很适合做国家分类面板,比如判断哪些国家是“转口贸易枢纽”,哪些是“最终需求市场”。不过要注意,如果有太多孤立节点,会把相似度矩阵拉低,建议先剔除完全没有连边的节点再跑。

4. 自旋玻璃社区检测实战

4.1 从统计物理到贸易集团

自旋玻璃(spin glass)社区检测算法,是我在这个项目里比较兴奋的一部分。它源自统计物理里的 Ising 模型和 Potts 模型,基本思想是:把每个节点看成一个“自旋”,自旋的状态就是它所属的社区编号;节点之间的边会对自旋状态产生约束,如果一条边的两个端点被分到同一个社区,系统能量就比较低;如果分到不同社区,能量就比较高。算法在随机扰动和局部更新中不断寻找使总能量最低的划分方式。

用这个算法来识别贸易网络,好处是它不要求社区内部特别稠密,而是可以捕捉到一些由权重和链接模式共同决定的隐含集团。通俗点说,它能帮我们看到 2018 年的贸易网络自然地形成了哪几个“派系”,而且每个派系内部往往带有区域贸易协定的影子。相比 walktrap 或 label propagation,自旋玻璃在面对权重分布不均的贸易数据时,社区边界更干净。

但这里必须提醒:igraph 里的cluster_spinglass()在标准版本里主要支持无向图。有向加权网络要先决定是转成无向,还是抽取其中的互惠边来跑。如果你硬传有向图,有些版本会忽略方向或直接报错。我处理的方法是把有向边权取两条方向的平均值,生成一个无向加权图,这样信息损失最小。

4.2 参数调节与结果可视化

实操时我通常先用walktrap.community()或者fastgreedy.community()快速看一下社区大概有多少个,再把数量作为spins的参考值传给cluster_spinglass()

set.seed(2024) sg <- cluster_spinglass(g, weights = E(g)$weight, spins = 8, gamma = 0.8) sg_membership <- membership(sg) sg_modularity <- modularity(sg)

spins是社区数上限,不是精确值。gamma控制社区粒度,gamma 越大越容易分出更多小社区。根据我的经验,gamma 在 0.5 到 1.5 之间变化比较敏感,建议多试几组,记录每个 gamma 下的模块度,最后选模块度最高的那组参数。

出来结果之后,常规做法是画社区图:

plot(sg, g, vertex.label = V(g)$name, vertex.size = 8, edge.arrow.size = 0.2, col = membership(sg))

不过直接用plot只是一张静态图。更好的方式是导出membership字段,合并到原始数据框里,用来做后续的统计或地图可视化。我还会顺便计算每个社区的总出口额占比,看看哪些集团主导了当年的贸易流。这个结果往往比单纯看模块度更有业务价值。一个常见的坑:自旋玻璃算法不是确定性的,不同随机种子会得到不同社区划分。所以不论是用脚本还是跑分析,都建议固定set.seed(),并且在报告中注明种子值,否则别人复现时对不上。

5. 指数随机图模型(ERGM)建模

5.1 ERGM 到底在做什么

前面算的密度、互易性、分类性,本质上都是“描述性指标”。它们告诉我们网络长什么样,但不能告诉我们这些结构是不是显著,更不能直接告诉我们是什么机制生成了这样的网络。指数随机图模型(ERGM)就是来解决这个问题的。

ERGM 的建模思路是把当前观察到的网络视为众多可能网络中的一个实现,用一批网络统计量当作解释变量,比如边数、互惠边数、三角形数量、节点属性是否相似,然后用最大似然估计或者 MCMC 估计出这些机制的回归系数。用贸易网络的话说,就是:在控制其他条件的情况下,某种连接模式是否显著地比随机网络更常见。

因为 ERGM 一般处理二值网络,所以需要先把加权贸易网络二值化。我这里的做法是:保留两条国家之间贸易额大于中位数的连接,定义成 1,否则定义成 0。这个阈值直接影响结果,后面我会再强调。

5.2 模型拟合与系数解读

R 里做 ERGM 的主力军是statnet套件的ergm包。先要把 igraph 对象转成network对象,然后加入节点属性。

library(statnet) library(ergm) net <- asNetwork(g) set.vertex.attribute(net, "region", V(g)$region) set.vertex.attribute(net, "gdp", V(g)$gdp) # 如果有 GDP 数据

然后拟合模型:

fit <- ergm(net ~ edges + mutual + triangles + nodematch("region"), control = control.ergm(seed = 1, MCMC.burnin = 1000, MCMC.interval = 200)) summary(fit)

输出里最关键的是 Estimate 和 Pr(>|z|)。比如edges系数为负,说明在给定其他项的条件下,形成边的概率比随机机会低,所以网络整体比较稀疏;mutual系数显著为正,说明双向连接比单向连接更容易出现;triangles显著为正,说明网络中存在大量三角闭合结构;nodematch("region")显著为正,说明同一区域内的国家之间更容易建立贸易连接。

还要看 AIC/BIC,比较不同模型时越低越好。拟合之后最好再跑一下gof(fit),用拟合优度检验看模型能不能捕捉网络的关键特征,比如边数分布、度数分布、最短路径分布。如果拟合样本和观察网络差太远,说明模型漏掉了重要机制,需要加项。这里我必须提醒:ERGM 的参数估计对网络规模非常敏感。节点超过 100 个、边超过 1000 条之后,MCMC 收敛会变得很慢,尤其在带triangles的模型里。你可以用control.ergm(MCMC.samplesize = 2000)parallel参数来加速。如果还不行,就要考虑用一个子网络或者先合并一些节点。

6. 常见问题与避坑经验

6.1 数据预处理中的隐性坑

整理这个项目的过程中,最常遇到的问题有三个:一是原始数据的国家代码不统一,比如有的数据源用 ISO3,有的用中文名,合并时会莫名丢失很多行;二是贸易额为 0 的情况没有提前判断,导致取对数时出现-Inf;三是某些年份某条边只有单向记录,另一方显示 0,在构建有向网络时,这个 0 和缺失值在后续 ERGM 中含义完全不同。

我的处理原则是:单独保存一份加权原始矩阵,保留真实 0;二值化矩阵时,0 和缺失都处理成 0,但要在文档里写明“未报告视为无贸易”。虽然这在统计上有点粗,但至少可复现。更好一点的做法是,如果某条边在数据库里完全没有记录,可以先用 0 填充,然后在 ERGM 里通过设置edge.missings来单独处理缺失,但这会增加不少复杂性。

6.2 指标计算不一致的说明

igraph 和 statnet 很多函数名容易混淆,比如 igraph 的reciprocity()和 statnet 的mutual都处理互惠,但前者是比率,后者是模型中一个参数;igraph 的transitivity()和 statnet 的transitiveties也是两个概念。如果你同时加载两个包,注意函数名前缀冲突,建议用igraph::reciprocity()这种写法明确调用。

另外,edge_density()在有向网络里自动除以n*(n-1),不会把自环算进去。有人误以为密度应该除以n*n,这是错的。写报告时最好标注清楚公式。平均路径长度也要注明是 “有向平均距离” 还是 “无向平均距离”,两者结果差异很大,尤其是在方向性明显的贸易网络中。

6.3 常见问题速查

现象可能原因解决方式
密度异常低或高数据未聚合、自环未删按国家-年份聚合,simplify 删除自环
mean_distance 返回 Inf有孤立节点/不可达节点对设置 unconnected = TRUE,或剔除孤立点
互易性接近 1未设置 ignore.loops设置 remove.loops / ignore.loops
spin glass 每次结果不同随机初始化set.seed 固定种子,并注明种子值
ERGM 一直不收敛网络太大或模型太复杂增大 MCMC、简化模型、抽样或合并节点
ERGM 结果符号和预期相反二值化阈值不合适尝试不同阈值,做敏感性分析

做完 TradeNetworkDistributionsAnalysis 这个项目,我的体会是:R 网络分析的价值不在于一句函数跑出漂亮数字,而在于你能从不同角度反复确认同一个故事。密度和路径长度告诉你网络骨架,互易性和传递性告诉你关系模式,自旋玻璃社区告诉你集团边界,ERGM 则把这些现象还原成可检验的机制。如果你想在自己的数据上复现,建议按这个顺序来,不要跳过结构等效性,它可以帮社区检测结果做解释。最后再分享一个小技巧:二值化网络做 ERGM 前,先把阈值画成曲线,看不同阈值下网络密度和互惠比例的变化,再选一个相对稳定的平台区间。这样模型结果不会因为某个随意定的阈值而变得不可信。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询