R语言入门学习教程,从入门到精通,R语言网络关系数据可视化(8)

R语言网络关系数据可视化

知识点总览

  • 节点链接图 (Node-Link Diagram):最经典的网络图,节点代表实体,边代表关系
  • 弧线图 (Arc Diagram):节点排列在直线上,用弧线表示连接
  • 蜂巢图 (Hive Plot):节点分布在径向轴上,边按规则连接
  • 和弦图 (Chord Diagram):圆形布局,用弦的宽度表示流量大小
  • 边绑定图 (Edge Bundling Diagram):将相似的边聚合成束,减少视觉杂乱

语法知识点

节点链接图是最经典的网络可视化方式。常用包:

  • networkD3: 基于D3.js的交互式网络图
  • igraph: 强大的网络分析包,支持多种布局算法
  • ggraph: ggplot2扩展,基于图形布局

核心概念

  • 节点 (Node): 网络中的实体,可设置大小、颜色
  • 边 (Edge): 节点间的连接关系,可设置粗细、颜色
  • 布局算法: 力导向布局、圆形布局、层次布局等

案例代码

1.1 使用igraph包创建静态网络图
r 复制代码
# 安装并加载igraph包
if(!require(igraph)) install.packages("igraph")
library(igraph)

# 创建示例数据:社交网络关系
# 方法1:通过边列表创建图
edges <- data.frame(
  from = c("张三", "张三", "李四", "李四", "王五", "赵六", "赵六", "钱七"),
  to = c("李四", "王五", "王五", "赵六", "赵六", "钱七", "张三", "李四"),
  weight = c(5, 3, 4, 2, 6, 3, 1, 4)  # 边的强度
)

# 创建图对象
g <- graph_from_data_frame(edges, directed = FALSE)  # 无向图

# 查看图的基本信息
print(g)  # 显示图的节点数和边数
summary(g)

# 添加节点属性
V(g)$name <- c("张三", "李四", "王五", "赵六", "钱七")  # 节点名称
V(g)$department <- c("销售部", "市场部", "技术部", "技术部", "销售部")  # 部门
V(g)$importance <- c(10, 8, 9, 7, 6)  # 重要性评分

# 设置节点颜色(根据部门)
V(g)$color <- ifelse(V(g)$department == "销售部", "lightblue",
                     ifelse(V(g)$department == "市场部", "lightgreen", "lightcoral"))

# 设置节点大小(根据重要性)
V(g)$size <- V(g)$importance * 3

# 设置边的宽度(根据权重)
E(g)$width <- E(g)$weight

# 选择布局算法
# 力导向布局(FR算法)适合大多数网络
layout_fr <- layout_with_fr(g)

# 绘制网络图
plot(g,
     layout = layout_fr,           # 布局算法
     vertex.label = V(g)$name,     # 节点标签
     vertex.label.cex = 1.2,       # 标签字体大小
     vertex.label.color = "black", # 标签颜色
     vertex.size = V(g)$size,      # 节点大小
     vertex.color = V(g)$color,    # 节点颜色
     vertex.frame.color = "gray",  # 节点边框颜色
     edge.width = E(g)$width,      # 边宽度
     edge.color = "gray50",        # 边颜色
     edge.curved = 0.2,            # 边的弯曲度
     main = "节点链接图:公司社交网络",
     cex.main = 1.5)

# 添加图例
legend("topright", 
       legend = c("销售部", "市场部", "技术部"),
       fill = c("lightblue", "lightgreen", "lightcoral"),
       title = "部门")

# 计算网络统计指标并标注
# 计算度中心性(连接数)
degree_centrality <- degree(g)
cat("度中心性:\n")
print(degree_centrality)

# 计算介数中心性(桥梁作用)
betweenness_centrality <- betweenness(g)
cat("\n介数中心性:\n")
print(betweenness_centrality)

# 在图上标注度中心性
plot(g,
     layout = layout_fr,
     vertex.label = paste(V(g)$name, "\ndeg:", degree_centrality),
     vertex.label.cex = 0.9,
     vertex.size = V(g)$size,
     vertex.color = V(g)$color,
     edge.width = E(g)$width,
     main = "节点链接图(带度中心性标注)")
1.2 使用networkD3创建交互式网络图
r 复制代码
# 安装并加载networkD3包
if(!require(networkD3)) install.packages("networkD3")
library(networkD3)

# 准备数据:网络D3要求特定的数据格式
# 边数据框:source和target使用从0开始的索引
links_d3 <- data.frame(
  source = c(0, 0, 1, 1, 2, 3, 3, 4),  # 起始节点索引
  target = c(1, 2, 2, 3, 3, 4, 0, 1),  # 目标节点索引
  value = c(5, 3, 4, 2, 6, 3, 1, 4)    # 连接强度
)

