弧形图

作者

弧形图是按弧形连接的图,显示了节点之间的相互关系。

示例

ArcDiagram DEMO

如图就是一个简单的弧形图,节点在图下方整齐排布,节点间通过弧形连接,构成了弧形图的主体。

环境配置

  • 系统要求: 跨平台(Linux/MacOS/Windows)

  • 编程语言:R

  • 依赖包:ggraph, tidyverse, viridis, patchwork, igraph, colormap

# 安装包
if (!requireNamespace("tidyverse", quietly = TRUE)) {
  install.packages("tidyverse")
}
if (!requireNamespace("viridis", quietly = TRUE)) {
  install.packages("viridis")
}
if (!requireNamespace("patchwork", quietly = TRUE)) {
  install.packages("patchwork")
}
if (!requireNamespace("igraph", quietly = TRUE)) {
  install.packages("igraph")
}
if (!requireNamespace("ggraph", quietly = TRUE)) {
  install.packages("ggraph")
}
if (!requireNamespace("colormap", quietly = TRUE)) {
  install.packages("colormap")
}

# 加载包
library(tidyverse) 
library(viridis)   
library(patchwork) 
library(igraph)    
library(ggraph)    
library(colormap)  

数据准备

数据包括自定义的数据、研究人员的合著网络、Cytoscape软件导出的PPI网络数据。

# 1.自定义数据
# links存储边信息,nodes存储点和点的分组信息
links <- data.frame(
source = c("A", "A", "A", "A", "B", "G", "G", "G", "G"),
  target = c("B", "C", "D", "F", "E", "H", "I", "J", "F")
)
nodes <- data.frame(
  point = c("A", "B", "C", "D", "E", "F", "G", "H", "I", "J"),
  groups = c(
    "group-one", "group-one", "group-one", "group-one", "group-one",
    "group-one", "group-two", "group-two", "group-two", "group-two"
  )
)

head(links)
  source target
1      A      B
2      A      C
3      A      D
4      A      F
5      B      E
6      G      H
head(nodes)
  point    groups
1     A group-one
2     B group-one
3     C group-one
4     D group-one
5     E group-one
6     F group-one
# 2.研究人员合著网络
# 将链接信息复制到txt文件,读取进行绘图
data_dif <- read.csv("https://bizard-1301043367.cos.ap-guangzhou.myqcloud.com/Arc.txt", header = T, sep = " ")

head(data_dif[,1:5])
          from A.Bateman A.Besnard A.Breil A.Cenci
1     A Armero        NA        NA       1      NA
2    A Bateman        NA        NA      NA      NA
3    A Besnard        NA        NA      NA      NA
4      A Breil        NA        NA      NA      NA
5      A Cenci        NA        NA      NA      NA
6 A Chifolleau        NA        NA      NA      NA
# 3.PPI网络的节点和边数据
# 读取从github下载的PPI网络信息
data_ppi <- read.csv("https://bizard-1301043367.cos.ap-guangzhou.myqcloud.com/string_interactions_short.tsv_1%20default%20edge.csv", header = TRUE)

head(data_ppi[,c(9,3)])
                           name combined_score
1   ABCB4 (interacts with) CFTR          0.458
2 ABCB4 (interacts with) CREBBP          0.440
3  ABCB4 (interacts with) ABCC8          0.410
4   ABCB5 (interacts with) CFTR          0.415
5  ABCB5 (interacts with) ABCC8          0.436
6  ABCB5 (interacts with) PROM1          0.786

可视化

1. 基本弧形图

# 基本弧形图
mygraph <- graph_from_data_frame(links, vertices = nodes) # 生成图结构

p <- ggraph(mygraph, layout = "linear") +
  geom_edge_arc(edge_colour = "black", edge_alpha = 0.3, edge_width = 0.4) +
  geom_node_point(color = "grey", size = 5) +
  geom_node_text(aes(label = name), repel = FALSE, size = 6, nudge_y = -0.15) +
  theme_void() +
  theme(
    legend.position = "none",
    plot.margin = unit(rep(2, 4), "cm")
  )

p

基本弧形图

如图是基本弧形图的绘制,节点和边少,所以弧形图结构简单。

2. 将分组信息映射到节点颜色上

# 分组映射到节点颜色上
mygraph <- graph_from_data_frame(links, vertices = nodes) # 生成图
p <- ggraph(mygraph, layout = "linear") +
  geom_edge_arc(edge_colour = "black", edge_alpha = 0.3, edge_width = 0.4) +
  geom_node_point(aes(color = groups), size = 5) +
  geom_node_text(aes(label = name), repel = FALSE, size = 6, nudge_y = -0.15) +
  theme_void() +
  theme(
    legend.position = "none",
    plot.margin = unit(rep(2, 4), "cm")
  )
p

将分组信息映射到节点颜色上

如图中color = groups将分组信息映射到节点的颜色上,可以在弧形图中直观得看出节点的分组情况。

