章节概述
本章聚焦图与网络数据分析。专题研究 1 使用GitHub 开发者社交网络数据,从原始关系数据构建图对象,分析开发者之间的连接结构,识别网络中的关键节点、群落划分与同质性特征。专题研究 2 使用纽约市 TLC 网约车出行数据与Taxi Zone 空间边界,构建具有真实地理坐标的有向加权空间网络,识别交通枢纽,并进一步分析城市流动的不平等与方向性失衡。
方法层面,本章主要涉及图对象实例化、中心性测度 (Degree, Betweenness, Closeness, PageRank)、群落发现 (Louvain)、同质性检验 (Assortativity),以及空间网络中的加权中心性、Lorenz 曲线与 Gini 系数和流动失衡指数等分析方法。
本章学习内容
欢迎开始 第 6 章 的学习。请点击 下方导航卡 进入相应小节:
网络数据导论
研究框架
专题研究 1
实战案例
总结与反思
专题研究 1
专题研究 2
实战案例
总结与反思
专题研究 2
章节练习
本章练习
💡 提示:学习完一个小节后,请再次点击 屏幕右下角的章节主页按钮回到本导航页
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
基本概念
承接第五章的核心概念:空间点数据(Point)以零维的坐标精准刻画了独立实体的微观位置。然而,在真实的物理空间与人类社会中,实体往往并非孤立存在。
如果说“点”揭示了“事物在哪里”,那么将点与点相连的线(Line/Edge)则刻画了事物之间的关系与互动。当海量的点(节点)与它们之间的连边交织在一起时,就构成了一种能够表达复杂系统底层逻辑的进阶数据结构——图与网络(Graphs & Networks)。
从地理空间到拓扑空间 (From Geography to Topology)
网络的本质是对“关系”的抽象。在网络分析中,我们关注的焦点往往从绝对的地理坐标转移到了相对的拓扑结构。两个节点在地理上可能相隔万里(如北京与纽约),但在网络结构中,它们可能仅需一次航班或一次学术合作即可连通(表现为一阶邻接)。
连接的多维语义 (Multidimensional Semantics of Connections)
线代表着一种连接,这种连接的语义极其丰富。在物理空间中,它可以是连接两座城市的高铁轨道,或是两地之间的人口迁徙流(OD Flow);在虚拟社会空间中,它可以是学者之间的论文合作(Co-authorship),或是社交媒体上的转发与关注(Retweets & Follows)。
理论基础
计算社会科学中空间与社会关联的底层逻辑,统一于数学中的图论(Graph Theory)。在进行数据分析与实操前,需明确网络架构的基础概念。
在形式化表达中,一个网络或图通常被定义为 \(G = (N, E)\) (在图论与网络科学文献中,节点集合常记为 \(V\) 或 \(N\),本章为保持上下文公式与变量指代的统一,采用 \(N\) 指代节点),其中:
连边的多维映射
连边在不同学科语境下具有多重形态:
涵盖物理联系(如轨道交通、通信线缆)、人际关联(如职场协作、社交互动)、情感映射(如偏好、态度)、传播路径(如文献引用、信息扩散),以及资源流动(如贸易往来、资金转移)等。
基于连边的不同物理或社会属性,网络主要衍生出以下几类核心形态:
Figure 1. 无向图与有向图
Figure 2. 加权图与无权图
图论源于 1736 年数学家欧拉 (Leonhard Euler) 对“哥尼斯堡七桥问题” (Seven Bridges of Königsberg) 的解答。
哥尼斯堡 (今俄罗斯加里宁格勒) 的普列戈利亚河将陆地划分为四个区域,并由七座桥梁连接。当时的学术兴趣在于验证是否存在一种路径,能够不重复地走过所有桥梁并返回起点。
欧拉将陆地抽象为“节点”,将桥梁抽象为“边”,排除了地理距离与面积等物理属性,仅保留核心的 拓扑连接关系。通过分析节点度数 与节点相连的边数 的奇偶性,欧拉证明了该路径在数学上不成立。这种从物理空间向抽象拓扑的建模方式,奠定了计算社会科学分析复杂网络的基础。
对此问题感兴趣的同学可以查看:图论的起源
网络分析: Network Science (Albert-László Barabási)
网络分析的核心在于通过定量指标描述节点及其关联的结构特征。以下为计算社会科学研究中常用的基础拓扑测度:
网络由节点 (Nodes/Vertices) 与连边 (Edges/Links) 构成,其基础属性主要包括:
网络规模 (Network
Size):
通常由网络中的节点总数 (记为 \(N\))
与连边总数 (记为 \(E\)) 来联合定义。
网络密度 (Network
Density):
指网络中实际存在的连边数量与理论最大可能连边数量的比值,用于衡量网络内部关联的紧密程度。
在无向图(Undirected Graph)中,若网络包含 \(N\) 个节点与 \(E\) 条连边,理论上的最大可能连边数为 \(\frac{N(N-1)}{2}\),其网络密度 \(D\) 的计算公式为:
\[D = \frac{2E}{N(N-1)}\]
而在有向图(Directed Graph)中,由于连边具有明确的源汇方向性,理论上的最大可能连边数翻倍(即 \(N(N-1)\)),其计算公式相应调整为:
\[D = \frac{E}{N(N-1)}\]
复杂网络分析中不存在普适的密度绝对阈值。密度的评估高度依赖于网络规模与具体学科语境。
规模依赖与稀疏性 (Sparsity):
随着节点规模 \(N\)
扩大,理论最大连边数呈平方级剧增。因此,超大规模真实系统(如千万级社交网络)的绝对密度通常极小(如 \(D \ll
0.01\)),此现象被称为真实网络的稀疏性。
相对评估与拓扑意义:
实证分析中,密度的“高低”多基于同类网络结构的横向对比:
重要
中心性测度构成了量化节点拓扑地位的核心指标系统,用于评估特定节点在网络结构、资源交互或信息传播中所处的控制力与影响力。
度中心性 (Degree
Centrality):
指与特定节点直接相连的连边总数。在有向网络中,可进一步细分为入度
(In-degree) 与出度 (Out-degree),分别代表该节点接收与输出连接的规模。
中介中心性 (Betweenness
Centrality):
衡量节点作为拓扑路径“桥梁”的能力,即网络中所有节点对之间的最短路径经过该节点的频次。该指标较高的节点通常控制着网络系统内部的信息或资源流动。
接近中心性 (Closeness
Centrality):
反映节点到达网络中其他所有节点的平均拓扑距离。该指标越高,代表节点在网络中的传播效率越高,或获取全局资源的路径成本越低。
特征向量中心性 (Eigenvector
Centrality):
基于节点邻居的拓扑重要性进行加权评估。若某节点连接至多个高影响力节点,则其自身的中心性得分随之提升,体现了影响力的传递效应。
除前述基础测度外,针对复杂的有向网络 (如网页超链接网络、引文网络),研究中还常使用更高阶的中心性算法。其中最经典的代表之一是 PageRank。
PageRank 可理解为特征向量中心性在有向图中的一种扩展形式。它引入随机游走 (Random Walk) 与阻尼系数 (Damping Factor) 的思想,不仅关注指向某节点的连边数量,也考虑这些来源节点本身的重要性,因此更适合用于刻画有向网络中的影响力传递。
在 R 中的实现方式:
igraph 实现:
可直接使用
page_rank() 计算节点的 PageRank 值。
🔗 igraph
官方文档:PageRank 算法
tidygraph 实现:
若希望与
dplyr 管道语法结合,可在 mutate() 中直接使用
centrality_pagerank(),这也是 tidygraph
生态下更自然的工作流。
🔗
tidygraph 官方文档:Centrality Measures
graph_tbl <- graph_tbl %>%
activate(nodes) %>%
mutate(
pagerank = centrality_pagerank(directed = TRUE)
)推荐参考资源:
理论与概念理解(中文):
可参考面向初学者的
PageRank 随机游走机制解析。
🔗 复杂网络与 PageRank
算法解析 (供概念理解参考)
R 语言实现(官方文档):
若使用
igraph,可参考 page_rank();若使用
tidygraph,可参考 centrality_pagerank()
在管道中的实现方式。
🔗 igraph
官方文档:PageRank 算法
🔗
tidygraph 官方文档:Centrality Measures
平均路径长度 (Average Path
Length):
指网络中所有节点对之间最短路径的平均值,用于衡量整体连通效率;数值越小,通常意味着信息或资源在网络中的传输更高效。
聚类系数 (Clustering
Coefficient):
衡量节点邻域内部相互连接的程度,用于刻画网络局部结构中的团簇化或社团化倾向。
实操提示:
在tidygraph中,节点层面的中心性指标可通过centrality_degree()、centrality_betweenness()、centrality_closeness()等函数在mutate()流程中批量计算;而网络整体层面的拓扑属性,则可进一步借助graph_mean_dist()、graph_transitivity()等函数进行测度。相较于直接使用igraph,tidygraph更适合与dplyr风格的管道语法结合,便于将图论分析融入统一的数据处理流程。
重点
在城市与社会研究中,依据空间约束程度及分析对象的差异,网络数据通常被划分为以下三类:
该类网络受地理空间约束显著。其连边通常对应具体的物理实体,节点则代表物理空间中的交点或设施。
该类网络将地理空间单元抽象为起点 (Origin) 与终点 (Destination),旨在量化不同区域间的要素流动强度。
该类网络的空间属性相对弱化,侧重于刻画非物质形式的拓扑关联与逻辑组织。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
图与网络数据分析 (SNA) 工作流
“从个体连接到社区结构”
本节以 GitHub 开发者社交数据为例,介绍社会网络分析 (SNA)的基本流程,包括图对象构建、中心性测度与群落发现。
点击下方卡片,快速跳转至相应模块:
课题与目标
SNA 核心框架
数据与重构
图对象实例化
拓扑与映射
基础网络可视化
中心性测度
核心节点挖掘
社区探测
同质性与聚类
💡 提示:学习完一个小节后,请再次点击 屏幕右下角的专题导航按钮回到本导航页
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
实战教学
本节以斯坦福大学网络分析项目(SNAP)开源的 GitHub 开发者社交网络为基础,通过真实数据演练社会网络分析的基础处理与统计建模流程。
本节引入 tidygraph 扩展包构建网络数据处理环境,并结合
ggraph
包执行拓扑可视化。该方法有效降低了底层图论算法的代码编写门槛,使分析重点得以聚焦于拓扑结构解释与数据视觉映射。
数据来源说明:
斯坦福大学 SNAP 开源网络平台:https://snap.stanford.edu/data/
GitHub 开发者社交网络数据集专页:https://snap.stanford.edu/data/github-social.html
示例课题
研究课题:
开源社区的技术圈层与拓扑结构分析:基于中心性算法与社区探测
研究背景与目标:
GitHub
作为广泛应用的代码托管平台,构成了典型的技术从业者社交图谱。在该实证数据中,节点代表活跃开发者,连边代表开发者之间的互相关注(Mutual
Follow)行为。这种双向的社交纽带超越了单纯的代码协作,隐含着知识溢出效应与显著的技术同质性聚集(Homophily,如 Web
开发者与机器学习开发者各自形成的内部圈层)。
GitHub 是目前全球最大规模的分布式代码托管与协作平台,其发展轨迹构成了现代社会网络演变的一个核心缩影。
前世今生与规模化扩张:
平台创立于 2008
年,其底层逻辑基于分布式版本控制系统 Git
构建。随着开源文化的普及与 2018 年的商业重组(被微软收购),该平台经历了指数级增长。截至 2025-2026
年的官方统计数据,GitHub 全球注册活跃用户已突破 1.5
亿,托管着超 4.2 亿个项目仓库,构成了目前人类历史上最大规模的数字化协作网络。
从“单一极客群体”向“跨界协作网络”的泛化:
传统上,GitHub 被视为纯粹的程序员与软件工程师群体的聚集地。然而,随着平台协作机制(如 Issues 讨论版、Markdown
页面建站)的完善,其节点类型产生了显著的异质性。越来越多的非程序员群体(如数据科学家、学术研究者、数据新闻记者及开源硬件爱好者)涌入社区,将其转化为跨学科的数据共享、学术论文版本控制与开放知识库的协同中心。
AI 开源浪潮下的新型聚合节点:
近年来,生成式人工智能(Generative
AI)的爆发极大重塑了 GitHub
的网络连边结构。全球开发者通过“克隆”(Fork)与“合并”(Pull
Request)共同维护着庞大的智能体与模型生态。例如,近期跃居全球高星标(Stars)榜首的现象级开源 AI 智能体应用
OpenClaw,以及各类轻量级大模型部署框架(如 llama.cpp、Hugging Face
衍生工具等)。这些超级节点的出现,标志着开源协作模式正从传统的软件工程向复合型的人工智能生态演进。
官方平台与数据探索:
GitHub 官方主页:https://github.com
探索全球实时热门与趋势开源项目 (GitHub Trending):https://github.com/trending
本实操聚焦于该网络的核心拓扑结构,依次实现以下三项分析目标:
关系数据的网络重构:
将结构化的连边表(Edge
List)与附带技术领域标签的节点属性表(Node
Attributes)合并,转化为 tidygraph
规范网络对象,并执行度分布与网络密度等基础规模评估。
中心性与控制力测度:
引入多维中心性指标(如中介中心性、PageRank),量化各节点在网络拓扑中的结构控制能力,识别掌握信息分发与跨领域传播能力的核心技术领袖。
同质性检验与社区可视化:
结合节点的专业领域标签(Web 与 ML
开发者),应用网络模块化算法(如 Louvain
聚类)提取隐性社群边界。通过力导向布局(Force-directed
Layout)直观揭示技术社区的微观聚集特征与圈层分化形态。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
本节实操所调用的数据为 GitHub 开发者社交网络 (GitHub Social Network)。该数据集通过公共 API 收集,构成了一个大型 GitHub 开发者社交网络,主要用于研究复杂网络中的二分类节点预测与同质性聚集现象。
获取途径与环境配置
数据下载路径:
访问数据集官方主页 (https://snap.stanford.edu/data/github-social.html),在页面下方的
Datasets 模块中,直接点击 github.zip
下载完整压缩包。
工作目录规范:
承接前期设定的 R Project
工作流(第一章内容),下载数据后,需在当前第六章的项目根目录下确认存在
data/
文件夹,并将压缩包内的所有文件完整解压至该路径下。
提示:解压完成后,建议首先在文本编辑器中打开目录下的
README.txt文件,查阅原版英文元数据说明与网络拓扑的基础属性。
数据结构与核心文件解析
在数据结构层面,该网络提取了真实的开发者交互记录与属性。解压至
data/ 目录后,本实证分析主要提取并重构以下两张基础表:
连边表 (Edge List, 对应文件
data/musae_git_edges.csv):
包含
id_1 与 id_2
两列,记录了构成互相关注关系的节点对。网络连边代表开发者之间的互相关注关系,由于互相关注需要双方确认,该网络被定义为一个标准的无向图(Undirected Graph, \(E=289,003\)),且不包含连边特征。
节点属性表 (Node Attributes,
对应文件 data/musae_git_target.csv):
包含开发者的唯一标识码(id)及其对应的二分类职业领域标签(ml_target)。网络节点(Nodes,
\(N=37,700\))代表拥有至少 10
个星标(Stars)仓库的活跃开发者账户;其附带的职业标签源自用户的真实职位,用于预测该 GitHub 用户是
Web
开发者还是机器学习开发者。
在学术研究与公开发表中使用第三方开源数据集时,遵循规范的引用(Citation)声明是科学研究的基本严谨性要求。该数据集源自 2019 年关于多尺度图嵌入算法的学术论文,若在正式研究中调用,需标注以下参考文献:
Rozemberczki, C., Allen, C., & Sarkar, A. (2019). Multi-scale Attributed Node Embedding. Journal of Complex Networks, 9(2), cnab014.
DOI: 10.1093/comnet/cnab014
网络构建
在 R 语言环境中,将结构化的关系型表格(CSV)重构为图论数据对象,是执行社会网络分析的前置基础。本节依托
tidygraph 扩展包,通过以下核心逻辑完成双子表(节点表与连边表)的合并与网络实例化:
拓扑主键对齐 (Key
Alignment):
源自计算机科学领域的开源网络数据通常采用从 0
开始的数值索引(0-based index),而 R
语言底层默认采用从 1
开始的索引。为规避底层节点映射错位,需将源数据中的数值 ID
强制转换为字符型(Character)。同时,必须严格遵循
tidygraph 的内部命名约束:节点表的主键需重命名为
name,连边表的源汇节点需分别重命名为 from 与
to。
图对象实例化 (Graph
Instantiation):
调用 tbl_graph()
函数执行网络对象的组装。该函数接收三个核心参数:nodes
用于挂载规范化后的节点属性数据框,edges
用于输入拓扑连边关系框,并设定 directed = FALSE
显式声明该网络为无向图。
(以下为 R 语言实操代码:)
# ==============================================================================
# 1. 环境构建与依赖项声明
# ==============================================================================
library(tidyverse)
library(tidygraph) # 现代网络分析核心引擎 (调用前需确保已安装)
# ==============================================================================
# 2. 原始数据集摄取
# ==============================================================================
# 注:执行前需确认 R 项目工作空间已配置 data/ 路径及对应源文件
raw_nodes <- read_csv("data/musae_git_target.csv")
raw_edges <- read_csv("data/musae_git_edges.csv")
# ==============================================================================
# 3. 数据清洗与拓扑规范化 (Tidy 格式约束)
# ==============================================================================
# 3.1 节点属性表规范化 (Node List)
nodes_df <- raw_nodes %>%
# 重命名原生 name 字段,规避与 tidygraph 底层命名空间的冲突
rename(github_name = name) %>%
# 将数值型 ID 强制转化为字符型并重命名为 name,构建网络拓扑主键
mutate(name = as.character(id)) %>%
# 重构列序,确保拓扑主键前置,提升数据结构可读性
select(name, github_name, ml_target)
# 3.2 拓扑连边表规范化 (Edge List)
edges_df <- raw_edges %>%
# 转换连边的源汇节点 ID 为字符型,以保障与节点主键的精确映射
mutate(
from = as.character(id_1),
to = as.character(id_2)
) %>%
# 筛选并保留标准的连边关系字段
select(from, to)
# ==============================================================================
# 4. 图对象实例化 (Graph Instantiation)
# ==============================================================================
# 基于规范化后的双子表合并实例化网络对象,并声明拓扑约束
github_graph <- tbl_graph(
nodes = nodes_df,
edges = edges_df,
directed = FALSE # 设定为无向图
)
# ==============================================================================
# 5. 网络对象结构检验
# ==============================================================================
print(github_graph)# A tbl_graph: 37700 nodes and 289003 edges
#
# An undirected simple graph with 1 component
#
# Node Data: 37,700 × 3 (active)
name github_name ml_target
<chr> <chr> <dbl>
1 0 Eiryyy 0
2 1 shawflying 0
3 2 JpMCarrilho 1
4 3 SuhwanCha 0
5 4 sunilangadi2 1
6 5 j6montoya 0
7 6 sfate 0
8 7 amituuush 0
9 8 mauroherlein 0
10 9 ParadoxZero 0
# ℹ 37,690 more rows
#
# Edge Data: 289,003 × 2
from to
<int> <int>
1 1 23978
2 2 34527
3 2 2371
# ℹ 289,000 more rows
tbl_graph 网络对象
在控制台中打印的 github_graph 对象,输出结果清晰地展示了
tidygraph “双子表”(Dual-tibble)的数据结构与网络基础拓扑特征:
基础规模与图属性:
网络包含
37,700 个节点与 289,003
条连边。undirected simple graph 表明该数据被定义为不包含方向性、无自环(Self-loops)且无多重连边(Multiple
Edges)的标准无向图。1 component
揭示了全网构成了一个单一的巨型连通分量(Giant Component),即理论上网络中任意两个开发者之间均存在至少一条关系路径。
节点表与激活状态 (Node Data,
active):
(active) 标识是 tidygraph
的核心设计模式,表明后续若直接接入 dplyr 管道函数(如 mutate(),
filter()),操作将默认作用于节点表。数据框中保留了重构后的字符型拓扑主键
name、真实的开发者账号 github_name 以及二分类职业标签 ml_target。
连边表的底层映射 (Edge
Data):
连边表记录了互相关注关系的源节点 from
与汇节点
to。值得注意的是,虽然输入时采用的是字符型主键,但在底层构建时,算法已自动将其映射为从
\(1\)
开始的整型索引(Integer Index, 标记为
<int>)。该机制有效化解了原始数据基于 \(0\) 索引与 R 语言基于 \(1\) 索引之间的底层映射冲突。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
基础可视化 不推荐
在完成图对象实例化后,首先可通过网络可视化观察其整体拓扑结构,为后续的探索性分析提供直观基础。在
tidygraph 与 ggraph
构成的技术框架中,网络绘图的基本逻辑与 ggplot2
较为接近:先指定布局算法,再叠加连边图层
geom_edge_link() 与节点图层
geom_node_point(),即可生成基础网络图。
不过,在处理规模较大且结构较复杂的社会网络时,直接进行全量绘制通常难以清晰呈现关键特征,反而容易使图形过度拥挤,降低可读性。这一现象在网络分析中常被称为 “毛线球效应” (Hairball Problem)。
⚠️ 渲染提示(Rendering Warning):
本研究使用的github_graph全量网络包含 37,700 个节点与 289,003 条连边。若直接对全量网络执行力导向布局等基于物理模拟的底层计算,较大的关联结构通常会带来较高的运算开销,并可能导致内存占用过高或绘图过程响应缓慢。因此,在初步结构探索阶段,可先采用随机抽样策略:此处从全量网络中随机抽取 2000 个节点,并据此提取对应的诱导子图,用于开展基础渲染与结构观察。
# ==============================================================================
# 基础网络可视化
# ==============================================================================
# 载入网络可视化扩展包
library(ggraph)
# ------------------------------------------------------------------------------
# 1. 随机抽样与子图提取
# ------------------------------------------------------------------------------
# 设定随机数种子,以保证抽样结果与布局坐标具有可复现性
set.seed(2025)
# 从全量网络中随机抽取 2000 个节点,并提取对应的诱导子图,
# 用于初步观察网络的整体拓扑结构
github_sample <- github_graph %>%
activate(nodes) %>%
sample_n(2000)
# ------------------------------------------------------------------------------
# 2. 基础拓扑渲染
# ------------------------------------------------------------------------------
# 采用 Fruchterman-Reingold 力导向布局 生成基础网络坐标
# 当前仅呈现节点与连边的几何结构,暂不映射大小、颜色等视觉属性
p_basic <- ggraph(github_sample, layout = "fr") +
geom_edge_link() + # 绘制连边图层
geom_node_point() + # 绘制节点图层
theme_graph() # 移除传统坐标轴与背景元素
# 输出图形,用于直观观察基础网络渲染中的“毛线球效应”
print(p_basic)在社会网络分析中,节点通常不具有预设的空间坐标。因此,在进行网络可视化时,通常需要借助布局算法 (layout),将抽象的拓扑关系映射为二维或三维空间中的几何位置。
ggraph 在底层调用 igraph
的布局引擎,可支持多种常见的网络布局方式。根据适用场景的不同,常见布局大致可分为以下三类:
第一类:力导向布局 (Force-directed Layouts)
这类方法将网络视为一个简化的物理系统:节点之间存在排斥作用,连边则产生吸引作用。经过迭代后,连接较紧密的节点往往会在图中相互靠近,因此较适合用于观察网络中的聚集结构与群落轮廓。
layout = "fr"):即
Fruchterman–Reingold
算法,是网络可视化中较常用的通用布局之一。其结果通常较为直观,适合用于展示无向网络的整体结构。layout = "kk"):即
Kamada–Kawai
算法。该方法尝试使图中节点之间的几何距离更接近其在网络中的最短路径距离,因此在结构表达上较为细致,但在节点规模较大时计算负担通常也更高。第二类:几何与规则布局 (Geometric Layouts)
这类布局不再强调物理模拟,而是按照预设的几何规则排列节点,更适合用于特定结构的展示或不同网络之间的形式比较。
layout = "circle"):将所有节点按顺序排列在圆周上,适合观察网络的整体连边密度,以及不同节点之间是否存在较多跨区域连接。layout = "grid"):将节点放置在规则网格中,常用于示意性展示,或用于结构较规则的网络场景。第三类:层级布局 (Hierarchical Layouts)
这类布局主要用于存在明确方向关系或层级关系的网络,尤其适合有向图 (Directed Graph) 的可视化表达。
layout = "tree"):以自上而下或自左向右的方式展开节点,适合展示树结构、组织关系或知识层级。layout = "sugiyama"):常用于有向无环图 (DAG, Directed
Acyclic Graph)。其主要目标之一是减少图中的连边交叉,从而使层级关系与信息流向更清晰。基础可视化 推荐
在学术报告或论文展示中,基于随机抽样生成的网络图通常更适合用于初步探索,而不一定能够稳定呈现网络的关键结构。为在有限计算资源下更清晰地展示大型网络的核心骨架,可进一步提取核心诱导子图 (Core Induced Subgraph) 进行可视化。
这一做法的思路是:先计算全网节点的度中心性 (Degree Centrality),即每个节点的连接数量;再按中心性高低筛选出最核心的一组节点 (如度中心性最高的 Top 500 节点),并据此构建诱导子图。在可视化阶段,可将节点的开发者类别映射为颜色,将度中心性大小映射为节点尺寸,从而同时呈现网络中的结构位置与类别差异。
# ==============================================================================
# 进阶可视化:核心子图提取与多维视觉映射
# ==============================================================================
# 1. 计算度中心性,并提取 Top 500 核心节点
github_core <- github_graph %>%
activate(nodes) %>%
# 计算节点的度中心性
mutate(degree = centrality_degree(mode = "all")) %>%
# 按度中心性降序保留前 500 个节点,提取核心诱导子图
slice_max(order_by = degree, n = 500)
# 2. 力导向布局与视觉映射
set.seed(2025)
# 使用 Fruchterman-Reingold 布局生成网络坐标
p_core <- ggraph(github_core, layout = "fr") +
# 2.1 连边图层
# 以较低透明度和较细线宽绘制连边,弱化背景干扰
geom_edge_link(color = "grey80", alpha = 0.1, width = 0.3) +
# 2.2 节点图层
# 颜色映射至开发者类型,大小映射至度中心性
geom_node_point(aes(color = as.factor(ml_target), size = degree),
alpha = 0.55, stroke = 0.1, shape = 21, fill = "white") +
# 叠加半透明内点,增强节点层次感
geom_node_point(aes(color = as.factor(ml_target), size = degree),
alpha = 0.6) +
# 2.3 自定义颜色图例
scale_color_manual(
values = c("0" = "#2C7BB6", "1" = "#D7191C"),
labels = c("0: Web Developer", "1: ML Developer"),
name = "Developer Field"
) +
# 2.4 自定义节点大小范围与图例断点
scale_size_continuous(
range = c(.5, 8),
name = "Degree Centrality",
breaks = c(100, 500, 1000, 2000) # 根据数据分布设置断点
) +
# 2.5 调整图例显示顺序与样式
guides(
color = guide_legend(override.aes = list(size = 4), order = 1),
size = guide_legend(order = 2)
) +
# 2.6 设置网络图主题
theme_graph(base_family = "sans") +
theme(
legend.position = "bottom",
legend.box = "horizontal", # 两个图例水平排列
legend.title = element_text(face = "bold", size = 10),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "grey40", size = 11, hjust = 0.5)
) +
# 2.7 添加标题与附注信息 (在此处融入学术引用)
labs(
title = "Core Topology of GitHub Developer Network",
subtitle = "Induced Subgraph of Top 500 Nodes by Degree Centrality",
# 使用 \n 进行换行,将算法说明与数据引用优雅地分行展示
caption = "Layout: Fruchterman-Reingold | Edge: Mutual Followers\nData: Rozemberczki et al. (2019) via SNAP"
) + coord_equal()
# 输出图形
print(p_core)© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
核心概念
在复杂网络中,不同节点在结构中的位置并不相同,其结构重要性也存在差异。中心性测度 (Centrality Metrics) 用于量化节点在网络拓扑中的相对位置与潜在影响,是网络分析中较常用的一类指标。
上一节的可视化主要使用了度中心性 (Degree
Centrality),即统计每个开发者与其他节点之间的直接连接数量。不过,连接数量较多的节点,并不一定处于网络中的关键信息通道位置。本节将依托
tidygraph
的分析框架,进一步引入另外两类常见的中心性指标,并对其进行全网计算与比较。
度中心性 (Degree
Centrality):
可理解为节点的“局部连接水平”。它衡量一个节点直接相连的邻居数量。在
GitHub 网络中,度中心性较高的节点通常连接更广,常表现为较活跃的局部枢纽 (local
hubs)。
中介中心性 (Betweenness
Centrality):
可理解为节点的“桥接作用”。它衡量一个节点出现在其他节点对最短路径 (Shortest
Path)上的频率。中介中心性较高的节点未必拥有最多的直接连接,但往往位于不同子群体之间,在跨群体联系中具有较强的中介作用
(如 Web 圈与 ML 圈之间)。
网页排名中心性 (PageRank):
可理解为节点的“整体影响力”。这一指标源自早期网页排序思想,其基本含义是:来自高影响力节点的连接,通常比来自边缘节点的连接包含更多结构信息。在无向网络中,PageRank
常用于识别处于网络核心区域、并与重要节点保持联系的个体。
全网中心性计算与提取
在 tidygraph 中,中心性指标已封装为一系列
centrality_* 函数,可直接在 mutate()
中对网络节点进行批量计算。
⚠️ 计算提示:
中介中心性的计算需要考虑网络中大量节点对之间的路径关系。对于本例中接近 4 万 个节点的网络,该步骤的运行时间可能相对较长,执行时可等待代码块完成计算。
# ==============================================================================
# 1. 计算多维中心性指标
# ==============================================================================
github_graph <- github_graph %>%
activate(nodes) %>%
mutate(
degree = centrality_degree(mode = "all"), # 度中心性:衡量节点的局部连接水平
betweenness = centrality_betweenness(directed = FALSE), # 中介中心性:衡量节点的路径桥接作用
pagerank = centrality_pagerank(directed = FALSE) # PageRank:衡量节点的全局重要性
)
# ==============================================================================
# 2. 识别“桥梁节点”:提取中介中心性较高的关键节点
# ==============================================================================
# 将节点属性表转为普通数据框,便于排序与筛选
top_brokers <- github_graph %>%
as_tibble() %>%
select(github_name, ml_target, degree, betweenness, pagerank) %>%
arrange(desc(betweenness)) %>%
slice_head(n = 20)
# 输出中介中心性排名前 20 的节点
print(top_brokers)# A tibble: 20 × 5
github_name ml_target degree betweenness pagerank
<chr> <dbl> <dbl> <dbl> <dbl>
1 dalinhuang99 0 9458 191574126. 0.0169
2 nfultz 0 7085 170925957. 0.0115
3 Bunlong 0 2958 39311706. 0.00473
4 addyosmani 0 3324 30845318. 0.00506
5 gabrielpconceicao 0 2468 25110415. 0.00393
6 rfthusn 0 2343 21914766. 0.00387
7 ronenhamias 0 1568 19620532. 0.00270
8 nelsonic 0 1924 18332586. 0.00345
9 shayan-taheri 0 1347 15107520. 0.00222
10 JonnyBanana 0 1404 14435791. 0.00213
11 SuriyaaKudoIsc 0 1507 13423234. 0.00228
12 pengliheng 0 1316 12986832. 0.00230
13 getify 0 1797 12442182. 0.00280
14 mdo 0 1571 10855956. 0.00240
15 mbostock 0 1368 9416078. 0.00211
16 sdras 0 1240 9189255. 0.00193
17 mcanthony 0 933 7288375. 0.00139
18 jeresig 0 1485 7247955. 0.00211
19 ahmetabdi 0 748 6837587. 0.00143
20 kentcdodds 0 1267 6722107. 0.00190
结果解释
github_name:开发者的 GitHub 用户名
ml_target:开发者类别标签,0通常表示 Web Developer,1表示 ML Developer
degree:度中心性,即该节点直接连接的开发者数量
betweenness:中介中心性,即该节点作为不同节点之间“桥梁”的程度
pagerank:PageRank 值,即该节点在整个网络中的整体影响力或重要性
中心性测度可视化 重要
识别网络中的桥梁节点
在完成中心性指标计算后,若仅依赖表格结果,通常较难直观判断高值节点在网络中的拓扑位置及其结构作用。为更清晰地展示这些关键节点,此处继续沿用上一节Top 500 核心节点的提取思路,并在可视化中将度中心性映射为节点大小,将中介中心性映射为节点颜色与透明度,从而突出那些位于不同社群之间、具有桥接作用的节点。此类节点通常可理解为跨越结构洞 (Structural Holes)的关键连接者。
# ==============================================================================
# 3. 拓扑映射:基于中介中心性的分层网络可视化
# ==============================================================================
# 需预先加载 classInt 包
library(classInt)
# 关闭科学计数法,便于输出与图例显示
options(scipen = 9999)
# ------------------------------------------------------------------------------
# 3.1 提取核心子图,并对中介中心性做数值缩放
# ------------------------------------------------------------------------------
github_brokers <- github_graph %>%
activate(nodes) %>%
slice_max(order_by = degree, n = 500) %>%
# 将中介中心性缩放为“百万”为单位,便于分层与显示
mutate(betweenness_m = betweenness / 1000000)
# ------------------------------------------------------------------------------
# 3.2 计算 Fisher-Jenks 分级断点,并生成分类变量
# ------------------------------------------------------------------------------
node_betweenness_m <- github_brokers %>%
as_tibble() %>%
pull(betweenness_m)
fisher_breaks <- classIntervals(
node_betweenness_m,
n = 7,
style = "fisher"
)$brks
github_brokers <- github_brokers %>%
activate(nodes) %>%
mutate(
betweenness_class = cut(
betweenness_m,
breaks = fisher_breaks,
include.lowest = TRUE,
dig.lab = 5
)
)
# ------------------------------------------------------------------------------
# 3.3 网络渲染与多通道映射
# ------------------------------------------------------------------------------
set.seed(2025)
p_betweenness <- ggraph(github_brokers, layout = "fr") +
# 连边图层:弱化处理,用作结构背景
geom_edge_link(color = "grey80", alpha = 0.1, width = 0.3) +
# 节点图层:颜色映射为中介中心性分层,大小映射为度中心性,
# 透明度映射为连续的中介中心性数值
geom_node_point(
aes(color = betweenness_class, size = degree, alpha = betweenness_m),
stroke = 0.1, shape = 21, fill = "white"
) +
geom_node_point(
aes(color = betweenness_class, size = degree, alpha = betweenness_m)
) +
# 颜色映射:使用 Mako 色盘表示中介中心性等级
scale_color_viridis_d(
option = "mako",
direction = 1,
name = "Betweenness\n(×10⁶ Paths)"
) +
# 尺寸映射:以度中心性表示节点连接规模
scale_size_continuous(
range = c(.5, 8),
name = "Degree Centrality",
breaks = c(100, 500, 1000, 2000)
) +
# 透明度映射:控制显示范围,避免低值节点完全消失
# 同时隐藏透明度图例,减少版面干扰
scale_alpha_continuous(
range = c(0.6, 0.85),
guide = "none"
) +
# 图例设置:调整顺序,并将高值显示在上方
guides(
color = guide_legend(override.aes = list(size = 4), order = 1, reverse = TRUE),
size = guide_legend(order = 2, reverse = TRUE)
) +
# 主题与标题设置
theme_graph(base_family = "sans") +
theme(
legend.position = "right",
legend.box = "vertical",
legend.title = element_text(face = "bold", size = 10),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "grey40", size = 11, hjust = 0.5)
) +
labs(
title = "Information Brokers in GitHub Network",
subtitle = "Node Size: Degree | Node Color & Alpha: Betweenness",
caption = "Layout: Fruchterman-Reingold | Data: Rozemberczki et al. (2019)"
) +
coord_equal()
# 输出图形
print(p_betweenness)识别网络中的核心权威节点
此处改用PageRank进行可视化展示,并继续沿用上一节Top 500 核心节点的提取思路。在图中,度中心性映射为节点大小,PageRank映射为节点颜色与透明度,以突出那些位于网络核心区域、并与其他重要节点保持较强联系的关键个体。一般而言,PageRank 较高的节点可理解为网络中的核心权威或高影响力节点。
# ==============================================================================
# 4. 拓扑映射:基于 PageRank 的分层网络可视化
# ==============================================================================
# ------------------------------------------------------------------------------
# 4.1 提取核心子图,并对 PageRank 进行缩放
# ------------------------------------------------------------------------------
github_auth <- github_graph %>%
activate(nodes) %>%
slice_max(order_by = degree, n = 500) %>%
# PageRank 数值通常较小,此处乘以 1000,便于分层与显示
mutate(pagerank_k = pagerank * 1000)
# ------------------------------------------------------------------------------
# 4.2 计算 Fisher-Jenks 断点,并生成分类变量
# ------------------------------------------------------------------------------
node_pagerank_k <- github_auth %>%
as_tibble() %>%
pull(pagerank_k)
fisher_breaks <- classIntervals(
node_pagerank_k,
n = 7,
style = "fisher"
)$brks
github_auth <- github_auth %>%
activate(nodes) %>%
mutate(
pagerank_class = cut(
pagerank_k,
breaks = fisher_breaks,
include.lowest = TRUE,
dig.lab = 4 # 控制图例标签的小数位数
)
)
# ------------------------------------------------------------------------------
# 4.3 网络渲染与多通道映射
# ------------------------------------------------------------------------------
set.seed(2025)
p_pagerank <- ggraph(github_auth, layout = "fr") +
# 连边图层:弱化显示,作为结构背景
geom_edge_link(color = "grey80", alpha = 0.1, width = 0.3) +
# 节点图层:颜色映射为 PageRank 分层,大小映射为度中心性,
# 透明度映射为连续的 PageRank 数值
geom_node_point(
aes(color = pagerank_class, size = degree, alpha = pagerank_k),
stroke = 0.1, shape = 21, fill = "white"
) +
geom_node_point(
aes(color = pagerank_class, size = degree, alpha = pagerank_k)
) +
# 颜色映射:使用 Plasma 色盘表示 PageRank 等级
scale_color_viridis_d(
option = "plasma",
direction = 1,
name = "PageRank\n(×10⁻³)"
) +
# 尺寸映射:以度中心性表示节点连接规模
scale_size_continuous(
range = c(.5, 8),
name = "Degree Centrality",
breaks = c(100, 500, 1000, 2000)
) +
# 透明度映射:控制显示范围,并隐藏透明度图例
scale_alpha_continuous(
range = c(0.6, 0.85),
guide = "none"
) +
# 图例设置:调整顺序,并将高值显示在上方
guides(
color = guide_legend(override.aes = list(size = 4), order = 1, reverse = TRUE),
size = guide_legend(order = 2, reverse = TRUE)
) +
# 主题与标题设置
theme_graph(base_family = "sans") +
theme(
legend.position = "right",
legend.box = "vertical",
legend.title = element_text(face = "bold", size = 10),
plot.title = element_text(face = "bold", size = 16, hjust = 0.5),
plot.subtitle = element_text(color = "grey40", size = 11, hjust = 0.5)
) +
labs(
title = "Global Authority in GitHub Network",
subtitle = "Node Size: Degree | Node Color & Alpha: PageRank (Plasma)",
caption = "Layout: Fruchterman-Reingold | Data: Rozemberczki et al. (2019)"
) +
coord_equal()
# 输出图形
print(p_pagerank)拓展延伸 实证实勘
通过 PageRank 的可视化结果,可以进一步识别网络中全局权威度较高的节点。与度中心性不同,PageRank
不仅关注一个节点拥有多少直接连接,也关注这些连接本身是否来自网络中同样重要的节点。换言之,它更强调连接质量,而不仅是连接数量。
# ==============================================================================
# 提取 PageRank 排名前 5 的 GitHub 关键节点
# ==============================================================================
top_5_authorities <- github_graph %>%
as_tibble() %>%
arrange(desc(pagerank)) %>%
slice_head(n = 5) %>%
select(github_name, pagerank, ml_target)
print(top_5_authorities)# A tibble: 5 × 3
github_name pagerank ml_target
<chr> <dbl> <dbl>
1 dalinhuang99 0.0169 0
2 nfultz 0.0115 0
3 addyosmani 0.00506 0
4 Bunlong 0.00473 0
5 gabrielpconceicao 0.00393 0
根据 PageRank 的计算结果,在近 4 万名活跃开发者中,以下
5 位节点表现出较高的全局影响力。结合 GitHub
主页的公开信息,可以将其理解为开源网络中几类具有代表性的高权威节点:
Top 1: dalinhuang99 (Dalin Huang) (PageRank: 0.0169)
👉 前往主页:以整理算法、数据结构与系统架构内容见长,其开源学习笔记具有较强影响力。
Top 2: nfultz (Neal Fultz) (PageRank: 0.0115)
👉 前往主页:具有统计与数据科学背景,在跨领域开源工具中表现出较高的结构影响力。
Top 3: addyosmani (Addy Osmani) (PageRank: 0.0051)
👉 前往主页:知名工程师与开源作者,其现实技术声望在网络结构中也得到明显体现。
Top 4: Bunlong (Bunlong Van) (PageRank: 0.0047)
👉 前往主页:活跃的前端生态建设者,长期维护多个常用开源工具与组件。
Top 5: gabrielpconceicao (Gabriel Conceição)
(PageRank: 0.0039)
👉 前往主页:具有较强的网络活跃性,在整体连接结构中占据较重要的位置。
💡 结果解读:高 PageRank 可能对应的三种网络机制
高 PageRank 并不只意味着“连接很多”,还通常反映以下几种情况:
1. 高质量连接:与其他高影响力节点保持联系。
2. 核心生态嵌入:长期参与重要项目或基础设施建设。
3. 广泛网络活跃:在互相关注网络中形成较强的结构嵌入。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
核心概念:从个体到群落
在 2.4 节中,分析重点放在网络中的关键节点及其中心性差异。进一步来看,社会网络的重要特征并不只体现在个体层面,也体现在节点之间逐步形成的群体结构。在开源社区中,开发者可能因技术背景、协作关系或兴趣方向的相近,而形成联系更为紧密的局部群体。
本节进一步引入两个常用的宏观分析维度:一是用于检验网络中是否存在“物以类聚”现象的同质性检验 (Homophily),二是基于网络拓扑结构进行无监督划分的群落发现 (Community Detection)。
同质性系数 (Assortativity
Coefficient):
用于衡量属性相似的节点是否更倾向于彼此连接。其取值范围为 \([-1, 1]\)。数值越接近
1,说明网络中的同质性越强;若接近
0,则表示连接关系与该属性的对应性较弱;若为负值,则说明网络中可能存在一定程度的异质性连接。
Louvain 算法 (模块度优化):
这是一种常用于大规模网络的无监督群落划分方法。其基本思路是通过迭代优化网络的模块度
(Modularity, \(Q\)),以识别内部连接较密、群体之间连接较疏的结构。该算法无需预先设定群落数量,划分结果主要由网络本身的拓扑特征决定。
实操演练:同质性计算与无监督群落划分
首先,使用 graph_assortativity() 计算二分类标签 (Web Developer 与 ML Developer)
的同质性系数;随后,调用 group_louvain()
在全网执行无监督群落划分,以识别网络中的主要社群结构。
# ==============================================================================
# 1. 提取网络结构特征:同质性检验与 Louvain 群落划分
# ==============================================================================
github_graph <- github_graph %>%
activate(nodes) %>%
# 计算基于开发者类别标签(ml_target)的同质性系数
# 该结果为全网层面的单一指标,因此每个节点得到的数值相同
mutate(assortativity = graph_assortativity(ml_target)) %>%
# 执行 Louvain 无监督群落划分,并将群落编号转为因子变量
mutate(louvain_comm = as.factor(group_louvain()))
# 提取并输出全网同质性系数
# 由于该指标是全局标量,这里取任意一个节点的结果即可
global_assortativity <- github_graph %>%
as_tibble() %>%
slice(1) %>%
pull(assortativity)
cat("GitHub 开发者职业圈层的同质性系数 (Assortativity):",
round(global_assortativity, 3), "\n")GitHub 开发者职业圈层的同质性系数 (Assortativity): 0.378
GitHub 开发者职业圈层的同质性系数为 0.378,说明网络中存在一定程度的同质性连接。也就是说,具有相同职业标签的开发者更倾向于彼此连接,例如 Web Developer 更可能与 Web Developer 相连,ML Developer 也更可能与同类节点形成联系。不过,这一数值并未接近 1,因此这种圈层分化并非完全封闭,网络中仍然存在一定比例的跨圈层连接。
聚类分析
此处继续对前面提取的Top 500 核心子图进行渲染,并以左右并列的方式比较真实标签与无监督群落。
视觉映射:真实标签与 Louvain 群落的并列对比
为直观检验网络中的同质性特征及
Louvain
群落划分的表现,此处采用双图并列对比 (Side-by-side Comparison) 的方式进行展示:
Louvain 算法依据网络拓扑识别出的群落归属 (Community
Membership)。为减少其他视觉通道带来的干扰,两幅图均采用固定节点大小,不再映射中心性数值;同时,共用同一套
Fruchterman-Reingold
布局坐标,以保证左右两图中的节点位置完全一致,从而便于进行逐点对应的空间比较。
# ==============================================================================
# 2. 拓扑映射:真实标签与 Louvain 群落的对比可视化
# ==============================================================================
# 需预先安装并加载 patchwork,用于图形拼接
library(patchwork)
# ------------------------------------------------------------------------------
# 2.1 提取核心子图,并整理群落类别
# ------------------------------------------------------------------------------
github_community <- github_graph %>%
activate(nodes) %>%
slice_max(order_by = degree, n = 500) %>%
mutate(
# 在 500 个核心节点上重新执行 Louvain 群落划分
louvain_comm = as.factor(group_louvain()),
# 保留规模最大的 6 个群落,其余合并为 "Other"
louvain_lumped = fct_lump_n(louvain_comm, n = 6, other_level = "Other")
)
# ------------------------------------------------------------------------------
# 2.2 预先计算布局坐标
# ------------------------------------------------------------------------------
# 固定随机种子,并预先生成 FR 布局
# 这样左右两张图可以使用完全一致的节点位置
set.seed(2025)
fixed_layout <- create_layout(github_community, layout = "fr")
# ------------------------------------------------------------------------------
# 2.3 左图:真实标签
# ------------------------------------------------------------------------------
p_truth <- ggraph(fixed_layout) +
geom_edge_link(color = "grey80", alpha = 0.1, width = 0.2) +
# 节点颜色表示开发者官方标签
geom_node_point(
aes(color = as.factor(ml_target)),
size = 2, alpha = 0.85, stroke = 0.2, shape = 21, fill = "white"
) +
geom_node_point(
aes(color = as.factor(ml_target)),
size = 2, alpha = 0.75
) +
scale_color_manual(
values = c("0" = "#2C7BB6", "1" = "#D7191C"),
labels = c("0: Web Developer", "1: ML Developer"),
name = "Official Label"
) +
theme_graph(base_family = "sans") +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(color = "grey40", size = 9, hjust = 0.5)
) +
labs(
title = "Ground Truth",
subtitle = "Nodes Colored by Developer Roles"
) +
coord_equal()
# ------------------------------------------------------------------------------
# 2.4 右图:Louvain 无监督群落
# ------------------------------------------------------------------------------
p_louvain <- ggraph(fixed_layout) +
geom_edge_link(color = "grey80", alpha = 0.1, width = 0.2) +
# 节点颜色表示 Louvain 识别出的群落类别
geom_node_point(
aes(color = louvain_lumped),
size = 2, alpha = 0.85, stroke = 0.2, shape = 21, fill = "white"
) +
geom_node_point(
aes(color = louvain_lumped),
size = 2, alpha = 0.75
) +
# 使用离散色盘区分不同群落
scale_color_brewer(palette = "Set2", name = "Louvain\nCommunity") +
theme_graph(base_family = "sans") +
theme(
legend.position = "bottom",
plot.title = element_text(face = "bold", size = 12, hjust = 0.5),
plot.subtitle = element_text(color = "grey40", size = 9, hjust = 0.5)
) +
labs(
title = "Unsupervised Clustering",
subtitle = "Nodes Colored by Louvain Algorithm"
) +
coord_equal()
# ------------------------------------------------------------------------------
# 2.5 拼合图像并添加总标题
# ------------------------------------------------------------------------------
# 将两张图并排显示,用于比较真实标签与无监督群落结构
p_combined <- p_truth + p_louvain +
plot_annotation(
title = "Homophily & Community Structure in GitHub Network",
caption = "Layout: Fruchterman-Reingold | Nodes: Top 500 Core Skeleton",
theme = theme(
plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
plot.caption = element_text(color = "grey50", face = "italic")
)
)
# 输出图形
p_combinedLouvain 算法由 Vincent D.
Blondel、Jean-Loup Guillaume、Renaud
Lambiotte 与 Etienne Lefebvre 于 2008
年提出,是网络分析中常用的一种群落发现算法 (Community
Detection)。它的主要任务,是根据网络中的连边结构,将节点自动划分为若干个内部连接较密、群体之间连接较疏的社群。
该算法的核心思想是不断提高网络的模块度 (Modularity)。模块度越高,通常说明网络被划分出的群落结构越清晰,也就是说,同一群落内部的连接更紧密,而不同群落之间的连接相对较少。
从计算过程来看,Louvain 算法大致分为两个阶段:
第一阶段:局部优化
初始状态下,每个节点先被视为一个独立群落。随后,算法逐一考察每个节点,并尝试将其移动到相邻节点所在的群落中;如果这一移动能够提升模块度,就保留该调整。经过多轮更新后,会形成一批初步群落。
第二阶段:群落聚合
将第一阶段得到的每一个群落压缩为一个新的“超级节点”,重新构建网络;然后在新的网络上再次重复第一阶段的优化。如此循环,直到模块度不再明显提升为止。
在实际分析中,可以将这一过程理解为一种网络聚类:算法不依赖任何先验标签,而是仅根据节点之间的连接关系,识别出网络中自然形成的“圈层”或“社群”。因此,Louvain
特别适合用于回答这样的问题:哪些节点在拓扑结构上更像是同一个群体?
在 tidygraph / igraph
的工作流中,group_louvain() 或
cluster_louvain() 就是这一思想的常见实现方式。
群落特征画像 重要
在完成真实标签与无监督群落的空间对比之后,还可以进一步考察不同
Louvain 群落在结构角色上的差异。为此,此处不再聚焦群落内部的标签构成,而是转向比较各群落节点在多种中心性指标上的分布特征。
具体而言,代码提取了每个节点的群落归属以及三类中心性测度——度中心性、中介中心性与PageRank,并以复合分布图的方式进行展示。图中结合了抖动散点、小提琴图与箱线图,从而同时呈现各群落内部中心性数值的离散程度、分布形态与中位数差异。
考虑到中心性指标通常存在明显的长尾分布,代码首先对三类指标统一进行
log1p()
变换,并对顶部极端值作封顶处理,以提升群落之间的视觉可比性。因此,这一部分的核心目的,是观察不同
Louvain 群落是否在连接规模、桥接作用与整体影响力上表现出系统性的分布差异。
# ==============================================================================
# 2.7 统计可视化:Louvain 群落的中心性复合分布图
# (log 变换 + 分位数封顶 + violin + boxplot)
# ==============================================================================
library(tidyverse)
library(tidygraph)
# ------------------------------------------------------------------------------
# 1. 提取群落与中心性指标,并整理群落显示顺序
# ------------------------------------------------------------------------------
# 先提取已有的群落标签
comm_levels <- github_community %>%
as_tibble() %>%
pull(louvain_lumped) %>%
as.character() %>%
unique()
# 将数值型群落按 1, 2, 3 ... 排序,并将 "Other" 固定放在最后
comm_numeric <- comm_levels[comm_levels != "Other"]
comm_numeric <- comm_numeric[order(parse_number(comm_numeric))]
comm_levels_ordered <- c(comm_numeric, if ("Other" %in% comm_levels) "Other")
# 提取后续绘图所需变量
community_centrality_df <- github_community %>%
as_tibble() %>%
mutate(
louvain_lumped = factor(louvain_lumped, levels = comm_levels_ordered)
) %>%
select(louvain_lumped, degree, betweenness, pagerank)
# ------------------------------------------------------------------------------
# 2. 宽表转长表,并统一进行 log1p 变换
# ------------------------------------------------------------------------------
# 由于三类中心性分布均存在明显右偏,先做 log1p 变换,
# 以减弱极端高值对箱线图与小提琴图的压缩效应
community_centrality_long <- community_centrality_df %>%
pivot_longer(
cols = c(degree, betweenness, pagerank),
names_to = "centrality_type",
values_to = "centrality_value"
) %>%
mutate(
centrality_log = log1p(centrality_value),
centrality_type = factor(
centrality_type,
levels = c("degree", "betweenness", "pagerank"),
labels = c(
"Degree Centrality [log1p]",
"Betweenness Centrality [log1p]",
"PageRank [log1p]"
)
)
)
# ------------------------------------------------------------------------------
# 3. 按“每种中心性指标整体”计算分位数上限,并进行封顶处理
# ------------------------------------------------------------------------------
# 这里采用全局封顶,而不是在每个群落内部单独截断,
# 以保证不同群落之间仍具有可比性
# 当前使用 0.90 分位数,表示将顶部 10% 的极端值压到统一上限
community_centrality_plot <- community_centrality_long %>%
group_by(centrality_type) %>%
mutate(
upper_cap = quantile(centrality_log, 0.90, na.rm = TRUE),
centrality_plot = pmin(centrality_log, upper_cap)
) %>%
ungroup()
# ------------------------------------------------------------------------------
# 4. 绘图:jitter + violin + boxplot 复合分布图
# ------------------------------------------------------------------------------
p_cluster_centrality <- ggplot(
data = community_centrality_plot,
mapping = aes(
x = louvain_lumped,
y = centrality_plot,
fill = louvain_lumped,
colour = louvain_lumped
)
) +
# 底层抖动散点:保留节点分布的离散感
geom_jitter(
width = 0.08,
height = 0,
alpha = 0.06,
size = 0.55,
colour = "grey35"
) +
# 中层小提琴图:展示各群落内部的分布轮廓
geom_violin(
alpha = 0.25,
trim = FALSE,
linewidth = 0.55
) +
# 顶层窄箱线图:突出中位数与四分位距
geom_boxplot(
width = 0.14,
alpha = 0.72,
colour = "black",
outlier.shape = NA
) +
# 按中心性类型分面展示,并允许各自使用独立 Y 轴
facet_wrap(~ centrality_type, scales = "free_y", ncol = 1) +
# 使用一致的离散色盘区分不同群落
scale_fill_brewer(palette = "Set2") +
scale_colour_brewer(palette = "Set2") +
# 标题与坐标轴标签
labs(
title = "Centrality Profiling across Louvain Communities",
subtitle = "All centrality measures are log-transformed; top 10% values are capped for visual comparison",
x = "Louvain Community",
y = "Centrality Value"
) +
# 主题与排版设置
theme_minimal(base_family = "sans") +
theme(
plot.title = element_text(face = "bold", size = 15, hjust = 0),
plot.subtitle = element_text(
size = 10, colour = "grey50", face = "italic",
margin = margin(b = 14)
),
axis.title.x = element_text(face = "bold", size = 11, margin = margin(t = 10)),
axis.title.y = element_text(face = "bold", size = 11, margin = margin(r = 10)),
axis.text.x = element_text(face = "bold", size = 10, colour = "black"),
axis.text.y = element_text(size = 10, colour = "black"),
# 优化分面标签外观
strip.background = element_rect(
fill = "#f0f0f0", colour = "grey80", linewidth = 0.5
),
strip.text = element_text(
face = "bold", size = 11, margin = margin(t = 6, b = 6)
),
# 增加面板边框,并简化网格线
panel.border = element_rect(
colour = "grey80", fill = NA, linewidth = 0.5
),
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
# 隐藏冗余图例
legend.position = "none"
)
# 输出图形
print(p_cluster_centrality)结果解读
该图比较了不同 Louvain 群落在度中心性、中介中心性与PageRank上的分布差异。图中小提琴图用于展示整体分布形态,内部箱线图则概括了中位数与四分位区间。需要说明的是,为提升可读性,三类指标均进行了
log1p()
变换,并对顶部极端值作了封顶处理,因此这里更适合进行群落之间的相对比较。
整体来看,不同群落之间确实存在一定的中心性差异,说明
Louvain
划分出的群落不仅在连边结构上有所区分,在节点的结构角色上也并不完全相同。
同时,不同群落之间的分布仍有一定重叠,这说明群落划分并不只是中心性高低的简单差异,还与网络中的连边模式及局部结构组织方式有关。总体而言,这张图表明不同
Louvain
群落在结构角色上并不均质,部分群落更接近网络中的高连接、高桥接或高影响力位置。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
重要
本章将对前述专题研究 1:“GitHub 开发者社交网络拓扑与群落分析”进行简要复盘。需要说明的是,受教学场景与篇幅所限,该示例更适合作为一个方法演示型案例,而非完整展开的正式科研项目。不过,其所呈现的社会网络分析 (Social Network Analysis, SNA) 工作流,确实是计算社会科学、信息传播研究与复杂系统分析中较常见的一类研究范式。
进阶阅读
什么是 SNA?
社会网络分析 (Social Network
Analysis, SNA) 是一种研究关系结构的定量分析框架,其理论基础主要来自图论 (Graph
Theory)与网络科学 (Network
Science)。在分析中,研究对象通常被表示为节点 (Nodes),互动关系则表示为连边 (Edges),从而用于刻画网络的拓扑结构、节点位置、群体划分与传播路径等问题。
为什么重要?
在计算社会科学中,SNA
是理解社交关系、协作结构、知识扩散与群体分化的重要方法。与传统更关注个体属性的分析不同,SNA
更强调关系本身及其形成的整体结构,因此尤其适合处理平台互动、线上社群与传播网络等研究议题。
方法上的价值
在进入更复杂的图建模之前,例如图嵌入 (Graph Embedding)、图神经网络 (Graph Neural
Networks,
GNNs)或其他图机器学习方法,基础的网络拓扑分析通常能够提供必要的描述性认识与结构诊断。
推荐拓展资源
Network Science with R (基于
tidygraph 与 ggraph 工作流):
面向 R
用户的网络分析与可视化教程,适合结合 tidyverse
工作流进行入门与进阶。
🔗
https://ona-book.org/
Neo4j Graph Data Science Documentation (官方算法文档):
可用于查阅中心性、群落发现、路径分析与链接预测等常见图算法。
🔗
https://neo4j.com/docs/graph-data-science/current/
回顾专题研究 1,本案例主要围绕 GitHub 开发者网络的拓扑结构、关键节点与群落分化展开分析。整体上,示例完成了从图对象构建、中心性测度,到同质性检验与无监督群落划分的一套基础社会网络分析流程。结合前文的实操内容,可将其概括为以下三个方面:
网络数据整理与图对象构建:
分析基于 GitHub 开发者社交网络数据展开,最终处理的网络包含
37,700 个节点与 289,003 条连边。通过
tidygraph
的图数据工作流,节点表与边表被组织为可进行管道操作的网络对象,为后续的中心性计算、群落识别与可视化分析提供了基础。
中心性测度与关键节点识别:
针对大规模网络直接渲染容易出现的“毛线球效应”,案例首先采用抽样与核心子图提取的方式进行结构观察;随后,以度中心性筛选出 Top 500
核心节点,并进一步计算中介中心性与PageRank。在可视化中,节点大小、颜色与透明度被用于呈现不同中心性指标,从而识别出网络中的桥梁节点与高影响力节点。
同质性检验与群落结构分析:
在群体结构层面,案例首先基于开发者类别标签计算了全网的同质性系数 (Assortativity),结果为
0.378,表明网络中存在一定程度的同质性连接。随后,利用
Louvain
算法进行无监督群落划分,并通过左右并列图比较真实标签与算法识别群落。结果显示,网络的拓扑分组与开发者类别之间具有较明显的对应关系,也说明该网络中存在一定的圈层分化现象。
重要
任何网络分析流程都包含一定程度的参数设定、指标选择与方法简化。前述案例虽然揭示了 GitHub 开发者网络中较明显的同质性与群落分化特征,但从学术研究的角度看,相关结论仍需结合数据来源、连边定义与分析尺度进行审慎理解。
以下几个问题,可作为对本案例的进一步反思:
网络快照的时效性:
本案例基于 2019 年 6 月的单一时间切片构建静态网络。但开源社区本身是持续变化的,节点的重要性、群落边界与技术圈层都可能随时间演化。因此,当前结果更适合被理解为某一时点的结构快照,而非稳定不变的长期格局。若进一步引入时序网络 (Temporal
Networks),或许能够更好地观察开发者位置的动态变化。
连边定义的局限性:
本例将连边定义为开发者之间的互相关注关系。这一关系能够反映一定程度的社交联系,但未必等同于真实的协作强度。若改用共同提交代码、Pull Request
互动或共同维护项目等关系来构网,网络结构及其中心性分布可能会有所不同。这说明,关系定义本身会直接影响网络拓扑的解释。
核心子图截取的影响:
为了便于可视化与结构观察,本案例多次聚焦于Top 500
核心节点。这一做法有助于突出网络骨架,但也意味着部分边缘节点与小规模群体未被纳入展示。因此,相关图形更多反映的是核心子网络的结构特征,而不是全网的完整分布。在更正式的研究中,还可以考虑
k-core 分解等其他网络简化方式。
群落发现算法的局限:
本例采用
Louvain
算法识别网络群落。该方法在大规模网络中具有较高效率,但其结果仍受到模块度优化逻辑的影响,较小的群体有时可能被合并到更大的群落中。因此,当前得到的群落划分应理解为一种近似的拓扑分组结果,而不宜视为唯一且绝对的“真实社群”边界。
从结构描述到机制解释
后续研究:
本案例较好展示了网络中的同质性、中心性差异与群落分化,但这些结果主要回答的是“网络呈现出怎样的结构”。若要进一步解释“这种分化为何形成”,则还需要进入更高阶的研究设计,例如结合纵向数据、行为机制与因果推断框架,去区分选择机制与影响机制在网络形成中的作用。
在 tidygraph
中,group_louvain()、group_leiden()、group_infomap()
等函数都可用于群落发现。这些函数在使用方式上与
dplyr 管道兼容,但其底层实现主要来自 igraph
的相关算法封装。对当前案例而言,Louvain
是一个高效且常用的起点,但它并不是唯一选择。
1. Louvain 需要预先设定聚类数量吗?
通常不需要。group_louvain() 并不像 k-means
那样先指定群落个数,而是通过优化模块度 ( Modularity, \(Q\)
),让群落数量随网络结构自然形成。因此,最终得到多少个群落,取决于网络本身的连边模式以及算法的分辨率设定。
2. 在 tidygraph
中,如何调节群落划分的粗细?
若希望控制群落划分的颗粒度,可以尝试使用
group_louvain(resolution = ...) 或
group_leiden(resolution = ...)。一般而言,较低的 resolution 往往得到更少、更大的群落,较高的
resolution 则更容易得到更多、更小的群落。
3.
在加权网络中,如何同时考虑“连接数量”与“连接强度”?
若网络是无向且带权的,例如边权表示共同合作次数、互动频率或关联人数,则通常可以从两个层面理解节点的重要性:
在 tidygraph
中,centrality_degree(weights = weight)
可以基于边权计算这种加权连接强度。因此,在加权网络中,常见做法并不是只看一种指标,而是将未加权 degree与加权 degree /
strength结合起来比较:前者回答“连了多少个对象”,后者回答“这些连接总体有多强”。
4.
如果不仅看连接数,还想看“连到的是不是重要节点”,怎么办?
这时可以进一步考虑支持权重的中心性指标,例如加权
PageRank、特征向量中心性或加权
betweenness。它们不仅考虑一个节点是否连接广,还会进一步考虑这些连接本身的重要性或路径作用。
5. 除了
Louvain,还有哪些更进阶的方法?
group_leiden():
是
Louvain 的常见改进方案。它同样支持加权网络,并提供更稳定的优化过程;若希望得到质量更高、群落内部连接更完整的结果,Leiden
往往值得优先比较。
group_infomap():
该方法更强调随机游走与信息流动。若研究问题更接近传播路径、访问流或信息扩散过程,Infomap
是很有价值的补充方案。
度修正随机块模型 (Degree-corrected Stochastic Block Model,
DCSBM):
当网络内部存在明显的度异质性时,即有些节点天然连接很多,有些节点连接很少,单纯依靠模块度优化的方法有时会受到影响。此时,DCSBM
这类模型会更适合,因为它会显式考虑节点之间的度差异。
group_leiden(node_weights = ...):
若研究中除了边权之外,还希望进一步考虑节点自身规模或暴露量,节点权重也可以纳入群落发现过程。对于某些加权网络,这种做法能够提供更细致的结构划分思路。
课外阅读
tidygraph:Community detection
了解
group_louvain()、group_leiden()、group_infomap()
等函数的参数与返回结果。
🔗
https://tidygraph.data-imaginist.com/reference/group_graph.html
tidygraph:Centrality measures
了解
centrality_degree(weights = ...)、centrality_pagerank(weights = ...)
等加权中心性的用法。
🔗
https://tidygraph.data-imaginist.com/reference/centrality.html
Leiden algorithm documentation
(igraph)
进一步了解
resolution、weights 与
node_weights 在群落发现中的作用。
🔗
https://r.igraph.org/reference/cluster_leiden.html
From Louvain to Leiden (Traag, Waltman & van Eck, 2019)
介绍
Leiden 算法相较 Louvain 的改进思路,是群落发现领域的重要阅读材料。
🔗
https://www.nature.com/articles/s41598-019-41695-z
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
空间流动网络分析
“从 OD 记录到空间流动网络”
本专题基于纽约市 TLC 公布的 HVFHV
出行数据,介绍有向加权空间网络的基本分析流程,包括网络实例化、流线映射、枢纽识别与流动失衡分析。
点击下方卡片,快速跳转至相应模块:
研究设计
课题、数据与分析目标
网络实例化
节点、连边与 OD 图对象
流线映射
空间渲染与骨架提取
枢纽制图
加权中心性空间返射
不平等与失衡
Lorenz、Gini 与源汇分化
💡 提示:学习完一个小节后,请再次点击屏幕右下角的专题导航按钮返回本导航页
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
实战教学
与前文讨论的无向无权社交网络不同,本专题聚焦于具有明确地理位置、方向属性与流量差异的城市空间流动网络。研究基于纽约市 HVFHV (High-Volume For-Hire Vehicle) 出行数据的抽样样本,旨在提取客流的空间分布特征,构建有向加权网络 (Directed, Weighted Network),并结合城市边界开展拓扑与地理叠加分析。
OD 矩阵 (Origin–Destination
Matrix):
用于描述空间流动关系的经典表示方式。其中,O 表示起点
(Origin),D 表示终点 (Destination)。矩阵中的元素反映两个地理单元之间的交互频次或流量规模。
加权有向图 (Weighted
Directed Graph):
城市交通流通常具有明显的方向性与强度差异。因此,网络连边既需要记录流动方向,也需要通过权重变量表示客流频次或流量规模。
空间锚定 (Spatial
Anchoring):
不同于仅依赖拓扑布局的抽象网络,空间网络中的节点具有明确的地理坐标 (通常可由面要素重心表示),其连边反映的是现实城市空间中的流动关系。
数据说明与研究边界
纽约市 HVFHV
月度数据规模较大。为控制计算复杂度并便于教学展示,本专题基于预处理后的抽样数据展开分析
(nyc_taxi_202601_sample.csv)。在研究范围上,仅剔除了位于纽约市主体边界之外的纽瓦克机场
(Newark Airport,
LocationID = 1),其余 Taxi Zone
均保留在分析范围内,以尽可能反映纽约市整体的空间流动格局。
说明:为减少样本体积并降低分析冗余,部分原始时间字段 (如上下车具体时间) 已在预处理阶段剔除。因此,本专题主要关注静态切片下的空间拓扑流动特征,而不涉及时序变化分析。
本专题所使用的数据源自纽约市出租车与轿车管理委员会 (NYC Taxi & Limousine Commission, TLC) 发布的公开行程记录。该数据集长期被用于城市计算、交通研究与空间网络分析,是研究纽约城市出行格局时较常见的开放数据来源。
在实际分析中,通常需要区分纽约市两类主要客运数据:
Figure 3. 纽约市黄、绿出租车
Figure 4. 纽约市FHV网约车
若需获取历年原始
Parquet文件或 Taxi Zone 空间边界数据,可访问纽约市 TLC 官方数据门户:
👉 NYC TLC Trip Record Data
数据下载与加载
本研究所使用的数据可从课程数据页面下载。需下载的文件包括:纽约市 Taxi Zone 边界数据 (GeoJSON 格式),以及 2026 年 1 月纽约市网约车 10% 抽样数据 (CSV 格式)。
请将上述文件统一保存至第六章 R Project 所在目录下的
data 文件夹中,以确保后续代码能够正确读取数据。
library(tidyverse)
library(sf)
library(tidygraph)
# 读取 OD 抽样数据与纽约市 Taxi Zone 边界
df_od_sample <- read_csv(
"data/nyc_taxi_202601_sample.csv",
show_col_types = FALSE
)
nyc_boundary <- st_read(
"data/nyc_boundary.geojson",
quiet = TRUE
) %>%
st_transform(crs = 4326) # 将坐标系转换为 wgs84
# 预览前 6 行数据
head(df_od_sample)## # A tibble: 6 × 4
## PULocationID DOLocationID trip_miles trip_time
## <dbl> <dbl> <dbl> <dbl>
## 1 36 36 0.797 356
## 2 247 119 0.84 301
## 3 169 235 0.54 272
## 4 201 117 1.68 428
## 5 216 260 9.81 1162
## 6 35 76 2.92 1246
## Simple feature collection with 6 features and 6 fields
## Geometry type: MULTIPOLYGON
## Dimension: XY
## Bounding box: xmin: -74.20326 ymin: 40.53583 xmax: -73.76671 ymax: 40.87422
## Geodetic CRS: WGS 84
## OBJECTID Shape_Leng Shape_Area zone LocationID
## 1 2 0.43346967 0.0048663404 Jamaica Bay 2
## 2 3 0.08434111 0.0003144142 Allerton/Pelham Gardens 3
## 3 4 0.04356653 0.0001118719 Alphabet City 4
## 4 5 0.09214649 0.0004979575 Arden Heights 5
## 5 6 0.15049054 0.0006064610 Arrochar/Fort Wadsworth 6
## 6 7 0.10741717 0.0003897880 Astoria 7
## borough geometry
## 1 Queens MULTIPOLYGON (((-73.82338 4...
## 2 Bronx MULTIPOLYGON (((-73.84793 4...
## 3 Manhattan MULTIPOLYGON (((-73.97177 4...
## 4 Staten Island MULTIPOLYGON (((-74.17422 4...
## 5 Staten Island MULTIPOLYGON (((-74.06367 4...
## 6 Queens MULTIPOLYGON (((-73.90414 4...
# ==============================================================================
# 1. 空间制图:研究区域 (Study Area) - NYC Boroughs & Taxi Zones
# ==============================================================================
library(ggplot2)
library(ggspatial)
library(sf) # 确保加载了 sf 包以处理空间矢量数据
p_study_area <- ggplot(data = nyc_boundary) +
# 1.1 添加浅色底图
annotation_map_tile(type = "cartolight", zoom = 12, alpha = 0.5) +
# 1.2 绘制面状 Taxi Zones,并按照 Borough 填充颜色
geom_sf(
aes(fill = borough), # 假设你的行政区字段名为 Borough
color = "grey20", # 保持 zone 边界线颜色
linewidth = 0.2, # 保持 zone 边界线粗细
alpha = 0.75 # 稍微调低一点透明度,让底图街道更清晰
) +
# 1.3 设置离散色盘 (针对分类变量 Borough)
# 这里推荐使用 Set2 或 Pastel1 等适合分类(定名)变量的调色板
scale_fill_brewer(
palette = "Set2",
na.value = "gray90",
name = "NYC Boroughs"
) +
# 1.4 添加比例尺与指北针
annotation_scale(
location = "bl",
width_hint = 0.25,
style = "ticks",
text_cex = 0.8,
text_col = "gray30"
) +
annotation_north_arrow(
location = "br",
which_north = "true",
style = north_arrow_fancy_orienteering(
fill = c("gray40", "white"),
line_col = "gray20"
)
) +
# 1.5 设置地图主题
theme_bw() +
theme(
legend.position = "bottom",
legend.title = element_text(size = 10, face = "bold"),
legend.text = element_text(size = 9),
plot.title = element_text(face = "bold", hjust = 0.5, size = 14),
plot.subtitle = element_text(color = "gray50", hjust = 0.5, size = 11),
panel.grid.major = element_line(color = "gray90", linetype = "dashed"),
axis.title = element_blank()
) +
# 1.6 设置坐标系与图题
coord_sf(crs = 4326) +
labs(
title = "Study Area: NYC Taxi Zones and Boroughs",
subtitle = "Spatial boundaries of TLC taxi zones categorized by administrative boroughs",
caption = "Data: NYC TLC Taxi Zone Shapefile | Basemap: CartoDB Light"
)
# 输出研究区域地图
print(p_study_area)NYC Taxi Zone 是纽约市 TLC (Taxi
& Limousine Commission)
为出行记录统计所设定的标准空间单元。TLC trip records
中的上下车位置并不直接提供精确坐标 [(GPS,
自2015年之后,保护隐私原因)]{note},而是以 LocationID
的形式对应到具体分区,以便后续进行边界连接、空间制图与 OD
分析。官方资料显示,这套分区体系共包含 263 个
LocationID。
Figure 5. 曼哈顿区Taxi Zones官方划分
在本专题中,PULocationID 表示上车分区 (Pick-up Location
ID),DOLocationID 表示下车分区 (Drop-off Location
ID)。因此,每一条出行记录都可以理解为一次从起点分区到终点分区的空间流动,这也是构建
OD (Origin–Destination) 网络的基础。
需要注意的是,LocationID = 1 对应 Newark
Airport。该分区位于新泽西州,不属于纽约市五大
borough
的主体空间范围。若研究目标聚焦于纽约市内部的城市流动结构,通常会将这一分区剔除,以使分析范围更好对应纽约市本身的空间边界。
核心分析目标
本专题遵循“数据聚合 \(\rightarrow\) 网络实例化 \(\rightarrow\) 网络测度 \(\rightarrow\) 空间回投”的基本分析路径,主要包括以下四个目标:
空间网络实例化:
基于网约车出行记录,统计不同起讫分区之间的客流频次,生成 OD
连边及其权重;同时提取纽约市 Taxi Zone 的几何中心 (Centroids)
作为节点坐标,从而构建具有明确地理位置的有向加权空间网络。
空间流线可视化:
在真实地理坐标基础上对网络进行渲染,而不再依赖抽象拓扑布局;通过连边宽度与透明度映射客流强度,以识别城市出行网络中的主要流动骨架。
加权中心性测度:
考虑到不同连边之间存在明显的流量差异,本专题进一步引入节点强度 (Node
Strength)与加权
PageRank等指标,识别城市交通网络中的关键枢纽;并将相关测度结果回接至
Taxi Zone 面状边界,以实现空间分布制图。
流入—流出不对称分析:
在有向网络框架下,进一步区分各区块的流入强度与流出强度,并比较其差异,以识别更偏向于客流汇聚、客流输出或双向交换的空间单元。该步骤有助于从方向性角度补充中心性分析,并进一步理解城市流动网络中的功能分化。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
几何降维与拓扑重构
在常规地理信息系统 (GIS) 中,城市空间单元通常以多边形的形式表示;而在图论框架下,网络节点则需要以点的形式进入分析。因此,构建空间网络的第一步,是将 Taxi Zone 多边形转换为对应的几何中心 (Centroids),并以此作为节点的地理坐标。这一过程本质上是将面状空间单元转化为可用于网络建模的点对象。
在此基础上,原始出行记录还需要进一步聚合为起点—终点 (OD, Origin–Destination) 关系。具体而言,每一条连边表示两个空间单元之间的出行联系,连边权重则用于刻画对应的客流频次。由于城市出行同时具有明确的方向性与显著的流量差异,因此最终构建的不是一般的无向图,而是一个有向加权网络 (Directed, Weighted Network)。
# ------------------------------------------------------------------------------
# 1. 节点表(Nodes):提取几何中心并生成节点坐标
# ------------------------------------------------------------------------------
library(sf)
library(tidyverse)
library(tidygraph)
# st_centroid() 在经纬度坐标系下会给出提示,这里将其抑制
suppressWarnings({
nyc_nodes <- nyc_boundary %>%
# 以每个 Taxi Zone 的几何中心作为网络节点位置
st_centroid() %>%
mutate(
lon = st_coordinates(.)[, 1],
lat = st_coordinates(.)[, 2]
) %>%
# 去除空间几何,仅保留节点属性表
st_drop_geometry() %>%
select(LocationID, borough, zone, lon, lat) %>%
# tidygraph 默认使用 name 作为节点标识
rename(name = LocationID) %>%
# 将节点 ID 转为字符型,避免数值型 ID 被误识别为行号索引
mutate(name = as.character(name))
})
# ------------------------------------------------------------------------------
# 2. 连边表(Edges):聚合 OD 客流并生成权重
# ------------------------------------------------------------------------------
nyc_edges <- df_od_sample %>%
# 按起点与终点分组,统计每一组 OD 的出行频次
group_by(from = PULocationID, to = DOLocationID) %>%
summarise(weight = n(), .groups = "drop") %>%
# 去除起点与终点相同的自循环连边
filter(from != to) %>%
# 与节点表保持一致,将 ID 统一为字符型
mutate(
from = as.character(from),
to = as.character(to)
) %>%
# 仅保留能够在节点表中成功匹配的有效连边
filter(from %in% nyc_nodes$name & to %in% nyc_nodes$name)
# ------------------------------------------------------------------------------
# 3. 图对象实例化:构建有向加权空间网络
# ------------------------------------------------------------------------------
nyc_graph <- tbl_graph(
nodes = nyc_nodes,
edges = nyc_edges,
directed = TRUE
)
# 查看生成的网络结构
print(nyc_graph)# A tbl_graph: 262 nodes and 44957 edges
#
# A directed simple graph with 3 components
#
# Node Data: 262 × 5 (active)
name borough zone lon lat
<chr> <chr> <chr> <dbl> <dbl>
1 2 Queens Jamaica Bay -73.8 40.6
2 3 Bronx Allerton/Pelham Gardens -73.8 40.9
3 4 Manhattan Alphabet City -74.0 40.7
4 5 Staten Island Arden Heights -74.2 40.6
5 6 Staten Island Arrochar/Fort Wadsworth -74.1 40.6
6 7 Queens Astoria -73.9 40.8
7 8 Queens Astoria Park -73.9 40.8
8 9 Queens Auburndale -73.8 40.8
9 10 Queens Baisley Park -73.8 40.7
10 11 Brooklyn Bath Beach -74.0 40.6
# ℹ 252 more rows
#
# Edge Data: 44,957 × 3
from to weight
<int> <int> <int>
1 1 29 1
2 1 123 1
3 1 179 1
# ℹ 44,954 more rows
分析与洞见
完成上述步骤后,原本分散的边界数据与 OD
记录已被整合为一个统一的 tbl_graph
对象。该网络不仅保留了表示客流频次的连边权重 (weight),同时也在节点属性中记录了对应的经纬度坐标
(lon,
lat)。这意味着,后续网络分析将不再依赖抽象拓扑布局,而可以直接在真实地理空间中展开。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
空间制图
在完成图对象实例化后,下一步是将网络结构映射到真实的二维地理空间中。与前文的虚拟社交网络不同,空间网络中的节点已经具有明确的经纬度坐标,因此不再需要依赖力导向布局 (如 Fruchterman–Reingold)来安排节点位置,而可以直接采用基于真实坐标的静态布局 (Manual Layout)进行渲染。
为保证空间表达的规范性,并与前文地图制图的基本要求保持一致 (参见第 4 章),初步的全局网络图通常需要同时包含底图图层 (Base Map)、网络图层 (Nodes and Edges),以及必要的地理参照要素 (如比例尺与指北针)。这一步的目的不仅是展示网络形态,也为后续识别空间流动骨架与诊断视觉过载问题提供基础。
基础制图 不推荐
本段代码使用 ggraph、ggplot2 与
ggspatial,在真实地理坐标基础上对已构建的纽约市 Taxi Zone
OD 网络进行初步渲染。其中,节点位置直接对应各分区的几何中心,连边表示不同分区之间的出行联系;同时叠加底图、边界图层、比例尺与指北针,以形成完整的空间网络底图。
这一结果的主要作用在于展示原始 OD 网络在地理空间中的总体分布,并作为后续筛选与优化的参照。需要说明的是,由于当前图形尚未对连边进行有效过滤,大量流动路径在空间上高度重叠,容易产生明显的视觉过载与“毛线球效应”。因此,这一图形更适合作为探索性草图,用于诊断网络密度与制图问题,而不宜直接作为科研论文中的正式结果图。
library(ggraph)
library(tidyverse)
library(ggspatial) # 用于添加比例尺、指北针与底图瓦片
# ==============================================================================
# 4.3 全局基础网络映射:原始 OD 空间网络的初步渲染
# ==============================================================================
# 基于节点自带的经纬度坐标,采用 manual 布局进行空间定位
p_draft <- ggraph(nyc_graph, layout = "manual", x = lon, y = lat) +
# 1. 添加浅色瓦片底图,提供基础地理参照 (zoom = 10-12)
annotation_map_tile(type = "cartolight", zoom = 12, alpha = 0.8) +
# 2. 绘制纽约市 Taxi Zone 边界,作为空间底图图层
geom_sf(
data = nyc_boundary,
aes(fill = "NYC Taxi Zones"),
color = "black",
linewidth = 0.5,
alpha = 0.05,
inherit.aes = FALSE # 避免继承 ggraph 的节点坐标映射
) +
# 3. 绘制 OD 连边
# 此处暂不进行主流线筛选,仅以低透明度展示原始网络密度
geom_edge_link(
color = "#2C7BB6",
alpha = 0.05,
width = 0.2,
show.legend = FALSE
) +
# 4. 绘制节点
# 节点位置对应 Taxi Zone 的几何中心
geom_node_point(
size = 0.5,
color = "#D7191C",
alpha = 0.8
) +
# 5. 添加空间参照要素
scale_fill_manual(
values = c("NYC Taxi Zones" = "darkred"),
name = NULL
) +
annotation_scale(
location = "bl",
width_hint = 0.25,
style = "ticks",
text_cex = 0.8,
text_col = "gray30"
) +
annotation_north_arrow(
location = "br",
which_north = "true",
style = north_arrow_fancy_orienteering(
fill = c("gray40", "white"),
line_col = "gray20"
)
) +
# 6. 图形主题设置
theme_bw() +
theme(
legend.position = "bottom",
legend.text = element_text(size = 10, face = "bold"),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(color = "gray50", hjust = 0.5, size = 10),
panel.grid.major = element_line(color = "gray90", linetype = "dashed"),
axis.title = element_blank() # 地图中通常不单独显示坐标轴标题
) +
# 7. 指定地图坐标参考系统
coord_sf(crs = 4326) +
# 8. 添加标题与说明信息
labs(
title = "NYC Taxi OD Network (Unfiltered Draft)",
subtitle = "Preliminary spatial rendering of the raw directed network",
caption = "Data: HVFHV 2026-01 (10% sample) | Basemap: CartoDB Light"
)
# 输出初步网络图
print(p_draft)进阶制图 推荐
在完成原始网络草图的诊断后,下一步可通过连边筛选与分级映射,进一步提取城市出行网络中的主要流动骨架。这一阶段不再尝试展示全部 OD 连边,而是聚焦于高频流动关系,并结合线宽与颜色对客流强度进行分层表达,从而提升图形的可读性与结构辨识度。
本段代码首先依据连边权重筛选出高流量 OD 关系,再利用
Fisher-Jenks
自然断点法对流量进行分级;随后,在真实地理坐标下绘制核心流动网络,并通过曲线连边减少双向流动的重叠问题。与前一图相比,这一结果更适合用于识别纽约市内部的主要出行通道与核心联系结构。
需要说明的是,此处采用暗色底图与高对比度配色,主要是为了增强主流线的视觉表现力,属于一种制图风格选项,而非唯一标准。在实际研究或论文写作中,也完全可以根据版式需求改用浅色底图或白色背景;关键并不在于底色本身,而在于是否能够清晰呈现主要流动关系,并避免不必要的视觉干扰。
# ==============================================================================
# 4.4 核心流动骨架提取与暗色空间网络制图
# ==============================================================================
# 载入分级与调色所需包
library(classInt) # 用于计算 Fisher-Jenks 自然断点
library(viridis) # 用于调用 viridis 系列色盘
library(tidygraph)
library(ggraph)
library(ggspatial) # 用于添加比例尺、指北针与底图瓦片
# ------------------------------------------------------------------------------
# 1. 提取高流量连边,并生成分级变量
# ------------------------------------------------------------------------------
# 这里的 weight 表示单向 OD 频次,因此筛选的是高频单向流动连边
nyc_graph_core <- nyc_graph %>%
activate(edges) %>%
filter(weight >= 300)
# 提取过滤后的权重,并计算 Fisher-Jenks 自然断点
core_weights <- nyc_graph_core %>%
activate(edges) %>%
pull(weight)
fisher_breaks <- classIntervals(
core_weights,
n = 5,
style = "fisher"
)$brks
# 将分级结果写回连边属性,用于后续颜色映射
nyc_graph_core <- nyc_graph_core %>%
activate(edges) %>%
mutate(
flow_class = cut(
weight,
breaks = fisher_breaks,
include.lowest = TRUE,
dig.lab = 5
)
)
# ------------------------------------------------------------------------------
# 2. 核心流动骨架的暗色空间网络制图
# ------------------------------------------------------------------------------
p_refined_dark <- ggraph(nyc_graph_core, layout = "manual", x = lon, y = lat) +
# 2.1 添加暗色瓦片底图,提供基础地理参照
annotation_map_tile(type = "cartodark", zoom = 12, alpha = 0.1) +
# 2.2 绘制 Taxi Zone 边界图层
geom_sf(
data = nyc_boundary,
aes(fill = "NYC Taxi Zones"),
color = "gray40",
linewidth = 0.3,
alpha = 0.1,
inherit.aes = FALSE
) +
# 2.3 绘制高流量 OD 连边 (这里用的曲线,防止in out重叠)
# 线宽映射真实流量,颜色映射 Fisher-Jenks 分级
geom_edge_fan(
aes(
edge_width = weight,
edge_color = flow_class
),
alpha = 0.85
) +
# 2.4 绘制节点
# 节点位置对应 Taxi Zone 的几何中心
geom_node_point(
size = 0.3,
color = "white",
alpha = 0.4
) +
# 2.5 设置连边颜色与线宽映射
scale_edge_color_viridis(
discrete = TRUE,
option = "rocket",
direction = 1,
name = "Flow Volume\n(Number of Trips)",
guide = guide_legend(
override.aes = list(edge_width = 2, alpha = 1)
)
) +
scale_edge_width_continuous(
range = c(0.1, 2),
guide = "none"
) +
scale_fill_manual(
values = c("NYC Taxi Zones" = "#1a1a1a"),
name = NULL
) +
# 2.6 添加比例尺与指北针
annotation_scale(
location = "bl",
width_hint = 0.25,
style = "ticks",
text_cex = 0.8,
text_col = "gray80",
line_col = "gray80"
) +
annotation_north_arrow(
location = "br",
which_north = "true",
style = north_arrow_fancy_orienteering(
fill = c("gray60", "#222222"),
line_col = "gray50",
text_col = "gray80"
)
) +
# 2.7 设置暗色地图主题
theme_bw() +
theme(
plot.background = element_rect(fill = "#111111", color = NA),
panel.background = element_rect(fill = "#111111", color = NA),
legend.position = "right",
legend.background = element_rect(fill = "#111111", color = NA),
legend.key = element_rect(fill = "#111111", color = NA),
legend.title = element_text(color = "white", size = 9, face = "bold"),
legend.text = element_text(color = "gray80", size = 8),
plot.title = element_text(color = "white", face = "bold", hjust = 0.5),
plot.subtitle = element_text(color = "gray60", hjust = 0.5, size = 10),
plot.caption = element_text(color = "gray45"),
panel.border = element_rect(color = "gray40", fill = NA),
panel.grid.major = element_line(color = "gray25", linetype = "dashed"),
axis.text = element_text(color = "gray50"),
axis.title = element_blank()
) +
# 2.8 设置坐标系统与图题
coord_sf(crs = 4326) +
labs(
title = "Major Taxi Flow Corridors in New York City",
subtitle = "High-volume directed OD links filtered from the January 2026 HVFHV sample",
caption = "Data: HVFHV 2026-01 (10% sample) | Basemap: CartoDB Dark | Classification: Fisher-Jenks"
)
# 输出图形
print(p_refined_dark)© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
枢纽挖掘与面状映射
在完成城市流动主通道的线状 (Line) 特征提取后,本节进一步将分析重点转向空间网络中的节点 (Nodes)。核心目标是借助图论测度量化各个 Taxi Zone 在整体流动网络中的枢纽地位,并将抽象的拓扑指标回接至二维地理空间,生成对应的面状热力图。
在常规的无向无权网络中,常以度中心性 (Degree Centrality)衡量节点的重要性,即统计与节点直接相连的边数。但在城市 OD 客流网络中,仅有连接数量并不足以反映节点的真实作用:一个区域即使连接较多,若客流规模有限,其网络地位仍可能较弱。
因此,在本专题中,更适合引入以下两类指标:
节点强度 (Node
Strength):
可理解为加权条件下的“度”。由于网络具有方向性,该指标可进一步区分为流入强度 (In-Strength)与流出强度
(Out-Strength),分别对应某一区域接收与发出的总客流量。
加权 PageRank (Weighted
PageRank):
该指标不仅考虑一个节点拥有多少流量,也考虑这些流量是否与其他重要节点相连。换言之,一个区域的网络地位,不仅取决于其客流规模,还取决于它与哪些关键枢纽发生联系。因此,相较于单纯的流量统计,PageRank
更能反映节点在整体网络结构中的系统性重要性。
算法计算与空间连接
接下来的操作分为三个环节:首先在 tidygraph 框架内对全量拓扑网络计算加权中心性指标,包括流入强度、流出强度与加权 PageRank;随后提取节点属性表;最后将其中的加权 PageRank回接至 sf
空间多边形对象,生成首张节点中心性的地图。至于流入—流出强度,则将在后续部分进一步用于不对称性分析。
# ==============================================================================
# 1. 网络测度:计算 in-strength、out-strength 与 weighted PageRank
# 本节首先展示 weighted PageRank 的空间分布
# ==============================================================================
library(classInt)
library(viridis)
# 中心性应基于全量网络计算,以保留整体拓扑结构
nyc_graph <- nyc_graph %>%
activate(nodes) %>%
mutate(
# 流入强度:某区块接收的总客流量
in_strength = centrality_degree(weights = weight, mode = "in"),
# 流出强度:某区块发出的总客流量
out_strength = centrality_degree(weights = weight, mode = "out"),
# 加权 PageRank:衡量节点在整体流动网络中的系统性重要性
pagerank = centrality_pagerank(weights = weight)
)
# ==============================================================================
# 2. 属性回接:将网络测度连接回空间边界,并进行分级
# ==============================================================================
# 2.1 提取节点属性表
node_metrics <- nyc_graph %>%
activate(nodes) %>%
as_tibble() %>%
select(name, in_strength, out_strength, pagerank)
# 2.2 将中心性指标连接回 Taxi Zone 边界
nyc_zones_centrality <- nyc_boundary %>%
mutate(LocationID_chr = as.character(LocationID)) %>%
left_join(node_metrics, by = c("LocationID_chr" = "name"))
# 2.3 对 PageRank 进行 Fisher-Jenks 自然断点分级
# 先去除缺失值,再计算断点
pr_values <- na.omit(nyc_zones_centrality$pagerank)
fisher_pr_breaks <- classIntervals(
pr_values,
n = 5,
style = "fisher"
)$brks
nyc_zones_centrality <- nyc_zones_centrality %>%
mutate(
# 将连续型 PageRank 转换为 5 级分类变量
pr_class = cut(
pagerank,
breaks = fisher_pr_breaks,
include.lowest = TRUE,
dig.lab = 2
)
)
# ==============================================================================
# 3. 空间制图:加权 PageRank 的面状热力图
# ==============================================================================
library(ggplot2)
library(ggspatial)
p_pagerank <- ggplot(data = nyc_zones_centrality) +
# 3.1 添加浅色底图
annotation_map_tile(type = "cartolight", zoom = 12, alpha = 0.5) +
# 3.2 绘制面状热力图
geom_sf(
aes(fill = pr_class),
color = "grey20",
linewidth = 0.2,
alpha = 0.85
) +
# 3.3 设置离散色盘
# 反转色盘,使高值区块呈现更深的颜色
scale_fill_viridis_d(
option = "mako",
direction = -1,
na.value = "gray90",
name = "Weighted PageRank\n(Fisher-Jenks Classes)",
guide = guide_legend(reverse = TRUE)
) +
# 3.4 添加比例尺与指北针
annotation_scale(
location = "bl",
width_hint = 0.25,
style = "ticks",
text_cex = 0.8,
text_col = "gray30"
) +
annotation_north_arrow(
location = "br",
which_north = "true",
style = north_arrow_fancy_orienteering(
fill = c("gray40", "white"),
line_col = "gray20"
)
) +
# 3.5 设置地图主题
theme_bw() +
theme(
legend.position = "bottom",
legend.title = element_text(size = 9, face = "bold"),
legend.text = element_text(size = 8),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(color = "gray50", hjust = 0.5, size = 10),
panel.grid.major = element_line(color = "gray90", linetype = "dashed"),
axis.title = element_blank()
) +
# 3.6 设置坐标系与图题
coord_sf(crs = 4326) +
labs(
title = "Spatial Distribution of Weighted PageRank in NYC Taxi Zones",
subtitle = "Choropleth mapping of global hub significance based on the directed weighted OD network",
caption = "Data: HVFHV 2026-01 (10% sample) | Basemap: CartoDB Light | Classification: Fisher-Jenks"
)
# 输出地图
print(p_pagerank)结果解读
从这张加权 PageRank空间分布图来看,纽约市 Taxi Zone 之间的网络地位存在较明显的空间差异。整体上,深色区块代表更高的 PageRank,说明这些区域不仅客流联系频繁,而且更可能与其他同样重要的枢纽区块保持紧密连接,因此在整体 OD 网络中具有更强的系统性枢纽地位。
最显著的特征是,图中两处机场相关区块呈现最高等级(图中的黑色地块,1)JFK肯尼迪机场位于地图东南部;2)LGA拉瓜迪亚机场位于地图皇后区北部),说明机场在网约车流动网络中具有非常突出的核心作用。这一结果并不意外:机场通常同时承担大量出发与到达需求,并与城市内部多个高流量区块保持强连接,因此 PageRank 往往较高。
除机场外,图中位于布鲁克林区中部附近的黑色地块以及曼哈顿中下部附近的一片较深色区块同样表现出很高的 PageRank。这表明,除航空枢纽之外,城市内部也存在若干承担关键中转与集散功能的核心区域。相较于单纯的客流总量,这类高 PageRank 区块更能反映其在整体网络中的结构重要性。
从整体空间格局看,高值区块主要集中在曼哈顿及其邻近区域,而外缘区域与部分边缘 borough 的 Taxi Zone 等级相对较低。这说明纽约市网约车出行网络呈现出较明显的中心—边缘结构:少数核心区块控制了更强的网络联系,而大部分外围区块则更多处于次级位置。
nyc_zones_centrality %>% st_drop_geometry() %>% group_by(borough) %>% summarise(median_pr = median(pagerank,na.rm = T))## # A tibble: 5 × 2
## borough median_pr
## <chr> <dbl>
## 1 Bronx 0.00342
## 2 Brooklyn 0.00400
## 3 Manhattan 0.00442
## 4 Queens 0.00269
## 5 Staten Island 0.00244
这一判断也可从 borough 层面的统计结果得到进一步支持。以各 borough 的PageRank 中位数来看,Manhattan 最高 (0.00442),其后依次为 Brooklyn (0.00400)、Bronx (0.00342)、Queens (0.00269) 与 Staten Island (0.00244)。这表明,从整体分布上看,曼哈顿的 Taxi Zone 更倾向于占据全市流动网络中的核心位置,而 Queens 与 Staten Island 的网络地位相对较弱。
AI的回答
在空间分布图中,布鲁克林中部的 Bedford-Stuyvesant (Bed-Stuy) 呈现出与 JFK、LGA 机场同级的黑色高值区域。机场作为天然物理枢纽容易理解,但 Bed-Stuy 作为一个常规城市街区,其极高的网络中心度主要源于以下复合城市动态:
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
洛伦兹曲线与失衡制图
本节进一步转向城市流动网络的整体结构特征。相较于前文关注“哪些区块更重要”,这里更关注另一个问题:客流是否高度集中在少数 Taxi Zone,以及不同区块在流动方向上是否存在明显的不平衡。
为此,本节首先引入经济学中常用的基尼系数 (Gini Coefficient)与洛伦兹曲线 (Lorenz Curve),用以刻画流入强度与流出强度在不同空间单元之间的集中程度;随后,进一步构建客流失衡指数,并将其映射回地理空间,以识别城市中更偏向于客流吸纳端或输出端的区块。
空间不平等测度:基尼系数与洛伦兹曲线
进阶阅读
1. 洛伦兹曲线 (Lorenz
Curve)
洛伦兹曲线由美国统计学家 Max O.
Lorenz 于 1905
年提出,最初用于描述收入分配的不均衡程度。其基本思想是:将研究对象按某一指标从低到高排序,再比较累计单元占比与累计资源占比之间的关系。
在图形上,横轴通常表示累计空间单元比例,纵轴表示累计流量或资源比例。若所有单元分布完全均等,则曲线会落在 45° 的完全均等线上;曲线越向右下方弯曲,说明分布越集中,不平等程度越高。
洛伦兹曲线本身并不直接给出一个单一数值,而是通过图形方式展示整体分布格局,因此特别适合用于比较不同指标或不同系统之间的集中性差异。
2. 基尼系数 (Gini
Coefficient)
基尼系数由意大利统计学家 Corrado
Gini 于 1912
年提出,是在洛伦兹曲线基础上进一步发展出的量化指标。它用于概括分布的不均衡程度,常见表达式为:
\[ G = \frac{\sum_{i=1}^{n}\sum_{j=1}^{n}|x_i - x_j|}{2n^2\mu} \]
其中,\(x_i\) 表示第 \(i\) 个单元的观测值,\(n\) 为单元总数,\(\mu\) 为该指标的平均值。
基尼系数通常取值于 \([0, 1]\) 之间:
在本节 OD 网络分析中,洛伦兹曲线与基尼系数并不用于衡量收入差距,而是用于刻画空间流动资源在不同 Taxi Zone 之间的分布格局。具体而言:
In-Strength 与 Out-Strength
的曲线形态相近,则说明流入端与流出端在宏观层面具有相似的集中格局。这两类工具特别适合用于回答这样的问题:城市流动是否高度集中于少数节点?不同方向的流量分布是否具有相似的不平等结构?
# ==============================================================================
# 1. 空间不平等测度:Lorenz 曲线与 Gini 系数
# ==============================================================================
library(ineq)
library(dplyr)
library(ggplot2)
library(tibble)
# ------------------------------------------------------------------------------
# 1.1 提取流入与流出强度,并计算 Gini 系数
# ------------------------------------------------------------------------------
# 去除缺失值后,分别计算 In-Strength 与 Out-Strength 的不平等程度
in_vals <- na.omit(nyc_zones_centrality$in_strength)
out_vals <- na.omit(nyc_zones_centrality$out_strength)
gini_in <- Gini(in_vals)
gini_out <- Gini(out_vals)
# ------------------------------------------------------------------------------
# 1.2 计算 Lorenz 曲线坐标
# ------------------------------------------------------------------------------
# Lc() 返回累计占比坐标,用于绘制 Lorenz 曲线
lc_in <- Lc(in_vals)
lc_out <- Lc(out_vals)
lorenz_df <- bind_rows(
tibble(
p = lc_in$p,
L = lc_in$L,
metric = "In-Strength"
),
tibble(
p = lc_out$p,
L = lc_out$L,
metric = "Out-Strength"
),
tibble(
p = c(0, 1),
L = c(0, 1),
metric = "Line of Equality" # 完全均等线
)
)
# ------------------------------------------------------------------------------
# 1.3 Lorenz 曲线可视化
# ------------------------------------------------------------------------------
# 由于 In 与 Out 曲线高度接近,这里同时使用颜色、线型与透明度进行区分
p_lorenz <- ggplot(
lorenz_df,
aes(x = p, y = L, colour = metric, linetype = metric, alpha = metric)
) +
geom_line(linewidth = 1.15) +
scale_colour_manual(
values = c(
"In-Strength" = "#D7191C",
"Out-Strength" = "#2C7BB6",
"Line of Equality" = "black"
)
) +
scale_linetype_manual(
values = c(
"In-Strength" = "solid",
"Out-Strength" = "longdash",
"Line of Equality" = "dashed"
)
) +
scale_alpha_manual(
values = c(
"In-Strength" = 0.75,
"Out-Strength" = 0.75,
"Line of Equality" = 0.90
)
) +
coord_equal() +
labs(
title = "Lorenz Curves of Spatial Flow Concentration",
subtitle = sprintf(
"Gini (In-Strength) = %.3f | Gini (Out-Strength) = %.3f",
gini_in, gini_out
),
x = "Cumulative share of Taxi Zones",
y = "Cumulative share of traffic flow"
) +
theme_bw(base_family = "sans") +
theme(
plot.title = element_text(face = "bold", size = 14, hjust = 0.5),
plot.subtitle = element_text(size = 10, colour = "grey40", hjust = 0.5),
legend.title = element_blank(),
legend.position = "bottom"
)
print(p_lorenz)解析:空间流动的不平等特征
基于洛伦兹曲线的形态及基尼系数结果,可以概括出纽约市网约车流动网络的两点特征:
较明显的空间集中性:
流入与流出的基尼系数均约为
0.44,且两条洛伦兹曲线均明显偏离完全均等线,说明客流并未均匀分布于所有
Taxi Zone,而是较明显地集中在少数核心区块。从图形上看,前半数区块所累积的客流占比仍然较低,这表明城市流动资源存在一定程度的中心—边缘分化。
流入与流出集中度的高度一致性:
流入强度 (In-Strength)
与流出强度 (Out-Strength)
的洛伦兹曲线几乎重合,对应的基尼系数也仅有极小差异 (0.444
vs. 0.442)。这说明在宏观层面上,城市客流的吸纳端与输出端具有非常接近的集中格局。换言之,承担大量客流吸纳功能的核心区块,往往也同时承担较强的客流输出功能。
非对称性映射:空间源汇制图
接下来计算区域的客流失衡指数,并利用发散型色盘(Diverging Palette)进行面状制图,以探测城市交通的潮汐特征。
进阶阅读
在有向空间网络中,仅知道一个区域的总流量规模往往并不足够。对于城市出行而言,更关键的问题还包括:某一区域究竟更偏向于吸纳客流,还是更偏向于输出客流?为此,可进一步构建客流失衡指数 (Flow Imbalance Index)。
其表达式为:
\[ Imbalance_i = \frac{InStrength_i - OutStrength_i}{InStrength_i + OutStrength_i} \]
其中,\(InStrength_i\) 表示区域 \(i\) 的流入强度,\(OutStrength_i\) 表示其流出强度。由于分子反映的是流入与流出的差值,而分母反映的是该区域的总流动规模,因此该指标本质上是一个标准化差值,可以在不同规模的区块之间进行比较。
如何理解这一指标?
该指标的理论取值范围为 \([-1, 1]\)。数值越接近两端,说明流动方向越不平衡;越接近 0,则说明该区域更像一个双向交换相对均衡的节点。
在本专题中的用途
在纽约市 Taxi Zone 的 OD
网络中,客流失衡指数可用于识别不同空间单元在城市流动系统中的功能角色。例如:
# ==============================================================================
# 空间非对称测度:计算客流失衡指数并进行制图
# ==============================================================================
library(ggspatial)
# ------------------------------------------------------------------------------
# 2.1 计算客流失衡指数
# ------------------------------------------------------------------------------
# 失衡指数保留方向信息:
# 正值表示净流入(sink),负值表示净流出(source)
nyc_zones_centrality <- nyc_zones_centrality %>%
mutate(
total_flow = in_strength + out_strength,
imbalance = dplyr::if_else(
total_flow > 0,
(in_strength - out_strength) / total_flow,
NA_real_
)
)
# ------------------------------------------------------------------------------
# 2.2 设定对称色标范围
# ------------------------------------------------------------------------------
# 这里采用 |imbalance| 的 95% 分位数作为
# 对称截断阈值,以兼顾可读性与极端值控制
imb_cap <- nyc_zones_centrality %>%
st_drop_geometry() %>%
pull(imbalance) %>%
abs() %>%
quantile(0.95, na.rm = TRUE)
# ------------------------------------------------------------------------------
# 2.3 构建发散型面状地图
# ------------------------------------------------------------------------------
p_imbalance <- ggplot(data = nyc_zones_centrality) +
# 添加浅色底图,保持与之前地图一致的制图风格
annotation_map_tile(type = "cartolight", zoom = 12, alpha = 0.5) +
# 绘制失衡指数的面状分布
geom_sf(
aes(fill = imbalance),
color = "grey20",
linewidth = 0.2,
alpha = 0.85
) +
# 使用以 0 为中点的发散色标:
# 红色表示净流出,蓝色表示净流入,白色表示相对平衡
scale_fill_gradient2(
low = "#B2182B",
mid = "white",
high = "#2166AC",
midpoint = 0,
limits = c(-imb_cap, imb_cap),
oob = scales::squish,
na.value = "grey90",
name = "Flow Imbalance Index\n(- Source / + Sink)",
guide = guide_colorbar(
barwidth = 15,
barheight = 0.8,
title.position = "top",
title.hjust = 0.5
)
) +
# 添加比例尺与指北针
annotation_scale(
location = "bl",
width_hint = 0.25,
style = "ticks",
text_cex = 0.8,
text_col = "gray30"
) +
annotation_north_arrow(
location = "br",
which_north = "true",
style = north_arrow_fancy_orienteering(
fill = c("gray40", "white"),
line_col = "gray20"
)
) +
# 地图主题设置
theme_bw() +
theme(
legend.position = "bottom",
legend.title = element_text(size = 9, face = "bold"),
legend.text = element_text(size = 8),
plot.title = element_text(face = "bold", hjust = 0.5),
plot.subtitle = element_text(color = "gray50", hjust = 0.5, size = 10),
panel.grid.major = element_line(color = "gray90", linetype = "dashed"),
axis.title = element_blank()
) +
# 坐标系与图题
coord_sf(crs = 4326) +
labs(
title = "Spatial Distribution of Flow Imbalance across NYC Taxi Zones",
subtitle = "Negative values indicate net outflow; positive values indicate net inflow",
caption = "Data: HVFHV 2026-01 (10% sample) | Basemap: CartoDB Light"
)
# 输出地图
print(p_imbalance)结果解读
该图表明,纽约市 Taxi Zone 的客流失衡在空间上具有一定分异,但整体幅度并不极端。多数区块的失衡指数接近 0,说明全市网约车流动总体上仍以相对平衡的双向交换为主;与此同时,局部区域已出现较明确的净流入或净流出特征,反映出城市内部存在一定程度的方向性分化。
nyc_zones_centrality %>%
st_drop_geometry() %>%
group_by(borough) %>%
summarise(median_imbalance = median(imbalance, na.rm = TRUE))## # A tibble: 5 × 2
## borough median_imbalance
## <chr> <dbl>
## 1 Bronx 0.00284
## 2 Brooklyn 0.00548
## 3 Manhattan -0.0154
## 4 Queens 0.00426
## 5 Staten Island -0.00899
从 borough 层面的中位数结果看,Brooklyn (0.00548)、Queens (0.00426)与 Bronx (0.00284)整体上更偏向于正值,而 Manhattan (-0.0154)与 Staten Island (-0.00899)则整体上更偏向于负值。这说明,不同 borough 在网约车流动系统中的方向性角色并不一致:部分区域整体更偏向流入,而另一些区域则更偏向流出。
结合地图与 borough 统计结果,可以认为纽约市网约车流动网络不仅存在中心—边缘差异,也表现出一定的方向性不均衡。这一结果说明,即使在总体集中格局相近的情况下,不同空间单元在流动系统中的功能角色仍然存在差异。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
重要
本节将对前述专题研究 2:“纽约市网约车空间流动网络分析”进行简要回顾。若说专题研究 1主要揭示了社会关系中的拓扑结构,那么专题研究 2 则进一步展示了网络分析如何应用于真实地理空间。这一过程提示,城市并不只是由静态的建筑、道路与设施构成,其功能组织同样深受内部持续发生的要素流动 (Flows of People and Goods)所塑造。
进阶阅读
什么是空间流动网络?
空间流动网络 (Spatial Interaction
Networks) 可理解为一种具有明确地理位置的有向加权图。与抽象社交网络不同,这类网络中的节点对应真实空间单元,连边则表示实体空间中的位移行为或交互联系。其分析目的,在于识别不同地理单元之间的相互作用强度 (Spatial
Interaction),并据此理解城市的功能结构与联系格局。
距离衰减效应 (Distance
Decay)
这是空间流动网络的重要特征之一。在社会网络中,节点之间的联系可跨越较大的物理距离;而在城市空间中,物理距离通常与连接概率或流动强度呈负相关关系。由此可见,对城市流动的分析,本质上也是在理解空间约束如何塑造人类活动范围。
从“面”到“网”的分析视角转换
传统空间分析往往聚焦于单一区域的密度、总量或平均水平;而流动网络分析则进一步关注区域之间的联系强度与方向结构。这种视角有助于识别那些在传统地图上并不突出、却在网络中承担关键连接或分配作用的隐性枢纽。
推荐拓展资源
Spatial Networks (Marc
Barthélemy):
讨论距离、平面性等物理约束如何影响空间网络结构与演化。
🔗
访问作者个人主页与论文集 | 🔗 Nature
Reviews 综述文章
Information Visualisation for OD Data:
关注空间流动网络制图中的视觉过载、流线聚合与表达优化问题。
🔗 Flowmap.blue | 🔗 OD Maps:Jo Wood
的可视化研究
《地理流空间分析》 (Spatial
Analysis of Geographical Flow):
系统介绍地理流的特征提取、模式识别与动力学模拟,是较有代表性的中文参考著作。
🔗 科学出版社书籍详情 |
🔗
个人主页
回顾专题研究 2,本案例依托 NYC TLC 网约车出行数据,构建了一套从数据聚合到结构分析的空间网络分析流程。其核心内容可概括为以下三个方面:
空间网络的地理锚定与拓扑重构 (从空间到图):
分析首先将 Taxi Zone
多边形转化为对应的几何中心,从而将 200
余个空间单元表达为网络节点;随后,通过聚合 10%
抽样出行记录,构建了包含有向连边与流量权重的加权空间网络。这一过程实现了从原始出行记录到整体流动拓扑的转换。
多维枢纽地位的定量识别 (中心度测度计算与制图):
在节点层面,案例不仅利用节点强度刻画了不同区域的客流规模,还进一步引入加权
PageRank识别其在整体网络中的结构重要性。结果表明,机场相关区块与曼哈顿核心区域不仅具有较高流量,也在城市流动系统中占据更关键的枢纽位置。通过空间回投制图,这些抽象的网络测度被转化为可直接解读的地图表达。
空间不平等与流向失衡的识别 (不平等量化与制图):
案例进一步通过基尼系数与洛伦兹曲线刻画了流入与流出在空间上的集中程度,揭示出纽约市网约车流动存在较明显的中心—边缘分化。与此同时,借助失衡指数,还识别出不同 borough
在客流吸纳与输出上的方向性差异,从而补充了对城市流动功能分化的理解。
重要
相较于纯粹的拓扑网络,空间网络同时受到地理空间结构与数据采样机制的共同约束,因此其结果解释通常更具条件性。在解读城市网约车流动网络时,至少需要注意以下几方面局限:
行政区划边界效应与
MAUP:MAUP
本研究以 Taxi Zone 作为网络节点,这已是 TLC
开放数据中可直接获取的较细空间单元。然而,若进一步将这些节点向上聚合为更粗尺度的地理单元,例如 Community
Districts (社区委员会分区) 或
Boroughs (行政区),网络的中心性格局与枢纽识别结果可能发生明显变化。这种因空间单元划分与聚合方式不同而导致统计结果变化的现象,属于典型的可变面域单元问题 (MAUP)。
静态快照对潮汐过程的遮蔽:Urban Dynamics
本案例基于月度聚合数据构建静态加权网络。这种处理有助于识别总体流动格局,但也会压缩城市出行在时间维度上的显著差异。交通运行的重要特征之一在于其潮汐性与时段波动,因此,若希望进一步刻画城市运行节律,则需要引入更高时间分辨率的数据,并考虑时空网络或多层网络的分析框架。
单一出行模式的局限:Mode
Split
本案例仅使用网约车出行数据来刻画城市流动结构。然而,在纽约这样高度依赖公共交通的城市,不同区域的出行需求可能由地铁、公交或通勤铁路等多种方式共同承载。因此,某些区域在网约车网络中的中心性较低,并不必然意味着其城市活力或出行需求较弱,而可能只是反映了交通方式分担上的差异。
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)
提醒
专题章节中的
6.0部分通常不再提供完整的可复现模板。请先认真学习本章两个专题研究中的代码与分析流程,再完成下列课堂练习。
课堂练习
本部分不再提供现成代码。请结合本章已经学习的图与网络数据分析方法,自主完成一个小型专题研究。
本章已经涉及图对象实例化、基础拓扑属性、中心性测度、网络可视化以及群落发现等内容。本练习的目标,不再是单一步骤的复现,而是围绕一个明确问题,将这些方法组织为一套完整的网络分析流程。
请使用以下任一来源的数据完成一次自主分析:
你的研究对象既可以是社会网络,也可以是空间流动网络,但需要明确说明:
方向 A:社交或关系网络
例如:用户关注关系、合作网络、同学互动网络、社群转发网络等。重点可放在中心节点识别与群落结构分析。
方向 B:空间流动网络
例如:区域间通勤、客流、迁移或配送关系。重点可放在流动枢纽、方向性与空间不平等分析。
方向 C:知识或信息网络
例如:论文引用、关键词共现、课程概念关联等。重点可放在影响力测度与结构分层识别。
你也可以自行拟定题目,但应确保研究问题、网络定义与分析方法之间保持一致。
你的 R Markdown 报告至少应包含以下内容:
研究对象与问题界定
说明你使用的数据来源、研究对象,以及你希望回答的核心问题。
网络构建过程
展示如何从原始数据构建网络,包括节点表、连边表,及图对象的实例化过程。
基础拓扑描述
至少报告网络的基本规模特征,例如:
中心性分析
至少计算 2 种中心性指标,例如:
并解释:哪些节点最重要?不同指标的结果是否一致?
网络图输出
至少输出 1
张网络图,并说明图中节点大小、颜色或连边宽度分别映射了什么信息。
可选进阶分析(二选一)
Louvain
等方法识别网络内部的聚类结构;结果解读
结合你的表格与图形,用简短文字回答:
© 华东师范大学 社会发展学院 人口研究所 | DAWN 研究组 | yzliu@soci.ecnu.edu.cn
课程负责人:刘贇喆 本章作者:刘贇喆 | 周鹤洋
最后更新:2026年03月19日 构建环境:R version 4.5.2 (2025-10-31)