# 节点数据框:包含节点名称和分组信息
nodes_d3 <- data.frame(
  name = c("节点A", "节点B", "节点C", "节点D", "节点E"),
  group = c("group1", "group1", "group2", "group2", "group3"),
  size = c(20, 15, 25, 18, 12)  # 节点大小
)

# 创建力导向网络图
force_network <- forceNetwork(
  Links = links_d3,              # 边数据框
  Nodes = nodes_d3,              # 节点数据框
  Source = "source",             # 源节点列名
  Target = "target",             # 目标节点列名
  Value = "value",               # 边权重列名
  NodeID = "name",               # 节点标签列名
  Group = "group",               # 节点分组列名(用于颜色)
  Nodesize = "size",             # 节点大小列名
  # 视觉设置
  radius = 10,                   # 节点半径
  opacity = 0.8,                 # 不透明度
  zoom = TRUE,                   # 允许缩放
  legend = TRUE,                 # 显示图例
  bounded = TRUE,                # 限制在框内
  # 交互设置
  fontSize = 14,                 # 字体大小
  fontFamily = "Arial",          # 字体
  linkColour = "#666",           # 边的颜色
  colourScale = JS('d3.scaleOrdinal(d3.schemeCategory10);')  # 颜色方案
)

# 显示交互式网络图
force_network

# 使用igraph创建图并转换为networkD3格式
if(!require(igraph)) install.packages("igraph")
library(igraph)

# 创建Zachary空手道俱乐部网络(经典社会网络数据集)
karate <- make_graph("Zachary")

# 计算社区结构
wc <- cluster_walktrap(karate)
members <- membership(wc)

# 转换为networkD3格式
karate_d3 <- igraph_to_networkD3(karate, group = members)

# 绘制力导向网络图
forceNetwork(
  Links = karate_d3$links,
  Nodes = karate_d3$nodes,
  Source = 'source',
  Target = 'target',
  NodeID = 'name',
  Group = 'group',
  opacity = 0.9,
  zoom = TRUE,
  legend = TRUE,
  fontSize = 12,
  linkColour = "#aaa",
  colourScale = JS('d3.scaleOrdinal(d3.schemeSet3);')
)

# 使用rD3plot包创建增强型网络图
if(!require(rD3plot)) install.packages("rD3plot")
library(rD3plot)

# 使用内置数据
data("crannetworkdata")
links <- crannetworkdata$links
nodes <- crannetworkdata$nodes

# 创建自定义网络
net <- network_rd3(
  links = links,
  nodes = nodes,
  lcolor = "Type",        # 边颜色映射
  size = "downloadsyear", # 节点大小映射
  info = "info",          # 节点信息
  color = "downloads",    # 节点颜色映射
  dir = "CRANnetwork"
)

# 绘制网络图
plot(net)

2. 弧线图 (Arc Diagram)

语法知识点

弧线图将节点排列在一条直线上,用弧线(半圆)表示节点间的连接。特点:

  • 适合展示线性顺序中的关系(如时间序列、基因组数据)
  • 可以有效避免视觉杂乱
  • 弧线的高度可编码边的权重

常用包

  • ggraph + geom_edge_arc
  • igraph + 自定义布局

案例代码

r 复制代码
# 安装并加载必要的包
if(!require(ggraph)) install.packages("ggraph")
if(!require(igraph)) install.packages("igraph")
if(!require(tidygraph)) install.packages("tidygraph")
library(ggraph)
library(igraph)
library(tidygraph)

# 创建示例数据:时间序列上的事件关联
# 创建节点:按时间顺序排列的事件
events <- data.frame(
  id = 1:10,
  name = c("事件A", "事件B", "事件C", "事件D", "事件E",
           "事件F", "事件G", "事件H", "事件I", "事件J"),
  time = c(1, 3, 5, 7, 9, 11, 13, 15, 17, 19),  # 时间顺序
  importance = c(5, 3, 8, 4, 6, 7, 5, 9, 4, 6)
)

# 创建边:事件之间的关联
edges_arc <- data.frame(
  from = c(1, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9),
  to = c(3, 5, 4, 5, 7, 6, 8, 9, 10, 9, 10, 10),
  weight = c(2, 1, 3, 2, 4, 1, 2, 3, 2, 1, 2, 3)
)

# 创建图对象
g_arc <- graph_from_data_frame(edges_arc, vertices = events, directed = FALSE)

# 方法1:使用ggraph绘制弧线图
# 创建线性布局(按时间顺序排列)
linear_layout <- create_layout(g_arc, layout = "linear", 
                                sort.by = time)  # 按时间排序

