數據挖掘--復雜網絡社團檢測代碼


聚類技術---復雜網絡社團檢測

一、實驗內容

​ 復雜網絡是描述復雜系統的有力工具,其中每個實體定義成一個節點, 實體間的交互關系定義為邊。復雜網絡社團結構定義為內緊外松的拓撲結構, 即一組節點的集合,集合內的節點交互緊密,與外界節點交互松散。

  1. 導入karate.gml中的空手道網絡數據;
  2. 根據網絡結構特征給出節點相似性度量指標;
  3. 采用層次聚類過程對網絡數據進行聚類;
  4. 計算模塊性指標Q值,當Q值最大時輸出聚類結果;
  5. 采用Cytoscape工具,可視化聚類結果。

二、實驗分析與設計

  1. 導入karate.gml中的空手道網絡數據;

    ​ 利用 igraph 庫中的 get_edgelist()函數,取出所有邊的列表, 遍歷列表計算出數據集的鄰接矩陣 A。若𝐴𝑖𝑗 = 1則表示數據集中點 i 和點 j 之間存在一條邊

  2. 根據網絡結構特征給出節點相似性度量指標;

    ​ 給定節點 i,其鄰居節點定義為與該節點相連接的所有節點組成
    的集合,即N(i) = {𝑗|𝐴𝑖𝑗 = 1,𝑗 = 1,2,…𝑚}。給定一對節點(𝑖 , 𝑗),其 相似性定義為這兩個節點的公共鄰居節點與鄰居節點的並的比值,即:

    \[similar_{ij}=\frac{|N(i)\cap N(j)|}{|N(i)\cup N(j)|} \]

    其中相似度度量的分子為兩節點鄰居節點的交集,分母為兩節點鄰居節點的並集的元素個數

  3. 根據相似度矩陣計算不同種類社團的Q值

    ​ 定義模塊度量值為:

    \[Q_i={\frac{Q_{real}-Q_{null}}{M}}=\frac{1}{2M}\sum_{ij}(a_{ij}-\frac{k_ik_j}{2M})\delta(C_i,C_j) \]

    其中

    \[M=給定的網絡中的邊的數量 \]

    \[k_i,k_j為節點node_i的degreed(度) \]

    \[\delta(C_i,C_j),如果Ci和C_j屬於同一個划分,\delta(C_i,C_j)=1,否則=0 \]

    \[A=[a_{ij}]為鄰接矩陣,所以a_{ij}為鄰接矩陣內的值 \]

  4. 采用層次聚類過程對網絡數據進行聚類,計算模塊性指標Q值,當Q值最大時輸出聚類結果

    ​ 聚類算法步驟:

    1. 將所有的節點看作一個單獨的簇
    2. 遍歷similar矩陣,得到相似度最高的兩個簇
    3. 選取最高相似度,檢測最高相似的是否高於某個閾值,如果是就結束聚類輸出結果(步驟5)
    4. 將相似度最高的兩個簇合並,並更新similar矩陣,將合並的兩個簇中的最小的similar值更新為新的簇的值(MAX全鏈),記錄合並的簇
    5. 計算模塊度量值Q,如果Q值大於當前最大值則輸出分類
  5. 采用Cytoscape工具,可視化聚類結果。

三、具體實現

  1. 利用 igraph 庫函數 get_edgelist()導入 karate.gml 數據集,設 m 為數據 集中節點個數,n 為數據集中邊的條數。neighbors 為各個節點的鄰居 節點的集合列表。之后計算鄰接矩陣 A,設置兩層 for 循環來遍歷所有 節點,若第 i 個節點的鄰居節點中有節點 j,則令A[i, j] = 1,且有 A[j, i] = 1。

    def compute_A():
    
        A = np.zeros((m, m), dtype=np.int)
        for i in range(m):
            for j in range(m):
                if i == j:
                    break
                if j in neighbors[i]:
                    A[i][j] = 1
                    A[j][i] = 1
    
        return A
    

    計算的得到的鄰接矩陣為:

