1
+ # ' Community internal strength
2
+ # ' @import igraph
3
+ community.strength.internal <- function (graph , community ) {
4
+ # weighted sum of edges that have both ends inside the community
5
+ gC = igraph :: induced_subgraph(graph , community )
6
+ return (Reduce(sum , igraph :: edge_attr(gC ,' weight' ,index = igraph :: E(gC ))))
7
+ }
8
+
9
+ # ' @import igraph
10
+ community.internal.density <- function (graph , community , weighted = FALSE ) {
11
+ gC = igraph :: induced_subgraph(graph , community )
12
+ nC = igraph :: vcount(gC )
13
+ avgW = ifelse(weighted ,
14
+ Reduce(sum , igraph :: edge_attr(graph , ' weight' , index = igraph :: E(graph )))/ igraph :: ecount(graph ),
15
+ 1 )
16
+ intW = ifelse(weighted ,
17
+ Reduce(sum , igraph :: edge_attr(gC , ' weight' , index = igraph :: E(gC ))),
18
+ igraph :: ecount(gC ))
19
+ maxDensity = ifelse(igraph :: is.directed(gC ),
20
+ nC * (nC - 1 ),
21
+ nC * (nC - 1 )/ 2 )
22
+ return (intW / avgW / maxDensity )
23
+ }
24
+
25
+ # ' calculate M measure, defined as
26
+ # '
27
+ # ' M(C) = (vol(C) - cut(C)) / (2 * cut(C))
28
+ # ' graph an igraph object
29
+ # ' community a vector of vertex indices
30
+ measure.M <- function (graph , community ) {
31
+ ccut = community.cut(graph , community ,weighted = TRUE )
32
+ return ((community.volume(graph , community ,weighted = TRUE ) - ccut )/ (2 . * ccut ))
33
+ }
34
+
35
+ # ' Community volume
36
+ # ' @import igraph
37
+ community.volume <- function (graph , community , weighted = FALSE ) {
38
+ if (weighted ) {
39
+ return (Reduce(sum ,igraph :: strength(graph , community )))
40
+ } else {
41
+ return (Reduce(sum ,igraph :: degree(graph , community )))
42
+ }
43
+ }
44
+
45
+ # ' Community cut
46
+ # '
47
+ # ' @import igraph
48
+ community.cut <- function (graph , community , weighted = FALSE ) {
49
+ shell = community.shell(graph , community )
50
+ possibleEdges = expand.grid(shell ,community )
51
+ cut = 0
52
+ if (nrow(possibleEdges ) > = 1 ) {
53
+ for (i in 1 : nrow(possibleEdges )) {
54
+ tryCatch({eIdx = igraph :: get.edge.ids(graph , vp = possibleEdges [i ,])},
55
+ error = function (e ) {
56
+ message(possibleEdges [i ,])
57
+ stop(e )
58
+ })
59
+ if (eIdx != 0 ) {
60
+ if (weighted ) {
61
+ cut = cut + igraph :: get.edge.attribute(graph , ' weight' , eIdx )
62
+ } else {
63
+ cut = cut + 1
64
+ }
65
+ }
66
+ }
67
+ }
68
+ return (cut )
69
+ }
70
+
71
+ # ' community.shell
72
+ # '
73
+ # ' @import igraph
74
+ # ' @export
75
+ community.shell <- function (graph , community ) {
76
+ neighs = (lapply(community , function (i ) {return (igraph :: neighbors(graph ,i ))}))
77
+ shell = setdiff(Reduce(union ,neighs ),community )
78
+ return (shell )
79
+ }
80
+
81
+ # ' Community detection by GCE
82
+ # '
83
+ # ' Starting from a set of seeds, a vertex in the shell is added to the community if it increases community quality metric M
84
+ # ' @import parallel
85
+ # ' @export
86
+ community.gce <- function (graph , seed ,ncores = 1 , version = 1 ) {
87
+ indicator = rep(0 , igraph :: vcount(graph ))
88
+ indicator [seed ] = 1
89
+ if (version == 1 ) {
90
+ if (ncores > 1 ) {
91
+ cl = parallel :: makeCluster(ncores )
92
+ parallel :: clusterExport(cl ,c(' measure.M' , ' community.cut' , ' community.volume' , ' community.shell' ))
93
+ return (community.gce.ind(graph , indicator ,cl = cl ))
94
+ } else {
95
+ return (community.gce.ind(graph , indicator ))
96
+ }
97
+ } else {
98
+ return (community.gce.ind2(graph , indicator ))
99
+ }
100
+ }
101
+
102
+
103
+ community.gce.ind <- function (graph , commIndicator , cluster = NULL ) {
104
+ comm = which(commIndicator == 1 )
105
+ shell = community.shell(graph ,comm )
106
+ if (length(shell ) < 1 ) {
107
+ # this check is pretty stupid, but must be here because R unpredictably loop through the vector 1:0 for once
108
+ return (comm )
109
+ } else {
110
+ cQuality = measure.M(graph ,comm )
111
+ if (is.null(cluster )) {
112
+ improvements = sapply(1 : length(shell ),FUN = function (i ) {
113
+ return (measure.M(graph , c(comm ,shell [i ])))
114
+ })
115
+ } else {
116
+ improvements = parallel :: parSapply(cluster , 1 : length(shell ),FUN = function (i ) {
117
+ return (measure.M(graph , c(comm ,shell [i ])))
118
+ })
119
+ }
120
+ improvements = improvements - cQuality
121
+ if (all(improvements < = 0 )) {
122
+ # parallel::stopCluster(cluster)
123
+ return (which(commIndicator == 1 ))
124
+ } else {
125
+ commIndicator [shell [which(improvements == max(improvements ))]] = 1
126
+ community.gce.ind(graph , commIndicator )
127
+ }
128
+ }
129
+ }
130
+
131
+ community.gce.ind2 <- function (graph , commIndicator ) {
132
+ comm = which(commIndicator == 1 )
133
+ shell = community.shell(graph , comm )
134
+
135
+ if (length(shell ) < 1 ) {
136
+ return (comm )
137
+ }
138
+ # equivalent to maximize M(seed +v) - M(seed)
139
+ mdiff = rep(0 ,length(shell ))
140
+ for (i in 1 : length(shell )) {
141
+ insideConn = 0
142
+ outsideConn = 0
143
+ iNeighbors = igraph :: neighbors(graph , shell [i ])
144
+ for (j in iNeighbors ) {
145
+ w = igraph :: get.edge.attribute(graph , ' weight' , index = igraph :: get.edge.ids(graph , vp = c(shell [i ],j )))
146
+ if (j %in% comm ) {
147
+ insideConn = insideConn + w
148
+ } else {
149
+ outsideConn = outsideConn + w
150
+ }
151
+ }
152
+ mdiff [i ] = (insideConn + outsideConn ) / (outsideConn - insideConn )
153
+ }
154
+ print(shell )
155
+ print(mdiff )
156
+ if (all(mdiff < = 0 )) {
157
+ return (which(commIndicator == 1 ))
158
+ } else {
159
+ commIndicator [shell [which(mdiff == max(mdiff ))]] = 1
160
+ community.gce.ind2(graph , commIndicator )
161
+ }
162
+ }
0 commit comments