igraph 是一系列网络分析工具,其强调效率,便携性和易用性,其不仅仅支持C/C++直接调用,同时还提供了R,python,Ruby等语言接口,其主要的功能是无向图,有向图的算法,包含生成图,最短路径,生成最小树等经典算法,还包含了计算中介中心性,网络分析等算法。
igraph库的主要目标是提供一组数据类型和函数,1)高效实现图算法,2)快速处理大图,具有数百万个顶点和边,3)给其他语言提供借口,进行快速原型设计
Igraph是免费开源的,其官网如下:
R可以通过 igraph这个包来调用这个网络工具
首先我们需要安装好这个包,使用’’’install.packages(“igraph”)```进行安装,安装好了这个包之后我们需要加载这个包。
library(igraph)
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
社交网络分析是一种无监督的机器学习方法,有一些类似于机器学习中的聚类算法,用于识别社交圈子。所谓物以类聚,人以群分,一旦我们可以将社交网络划分成为不同的圈子,就可以有很多的应用。
例如:
还有很多可以应用的领域
社交网络通常使用图来描述,图可以非常直观的描述事物之间的关系。在图中,节点(node)表示一个人,或者一个事物,边(edge)代表人或者事物之间的关系。有向图用箭头表示事物之间的关系,无向图直接使用线段来描述.
关于图,有几个重要的概念:
这个概念是用来描述连接点(vertices)的活跃性,指的是与点相连接的边的数目。在有向图中,还区分为出度(out degree)和入度(in degree)。出度指的是以某一个节点为起点指向另外一个点的边的数目。入度指的是以某一个节点为终点的边的数目。有向图某个节点的度(degree)是出度和入度的和。
边分为有向边和无向边
计算的公式是整个图的边数处以整个图可能的边数
这里举一个简单的例子来看一下:
构造一个简单的网络
构造一个网络一般会使用两个函数:
首先使用graph进行构建网络,首先我们来查看一下graph的使用方法?graph
myfristnewwork <- graph(edges = c("Alice","Sam","Sam","Sam","Sam","David","David","Alice","Frank","David"),directed = TRUE)
# 进行绘图
plot(myfristnewwork)
myfristnewwork[]
## 4 x 4 sparse Matrix of class "dgCMatrix"
## Alice Sam David Frank
## Alice . 1 . .
## Sam . 1 1 .
## David 1 . . .
## Frank . . 1 .
directed 参数的含义表明我们的图是否是一个有向图。edges传入一个响亮,向量里面的元素两两一组,构成一个链接
然后我们使用graph.data.frame
这个函数来构建图
myfristnewwork <- graph.data.frame(data.frame(c("Alice","Sam","Sam","David","Frank"),c("Sam","Sam","David","Alice","David")))
plot(myfristnewwork)
myfristnewwork[]
## 4 x 4 sparse Matrix of class "dgCMatrix"
## Alice Sam David Frank
## Alice . 1 . .
## Sam . 1 1 .
## David 1 . . .
## Frank . . 1 .
使用graph.data.frame
这个函数来构建网络需要传递一个数据框,这个数据框有两列,这两列一一对应,构成网络,其中,默认是,directed = TRUE,其表明是有向图,
有了有向图,可以通过degree函数进行查看图的度数,直接输入网络图,可以查看整个图的总度数
degree(myfristnewwork)
## Alice Sam David Frank
## 2 4 3 1
查看某一个节点的度数
degree(myfristnewwork,v = "Alice")
## Alice
## 2
如果想知道网络图中有哪些节点,可以使用V()
V(myfristnewwork)
## + 4/4 vertices, named, from b8a2fee:
## [1] Alice Sam David Frank
计算出度与如度
degree(myfristnewwork,mode = "in")
## Alice Sam David Frank
## 1 2 2 0
degree(myfristnewwork,mode = "out")
## Alice Sam David Frank
## 1 2 1 1
vcount(myfristnewwork)
## [1] 4
ecount(myfristnewwork)
## [1] 5
diameter(myfristnewwork,directed = F,weights = NA)
## [1] 2
使用这个函数可以得到图的最长的路径
edge_density(myfristnewwork)
## [1] 0.4166667
计算的公式是整个图的边数处以整个图可能的边数
ecount(myfristnewwork)
## [1] 5
ecount(myfristnewwork)/(vcount(myfristnewwork)*(vcount(myfristnewwork)-1))
## [1] 0.4166667
这里通过顶点数量与边的数量计算了整个图的密度
特定图模型的构建
eg <- make_empty_graph(40)
plot(eg, vertex.size=10, vertex.label=NA)
fg <- make_full_graph(40)
plot(fg, vertex.size=10, vertex.label=NA)
st <- make_star(40)
plot(st, vertex.size=10, vertex.label=NA)
tr <- make_tree(40, children = 3, mode = "undirected")
plot(tr, vertex.size=10, vertex.label=NA)
rn <- make_ring(40)
plot(rn, vertex.size=10, vertex.label=NA)
er <- sample_gnm(n=100, m=40)
plot(er, vertex.size=6, vertex.label=NA)
sw <- sample_smallworld(dim=2, size=10, nei=1, p=0.1)
plot(sw, vertex.size=6, vertex.label=NA, layout=layout_in_circle)
这里使用的是plot函数进行绘图,plot是范型函数,在这里调用plot其实是调用plot.graph,这里介绍一下绘图的参数:
比如我们想要修改颜色
plot(myfristnewwork,vertex.color="red")
去掉节点的标识
plot(myfristnewwork,vertex.color="red",vertex.label = c("a","b","c"))
在我们开始之前,我们需要将我们的网络图转化成为无向图,最常用的方法是使用as.undirected
myfristnewwork_undirected<- as.undirected(myfristnewwork)
plot(myfristnewwork)
plot(myfristnewwork_undirected)
另外一种就是在创建图的时候就直接指定无向图:
myfristnewwork1 <- graph(edges = c("Alice","Sam","Sam","Sam","Sam","David","David","Alice","Frank","David"),directed = FALSE)
plot(myfristnewwork1)
第一个介绍的方法是基于边界介于中间的社区检测(Newman-Girvan)
GGirvan-Newman算法通过不断地删除网络中的边来检测网络中的社区。在最终剩余的网络中的连通分量也就是社区。Girvan-Newman算法并不是去测量哪些边具有最高的中心度,而是去关注哪些最有可能连接着社区。
顶点介数是一种反应网络中顶点的中心度的指标。对于一个顶点i,顶点介数是指网络中经过该顶点的所有最短路径的数量。
Girvan-Newman算法将介数扩展到了边。类似地,一条边的边介数是指网络中经过该边的最短路径的数量。如果一个网络所包含的社区之间是由少量的边连接的,那么经由不同社区的最短路径都需要从这些边上经过,因此这些边的边介数会非常高。通过移除这些边,各个社区之间将会被分割开来,从而可以发现这些社区。
Girvan-Newman算法的步骤如下:
重新计算剩余每条边的边介数需要不少计算时间,然而,并没有较好的办法省去这些开销。当一条边被删除了之后,网络的结构发生了变化,举个例子来说,假设两个社区之间有不止一条边连接,那么并不是每条边都会有较高的边介数,我们只能逐渐地删除这些边,在这个过程中会发现至少有一条边会有较高的边介数。
Girvan-Newman算法的结果是一个树状图。这个树状图自顶向下地描绘了社区的结构。树状图的叶子则是图的每个结点。
g <- tr <- make_tree(40, children = 3, mode = "undirected")
# 首先构造一个网络
plot(g)
ceb <- cluster_edge_betweenness(g) # 构造模型
dendPlot(ceb) # 可视化
plot(ceb,g)
ceb
## IGRAPH clustering edge betweenness, groups: 7, mod: 0.68
## + groups:
## $`1`
## [1] 1 4 13 38 39 40
##
## $`2`
## [1] 2 6 7 17 18 19 20 21 22
##
## $`3`
## [1] 3 9 10 26 27 28 29 30 31
##
## $`4`
## + ... omitted several groups/vertices
可以看到,这里将网络划分成为了7个社群
这种方法的基本思想是:每一个点的社群归属是由相邻的节点标签决定的,相邻节点中出现最多的那个标签就是该节点的标签。类似于机器学习中的KNN算法。
在最一开始,每一个节点都有一个独特的标签,然后密集链接的节点标签归位一类,然后进行不断的迭代,直到这些具有相同标签的节点组成一个社群。
clp <- cluster_label_prop(g)
plot(clp,g)
基于贪婪优化模块的社区检测 是另一种分层方法,但它是自下而上而不是自上而下。它试图以贪婪的方式优化称为模块化的质量功能。最初,每个顶点属于一个单独的社区,并且迭代地合并社区,使得每个合并是局部最优的(即,产生模块化的当前值的最大增加)。当不可能再增加模块性时,算法停止,因此它为您提供分组和树形图。该方法很快,并且它通常作为第一近似尝试的方法,因为它没有要调整的参数。然而,已知会受到分辨率限制,即低于给定大小阈值的社区(取决于节点和边缘的数量,如果我没记错的话)将始终与邻近社区合并。
cfg <- cluster_fast_greedy(g)
自旋玻璃源于物理学,他是某些特制的物理材料的一种无序状态,如果把关系网络看成一个随机网络场,网络中某一节点与其他节点链接或者不连接,可以看作磁性材料相互作用与反磁性相互作用,两种相互作用形成一个能力的函数,当能量函数小的时候,关系网络的层次结构被解释称为旋转配置,此时网络内部的社群可看作自旋转系统的状态,该过程类似于层次聚类。
sping <- spinglass.community(g)
plot(sping,g)
这个安利的数据来自于weibo数据,在weibo数据中,存在一些用户是spammer,我们称这类用户为垃圾用户。数据已经有了部分的标签。我们尝试对weibo的用户数据进行社交网络分析,划分出不同的社交网络群体
首先我们需要读取数据
require(tidyverse)
follower<- readr::read_csv("/Users/milin/Downloads/microblogPCU/follower_followee.csv")
post <- readr::read_csv("/Users/milin/Downloads/microblogPCU/post.csv")
user_post <- readr::read_csv("/Users/milin/Downloads/microblogPCU/user_post.csv")
weibo_user <- readr::read_csv("/Users/milin/Downloads/microblogPCU/weibo_user.csv")
dim(follower)
## [1] 142368 10
dim(post)
## [1] 35 11
dim(user_post)
## [1] 48813 9
dim(weibo_user)
## [1] 781 10
load(file = "/Users/milin/Downloads/兼职/id1.RData")
数据集的属性信息:
weibo_user.csv
user_post.csv
followe-followee.csv
post.csv和user_post.csv类似
现在取出来一部分建立关系图
library(igraph)
library(Hmisc)
## Loading required package: lattice
## Loading required package: survival
## Loading required package: Formula
##
## Attaching package: 'Hmisc'
## The following objects are masked from 'package:dplyr':
##
## src, summarize
## The following objects are masked from 'package:base':
##
## format.pval, units
follower <- follower %>% filter(follower_id %nin% id1)
follower_followee_part <- follower[1:1000,]
gg<-graph.data.frame(data.frame(er=follower_followee_part$follower_id,ee=follower_followee_part$followee_id))
plot(gg,
vertex.label=NA, ##不显示标签
edge.arrow.mode='-', ##不使用箭头
vertex.size = 5 ##设置结点圆的大小
)
自旋玻璃源于物理学,他是某些特制的物理材料的一种无序状态,如果把关系网络看成一个随机网络场,网络中某一节点与其他节点链接或者不连接,可以看作磁性材料相互作用与反磁性相互作用,两种相互作用形成一个能力的函数,当能量函数小的时候,关系网络的层次结构被解释称为旋转配置,此时网络内部的社群可看作自旋转系统的状态,该过程类似于层次聚类。
sping <- spinglass.community(gg)
plot(sping,gg,vertex.label=NA, ##不显示标签
edge.arrow.mode='-', ##不使用箭头
vertex.size = 5 ) ##设置结点圆的大小 )
sping$membership
## [1] 7 10 8 6 4 2 5 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
## [24] 11 11 11 11 11 11 8 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
## [47] 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 7 7
## [70] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [93] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 10 10 10 10 8 8
## [116] 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [139] 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [162] 8 8 8 8 8 8 8 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [185] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [208] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [231] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 4 4 4
## [254] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [277] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 2 2 2 2 2 2 2 2
## [300] 2 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [323] 5 5 5 5 11 11 11 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [346] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [369] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [392] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [415] 7 7 7 7 7 7 7 7 7 7 7 10 10 10 10 10 10 10 10 10 10 10 10
## [438] 10 10 10 10 10 10 10 10 10 10 2 2 10 10 10 10 10 10 10 10 10 10 10
## [461] 10 10 10 10 10 10 10 10 2 10 10 10 10 10 10 10 10 10 10 10 10 10 10
## [484] 10 10 10 10 10 10 10 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [507] 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [530] 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [553] 8 8 8 8 8 1 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8
## [576] 8 8 8 8 8 8 8 8 8 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [599] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [622] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [645] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 9
## [668] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 4 11 4 4 4 4 4
## [691] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [714] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [737] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [760] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [783] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [806] 2 2 2 2 2 2 5 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [829] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
## [852] 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 5 5 5 5
## [875] 5 5 5 5 5 5 12 5 5 5 5 5 5 3 5 5 5 5 5 5 5 5 5
## [898] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [921] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 5 5 5 5 5 5 5 5
## [944] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
weibo_user$user_id <- as.character(weibo_user$user_id)
social <- data.frame(sping$names,sping$membership)
social$sping.names <- as.character(social$sping.names)
social <- social %>% left_join(weibo_user[,c(1,10)],by=c("sping.names"="user_id"))
table(social$sping.membership,social$is_spammer,useNA = "always")
##
## -1 1 <NA>
## 1 0 0 2
## 2 1 0 99
## 3 0 0 1
## 4 0 1 139
## 5 1 0 113
## 6 0 1 180
## 7 0 1 138
## 8 0 1 149
## 9 0 0 1
## 10 0 0 67
## 11 0 16 47
## 12 0 0 1
## <NA> 0 0 0
可以看到,关系网络划分成为了10个群体,其中第8个群体是spammer群体
基于边缘中介的社区结构检测
weibo_network<-graph.data.frame(data.frame(er=follower_followee_part$follower_id,ee=follower_followee_part$followee_id),directed = F)
clus <- cluster_edge_betweenness(weibo_network)
plot(clus,weibo_network,vertex.label=NA, ##不显示标签
edge.arrow.mode='-', ##不使用箭头
vertex.size = 5 ) ##设置结点圆的大小 )
查看社群的分类
clus$membership
## [1] 1 2 3 4 5 6 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 3 8 8 8 8 8
## [36] 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 1 1 1
## [71] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [106] 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [141] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4
## [176] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [211] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [246] 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [281] 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [316] 7 7 7 7 7 7 7 7 7 7 7 8 8 8 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [351] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [386] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
## [421] 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 7 2 2 2 2 2 2 2 2 2 2 2
## [456] 2 2 2 2 2 2 2 7 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 7 2
## [491] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [526] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [561] 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4
## [596] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [631] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4
## [666] 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 4 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [701] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [736] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
## [771] 5 5 5 5 5 5 5 5 5 5 5 5 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [806] 6 7 6 6 6 6 7 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6
## [841] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 7 7 7 7 7
## [876] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [911] 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7 7
## [946] 7 7 7 7 7 7 7 7 7 7 7 7 7 7
social <- data.frame(clus$names,clus$membership)
social$clus.names <- as.character(social$clus.names)
social <- social %>% left_join(weibo_user[,c(1,10)],by=c("clus.names"="user_id"))
table(social$clus.membership,social$is_spammer,useNA = "always")
##
## -1 1 <NA>
## 1 0 1 138
## 2 0 0 67
## 3 0 1 150
## 4 0 1 181
## 5 0 1 140
## 6 1 0 95
## 7 1 0 120
## 8 0 16 46
## <NA> 0 0 0
可以看到,关系网络划分成为了8个群体,其中第8个群体是spammer群体