# 绘制弧线图
ggraph(linear_layout) +
  # 绘制弧线边
  geom_edge_arc(aes(alpha = weight, width = weight),  # 弧线透明度/宽度映射权重
                strength = 0.5,                        # 弧的弯曲程度
                colour = "steelblue") +
  # 绘制节点
  geom_node_point(aes(size = importance),             # 节点大小映射重要性
                  colour = "darkred", alpha = 0.8) +
  # 添加节点标签
  geom_node_text(aes(label = name), 
                 repel = TRUE,                        # 避免标签重叠
                 size = 4, vjust = -1) +
  # 主题设置
  theme_void() +
  labs(title = "弧线图:事件时间序列关联",
       subtitle = "节点按时间顺序排列,弧线表示事件间的关联") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        plot.subtitle = element_text(hjust = 0.5))

# 方法2:手动创建弧线图(使用ggplot2绘制基础弧线)
if(!require(ggplot2)) install.packages("ggplot2")
library(ggplot2)

# 准备节点位置:x坐标为时间顺序,y坐标均为0
node_positions <- data.frame(
  id = events$id,
  name = events$name,
  x = events$time,
  y = 0
)

# 准备弧线数据:计算每条弧线的控制点
# 弧线方程为半圆:y = sqrt(r^2 - (x - center)^2)
arc_data <- data.frame()

for(i in 1:nrow(edges_arc)) {
  from_id <- edges_arc$from[i]
  to_id <- edges_arc$to[i]
  weight <- edges_arc$weight[i]
  
  # 获取起止点的x坐标
  x1 <- node_positions$x[node_positions$id == from_id]
  x2 <- node_positions$x[node_positions$id == to_id]
  
  # 计算弧的参数
  center_x <- (x1 + x2) / 2          # 圆心x坐标
  radius <- abs(x2 - x1) / 2         # 半径
  max_height <- radius * (weight / max(edges_arc$weight))  # 弧高与权重成正比
  
  # 生成弧线上的点
  t <- seq(0, pi, length.out = 50)   # 角度序列(半圆)
  arc_x <- center_x + radius * cos(t)
  arc_y <- max_height * sin(t)
  
  arc_data <- rbind(arc_data, 
                    data.frame(x = arc_x, y = arc_y, 
                               weight = weight,
                               edge_id = i))
}

# 绘制手动弧线图
ggplot() +
  # 绘制弧线
  geom_path(data = arc_data, 
            aes(x = x, y = y, group = edge_id, alpha = weight),
            colour = "steelblue", size = 1) +
  # 绘制节点
  geom_point(data = node_positions, 
             aes(x = x, y = y, size = events$importance),
             colour = "darkred", alpha = 0.8) +
  # 节点标签
  geom_text(data = node_positions,
            aes(x = x, y = y - 0.5, label = name),
            size = 4, vjust = 1) +
  # 主题
  theme_minimal() +
  labs(title = "弧线图(手动版)",
       x = "时间", y = "弧线高度") +
  theme(axis.text.y = element_blank(),
        panel.grid.minor = element_blank())

3. 蜂巢图 (Hive Plot)

语法知识点

蜂巢图将节点分布在几条径向轴上,边按照规则连接。特点:

  • 节点按属性分配到不同轴
  • 轴上的位置反映节点的某种度量
  • 有效避免视觉杂乱,适合大型网络

常用包

  • HiveR: 专门绘制蜂巢图的包
  • ggforce: 提供径向布局支持

核心概念

  • 轴 (Axis): 每条轴代表一类节点
  • 节点位置: 沿轴的距离反映节点属性值
  • : 用曲线连接不同轴或同轴上的节点

案例代码

r 复制代码
# 安装并加载HiveR包
if(!require(HiveR)) install.packages("HiveR")
library(HiveR)

# 创建示例数据:组织通信网络
# 节点按部门分配到3条轴:销售部、市场部、技术部
set.seed(456)

# 创建节点数据
nodes_hive <- data.frame(
  id = 1:30,
  name = paste0("员工", 1:30),
  department = sample(c("销售部", "市场部", "技术部"), 30, replace = TRUE,
                      prob = c(0.3, 0.3, 0.4)),
  # 在轴上的位置(基于工作年限或绩效)
  position = runif(30, 0, 100)
)

# 创建边数据(通信关系)
edges_hive <- data.frame()
for(i in 1:50) {
  from <- sample(1:30, 1)
  to <- sample(1:30, 1)
  if(from != to) {
    edges_hive <- rbind(edges_hive, 
                        data.frame(from = from, to = to,
                                   weight = runif(1, 1, 10)))
  }
}
edges_hive <- unique(edges_hive)  # 去重

# 准备HiveR所需的数据格式
# 为每个节点分配轴编号
nodes_hive$axis <- ifelse(nodes_hive$department == "销售部", 1,
                          ifelse(nodes_hive$department == "市场部", 2, 3))