3. 复杂图结构绘图

一般的原始数据的节点和边很多,图结构复杂,在绘图时要对节点进行分类和排序,对节点和边的大小进行度量,以显示排列美观的弧形图。这里使用研究人员的合著网络数据。

# 合著网络数据处理和绘图
# 1.数据排布转换
connect <- data_dif %>% # connect保存节点的边信息
  gather(key = "to", value = "value", -1) %>% # 使用将除第一列的数据转化成两列(1列列名变量,1列值)
  mutate(to = gsub("\\.", " ", to)) %>% # 更改列"to"名称形式与"from"保持一致
  na.omit() # 去除NA值

# 2.节点统计
coauth <- # coauth保存节点名和出入度信息,后面会加上分组信息
  c(as.character(connect$from), as.character(connect$to)) %>%
  as.tibble() %>% # 类似数据框类型,列名默认为"value"————
  group_by(value) %>% # 按"value"分组
  summarize(n = n()) # 统计节点名的出入度
colnames(coauth) <- c("name", "n") # 更改列名

# 3.进行节点分组
mygraph <- graph_from_data_frame(connect, vertices = coauth, directed = FALSE) # 生成一个图
com <- walktrap.community(mygraph) # 根据节点的路径大小进行分组————
# coauth添加分组信息
coauth <- coauth %>%
  mutate(grp = com$membership) %>% # 添加分组
  arrange(grp) %>% # 按照分组进行排序
  mutate(name = factor(name, name))

# 4.筛选前15个组
coauth <- coauth[coauth$grp < 16, ]
# 过滤只在前15组点出现的连接
connect <- connect %>%
  filter(from %in% coauth$name) %>%
  filter(to %in% coauth$name)

# 5.生成一个图
mygraph <- graph_from_data_frame(connect, vertices = coauth, directed = FALSE)
# 按分组生成颜色
mycolor <- colormap(colormap = colormaps$viridis, nshades = max(coauth$grp))
mycolor <- sample(mycolor, length(mycolor))
# ggraph画图
ggraph(mygraph, layout = "linear") +
  geom_edge_arc(edge_colour = "black", edge_alpha = 0.2, edge_width = 0.3, fold = TRUE) +
  geom_node_point(aes(size = n, color = as.factor(grp), fill = grp), alpha = 0.5) + # 将n和分组变量映射到大小、颜色上
  scale_size_continuous(range = c(0.5, 8)) + # 设置size范围
  scale_color_manual(values = mycolor) + # 设置分组的颜色
  geom_node_text(aes(label = name), angle = 65, hjust = 1, nudge_y = -1.1, size = 2.3) + # 设置节点标签,并倾斜避免重叠
  theme_void() + # 去除背景表格,坐标轴主题
  theme(
    legend.position = "none", # 去除图例
    plot.margin = unit(c(0, 0, 0.4, 0), "null"),
    panel.spacing = unit(c(0, 0, 3.4, 0), "null")
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-5.6, 1.2))

复杂图结构绘图

如图是根据复杂图结构绘制的弧形图。绘制弧形图(或者图)需要将数据预处理成两个分别包含节点信息和边信息的数据框。在这次数据处理中,有一下步骤:

  1. 改变数据排布生成边信息(connect存储);
  2. 依据边信息提取所有节点信息(coauth存储);
  3. 根据walktrap.community()进行分组,分组特征可以映射到节点颜色上,从而显示不同分组,同时,为了美观,对分组节点进行排序;
  4. 只选择前面的分组,同时要筛选connect前面分组的点;
  5. 生成图,选择颜色进行ggraph()进行绘图。

复杂数据绘图不进行分组和排序时(做对比)

# 不进行分组和排序(对比)
# 1.数据排布转换
connect <- data_dif %>% # connect保存节点的边信息
  gather(key = "to", value = "value", -1) %>% # 使用将除第一列的数据转化成两列(1列列名变量,1列值)
  mutate(to = gsub("\\.", " ", to)) %>% # 更改列"to"名称形式与"from"保持一致
  na.omit() # 去除NA值

# 2.节点统计
coauth <- # coauth保存节点名和出入度信息,后面会加上分组信息
  c(as.character(connect$from), as.character(connect$to)) %>%
  as.tibble() %>% # 类似数据框类型,列名默认为"value"————
  group_by(value) %>% # 按"value"分组
  summarize(n = n()) # 统计节点名的出入度
colnames(coauth) <- c("name", "n") # 更改列名