image-20200602150314950

  1. 根據數據集的節點個數m,建立一個m*m大小的矩陣存放相似度,同時維護兩個 _list P_和_Q_分別存放節點的鄰居節點的交集和並集,遍歷兩個節點的鄰居節點,將其交集和並集的結果存放到 _list P_和_Q_中,最后將由_P_和_Q_計算得到的𝑠𝑖𝑚𝑖𝑙𝑎𝑟,分別將其存放到矩陣的[i, j]及其對稱點[j, i],是的相似度矩陣成為對稱矩陣

    def similarity():  # 計算相似度
    
        similar = np.zeros((m, m), dtype=np.float)
        for i in range(m):
            for j in range(m):
                p = []
                q = []
                for k in range(m):
    
                    if(i in neighbors[k] and j in neighbors[k]):
                        p.append(k)
                    if(i in neighbors[k] or j in neighbors[k]):
                        q.append(k)
                l1 = len(p)
                l2 = len(q)
                similar[i][j] = l1/l2
                similar[j][i] = l1/l2
        print('similar矩陣:\n', similar)
        return similar
    

  2. 根據相似度矩陣進行層次聚類,這里采用全鏈聚類,全鏈聚類比單鏈聚類更是適合於現實問題,對離群點和噪聲更不敏感,同時更新相似度矩陣,進行一次分類,返回合並的兩個簇x ,y(這里的x y 為要合並的兩個簇的某個節點,用這兩個節點更新簇的集合)

    # ----------------------層次聚類函數--------------------------------------# 
    def cluster(sim):
        m = len(sim)
        max_s = 0
        x = 0
        y = 0
        # 選出最大相似度
        for i in range(m):
            for j in range(m):
                if(sim[i][j] > max_s):
                    x = i
                    y = j
                    max_s = sim[i][j]
        # 全鏈算法更新相似度矩陣
        for i in range(m):
            sim[x][i] = min(sim[x][i], sim[y][i])
            sim[y][i] = min(sim[x][i], sim[y][i])
        return x, y	
    
  3. 計算模塊化量值Q並與閾值進行比較,輸出完成聚類的簇,設分類標簽_clusters_為一維List,clusters[i]的值表示節點__i__屬於簇__clusters[i]__,初始將clusters置為-1,表示所有的點都未分類

    同時對於模塊化量值Q的計算,利用sum_cluster和counter 進行計算

    # -------------------------模塊化度量值----------------------------------#
    def init_compute_clusters(clusters, m):  # 初始化clusters列表,將clusters的每一位置為-1,表示未分類
        for i in range(m):
            clusters.append(-1)
        return clusters
    
    
    # 模塊度Q計量
    def compute_Q(f, adjacency):
        Q = 0
        for i in range(M):
            for j in range(M):
                if f[i] == f[j]:
                    Q += (adjacency[i][j]-(k[i]*k[j]/(2*N)))/(2*N)
                else:
                    Q += 0
        Q_list.append(Q)
        return Q
    
  4. 主函數負責定義初始變量及對聚類函數返回的x ,y 值進行聚類更新及clusters聚類標簽的更新

    # -------------------------主函數-----------------------------------#
    A = compute_A(M)
    similar = similarity(M)
    clusters = []
    cluster_complete = []
    clusters = init_compute_clusters(clusters, M)
    i = 0
    flag = 0
    Q = 0
    q_max = -1
    while(i < 100):
        x, y = cluster(similar)
        if clusters[x] != -1 and clusters[y] != -1:   # x,y都已經分類,則將x, y的聚類合並成為一個,這里采用合並樹的思想,將相同簇的節點遍歷並進行修改
            temp = clusters[y]
            for j in range(M):
                if clusters[j] == temp:
                    clusters[j] = clusters[x]
        if clusters[x] == -1 or clusters[y] == -1:      # 兩個簇中至少有一個未分類
            if clusters[x] == -1 and clusters[y] == -1:     # 兩個簇都未分類
                flag += 1                                   # 新建一個分類保存此簇
                clusters[x] = clusters[y] = flag
            elif clusters[x] == -1:                 # x未分類y已分類,將x並到y上去
                clusters[x] = clusters[y]
            elif clusters[y] == -1:                 # x已分類y未分類,將y並到x上去
                clusters[y] = clusters[x]
        Q = compute_Q(clusters, A)
        if(Q > q_max):		# 輸出Q值最大的情況的分類
            outputData(clusters, edgelist)
            q_max = Q
            print(clusters)
        i += 1
    draw()
    
  5. 輸出函數,將分類與無向圖分別保存在graph.csv和class.csv並輸出

    def outputData(clusters,  edges):   # 輸出無向圖的連接關系,輸出分類結果
        edge_out = []
        graph_file = data_source+'grafh.csv'
        name1 = ['nodea',  'edgetype',  'nodeb']
        for edge in edges:
            edge_out.append((edge[0]+1, 1, edge[1]+1))
        out1 = pd.DataFrame(columns=name1, data=edge_out)
        out1.to_csv(graph_file)
    
        attribute_file = data_source+'class.csv'
        name2 = ['node',  'class']
        attribute_out = []
        for i in range(1, M+1):
            attribute_out.append((i, clusters[i-1]))
        out2 = pd.DataFrame(columns=name2,  data=attribute_out)
        out2.to_csv(attribute_file)
    
  6. 畫圖函數

    # ---------------------畫圖函數------------------------------------#
    def draw():
        plt.figure(1)
        plt.plot(range(len(Q_list)),  Q_list,  color="b",  linewidth=2)
        plt.xlabel('merge times')
        plt.ylabel("Q")
        plt.scatter(range(len(Q_list)), Q_list, linewidths=3, s=3, c='b')
        f1 = plt.gcf()
        plt.show()
    

四、實驗結果

未分類前的karate節點數據:

最終分類結果:

[9, 9, 9, 9, 4, 4, 4, 9, 3, 3, 4, 9, 9, 9, 3, 3, 4, 9, 3, 9, 3, 9, 3, 3, 5, 5, 3, 3, 3, 3, 3,
5, 3, 3]

分類后的簇(使用cytoscape進行可視化)

mergeTimes-Q圖像

五、實驗總結

​ 經過本次實驗我初步理解了基本的層次聚類技術及其應用,更好的掌握了數據挖掘的相關知識與模型,代碼實現較實驗一較為簡單,較好的應用了已學知識,更好的理解了本學習學習的理論知識的應用,同時有機會應用了 igraph 庫和 pandas 庫等很簡潔好用的庫函數,這大大簡化了我的實驗過程。對於分類結果我個人認為符合我的預期,但在定義模塊化量值Q時遇到了很多困難,ppt中的各個參數的意義不明,,最后在與其他同學交流與網絡查詢中基本解決,希望在以后的實驗中可以做的更好


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM