欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

用R语言绘制共表达网络:igraph包指南

最编程 2024-01-13 19:04:28
...

R语言绘图包系列:

  • R语言绘图包01--优秀的拼图包patchwork
  • R语言绘图包02--热图pheatmap
  • R语言绘图包03--火山图EnhancedVolcano
  • R语言绘图包04--GOplot:富集分析结果可视化
  • R语言绘图包05--韦恩图的绘制:ggvenn和VennDiagram
  • R语言绘图包06--基因表达相关性绘图corrplot
  • R语言绘图包07--多集合可视化UpSetR
  • R语言绘图包08--森林图的绘制:forestplot
  • R语言绘图包09--序列分析图的绘制ggseqlogo
  • R语言绘图包10--Circos图的绘制:circlize包
  • R语言绘图包11--森林图的绘制(2):forestploter包
  • R语言绘图包12--向日葵图的绘制

网络图,是一种包含了节点V(即网络参与者,也称顶点)与边E(即节点之间的连接关系)的数学结构,记作G={V,E}。可以使用一个矩阵来存放节点之间的连接关系,这个矩阵称为邻接矩阵。如果网络中两个节点之间的边是有方向的,即从节点u出发指向节点v,这就是一个有向网络(有向图),否则称为无向网络(无向图)。网络的边也可以赋予权重,称为加权网络。

1. igraph绘图

1.1 基础绘图
###安装并载入演示数据集
library(devtools)
install_github("kassambara/navdata")
library("navdata")
###载入数据集
data("phone.call") #示例集来自于navdata包(是一个假想的数据集,里边有一个不存在的国家Slovania)
这个数据集表示的是在某段时间内欧洲部分国家*之间的通话次数。由数据可见,这是一个由source指向destination,以通话次数为权重的有向加权网络。

igraph有特定的网络(图)对象类“igraph”。建立一个图的最简单的方法是用函数graph.formula()手工输入节点的对应关系,考虑到实际上的网络规模都是比较大的,我们几乎不会用到这种方法。更常用的方式有两种:

  • 从数据框(节点列表和边列表)建立网络对象

这种方法需要我们先建立两个数据框:
边列表:包含节点标签和节点的其他属性
节点列表:包含节点之间联系(也就是边)和边的属性
此时,可以使用graph_from_data_frame()建立igraph对象。

  • 从邻接矩阵建立网络对象

如果数据集是表示节点连接关系的矩阵形式(比如说就像相关系数矩阵那样),可以使用graph_from_adjacency_matrix()建立igraph对象
对于phone.call这个数据集,要创建节点列表,需要将source和destination这两列包含的各个国家的名称提取出来,即提取独特的行观测,这使用dplyr包的distinct函数来实现。然后,我们创建一个新变量location,表示这些节点的地理位置:

library(tidyverse)
name<-data.frame(c(phone.call$source,phone.call$destination))
nodes<-name%>%
    distinct()%>%
mutate(location=c("western","western","central","nordic","southeastern",
     "southeastern","southeastern","southern","sourthern",
     "western","western","central","central","central","central","central"))
colnames(nodes)<-c("label","location")

下面来看边列表。数据集phone.call的前两列分别表示了边出发和终结的节点。所以这个数据集就是边列表的形式,第三列n.call可以作为边的权重。我们把这些列的名称改成更直观的名字:

edges<-phone.call%>%rename(from=source,to=destination,weight=n.call)

现在可以创建一个igraph对象了:

library(igraph)

net_pc<-graph_from_data_frame(
   d=edges,vertices=nodes,
   directed=TRUE)

net_pc

可以看到输出的igraph对象:

3f802ae是这个对象的名称,DNW- 16 18 --表示这是一个有向(D)、有命名(N)且加权(W)的网络,它有16个节点和18条边。它具有的节点属性是name , location ,具有的边属性是weight。

我们可以通过V()和E()对节点和边的属性进行访问,如:

V(net_pc)
# + 16/16 vertices, named, from 3f802ae:
#  [1] France         Belgium        Germany        Danemark      
#  [5] Croatia        Slovenia       Hungary        Spain         
#  [9] Italy          Netherlands    UK             Austria       
# [13] Poland         Switzerland    Czech republic Slovania     
V(net_pc)$location 
# [1] "western"      "western"      "central"      "nordic"      
#  [5] "southeastern" "southeastern" "southeastern" "southern"    
#  [9] "sourthern"    "western"      "western"      "central"     
# [13] "central"      "central"      "central"      "central"     

这种方法也可以用于指定或修正相应的节点和边的属性,这在igraph可视化中是很常用的。

还可以从net_pc这个网络中提取边列表或者邻接矩阵:

###提取边列表
as_edgelist(net_pc, names=T) 
      [,1]       [,2]            