# 创建蜂巢图数据对象
hive_data <- list()
hive_data$nodes <- data.frame(
  id = nodes_hive$id,
  axis = nodes_hive$axis,
  radius = nodes_hive$position / 100,  # 归一化到[0,1]
  size = 1,
  color = ifelse(nodes_hive$axis == 1, "#2E86AB",
                 ifelse(nodes_hive$axis == 2, "#A23B72", "#F18F01"))
)

# 创建边数据
hive_data$edges <- data.frame(
  id1 = edges_hive$from,
  id2 = edges_hive$to,
  weight = edges_hive$weight / max(edges_hive$weight)  # 归一化权重
)

# 绘制蜂巢图
# 注意:HiveR包的绘图函数需要特定的数据对象结构
# 这里展示使用HiveR包的标准方法

# 方法2:使用ggplot2手动创建蜂巢图
if(!require(ggplot2)) install.packages("ggplot2")
library(ggplot2)

# 定义三条轴的角度
axis_angles <- c(0, 120, 240) * pi / 180  # 转换为弧度

# 计算每个节点的笛卡尔坐标
nodes_hive$x <- nodes_hive$position * cos(axis_angles[nodes_hive$axis])
nodes_hive$y <- nodes_hive$position * sin(axis_angles[nodes_hive$axis])

# 计算边的曲线控制点
edge_curves <- data.frame()

for(i in 1:nrow(edges_hive)) {
  from_node <- nodes_hive[nodes_hive$id == edges_hive$from[i], ]
  to_node <- nodes_hive[nodes_hive$id == edges_hive$to[i], ]
  weight <- edges_hive$weight[i]
  
  # 使用贝塞尔曲线连接两点
  # 控制点取两点连线的中垂线方向
  mid_x <- (from_node$x + to_node$x) / 2
  mid_y <- (from_node$y + to_node$y) / 2
  
  # 垂直方向偏移(与边权重成正比)
  perp_x <- -(to_node$y - from_node$y)
  perp_y <- to_node$x - from_node$x
  norm <- sqrt(perp_x^2 + perp_y^2)
  
  if(norm > 0) {
    perp_x <- perp_x / norm
    perp_y <- perp_y / norm
  }
  
  ctrl_x <- mid_x + perp_x * weight * 20
  ctrl_y <- mid_y + perp_y * weight * 20
  
  # 生成贝塞尔曲线上的点
  t <- seq(0, 1, length.out = 30)
  curve_x <- (1-t)^2 * from_node$x + 2*(1-t)*t * ctrl_x + t^2 * to_node$x
  curve_y <- (1-t)^2 * from_node$y + 2*(1-t)*t * ctrl_y + t^2 * to_node$y
  
  edge_curves <- rbind(edge_curves,
                       data.frame(x = curve_x, y = curve_y,
                                  weight = weight, edge_id = i))
}

# 绘制蜂巢图
ggplot() +
  # 绘制边曲线
  geom_path(data = edge_curves,
            aes(x = x, y = y, group = edge_id, alpha = weight),
            colour = "gray50", size = 0.5) +
  # 绘制轴(虚线)
  geom_abline(data = data.frame(slope = tan(axis_angles), axis = 1:3),
              aes(slope = slope, intercept = 0),
              linetype = "dashed", colour = "gray70") +
  # 绘制节点
  geom_point(data = nodes_hive,
             aes(x = x, y = y, color = department, size = position),
             alpha = 0.8) +
  # 节点标签
  geom_text_repel(data = nodes_hive,
                  aes(x = x, y = y, label = name),
                  size = 3) +
  # 颜色方案
  scale_color_manual(values = c("销售部" = "#2E86AB",
                                "市场部" = "#A23B72",
                                "技术部" = "#F18F01")) +
  # 主题
  theme_void() +
  coord_equal() +
  labs(title = "蜂巢图:部门通信网络",
       subtitle = "节点分布在三条轴上,位置反映工作年限,曲线表示通信频率",
       color = "部门", size = "轴位置(工作年限)") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        legend.position = "bottom")

4. 和弦图 (Chord Diagram)

语法知识点

和弦图以圆形布局展示节点间的流动关系。特点:

  • 节点沿圆周排列
  • 弦的宽度表示流量大小
  • 适合展示转移矩阵、贸易流等

常用包

  • circlize: 功能最全面的和弦图包
  • chorddiag: 基于D3.js的交互式弦图
  • edgebundleR: 支持边绑定的圆形图

案例代码

r 复制代码
# 安装并加载circlize包
if(!require(circlize)) install.packages("circlize")
library(circlize)

# 创建示例数据:国际贸易流量
# 定义国家/地区
regions <- c("中国", "美国", "日本", "德国", "英国", "法国", "韩国", "澳大利亚")

# 创建贸易流量矩阵(随机生成)
set.seed(789)
trade_matrix <- matrix(runif(64, 0, 100), nrow = 8, ncol = 8)
diag(trade_matrix) <- 0  # 对角线设为0(无自贸易)

# 设置行列名
rownames(trade_matrix) <- regions
colnames(trade_matrix) <- regions

# 使矩阵不对称(出口≠进口)
trade_matrix <- round(trade_matrix, 1)

# 查看矩阵
print("贸易流量矩阵(百万美元):")
print(trade_matrix)

# 方法1:使用circlize包绘制和弦图
# 设置颜色
region_colors <- c("#2E86AB", "#A23B72", "#F18F01", "#C73E1D",
                   "#6A994E", "#BC4A3C", "#8B8C89", "#3A6EA5")

# 初始化圆形布局
circos.clear()
circos.par(start.degree = 90, gap.degree = 5)  # 起始角度和间隙

# 创建和弦图
chordDiagram(
  trade_matrix,
  # 基本设置
  grid.col = setNames(region_colors, regions),  # 每个扇区的颜色
  transparency = 0.3,                            # 透明度
  # 方向设置
  directional = 1,                               # 有向图(1表示有方向)
  direction.type = c("diffHeight", "arrows"),    # 箭头表示方向
  diffHeight = 0.04,                             # 箭头高度差异
  # 标签设置
  annotationTrack = c("name", "grid"),           # 显示名称和网格
  annotationTrackHeight = c(0.03, 0.05),         # 轨道高度
  # 链接设置
  link.sort = TRUE,                              # 排序链接
  link.decreasing = TRUE,                        # 降序排列
  # 缩放
  scale = TRUE
)

# 添加标题
title("和弦图:国际贸易流量(百万美元)")

# 添加图例
legend("topright", 
       legend = regions,
       fill = region_colors,
       title = "国家/地区",
       cex = 0.8)

# 方法2:使用chorddiag包创建交互式弦图
if(!require(chorddiag)) install.packages("chorddiag")
library(chorddiag)

# 创建更丰富的贸易数据
# 不同商品类别的贸易
sectors <- c("电子产品", "汽车", "农产品", "纺织品", "机械设备", "化学品")

# 创建转移矩阵(行业间流动)
flow_matrix <- matrix(c(
  0, 120, 45, 30, 80, 25,    # 电子产品 -> 各行业
  80, 0, 35, 25, 110, 30,    # 汽车 -> 各行业
  40, 25, 0, 55, 30, 15,     # 农产品 -> 各行业
  25, 20, 45, 0, 35, 20,     # 纺织品 -> 各行业
  90, 100, 40, 35, 0, 45,    # 机械设备 -> 各行业
  30, 25, 20, 15, 40, 0      # 化学品 -> 各行业
), nrow = 6, byrow = TRUE)

rownames(flow_matrix) <- sectors
colnames(flow_matrix) <- sectors

# 创建交互式弦图
chorddiag(
  flow_matrix,
  # 类型设置
  type = "bipartite",        # 二分图类型
  # 视觉设置
  groupnameFontsize = 14,    # 组名大小
  groupPadding = 5,          # 组间距
  margin = 90,               # 边距
  # 颜色
  showTicks = FALSE,         # 不显示刻度
  # 交互设置
  tooltipGroupConnector = " → "  # 悬停提示格式
)

# 方法3:使用edgebundleR创建圆形边绑定图
if(!require(edgebundleR)) install.packages("edgebundleR")
if(!require(igraph)) install.packages("igraph")
library(edgebundleR)
library(igraph)

# 创建小世界网络示例
ws_graph <- watts.strogatz.game(1, 50, 4, 0.05)

# 绘制圆形边绑定图
edgebundle(
  ws_graph,
  tension = 0.5,      # 边张力(0-1之间)
  fontsize = 12,      # 字体大小
  padding = 100,      # 内边距
  nodesize = c(5, 20) # 节点大小范围
)

# 方法4:使用ggplot2手动创建和弦图
# 准备节点位置(圆周上的点)
angles <- seq(0, 2*pi - 2*pi/8, length.out = 8)
node_pos_circle <- data.frame(
  name = regions,
  x = cos(angles),
  y = sin(angles),
  color = region_colors
)

# 准备弦数据
chord_data <- data.frame()

for(i in 1:nrow(trade_matrix)) {
  for(j in 1:ncol(trade_matrix)) {
    if(trade_matrix[i,j] > 0) {
      # 起点和终点的角度
      start_angle <- angles[i]
      end_angle <- angles[j]
      
      # 计算弦的控制点
      # 使用贝塞尔曲线绘制弦
      t <- seq(0, 1, length.out = 100)
      
      # 弦的曲线参数
      # 起点和终点在圆周上
      start_x <- cos(start_angle)
      start_y <- sin(start_angle)
      end_x <- cos(end_angle)
      end_y <- sin(end_angle)
      
      # 控制点:沿圆周方向偏移
      # 使用三次贝塞尔曲线
      ctrl1_x <- cos(start_angle + (end_angle - start_angle) * 0.3) * 1.2
      ctrl1_y <- sin(start_angle + (end_angle - start_angle) * 0.3) * 1.2
      ctrl2_x <- cos(start_angle + (end_angle - start_angle) * 0.7) * 1.2
      ctrl2_y <- sin(start_angle + (end_angle - start_angle) * 0.7) * 1.2
      
      # 贝塞尔曲线公式
      curve_x <- (1-t)^3 * start_x + 3*(1-t)^2*t * ctrl1_x + 
                 3*(1-t)*t^2 * ctrl2_x + t^3 * end_x
      curve_y <- (1-t)^3 * start_y + 3*(1-t)^2*t * ctrl1_y + 
                 3*(1-t)*t^2 * ctrl2_y + t^3 * end_y
      
      chord_data <- rbind(chord_data,
                          data.frame(x = curve_x, y = curve_y,
                                     value = trade_matrix[i,j],
                                     from = regions[i], to = regions[j]))
    }
  }
}

# 绘制基础和弦图
ggplot() +
  # 绘制弦
  geom_path(data = chord_data,
            aes(x = x, y = y, group = interaction(from, to),
                color = value, alpha = value),
            size = 0.8) +
  # 绘制圆周上的节点
  geom_point(data = node_pos_circle,
             aes(x = x, y = y, fill = name),
             size = 10, shape = 21, color = "white") +
  # 节点标签
  geom_text(data = node_pos_circle,
            aes(x = x * 1.2, y = y * 1.2, label = name),
            size = 4) +
  # 颜色映射
  scale_color_gradient(low = "lightblue", high = "darkred", name = "贸易额") +
  scale_alpha_continuous(range = c(0.3, 0.8), guide = "none") +
  scale_fill_manual(values = setNames(region_colors, regions), guide = "none") +
  # 主题
  theme_void() +
  coord_equal() +
  labs(title = "和弦图(手动版):国际贸易流量",
       color = "贸易额\n(百万美元)") +
  theme(plot.title = element_text(hjust = 0.5, face = "bold"),
        legend.position = "right")

5. 边绑定图 (Edge Bundling Diagram)

语法知识点

边绑定图通过将相似的边聚合成束来减少视觉杂乱。特点:

  • 基于层次结构引导边的走向
  • 有效减少大型网络中的"毛球"效应
  • 突出显示整体模式而非单个连接

常用包

  • ggraph::geom_conn_bundle: 层次边绑定
  • edgebundleR: 圆形布局边绑定
  • geom_edge_bundle (ggraph扩展)

核心概念

  • 层次结构: 边的绑定基于隐含的树状结构
  • 张力参数: 控制边的弯曲程度和绑定强度
  • 捆绑路径: 多条边沿着相似的路径走向

案例代码

r 复制代码
# 安装并加载必要的包
if(!require(ggraph)) install.packages("ggraph")
if(!require(tidygraph)) install.packages("tidygraph")
if(!require(igraph)) install.packages("igraph")
library(ggraph)
library(tidygraph)
library(igraph)

# 案例:软件包依赖关系网络
# 使用flare数据集(类层次结构和导入关系)
# 创建示例层次数据:文件系统的目录结构

# 创建节点:目录和文件
dir_nodes <- data.frame(
  id = 1:20,
  name = c("root", "src", "docs", "tests", "R", "man", "vignettes",
           "utils.R", "stats.R", "plot.R", "intro.Rmd", "advanced.Rmd",
           "test1.R", "test2.R", "test_utils.R", "README.md", "LICENSE",
           "DESCRIPTION", "NAMESPACE", "NEWS.md"),
  type = c("dir", "dir", "dir", "dir", "dir", "dir", "dir",
           rep("file", 13)),
  level = c(0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1)
)

# 创建层次边(目录结构)
dir_edges <- data.frame(
  from = c(1, 1, 1, 2, 2, 2, 3, 4, 5, 6,  # 目录结构
           2, 2, 2, 4, 4, 5, 5),          # 文件位置
  to = c(2, 3, 4, 5, 6, 7, 16, 17, 8, 9,
         10, 11, 12, 13, 14, 15, 18),
  type = c(rep("hierarchy", 10), rep("import", 7))
)

# 创建导入关系(用于边绑定)
import_edges <- data.frame(
  from = c(8, 9, 10, 11, 12, 13, 14, 15, 18, 19),
  to = c(9, 10, 8, 12, 11, 14, 15, 13, 20, 18),
  weight = runif(10, 0.5, 2)
)

# 创建完整的图对象
graph <- tbl_graph(
  nodes = dir_nodes,
  edges = rbind(dir_edges[, c("from", "to")], import_edges[, c("from", "to")]),
  directed = TRUE
)

