4
4
import sys
5
5
import copy
6
6
7
- from itertools import combinations
8
7
from itertools import permutations
9
8
from collections import defaultdict
10
9
@@ -24,90 +23,98 @@ def run(cls, graph):
24
23
node2com , edge_weights = cls ._setNode2Com (graph )
25
24
all_edge_weights = graph .size (weight = "weight" )
26
25
27
- node2com = cls ._runInitFirstPhase (node2com , edge_weights , all_edge_weights , graph )
28
- node2coms = [node2com ]
26
+ node2com = cls ._runFirstPhase (node2com , edge_weights )
29
27
cls .show (node2com )
30
- print ()
31
28
29
+ partition = copy .deepcopy (node2com )
32
30
new_node2com , new_edge_weights = cls ._runSecondPhase (node2com , edge_weights )
33
- node2coms .append (new_node2com )
34
31
cls .show (new_node2com )
35
- print ()
36
32
37
33
while True :
38
34
new_node2com = cls ._runFirstPhase (new_node2com , new_edge_weights )
39
- node2coms . append ( copy . deepcopy ( new_node2com ) )
35
+ partition = cls . _updatePartition ( new_node2com , partition )
40
36
_new_node2com , _new_edge_weights = cls ._runSecondPhase (new_node2com , new_edge_weights )
41
- sys .exit ()
37
+ if new_node2com == _new_node2com :
38
+ break
39
+ return partition
40
+
41
+ @classmethod
42
+ def computeModularity (cls ):
43
+ pass
42
44
43
45
@classmethod
44
- def _runInitFirstPhase (cls , node2com , edge_weights , all_edge_weights , graph ):
46
+ def _updatePartition (cls , new_node2com , partition ):
47
+ # new_node2com : {'古いcom_id' : "新しいcom_id"}
48
+ reverse_partition = {v :k for k ,v in partition .items ()}
49
+ for old_com_id , new_com_id in new_node2com .items ():
50
+ partition [reverse_partition [old_com_id ]] = new_com_id
51
+ return partition
52
+
53
+ @classmethod
54
+ def _runFirstPhase (cls , node2com , edge_weights ):
55
+ all_edge_weights = sum ([weight for start in edge_weights .keys () for end , weight in edge_weights [start ].items ()]) / 2
45
56
status = True
46
57
while status :
47
- print (sorted (graph .nodes ()))
48
58
statuses = []
49
- for node in sorted (graph . nodes ( )):
59
+ for node in sorted (list ( node2com . keys () )):
50
60
statuses = []
51
61
com_id = node2com [node ]
52
- neigh_nodes = list ( graph [ node ]. keys () )
62
+ neigh_nodes = sorted ([ edge [ 0 ] for edge in cls . getNeighborNodes ( node , edge_weights )] )
53
63
54
64
max_delta = 0.
55
65
max_com_id = com_id
56
66
max_n = None
67
+ communities = {}
57
68
for neigh_node in sorted (neigh_nodes ):
58
69
node2com_copy = copy .deepcopy (node2com )
70
+ if node2com_copy [neigh_node ] in communities :
71
+ continue
72
+ communities [node2com_copy [neigh_node ]] = 1
59
73
node2com_copy [node ] = node2com_copy [neigh_node ]
60
74
61
75
# 隣接クラスタに移動した際のModularityの差分
62
- delta_q = 2 * cls .getNodeWeightInCluster (node , node2com_copy , edge_weights , graph ) - cls .getTotWeight (node , node2com_copy , edge_weights ) * cls .getNodeWeights (node , edge_weights ) / all_edge_weights
63
- print ("neighbor_id: {}, k_i_in: {}, tot: {}, k_i: {}, max_delta: {}" .format (neigh_node ,
64
- cls .getNodeWeightInCluster (node , node2com_copy , edge_weights , graph ),
65
- cls .getTotWeight (node , node2com_copy , edge_weights ),
66
- cls .getNodeWeights (node , edge_weights ),
76
+ delta_q = 2 * cls .getNodeWeightInCluster (node , node2com_copy , edge_weights ) - cls .getTotWeight (node , node2com_copy , edge_weights ) * cls .getNodeWeights (node , edge_weights ) / all_edge_weights
77
+ # print("neighbor_id: {}, k_i_in: {}, tot: {}, k_i: {}, max_delta: {}".format(neigh_node,
78
+ # cls.getNodeWeightInCluster(node, node2com_copy, edge_weights),
79
+ # cls.getTotWeight(node, node2com_copy, edge_weights),
80
+ # cls.getNodeWeights(node, edge_weights),
67
81
delta_q ))
68
82
69
83
if delta_q > max_delta :
70
84
max_delta = delta_q
71
85
max_com_id = node2com_copy [neigh_node ]
72
86
max_n = copy .deepcopy (node2com_copy )
73
87
74
- print ("k_i_in: {}, tot: {}, k_i: {}, max_delta: {}" .format (cls .getNodeWeightInCluster (node , node2com_copy , edge_weights , graph ),
75
- cls .getTotWeight (node , max_n , edge_weights ),
76
- cls .getNodeWeights (node , edge_weights ),
77
- max_delta ))
78
- print (node2com , max_com_id )
88
+ # print("k_i_in: {}, tot: {}, k_i: {}, max_delta: {}".format(cls.getNodeWeightInCluster(node, node2com_copy, edge_weights),
89
+ # cls.getTotWeight(node, max_n, edge_weights),
90
+ # cls.getNodeWeights(node, edge_weights),
91
+ # max_delta))
92
+ # print(node2com, max_com_id)
79
93
node2com [node ] = max_com_id
80
- print (com_id , max_com_id )
94
+ # print(com_id, max_com_id)
81
95
statuses .append (com_id != max_com_id )
82
96
83
97
if sum (statuses ) == 0 :
84
98
break
85
99
86
100
return node2com
87
101
88
- @classmethod
89
- def _runFirstPhase (cls , node2com , all_edge_weights ):
90
- pass
91
-
92
102
@classmethod
93
103
def _runSecondPhase (cls , node2com , edge_weights ):
94
- # クラスタ内のノードの一括集約
95
- # 1. cluster内のノードを一括集約. cluster内のエッジの重みの総和を2乗して,自身へのエッジとする
96
- # 2. cluster間ノードはcluster内のノードが別のクラスタへ接続していたノードへのエッジの総和とする
97
104
com2node = defaultdict (list )
98
105
99
106
new_node2com = {}
100
107
new_edge_weights = defaultdict (lambda : defaultdict (float ))
101
108
102
- # ノードの集約
103
109
for node , com_id in node2com .items ():
104
110
com2node [com_id ].append (node )
105
111
if com_id not in new_node2com :
106
112
new_node2com [com_id ] = com_id
107
113
108
- # cluster間エッジの重み
109
114
nodes = list (node2com .keys ())
110
115
for edge in permutations (nodes , 2 ):
116
+ if edge [0 ] not in edge_weights : continue
117
+ if edge [1 ] not in edge_weights [edge [0 ]]: continue
111
118
new_edge_weights [new_node2com [node2com [edge [0 ]]]][new_node2com [node2com [edge [1 ]]]] += edge_weights [edge [0 ]][edge [1 ]]
112
119
113
120
for node in new_node2com .keys ():
@@ -117,7 +124,6 @@ def _runSecondPhase(cls, node2com, edge_weights):
117
124
118
125
@classmethod
119
126
def getTotWeight (cls , node , node2com , edge_weights ):
120
- # 街灯ノードを除いたクラスタの隣接エッジの総和
121
127
nodes = []
122
128
for n , com_id in node2com .items ():
123
129
if com_id == node2com [node ] and node != n :
@@ -129,21 +135,22 @@ def getTotWeight(cls, node, node2com, edge_weights):
129
135
return weight
130
136
131
137
@classmethod
132
- def getNodeWeightInCluster (cls , node , node2com , edge_weights , graph ):
133
- # nodeに接続しているcluster内のedgeの重みの総和
134
- neigh_nodes = graph [node ].items ()
138
+ def getNeighborNodes (cls , node , edge_weights ):
139
+ if node not in edge_weights :
140
+ return 0
141
+ return list (edge_weights [node ].items ())
142
+
143
+ @classmethod
144
+ def getNodeWeightInCluster (cls , node , node2com , edge_weights ):
145
+ neigh_nodes = cls .getNeighborNodes (node , edge_weights )
135
146
node_com = node2com [node ]
136
147
weights = 0.
137
148
for neigh_node in neigh_nodes :
138
149
if node_com == node2com [neigh_node [0 ]]:
139
- weights += neigh_node [1 ][ "weight" ]
150
+ weights += neigh_node [1 ]
140
151
141
152
return weights
142
-
143
- @classmethod
144
- def _getNodeWeights (cls , node , graph ):
145
- return graph .degree (node , weight = "weight" )
146
-
153
+
147
154
@classmethod
148
155
def getNodeWeights (cls , node , edge_weights ):
149
156
return sum ([weight for weight in edge_weights [node ].values ()])
@@ -152,11 +159,11 @@ def getNodeWeights(cls, node, edge_weights):
152
159
def _setNode2Com (cls , graph ):
153
160
# initialize
154
161
node2com = {}
155
- edge_weights = defaultdict ( lambda : defaultdict ( float ))
162
+ edge_weights = {}
156
163
for idx , node in enumerate (sorted (graph .nodes ())):
157
164
node2com [node ] = idx
165
+ edge_weights [node ] = {}
158
166
for edge in graph [node ].items ():
159
- # edge = (node, {"weight" : edge_weight})
160
167
edge_weights [node ][edge [0 ]] = edge [1 ]["weight" ]
161
168
162
169
return node2com , edge_weights
0 commit comments