# 5.生成一个图
mygraph <- graph_from_data_frame(connect, vertices = coauth, directed = FALSE)
# ggraph画图
ggraph(mygraph, layout = "linear") +
  geom_edge_arc(edge_colour = "black", edge_alpha = 0.2, edge_width = 0.3, fold = TRUE) +
  geom_node_point(aes(size = n), alpha = 0.5) + # 将n和分组变量映射到大小、颜色上
  scale_size_continuous(range = c(0.5, 8)) + # 设置size范围
  geom_node_text(aes(label = name), angle = 65, hjust = 1, nudge_y = -1.1, size = 2.3) + # 设置节点标签,并倾斜避免重叠
  theme_void() + # 去除背景表格,坐标轴主题
  theme(
    legend.position = "none", # 去除图例
    plot.margin = unit(c(0, 0, 0.4, 0), "null"),
    panel.spacing = unit(c(0, 0, 3.4, 0), "null")
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-5.6, 1.2))

复杂数据绘图不进行分组和排序时(做对比)

如图是对复杂图数据未经过分组和排序时绘制的弧形图,可以看到图中的节点以及弧非常混乱,而且无法对弧形图的内容进行解读。因此,在复杂图数据绘制弧形图时,一定要对节点进行分组以及排序,使用颜色映射将不同的点进行分类。

4. 按照边权重定义弧形粗细

图的边有时候设置有权重,可以映射到弧形粗细来表示。

# 1.从PPI数据表中提取边信息,使用结合分数的值作为权重
data_ppi <- data_ppi[, c(9, 3)] # 提取第3、9列
data_ppi$from <- gsub(" \\(.*", "", data_ppi[, 1]) # 截取name的左边节点名
data_ppi$to <- gsub(".*\\) ", "", data_ppi[, 1]) # 截取name的右边节点名
connect <- data_ppi[, -1] # 去除第一列
connect <- connect[, c(2, 3, 1)] # 将两列边信息放在前面(from和to列)

# 2.节点统计
coauth <- # coauth保存节点名和出入度信息,后面会加上分组信息
  c(as.character(connect$from), as.character(connect$to)) %>%
  as.tibble() %>% # 类似数据框类型,列名默认为"value"————
  group_by(value) %>% # 按"value"分组
  summarize(n = n()) # 统计节点名的出入度
colnames(coauth) <- c("name", "n") # 更改列名

# 3.进行节点分组
mygraph <- graph_from_data_frame(connect, vertices = coauth, directed = FALSE) # 生成一个图
com <- walktrap.community(mygraph) # 根据节点的路径大小进行分组————
## coauth添加分组信息
coauth <- coauth %>%
  mutate(grp = com$membership) %>% # 添加分组
  arrange(grp) %>% # 按照分组进行排序
  mutate(name = factor(name, name))

# 4.筛选前15个组
coauth <- coauth[coauth$grp < 16, ]
# 过滤只在前15组点出现的连接
connect <- connect %>%
  filter(from %in% coauth$name) %>%
  filter(to %in% coauth$name)

# 5.生成一个图
mygraph <- graph_from_data_frame(connect, vertices = coauth, directed = FALSE)
# 按分组生成颜色
mycolor <- colormap(colormap = colormaps$viridis, nshades = max(coauth$grp))
mycolor <- sample(mycolor, length(mycolor))
# ggraph画图
ggraph(mygraph, layout = "linear") +
  geom_edge_arc(aes(edge_width = connect$combined_score), edge_colour = "black", edge_alpha = 0.2, fold = TRUE) +
  geom_node_point(aes(size = n, color = as.factor(grp), fill = grp), alpha = 0.5) + # 将n和分组变量映射到大小、颜色上
  scale_size_continuous(range = c(0.5, 8)) + # 设置size范围
  scale_color_manual(values = mycolor) + # 设置分组的颜色
  geom_node_text(aes(label = name), angle = 65, hjust = 1, nudge_y = -1.1, size = 2.3) + # 设置节点标签,并倾斜避免重叠
  theme_void() + # 去除背景表格,坐标轴主题
  theme(
    legend.position = "none", # 去除图例
    plot.margin = unit(c(0, 0, 0.4, 0), "null"),
    panel.spacing = unit(c(0, 0, 3.4, 0), "null")
  ) +
  expand_limits(x = c(-1.2, 1.2), y = c(-5.6, 1.2))

按照边权重定义弧形粗细

如图使用了PPI的边数据绘制的弧形图,节点表示基因,弧线表示基因间关系,弧形粗细表示基因间关系的权重(强弱)。与上一张图不同的是,提取边信息部分,和绘图中使用edge_width = connect\$combined_score,将权重映射到了弧线的粗细上。

应用场景

ArcDiagramApp1
图 1: 弧形图应用

Hi-C 在分辨率为 500Kb 和 1 Mb的整个 Dixon 染色体上的 相互作用弧图,由 GOTHiC 建模。a 弧形图,用于显示至少 50 次读数(500Kb 分辨率)和 100 次读数(1 Mb 分辨率)的交互。b 显著交互作用的弧图。 [1]

参考文献

[1] KHAKMARDAN S, REZVANI M, POUYAN A A, et al. MHiC, an integrated user-friendly tool for the identification and visualization of significant interactions in Hi-C data[J]. BMC Genomics, 2020,21(1): 225.