#  [1,] "France"   "Germany"       
#  [2,] "Belgium"  "France"        
#  [3,] "France"   "Spain"         
#  [4,] "France"   "Italy"         
#  [5,] "France"   "Netherlands"   
#  [6,] "France"   "UK"            
#  [7,] "Germany"  "Austria"       
#  [8,] "Germany"  "Poland"        
#  [9,] "Belgium"  "Germany"       
# [10,] "Germany"  "Switzerland"   
# [11,] "Germany"  "Czech republic"
# [12,] "Germany"  "Netherlands"   
# [13,] "Danemark" "Germany"       
# [14,] "Croatia"  "Germany"       
# [15,] "Croatia"  "Slovania"      
# [16,] "Croatia"  "Hungary"       
# [17,] "Slovenia" "Germany"       
# [18,] "Hungary"  "Slovania"     
###提取邻接矩阵
as_adjacency_matrix(net_pc, attr="weight")
# 16 x 16 sparse Matrix of class "dgCMatrix"
#   [[ suppressing 16 column names ‘France’, ‘Belgium’, ‘Germany’ ... ]]
                                                
# France         . . 9 . . . . 3 4 2 3 . . . . .  
# Belgium        4 . 3 . . . . . . . . . . . . .  
# Germany        . . . . . . . . . 2 . 2 2 2 2 .  
# Danemark       . . 2 . . . . . . . . . . . . .  
# Croatia        . . 2 . . . 2 . . . . . . . . 2.0
# Slovenia       . . 2 . . . . . . . . . . . . .  
# Hungary        . . . . . . . . . . . . . . . 2.5
# Spain          . . . . . . . . . . . . . . . .  
# Italy          . . . . . . . . . . . . . . . .  
# Netherlands    . . . . . . . . . . . . . . . .  
# UK             . . . . . . . . . . . . . . . .  
# Austria        . . . . . . . . . . . . . . . .  
# Poland         . . . . . . . . . . . . . . . .  
# Switzerland    . . . . . . . . . . . . . . . .  
# Czech republic . . . . . . . . . . . . . . . .  
# Slovania       . . . . . . . . . . . . . . . .  

现在可以看一下这个网络了(图3):

plot(net_pc)
1.2 可视化基础

igraph的plot函数中提供了大量的参数用于展示节点、边以及图形的各种属性,其中涉及节点的参数是以vertex.开头的,涉及边的参数是以edge.开头的。

除了在plot()中指定节点和边的参数之外,还可以使用前面提到的V()E(),直接在igraph对象中添加相应的属性。这两种方法的区别在于,在plot()中指定的参数并不会改变图的属性。比如,我们先按照位置指定节点的颜色,按照权重指定边的宽度(这两个属性会保存在net_pc对象中),然后在plot()的参数中指定节点的大小(与节点的度成比例,节点度是与这个节点相连的边的个数)、节点标记的大小和位置、边的颜色、箭头的大小和边的弯曲程度

###计算节点的度
deg<-degree(net_pc,mode="all")
###设置颜色
vcolor<-c("orange","red","lightblue","tomato","yellow")
###指定节点的颜色
V(net_pc)$color<-vcolor[factor(V(net_pc)$location)]
###指定边的颜色
E(net_pc)$width<-E(net_pc)$weight/2

plot(net_pc,vertex.size=3*deg,
vertex.label.cex=.7,vertex.label.dist=1,
edge.color="gray50",edge.arrow.size=.4, edge.curved=.1)
###添加图例
legend(x=1.5,y=1.5,levels(factor(V(net_pc)$location)),pch=21,col="#777777",pt.bg=vcolor)
1.3 网络布局

网络布局指的是确定网络中每个节点坐标的方法。

igraph中提供了多种布局算法,其中最有用的是几种力导向(Force-directed)布局算法。力导向布局试图得到一个美观的图形,其中的边在长度上相似,并且尽可能少地交叉。它们把图形模拟成一个物理系统。节点是带电粒子,当它们靠得太近时会互相排斥。这些边充当弹簧,将连接的节点吸引在一起。结果,节点在图示区域中均匀分布,并且布局直观,因为共享更多连接的节点彼此更接近。这些算法的缺点是速度很慢,因此在大于1000个顶点的图中使用的频率较低。

使用力导向布局时,可以使用niter参数控制要执行的迭代次数。默认设置为500次迭代。对于大型的图,可以降低该数字以更快地获得结果,并检查它们是否合理。

Fruchterman-Reingold是最常用的力导向布局算法:

l <- layout_with_fr(net_pc) 
plot(net_pc, layout=l)

Fruchterman Reingold布局具有随机性,不同的运行将导致略有不同的布局配置。将布局保存在对象l当中,可以使我们多次获得完全相同的结果(也可以实现指定随机数种子seed())。

我们通过一张图来展示所有的(用于单模图的)布局方式:

layouts <- grep("^layout_", ls("package:igraph"), value=TRUE)[-1]
# Remove layouts that do not apply to our graph.
layouts <- layouts[!grepl("bipartite|merge|norm|sugiyama|tree", layouts)]
par(mfrow=c(5,3), mar=c(1,1,1,1)) 
for (layout in layouts) {
print(layout)
l <- do.call(layout, list(net_pc))
plot(net_pc, vertex.label="",edge.arrow.mode=0, layout=l, main=layout) }

参考:R语言网络数据可视化(1):igraph基础