# 方法1:使用ggraph的层次边绑定
# 创建树状布局(基于层次结构)
ggraph(graph, layout = 'dendrogram', circular = TRUE) +
  # 绘制绑定的边(使用导入关系)
  geom_conn_bundle(
    data = get_con(import_edges$from, import_edges$to),  # 获取连接数据
    aes(colour = after_stat(index)),                     # 颜色映射
    edge_alpha = 0.3,                                    # 边透明度
    tension = 0.8,                                       # 张力(绑定强度)
    width = 1                                            # 边宽度
  ) +
  # 绘制节点
  geom_node_point(
    aes(filter = type == "file", colour = level),       # 只显示文件节点
    size = 3
  ) +
  # 添加节点标签
  geom_node_text(
    aes(filter = type == "file", label = name, 
        angle = node_angle(x, y)),                      # 标签角度适应圆形布局
    size = 3, hjust = "outside"
  ) +
  # 颜色方案
  scale_edge_colour_distiller('方向', direction = 1, guide = 'edge_direction') +
  scale_colour_viridis_c(option = "plasma") +
  # 主题
  coord_fixed() +
  theme_void() +
  labs(title = "边绑定图:软件包依赖关系",
       subtitle = "边绑定减少视觉杂乱,突出依赖模式")

# 方法2:使用edgebundleR创建圆形边绑定图
if(!require(edgebundleR)) install.packages("edgebundleR")
library(edgebundleR)

# 创建相关矩阵作为输入
# 生成一个对称的相关矩阵
set.seed(123)
cor_matrix <- matrix(runif(400, -0.5, 0.8), nrow = 20)
cor_matrix <- (cor_matrix + t(cor_matrix)) / 2  # 对称化
diag(cor_matrix) <- 1

# 设置行名列名
var_names <- paste0("Var", 1:20)
rownames(cor_matrix) <- var_names
colnames(cor_matrix) <- var_names

# 只保留显著相关的连接(|r| > 0.3)
cor_matrix[abs(cor_matrix) < 0.3] <- 0

# 使用edgebundle绘制
edgebundle(
  cor_matrix,
  tension = 0.3,          # 张力(值越小绑定越紧)
  cutoff = 0.3,           # 连接阈值
  fontsize = 12,          # 字体大小
  padding = 80,           # 内边距
  nodesize = c(3, 15)     # 节点大小范围
)

# 方法3:使用igraph创建层次边绑定的基础结构
if(!require(igraph)) install.packages("igraph")
library(igraph)

# 创建更复杂的网络:学术引用网络
# 生成论文节点(按主题聚类)
n_papers <- 40
topics <- sample(c("ML", "Stats", "Bioinfo", "Physics"), n_papers, replace = TRUE)

# 创建引用关系(同一主题内引用更多)
citation_matrix <- matrix(0, n_papers, n_papers)
for(i in 1:n_papers) {
  for(j in 1:n_papers) {
    if(i != j) {
      # 同主题引用概率高,不同主题概率低
      if(topics[i] == topics[j]) {
        citation_matrix[i,j] <- rbinom(1, 1, 0.3)
      } else {
        citation_matrix[i,j] <- rbinom(1, 1, 0.05)
      }
    }
  }
}

# 创建图
citation_graph <- graph_from_adjacency_matrix(citation_matrix, mode = "directed")

# 添加节点属性
V(citation_graph)$topic <- topics
V(citation_graph)$size <- runif(n_papers, 5, 15)

# 计算社区结构(作为层次绑定的基础)
communities <- cluster_louvain(citation_graph)
V(citation_graph)$community <- membership(communities)

# 使用Kamada-Kawai布局(力导向布局)
layout_kk <- layout_with_kk(citation_graph)

# 绘制基础网络(无边绑定)
par(mfrow = c(1, 2), mar = c(1, 1, 3, 1))

plot(citation_graph,
     layout = layout_kk,
     vertex.color = as.numeric(factor(topics)),
     vertex.size = V(citation_graph)$size / 2,
     vertex.label = NA,
     edge.arrow.size = 0.2,
     edge.color = rgb(0.5, 0.5, 0.5, 0.3),
     main = "标准网络图(杂乱)")

# 简化网络:按社区分组后绘制边绑定效果
# 计算社区间的关系强度
community_edges <- matrix(0, max(communities), max(communities))
for(e in 1:ecount(citation_graph)) {
  from_comm <- V(citation_graph)$community[head_of(citation_graph, e)]
  to_comm <- V(citation_graph)$community[tail_of(citation_graph, e)]
  community_edges[from_comm, to_comm] <- community_edges[from_comm, to_comm] + 1
}

# 绘制简化后的社区级网络
community_graph <- graph_from_adjacency_matrix(community_edges, weighted = TRUE)
plot(community_graph,
     edge.width = E(community_graph)$weight / 2,
     vertex.size = 20,
     vertex.label = 1:max(communities),
     main = "边绑定简化效果\n(社区级聚合)")

# 添加图例
legend("topright", 
       legend = unique(topics),
       fill = 1:4,
       title = "主题")

par(mfrow = c(1, 1))

# 方法4:使用geom_edge_bundle_path(ggraph的路径边绑定)
# 创建具有层次结构的网络
hierarchy_nodes <- data.frame(
  id = 1:15,
  name = c("Root", "A", "B", "C", "A1", "A2", "B1", "B2", "C1", "C2",
           "A1a", "A1b", "B1a", "B1b", "C1a"),
  level = c(0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3)
)

# 层次边(树结构)
hierarchy_edges <- data.frame(
  from = c(1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7),
  to = c(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 10)
)

# 额外连接(用于边绑定)
extra_edges <- data.frame(
  from = c(11, 12, 13, 14, 15, 11, 12, 8),
  to = c(13, 14, 15, 11, 12, 15, 13, 15)
)

# 合并所有边
all_edges <- rbind(
  cbind(hierarchy_edges, type = "hierarchy"),
  cbind(extra_edges, type = "bundle")
)

# 创建图对象
bundle_graph <- tbl_graph(
  nodes = hierarchy_nodes,
  edges = all_edges
)

# 绘制带路径边绑定的网络
ggraph(bundle_graph, layout = "dendrogram", circular = FALSE) +
  # 层次边(灰色背景)
  geom_edge_link(aes(filter = type == "hierarchy"),
                 colour = "gray80", width = 0.5) +
  # 绑定边(彩色,沿层次路径弯曲)
  geom_edge_bundle_path(
    aes(filter = type == "bundle", colour = after_stat(index)),
    tension = 0.6,                          # 张力
    edge_width = 1,
    edge_alpha = 0.7
  ) +
  # 节点
  geom_node_point(aes(size = level), colour = "steelblue", alpha = 0.8) +
  geom_node_text(aes(label = name), size = 3, vjust = -1, hjust = 0) +
  # 颜色
  scale_edge_colour_viridis_c(option = "inferno", guide = "none") +
  # 主题
  theme_graph() +
  labs(title = "边绑定图:层次结构中的额外连接",
       subtitle = "额外连接沿层次路径弯曲,形成自然束")

本章小结

图表类型 主要用途 关键函数/包 适用场景
节点链接图 通用网络可视化 igraph::plot, networkD3::forceNetwork 社交网络、知识图谱
弧线图 线性顺序中的关系 ggraph::geom_edge_arc 时间序列关联、基因组数据
蜂巢图 避免视觉杂乱的网络 HiveR 大型网络、多属性节点
和弦图 流动/转移关系 circlize::chordDiagram 贸易流、迁移数据
边绑定图 减少"毛球"效应 ggraph::geom_conn_bundle 大型层次网络、依赖关系

选择建议

  • 节点链接图:最直观,适合中小型网络(<100节点)
  • 弧线图:适合有时间或线性顺序的网络
  • 蜂巢图:当节点有明确分类属性时使用,可处理较大网络
  • 和弦图:适合展示流量/转移矩阵,强调节点间的流动量
  • 边绑定图:适合大型层次网络,通过绑定相似边减少杂乱

性能考虑

  • 对于大型网络(>1000节点),推荐使用networkD3edgebundleR的交互式版本
  • 静态绘图建议使用ggraph,可与ggplot2语法无缝集成
  • 边绑定图的计算量较大,大型网络可能需要调整tension参数以获得合理的渲染时间

所有代码均可直接复制到R环境中运行(需联网安装缺失包)。建议根据实际数据规模选择合适的图表类型和包。

相关推荐
xieliyu.1 小时前
Java手搓数据结构:栈与队列模拟实现
java·数据结构·学习
zhangrelay1 小时前
ROS Kinetic-信号与系统-趣味案例
linux·笔记·学习·ubuntu
财经资讯数据_灵砚智能2 小时前
基于全球经济类多源新闻的NLP情感分析与数据可视化(日间)2026年5月2日
人工智能·python·信息可视化·自然语言处理·ai编程
承渊政道2 小时前
【动态规划算法】(回文串问题解题框架与经典案例)
数据结构·c++·学习·算法·leetcode·动态规划·哈希算法
HERR_QQ2 小时前
端到端课程自用 5 规划 基于Difussion 的端到端planner AI 笔记
人工智能·笔记·学习·自动驾驶
lilihuigz10 小时前
Tutor LMS 4.0 Beta版全新上线:以学习者为中心的移动优先学习体验
学习·在线教育·lms
kuinnebula13 小时前
RTSP学习
学习
北顾笙98015 小时前
LLM学习-day04
学习
lzj_pxxw16 小时前
W25Q64存储芯片 软件设计刚需常识
stm32·单片机·嵌入式硬件·mcu·学习