基于GN算法(Girvan-Newman)实现社交网络中社区划分
1 背景
網(wǎng)絡(luò)中社區(qū)結(jié)構(gòu)的研究是了解整個(gè)網(wǎng)絡(luò)結(jié)構(gòu)和功能的重要途經(jīng)。對(duì)于網(wǎng)絡(luò)中社區(qū)結(jié)構(gòu)的研究是了解整個(gè)網(wǎng)絡(luò)結(jié)構(gòu)和功能的重要途經(jīng)。一般來說,社區(qū)結(jié)構(gòu)是大規(guī)模網(wǎng)絡(luò)中普遍存在的基本結(jié)構(gòu)。網(wǎng)絡(luò)中的頂點(diǎn)可以進(jìn)行分組,組內(nèi)頂點(diǎn)間的連接比較“稠密”,組間頂點(diǎn)的連接比較“稀疏”,比如下面這個(gè)網(wǎng)絡(luò)結(jié)構(gòu)模型。
在信息網(wǎng)絡(luò)中,社區(qū)經(jīng)常對(duì)應(yīng)著功能相同,性質(zhì)相近或者關(guān)系比較密切的結(jié)點(diǎn)集合,比如,社會(huì)關(guān)系網(wǎng)絡(luò)中的朋友圈。GN算法是Girvan和Newman 提出的一個(gè)比較經(jīng)典的社區(qū)劃分算法。
2 算法思想
社區(qū)與社區(qū)之間的連接比較少,從一個(gè)社區(qū)到另一個(gè)社區(qū)至少要通過這些連接中的一條。如果找到這些重要通道并將他們移除則自然分出了社區(qū)。為了定量的描述邊的“重要”程度,提出了邊介數(shù)的概念。
邊介數(shù):網(wǎng)絡(luò)中通過這條邊的最短路徑的數(shù)目。
一個(gè)網(wǎng)絡(luò)的社區(qū)的劃分可以有很多劃分方法,哪種劃分方法比較好呢?為了定量描述社區(qū)劃分的好壞,Girvan和Newman 提出了模塊度Q的概念,對(duì)社區(qū)進(jìn)行模塊化描述。對(duì)于一個(gè)可以表示為n×n矩陣的無向網(wǎng)絡(luò),模塊化Q函數(shù)如下:
? ? Q=?
其中,i代表的是第i個(gè)社區(qū),eii表示社區(qū)i的邊占原始網(wǎng)絡(luò)所有邊的比例,ai表示所有連接了社區(qū)i中的頂點(diǎn)的邊占總邊數(shù)的比例。
模塊度的含義其實(shí)就是一個(gè)網(wǎng)絡(luò)在某種社區(qū)劃分下與隨機(jī)網(wǎng)絡(luò)的差異,因?yàn)殡S機(jī)網(wǎng)絡(luò)不具備社區(qū)結(jié)構(gòu),對(duì)應(yīng)的差距越大,說明社區(qū)的劃分結(jié)果越好。
查閱資料得知,Q值取值范圍:-0.5到1 。
3 算法步驟
1)計(jì)算每條邊的邊介數(shù);
2)找出邊介數(shù)最大的邊,并移除;
3)計(jì)算所得社區(qū)結(jié)構(gòu)的Q值,尋找Q值最大的情況;
4)重新計(jì)算網(wǎng)絡(luò)中剩余邊的邊介數(shù),重復(fù)2、3兩步,直至網(wǎng)絡(luò)中所有邊都被移除 。
GN的過程對(duì)應(yīng)著一顆自頂向下構(gòu)建的層次樹,在層次樹中選擇一個(gè)合適的層次分割即可。例如,下圖這個(gè)層次樹,移除掉邊介數(shù)最大的一條邊,效果就像從紅線那一層分割,其他同理。
4 實(shí)驗(yàn)復(fù)現(xiàn)
選取比較經(jīng)典的美國一所大學(xué)空手道俱樂部成員間的人際關(guān)系的數(shù)據(jù),使用python語言進(jìn)行復(fù)現(xiàn)。
Python實(shí)現(xiàn)該社交網(wǎng)絡(luò)的社區(qū)劃分代碼(完整代碼以及實(shí)驗(yàn)數(shù)據(jù))如下:
#util.py文件 #coding=utf-8 import networkx as nx# 加載網(wǎng)絡(luò) def load_graph(path):G = nx.Graph()with open(path) 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# 克隆 def clone_graph(G):cloned_graph = nx.Graph()for edge in G.edges():cloned_graph.add_edge(edge[0], edge[1])return cloned_graph# 計(jì)算Q值 def cal_Q(partition, G):m = len(list(G.edges())) #邊的個(gè)數(shù)a = []e = []# 計(jì)算每個(gè)社區(qū)的a值for community in partition:t = 0for node in community:t += len(list(G.neighbors(node)))a.append(t / float(2 * m))# 計(jì)算每個(gè)社區(qū)的e值for community in partition:t = 0for i in range(len(community)):for j in range(len(community)):if i != j:if G.has_edge(community[i], community[j]):t += 1e.append(t / float(2 * m))# 計(jì)算Qq = 0for ei, ai in zip(e, a):q += (ei - ai ** 2)return q#GN.py文件 # coding=utf-8 # 首先導(dǎo)入包 import networkx as nx import matplotlib.pyplot as plt import utilclass GN(object):"""docstring for GN"""def __init__(self, G):self._G_cloned = util.clone_graph(G)self._G = Gself._partition = [[n for n in G.nodes()]]self._max_Q = 0.0# GN算法def execute(self):while len(self._G.edges()) > 0:# 1.計(jì)算所有邊的edge betweennessedge = max(nx.edge_betweenness(self._G).items(),key=lambda item: item[1])[0]# 2.移去edge betweenness最大的邊self._G.remove_edge(edge[0], edge[1])# 獲得移去邊后的子連通圖components = [list(c) for c in list(nx.connected_components(self._G))]if len(components) != len(self._partition):# 3.計(jì)算Q值cur_Q = util.cal_Q(components, self._G_cloned)# print(cur_Q)if cur_Q > self._max_Q:self._max_Q = cur_Qself._partition = componentsreturn self._partition# 可視化劃分結(jié)果 def showCommunity(G, partition, pos):# 劃分在同一個(gè)社區(qū)的用一個(gè)符號(hào)表示,不同社區(qū)之間的邊用黑色粗體cluster = {}labels = {}for index, item in enumerate(partition):for nodeID in item:labels[nodeID] = r'$' + str(nodeID) + '$' # 設(shè)置可視化labelcluster[nodeID] = index # 節(jié)點(diǎn)分區(qū)號(hào)# 可視化節(jié)點(diǎn)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間的linkif cluster[link[0]] != cluster[link[1]]:edges[len(partition)].append(link)else:# cluster內(nèi)的linkif 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內(nèi)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])# 可視化labelnx.draw_networkx_labels(G, pos, labels, font_size=12)plt.axis('off')plt.show()if __name__ == '__main__':# 加載網(wǎng)絡(luò)數(shù)據(jù)并可視化G = util.load_graph("network/data.txt")pos = nx.spring_layout(G)nx.draw(G, pos, with_labels=True, font_weight='bold')plt.show()# GN算法algo = GN(G)partition = algo.execute()print(partition)# 可視化結(jié)果showCommunity(algo._G_cloned, partition, pos)##數(shù)據(jù)文件data.txt如下 1 2 1 3 1 4 1 5 1 6 1 7 1 8 1 9 1 11 1 12 1 13 1 14 1 18 1 20 1 22 1 32 2 3 2 4 2 8 2 14 2 18 2 20 2 22 2 31 3 4 3 8 3 9 3 10 3 14 3 28 3 29 3 33 4 8 4 13 4 14 5 7 5 11 6 7 6 11 6 17 7 17 9 31 9 33 9 34 10 34 14 34 15 33 15 34 16 33 16 34 19 33 19 34 20 34 21 33 21 34 23 33 23 34 24 26 24 28 24 30 24 33 24 34 25 26 25 28 25 32 26 32 27 30 27 34 28 34 29 32 29 34 30 33 30 34 31 33 31 34 32 33 32 34 33 34復(fù)現(xiàn)結(jié)果如下:
該空手道俱樂部初始社交網(wǎng)絡(luò)結(jié)構(gòu):
進(jìn)行社區(qū)劃分之后的效果:
劃分成了五個(gè)子社區(qū),這與論文里面的實(shí)驗(yàn)結(jié)果也是相一致的。
5 算法優(yōu)缺點(diǎn)
?優(yōu)點(diǎn):計(jì)算簡潔、易于程序?qū)崿F(xiàn); ?缺點(diǎn):復(fù)雜度高(由于需要不斷地通過求解最短路徑來求解邊介數(shù),復(fù)雜度比較高);總結(jié)
以上是生活随笔為你收集整理的基于GN算法(Girvan-Newman)实现社交网络中社区划分的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机网络的局限性的表现,计算机网络系统
- 下一篇: 理性和感性哪个对做产品更重要