R 数据可视化 —— ggraph 图形创建与布局

前言

在前面几节,我们对 igraph 进行了一些简单介绍。接下来我们要介绍的是 ggraph 包的使用

通常,ggraph 会和 tidygraph 一起搭配使用,这两个包都是 Thomas Lin Pedersen 写的。

tidygraph 提供了一系列 tidy API 用于操作图,通常可以将图对象设计为两个 tidy 表,一个用于表示 node 数据,另一个用于表示 edge 数据。还提供了一些额外的动词用于操作这两个表,同时也提供了很多的图论算法,让图形对象的处理看起来更像是在处理数据框。

ggraphggplot2 的扩展,用于绘制关系型数据结构,如网络、图和树等。ggraph 包含 3 个核心概念:

  • layout:定义图的布局,包含所有的 igraph 布局以及额外的一些布局,如蜂巢图、圆堆积图等

  • nodes:定义节点图形属性,使用 geom_node_*() 函数来控制

  • edges:定义边的图形属性,使用 geom_edge_*() 函数来控制

两个包搭配使用,可以让网络对象的处理与绘制,和我们绘制数据框结构更相似,以 ggplot2 的方式来绘制网络对象

安装导入

install.packages('ggraph')
install.packages('tidygraph')

# 开发版
devtools::install_github('thomasp85/ggraph')
devtools::install_github('thomasp85/tidygraph')

library(ggraph)
library(tidygraph)

创建图

我们还是以 KEGG 的通路作为示例,这次我们找的是 p53 signaling pathway,网络结构的获取方式见前两节的代码

我们使用 tbl_graph() 函数来创建 tbl_graph 对象

edges <- read.csv('~/Downloads/p53_signaling_pathway.csv')
colnames(edges) <- c("from", "to")
nodes <- data.frame(name = unique(union(edges$from, edges$to)))

g <- tbl_graph(nodes = nodes, edges = edges)

查看对象类型

> class(g)
[1] "tbl_graph" "igraph" 

tbl_graph 对象本质上是一个 igraph 对象,可以看到 tbl_graph() 函数与 graph_from_data_frame() 函数的参数是很像的

也可以用 igraph 图对象来创建 tbl_graph 对象

library(igraph)

ig <- graph_from_data_frame(d = edges, vertices = nodes)
ig_tidy <- as_tbl_graph(ig)

查看这三个对象的类型

> class(g)
[1] "tbl_graph" "igraph"   
> class(ig)
[1] "igraph"
> class(ig_tidy)
[1] "tbl_graph" "igraph"
>
> is.tbl_graph(g)
[1] TRUE
> is.tbl_graph(ig)
[1] FALSE
> is.tbl_graph(ig_tidy)
[1] TRUE

查看 tbl_graph 对象

> g
# A tbl_graph: 61 nodes and 68 edges
#
# A directed acyclic simple graph with 1 component
#
# Node Data: 61 x 1 (active)
  name  
  <chr> 
1 MDM4  
2 CHEK1 
3 ATR   
4 CHEK2 
5 ATM   
6 IGFBP3
# … with 55 more rows
#
# Edge Data: 68 x 2
   from    to
  <int> <int>
1     1    22
2     1    15
3     2    15
# … with 65 more rows

as_tbl_graph() 函数还可以将很多其他类型的数据转换为 tbl_graph 对象,例如 hclust 对象、邻接矩阵、dendrogram 对象等等

tidygraph 也提供了很多简便函数,create_*() 函数用于创建一些常见的图结构

par(mfrow= c(1, 3))
plot(create_star(10))
plot(create_ring(10))
plot(create_tree(n = 20, children = 3), edge.arrow.size = .4)

play_*() 函数用于创建模拟图

par(mfrow= c(1, 3))
plot(play_geometry(6, 1, torus = FALSE))
plot(play_islands(4, 10, 0.7, 3))
plot(play_forestfire(20, 0.5), edge.arrow.size = .2)

可以使用 igraph 的绘制方式来调整和绘制 tbl_graph 图对象

布局

使用 ggraph 来绘制图形

ggraph(g) +
  geom_edge_link() +
  geom_node_point() +
  theme_graph()

ggraph() 函数相当于 ggplot2::ggplot(),根据传入的图对象以及布局来创建绘图对象

ggraph 默认会根据图结构自动推断布局,也可以使用指定 layout 参数的值

ggraph(g, layout = 'kk') +
  geom_edge_link() +
  geom_node_point() +
  theme_graph()

如果布局算法可以接受额外的参数,也可以在 ggraph() 函数中一并指定

