-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathFN.py
More file actions
214 lines (192 loc) · 7.63 KB
/
Copy pathFN.py
File metadata and controls
214 lines (192 loc) · 7.63 KB
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
import time
# def G_name2id(path):
# '''
# 将graph的name转换为数字id(id从0开始)
#
# path:(源点、端点)文件路径
# '''
# G = nx.read_edgelist(path, delimiter=',')
# N = dict.fromkeys(G.nodes()) # node_id
# i = 0
# for name, id in N.items():
# N[name] = i
# i = i+1
#
# G_n = nx.Graph()
# with open(path,'r') as f:
# lines = f.readlines()
# for line in lines:
# n = line.strip().split(',')
# G_n.add_edge(N[n[0]],N[n[1]])
#
# return G_n
# 加载图数据集
def load_graph(path):
G = nx.Graph()
with open(path, 'r') as text:
for line in text:
vertices = line.strip().split(' ')
source = int(vertices[0])
target = int(vertices[1])
G.add_edge(source, target)
return G
class FastNewman:
def __init__(self, path):
self.G = load_graph(path)
# G = nx.read_gml('dolphins.gml')
self.A = nx.to_numpy_array(self.G) # 邻接矩阵
self.num_node = len(self.A) # 点数
self.num_edge = sum(sum(self.A)) # 边数
self.c = {} # 记录所有Q值对应的社团分布
# def merge_community(self, iter_num, detaQ, e, b):
# # 一起合并容易出bug 查询的结果I在遍历过程中 可能在已经前面某次作为J被合并了
# # 比如某次是[ 3, 11] [11, 54] 第一轮迭代中11被合并 第二轮54合并到旧的11中 会导致后面被删除 导致节点消失 需要将54合并到现在11所在位置 比较麻烦 不如一个个合并
# b_num = sum([len(i) for i in b])
# det_max = np.amax(detaQ)
#
# (I, J) = np.where(detaQ == det_max)
# print((I, J) )
# # 由于先遍历的I I中可能有多个相同值 所以合并时候因应该将J合并到I中
# # 如果将I合并到J中 后续删除删不到
# for m in range(len(I)):
# # 确保J还未被合并
# if J.tolist().index(J[m]) == m:
# # 将第J合并到I 然后将J清零
# e[I[m], :] = e[J[m], :] + e[I[m], :]
# e[J[m], :] = 0
# e[:, I[m]] = e[:, J[m]] + e[:, I[m]]
# e[:, J[m]] = 0
# b[I[m]] = b[I[m]] + b[J[m]]
#
# e = np.delete(e, J, axis=0)
# e = np.delete(e, J, axis=1)
# J = sorted(list(set(J)), reverse=True)
# for j in J:
# b.remove(b[j]) # 删除第J组社团,(都合并到I组中了)
# b_num2 = sum([len(i) for i in b])
# if b_num2 != b_num:
# print("111")
# self.c[iter_num] = b.copy()
# return e, b
def merge_community(self, iter_num, detaQ, e, b):
# 一个个合并
(I, J) = np.where(detaQ == np.amax(detaQ))
# 由于先遍历的I I中可能有多个相同值 所以合并时候因应该将J合并到I中
# 如果将I合并到J中 后续删除删不到
e[I[0], :] = e[J[0], :] + e[I[0], :]
e[J[0], :] = 0
e[:, I[0]] = e[:, J[0]] + e[:, I[0]]
e[:, J[0]] = 0
b[I[0]] = b[I[0]] + b[J[0]]
e = np.delete(e, J[0], axis=0)
e = np.delete(e, J[0], axis=1)
b.remove(b[J[0]]) # 删除第J组社团,(都合并到I组中了)
self.c[iter_num] = b.copy()
return e, b
def Run_FN(self):
e = self.A / self.num_edge # 社区i,j连边数量占总的边的比例
a = np.sum(e, axis=0) # e的列和,表示与社区i中节点相连的边占总边数的比例
b = [[i] for i in range(self.num_node)] # 本轮迭代的社团分布
Q = []
iter_num = 0
while len(e) > 1:
num_com = len(e)
detaQ = -np.power(10, 9) * np.ones((self.num_node, self.num_node)) # detaQ可能为负数,初始设为负无穷
for i in range(num_com - 1):
for j in range(i + 1, num_com):
if e[i, j] != 0:
detaQ[i, j] = 2 * (e[i, j] - a[i] * a[j])
if np.sum(detaQ + np.power(10, 9)) == 0:
break
e, b = self.merge_community(iter_num, detaQ, e, b)
a = np.sum(e, axis=0)
# 计算Q值
Qt = 0.0
for n in range(len(e)):
Qt += e[n, n] - a[n] * a[n]
Q.append(Qt)
iter_num += 1
max_Q, community = self.get_community(Q)
return max_Q, community
def get_community(self, Q):
max_k = np.argmax(Q)
community = self.c[max_k]
return Q[max_k], community
def showCommunity(G, partition, pos):
# 划分在同一个社区的用一个符号表示,不同社区之间的边用黑色粗体
cluster = {}
labels = {}
for index, item in enumerate(partition):
for nodeID in item:
labels[nodeID] = r'$' + str(nodeID) + '$' # 设置可视化label
cluster[nodeID] = index # 节点分区号
# 可视化节点
colors = ['r', 'g', 'b', 'y', 'm']
shapes = ['v', 'D', 'o', '^', '<']
for index, item in enumerate(partition):
nx.draw_networkx_nodes(G, pos, nodelist=item,
node_color=colors[index],
node_shape=shapes[index],
node_size=350,
alpha=1)
# 可视化边
edges = {len(partition): []}
for link in G.edges():
# cluster间的link
if cluster[link[0]] != cluster[link[1]]:
edges[len(partition)].append(link)
else:
# cluster内的link
if cluster[link[0]] not in edges:
edges[cluster[link[0]]] = [link]
else:
edges[cluster[link[0]]].append(link)
for index, edgelist in enumerate(edges.values()):
# cluster内
if index < len(partition):
nx.draw_networkx_edges(G, pos,
edgelist=edgelist,
width=1, alpha=0.8, edge_color=colors[index])
else:
# cluster间
nx.draw_networkx_edges(G, pos,
edgelist=edgelist,
width=3, alpha=0.8, edge_color=colors[index])
# 可视化label
nx.draw_networkx_labels(G, pos, labels, font_size=12)
plt.axis('off')
plt.show()
# def get_value(G, community):
# '''
# Each node gets respective value. Nodes in one community have same value
# community: 形如 [[1,2,3],[4,5],[6,7,8]]
# '''
# num_node = nx.number_of_nodes(G)
# value = [[] for i in range(num_node)]
# for index, com in enumerate(community):
# for q in com:
# value[q] = index
# return value
#
#
# def draw_community(G, com):
# value = get_value(G, com)
# pos = nx.spring_layout(G)
# nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'), node_color=value, node_size=20)
# nx.draw_networkx_edges(G, pos, edge_color='gray', alpha=0.5)
# plt.show()
# # plt.savefig('community.jpg')
if __name__ == "__main__":
start_time = time.time()
Q, community = FastNewman('data/club.txt').Run_FN()
print(Q)
print(community)
end_time = time.time()
print(f'算法执行时间{end_time - start_time}')
# end_time = time.time()
G = load_graph('data/club.txt')
pos = nx.spring_layout(G)
showCommunity(G, community, pos)