ggraph(g, layout = 'kk', maxiter = 100) +
  geom_edge_link() +
  geom_node_point() +
  theme_graph()

或者用 create_layout() 函数来创建布局,它接受的参数与 ggraph() 一样,但是返回的是 layout_ggraph 对象,可以在后续的图结构中使用。例如

layout <- create_layout(g, layout = 'eigen')

ggraph(layout) +
  geom_edge_link() +
  geom_node_point() +
  theme_graph()

我们可以看到,返回的对象是包含节点位置及属性信息的数据框

> head(layout)
            x          y circular   name .ggraph.orig_index .ggraph.index
1 -0.04065655 0.05622353    FALSE   MDM4                  1             1
2 -0.01929743 0.04913762    FALSE  CHEK1                  2             2
3 -0.01929743 0.04913762    FALSE    ATR                  3             3
4 -0.01929743 0.04913762    FALSE  CHEK2                  4             4
5 -0.01929743 0.04913762    FALSE    ATM                  5             5
6  0.50281399 0.07229403    FALSE IGFBP3                  6             6

查看对象的所有属性

> attributes(layout)
$names
[1] "x"                  "y"                  "circular"           "name"               ".ggraph.orig_index"
[6] ".ggraph.index"     

$row.names
 [1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
[40] 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61

$class
[1] "layout_tbl_graph" "layout_ggraph"    "data.frame"      

$graph
# A tbl_graph: 61 nodes and 68 edges
#
# A directed acyclic simple graph with 1 component
#
# Node Data: 61 x 2 (active)
  name   .ggraph.orig_index
  <chr>               <int>
1 MDM4                    1
2 CHEK1                   2
3 ATR                     3
4 CHEK2                   4
5 ATM                     5
6 IGFBP3                  6
# … with 55 more rows
#
# Edge Data: 68 x 2
   from    to
  <int> <int>
1     1    22
2     1    15
3     2    15
# … with 65 more rows

$circular
[1] FALSE

ggraph 包提供了许多不同的布局,包括 igraph 所有的布局,同时也提供了一些它自己设计的布局,有超过 20 种布局可供选择。

通常来说,不同的布局算法对图的展示差异很大,因此,为自己的数据找到一个合适的布局很重要。

也可以自己设计一个布局函数,接受一个 tbl_graph 对象,并返回一个位置数据框。或者直接提供一个位置矩阵或数据框作为布局

1. 圆形布局

一些布局既可以显示在笛卡尔坐标系中,也可以在极坐标中有效地进行展示。对于 ggplot2 来说,可以使用 coord_polar() 转换为极坐标轴。但这并不适用于 ggraph,我们通过 circular 参数将布局转换为径向表示。

我们先为边添加一列信息,用于表示基因之间的表达值的相关性,-1 表示负相关,1 为正相关,0 为不相关

edges <- edges %>%
  mutate(corr = sample(-1:1, size = n(), replace = TRUE))
  
g <- tbl_graph(nodes = nodes, edges = edges)
  1. linear 布局

对于线性布局,即将所有基因放置在一条直线上,然后使用 geom_edge_arc 将边绘制成弧形

ggraph(g, layout = 'linear') + 
  geom_edge_arc(aes(colour = factor(corr))) +
  geom_node_point() +
  theme_graph()

设置 circular = TRUE,转换为径向表示

ggraph(g, layout = 'linear', circular = TRUE) + 
  geom_edge_arc(aes(colour = factor(corr))) +
  geom_node_point() +
  coord_fixed() +
  theme_graph()
  1. partition 布局

分区布局是一种显示分层结构的方式,每一层都会对前一层的切片进行分割。

例如,我们将节点的形状设置为条块 geom_node_tile

graph <- tbl_graph(flare$vertices, flare$edges)

ggraph(graph, 'partition') + 
  geom_node_tile(aes(fill = depth), size = 0.25) +
  theme_graph()

分区布局的圆形表示,注意节点的形状变成了圆弧条形 geom_node_arc_bar

ggraph(graph, 'partition', circular = TRUE) + 
  geom_node_arc_bar(aes(fill = depth), size = 0.25) +
  coord_fixed() +
  theme_graph()

并不是所有的布局都支持圆形表示,下面的布局将会忽略 circular 参数

2. node — edge 布局

我们可以直接使用 igraph 中定义的布局算法,例如

lay <- c('stress', 'fr', 'lgl', 'graphopt')
plot_fun <- function(g, layout) {
  p <- ggraph(g, layout = layout) + 
    geom_edge_link(aes(colour = factor(corr)), show.legend = FALSE) +
    geom_node_point() + 
    labs(caption = paste0('Layout: ', layout)) +
    theme_graph()
  return(p)
}

grid.newpage()
pushViewport(viewport(layout = grid.layout(2, 2)))

for (i in seq_along(lay)) {
  x = (i - 1) %/% 2 + 1
  y = (i - 1) %% 2 + 1
  p = plot_fun(g, lay[i])
  print(p, vp = viewport(layout.pos.row = x, layout.pos.col = y))
}

3. 蜂巢图

蜂巢图也是一种 node-edge 图,它使用的是节点的信息,将节点进行分类

g <- g %>%
  mutate(friends = ifelse(
    centrality_degree(mode = 'all') < 3, "few",
    ifelse(centrality_degree(mode = 'all') > 3, "many", "medium")
  ))

ggraph(g, 'hive', axis = friends) + 
  geom_edge_hive(aes(colour = factor(corr))) + 
  geom_axis_hive(aes(colour = friends), size = 2, label = FALSE) + 
  coord_fixed() +
  theme_graph()

4. 焦点布局

将焦点聚集在一个或一组节点上,其他节点则相对于该位置放置

ggraph(g, 'focus', focus = node_is_center()) + 
  ggforce::geom_circle(aes(x0 = 0, y0 = 0, r = r), data.frame(r = 1:5), colour = 'grey') +
  geom_edge_link(aes(colour = factor(corr)), show.legend = FALSE) + 
  geom_node_point() + 
  coord_fixed() +
  theme_graph()

5. 层次布局

  1. 圆堆积图:以包含的方式来展示层次结构
graph <- tbl_graph(flare$vertices, flare$edges)

ggraph(graph, 'circlepack', weight = size) + 
  geom_node_circle(aes(fill = factor(depth)), size = 0.25, n = 50) + 
  coord_fixed() +
  theme_graph()
  1. 堆积树状图
ggraph(graph, 'circlepack', weight = size) + 
  geom_edge_link() + 
  geom_node_point(aes(colour = factor(depth))) +
  coord_fixed() +
  theme_graph()
  1. 树状图

矩形层次关系

ggraph(graph, 'treemap', weight = size) + 
  geom_node_tile(aes(fill = factor(depth)), size = 0.25) +
  theme_graph()
ggraph(graph, 'treemap', weight = size) + 
  geom_edge_link() + 
  geom_node_point(aes(colour = factor(depth))) +
  theme_graph()

根据树的不同深度进行绘制

ggraph(graph, 'tree') + 
  geom_edge_diagonal() +
  theme_graph()

层次聚类树状图

dendrogram <- hclust(dist(iris[, 1:4]))
ggraph(dendrogram, 'dendrogram', height = height) + 
  geom_edge_elbow() +
  theme_graph()

圆形树状图

ggraph(dendrogram, 'dendrogram', circular = TRUE) + 
  geom_edge_elbow() + 
  coord_fixed() +
  theme_graph()

系统发育树,不存在根节点,使用无根布局

tree <- create_tree(100, 2, directed = FALSE) %>% 
  activate(edges) %>% 
  mutate(length = runif(n()))

ggraph(tree, 'unrooted', length = length) + 
  geom_edge_link() +
  theme_graph()

6. 矩阵布局

矩阵布局是将节点放置在对角线,如果对应位置的两个节点之间有交叠,那矩阵中对应的行列将会绘制一个点或矩形。

graph <- create_notable('zachary')
ggraph(graph, 'matrix', sort.by = node_rank_leafsort()) + 
  geom_edge_point(mirror = TRUE) + 
  coord_fixed() +
  theme_graph()

不同的节点顺序,会影响矩阵布局的形状

ggraph(graph, 'matrix', sort.by = node_rank_spectral()) + 
  geom_edge_point(mirror = TRUE) + 
  coord_fixed() +
  theme_graph()

7. Fabric 布局

Fabric 布局是一种可扩展的特殊的 BioFabric 布局。其特殊的地方在于,它将节点表示为水平线,边表示为连接两个水平线的竖直线

ggraph(g, 'fabric', sort.by = node_rank_fabric()) + 
  geom_node_range(colour = 'grey') + 
  geom_edge_span(end_shape = 'square') + 
  coord_fixed() +
  theme_graph()

添加一份重复的阴影边

ggraph(g, 'fabric', sort.by = node_rank_fabric(), shadow.edges =TRUE) + 
  geom_node_range(colour = 'grey') + 
  geom_edge_span(aes(filter = shadow_edge), colour ='lightblue' , end_shape = 'square') + 
  geom_edge_span(aes(filter = !shadow_edge), end_shape = 'square') + 
  coord_fixed() +
  theme_graph()
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容