在複雜網絡系統中,理解內部結構是制定策略的關鍵。社群偵測演算法,如 Girvan-Newman,提供自頂向下的視角,透過移除關鍵橋樑來揭示宏觀社群邊界。與此同時,團體分析則採自底向上的觀點,專注於識別凝聚力最強的核心子群體。結合這兩種方法,分析師能從宏觀與微觀層面完整洞察網絡結構,為組織分析、市場區隔或系統韌性評估提供數據基礎。

Girvan-Newman 社群劃分的視覺化與解讀

本章節將聚焦於 Girvan-Newman 演算法在 Zachary 空手道網路上的應用,並透過視覺化來呈現其第一次分割所產生的兩個社群。我們將運用與先前相同的繪圖策略,以區分社群內部與外部的連接,並解讀視覺化結果。

視覺化程式碼實踐:呈現兩個社群

Girvan-Newman 演算法的第一次分割通常會將網路劃分為兩個主要的社群。為了清晰地展示這一結果,我們將採用分層繪製的方式:

  1. 準備邊列表與顏色

    • 外部邊 (External Edges)
      • 如前所述,我們首先識別出連接這兩個新社群的邊。這些邊的 'community' 屬性被標記為 0。
      • external = [(v, w) for v, w in G_karate.edges if G_karate.edges[v, w]['community'] == 0] 創建了外部邊的列表。
    • 內部邊 (Internal Edges)
      • 接著,我們識別出屬於兩個獨立社群內部的邊。它們的 'community' 屬性將是 1 或 2。
      • internal = [(v, w) for v, w in G_karate.edges if G_karate.edges[v, w]['community'] > 0] 創建了內部邊的列表。
    • 內部邊顏色
      • internal_color = [get_color(G_karate.edges[e]['community']) for e in internal] 為每個內部邊生成對應其所屬社群的顏色。由於 Girvan-Newman 的第一次分割只產生兩個社群,get_color() 函數將會生成兩種不同的顏色。
  2. 繪製圖形

    • 繪製外部邊
      • nx.draw_networkx(G_karate, pos=karate_pos, node_size=0, edgelist=external, edge_color="#333333", with_labels=False)
      • 此步驟繪製連接兩個主要社群的邊。它們以深灰色顯示,線條較細,且不顯示節點,以突出社群結構。
    • 繪製節點與內部邊
      • nx.draw_networkx(G_karate, pos=karate_pos, node_color=node_color, edgelist=internal, edge_color=internal_color)
      • 此步驟繪製節點和社群內部的邊。
        • node_color=node_color 為節點賦予根據其所屬社群(由 set_node_community 設定)生成的顏色。
        • edgelist=internaledge_color=internal_color 確保內部邊以與其所屬社群相符的顏色繪製。

視覺化結果與解讀

繪製完成的圖形將清晰地展示 Zachary 空手道網路被 Girvan-Newman 演算法劃分為兩個社群的結果:

  • 兩個主要社群
    • 圖形會顯示出兩個明顯不同的節點群集,每個群集代表一個社群。
    • 節點的顏色和內部邊的顏色將一致地反映這兩個社群的劃分。
  • 連接的橋樑
    • 外部的灰色邊線將這兩個社群連接起來。這些邊代表了 Girvan-Newman 演算法在該階段識別出的「橋樑」或「關鍵連接」。
    • 觀察這些外部邊的數量和位置,可以了解兩個主要社群之間的聯繫程度。
  • 與現實的對應
    • Girvan-Newman 演算法的第一次分割結果,通常會捕捉到 Zachary 空手道組織分裂的兩個主要派系。這兩個社群的劃分,反映了當時組織內部的權力鬥爭和派系形成。
看圖說話:

此圖示總結了「Girvan-Newman 社群劃分的視覺化與解讀」的內容,重點在於展示 Girvan-Newman 演算法在 Zachary 空手道網路上的第一次分割結果,並透過視覺化進行呈現與解讀。流程開頭首先聚焦於「Girvan-Newman 社群劃分的視覺化與解讀」,透過「分割」結構,詳細闡述了「視覺化程式碼實踐:呈現兩個社群」(概述了「準備邊列表與顏色」,並描述了「繪製圖形 (分層)」的過程),接著探討了「視覺化結果與解讀」(指出了「兩個主要社群」,描述了「連接的橋樑」,並說明了「與現實的對應」),最後以「總結與展望」作結,對比了「演算法的視角」,並指出其「為後續分析奠定基礎」。

迭代式社群劃分與團體結構分析

本章節將延續 Girvan-Newman 演算法的討論,探討如何控制社群劃分的精細程度,並引入另一種重要的網路結構分析概念——團體 (Cliques)。

精確控制社群數量

Girvan-Newman 演算法的迭代特性允許我們在不同階段停止分割,從而獲得不同數量的社群。

  • 迭代器與 itertools.islice()
    • nxcom.girvan_newman() 函數返回的迭代器會逐步細分網路。每一次迭代都代表了一次社群的進一步劃分。
    • 為了獲取特定數量的社群,我們可以利用 Python 的 itertools.islice() 函數來精確地從迭代器中提取所需的分割結果。
  • 範例:獲取四個社群
    • result = nxcom.girvan_newman(G_karate):獲取 Girvan-Newman 演算法的迭代器。
    • communities = next(itertools.islice(result, 2, 3))
      • itertools.islice(result, 2, 3) 會從迭代器 result 中跳過前兩個元素(即前兩次分割),然後取第三個元素。
      • 在 Girvan-Newman 演算法中,第一次分割通常產生 2 個社群,第二次分割(迭代器中的第三個元素)則會將這 2 個社群進一步細分,從而產生 4 個社群。
    • 視覺化
      • 獲取到包含四個社群的分割後,可以使用與先前相同的視覺化程式碼來呈現結果。
      • 圖形將顯示網路被劃分為四個更小的社群,這些社群是先前兩個社群的子集。
  • 後續分割
    • 迭代器 result 中後續的值將會進一步細分現有的社群,產生更多、更小的社群群集。

團體 (Cliques) 的識別與分析

團體是網路中最密集、連接最緊密的子結構之一。

  • 團體的定義
    • 團體 (Clique) 是指網路中一組節點,其中任意兩個節點之間都存在直接連接。換句話說,團體是一個完全連通的子圖。
  • 團體的意義
    • 團體代表了網路中最緊密的關係或互動。
    • 團體中的節點通常屬於同一個社群,團體常常構成社群的核心。
    • 識別團體有助於揭示網路底層的結構,特別是與關聯網路 (affiliation networks) 相關的結構。
  • 關聯網路與單模網路的轉換
    • 在關聯網路中,節點可能代表個人,邊代表個人與某個群體(如社團、活動)的關聯。
    • 透過投影 (projection) 的方式,可以將關聯網路轉換為單模網路(例如,只有個人節點的網路)。在這種轉換過程中,如果某個群體中的所有成員都被移除,則該群體中的成員之間會被連接成一個團體。
  • NetworkX 的團體查找功能
    • NetworkX 提供了 nx.find_cliques() 函數來查找網路中的所有團體。
    • 此函數返回一個迭代器,其中每個元素都是一個團體(節點列表)。
    • cliques = list(nx.find_cliques(G_karate)):將 Zachary 空手道網路中的所有團體查找出來,並轉換為列表。
    • 範例輸出顯示了找到的一些團體,例如 [0, 1, 17][0, 1, 2, 3, 13]

最大團體 (Maximal Clique) 的識別與視覺化

  • 最大團體
    • 在所有團體中,最大團體 (Maximal Clique) 是指那個包含最多節點的團體。它代表了網路中最密集的部分。
  • 查找最大團體
    • max_clique = max(cliques, key=len):利用 Python 的 max() 函數,並指定 key=len,可以從團體列表中找到長度(節點數量)最大的團體。
    • 範例輸出顯示 Zachary 空手道網路的最大團體是 [0, 1, 2, 3, 13],包含 5 個節點。
  • 視覺化最大團體
    • 為了視覺化最大團體,我們可以將屬於該團體的節點標記為特定顏色(例如,鮮豔的顏色),而其他節點則用較為柔和的顏色顯示。
    • 程式碼片段展示了如何設置節點顏色:
      • 首先,將所有節點的顏色初始化為灰色 (0.5, 0.5, 0.5)
      • 然後,遍歷節點,如果節點屬於最大團體 max_clique,則將其顏色更改為醒目的顏色。
import networkx as nx
import networkx.community as nxcom
import matplotlib.pyplot as plt
import itertools

# --- 創建 Zachary 空手道網路 ---
G_karate_demo = nx.karate_club_graph()

# --- Girvan-Newman 迭代式社群劃分 (獲取四個社群) ---
communities_gn_4 = []
print("\n--- 使用 Girvan-Newman 演算法獲取四個社群 ---")
try:
    girvan_newman_iterator = nxcom.girvan_newman(G_karate_demo)

    # 使用 itertools.islice 跳過前兩個分割,獲取第三個分割 (通常產生 4 個社群)
    # islice(iterator, start, stop) - stop 是 exclusive
    communities_gn_4 = next(itertools.islice(girvan_newman_iterator, 2, 3))

    num_communities_gn_4 = len(communities_gn_4)
    print(f"Girvan-Newman 迭代 3 得到的社群數量: {num_communities_gn_4}")
    print("四個社群的大小:")
    for i, comm in enumerate(communities_gn_4):
        print(f"  社群 {i+1}: {len(comm)} 個節點")

except Exception as e:
    print(f"獲取四個社群時發生錯誤: {e}")

# --- 團體 (Cliques) 查找與分析 ---
print("\n--- 查找團體 (Cliques) ---")
cliques = []
max_clique = []
try:
    # 查找所有團體
    cliques = list(nx.find_cliques(G_karate_demo))
    print(f"找到的團體總數: {len(cliques)}")

    # 查找最大團體
    if cliques:
        max_clique = max(cliques, key=len)
        print(f"最大團體 (Maximal Clique) 包含 {len(max_clique)} 個節點: {max_clique}")
    else:
        print("網路中未找到任何團體。")

except Exception as e:
    print(f"查找團體時發生錯誤: {e}")

# --- 視覺化輔助函數 (沿用前述定義) ---
def set_node_community(G, communities_list):
    if not communities_list: return
    for c, nodes_in_community in enumerate(communities_list):
        for node in nodes_in_community:
            G.nodes[node]['community'] = c + 1

def set_edge_community(G):
    for u, v in G.edges():
        if 'community' in G.nodes[u] and 'community' in G.nodes[v]:
            if G.nodes[u]['community'] == G.nodes[v]['community']:
                G.edges[u, v]['community'] = G.nodes[u]['community']
            else:
                G.edges[u, v]['community'] = 0
        else:
            pass

def get_color(community_id, n_communities, low=0.1, high=0.9):
    if community_id == 0: return (0.5, 0.5, 0.5) # 灰色
    if n_communities <= 1: return (0.8, 0.2, 0.2)
    normalized_community_id = (community_id - 1) / (n_communities - 1)
    return plt.cm.viridis(normalized_community_id)

# --- 視覺化 Girvan-Newman 四個社群 ---
if communities_gn_4 and G_karate_demo.number_of_nodes() > 0:
    print("\n--- 繪製 Girvan-Newman 四個社群結構圖 ---")
    # 設置節點和邊的社群屬性
    set_node_community(G_karate_demo, communities_gn_4)
    set_edge_community(G_karate_demo)

    # 準備節點顏色
    n_communities_gn_4 = len(communities_gn_4)
    node_colors_gn_4 = []
    for node in G_karate_demo.nodes():
        community_id = G_karate_demo.nodes[node].get('community', 0)
        node_colors_gn_4.append(get_color(community_id, n_communities_gn_4))

    # 準備邊列表與顏色
    external_edges_gn_4 = []
    internal_edges_gn_4 = []
    internal_edge_colors_gn_4 = []

    for u, v, d in G_karate_demo.edges(data=True):
        community_id = d.get('community', 0)
        if community_id == 0:
            external_edges_gn_4.append((u, v))
        else:
            internal_edges_gn_4.append((u, v))
            internal_edge_colors_gn_4.append(get_color(community_id, n_communities_gn_4))

    # 計算佈局
    karate_pos = nx.spring_layout(G_karate_demo, seed=42, k=0.3)

    plt.figure(figsize=(10, 8))

    # 繪製外部邊
    nx.draw_networkx(
        G_karate_demo, pos=karate_pos, node_size=0,
        edgelist=external_edges_gn_4, edge_color="#333333", width=0.5, alpha=0.4, style='dashed'
    )

    # 繪製節點和內部邊
    nx.draw_networkx(
        G_karate_demo, pos=karate_pos,
        node_color=node_colors_gn_4, node_size=300, alpha=0.9,
        edgelist=internal_edges_gn_4, edge_color=internal_edge_colors_gn_4, width=1.5, alpha=0.7
    )

    nx.draw_networkx_labels(G_karate_demo, karate_pos, font_size=10, font_weight='bold')

    plt.title("Zachary Karate Club Network - Girvan-Newman Communities (4 Communities)", fontsize=16)
    plt.axis('off')
    plt.show()

else:
    print("\n無法繪製四個社群的視覺化,因為社群偵測失敗或網路為空。")

# --- 視覺化最大團體 ---
if max_clique and G_karate_demo.number_of_nodes() > 0:
    print("\n--- 視覺化最大團體 (Maximal Clique) ---")

    # 設置節點顏色:最大團體節點為醒目顏色,其他為灰色
    node_colors_max_clique = [(0.5, 0.5, 0.5) for _ in G_karate_demo.nodes()] # 預設灰色
    max_clique_nodes = set(max_clique)

    for i, v in enumerate(G_karate_demo.nodes()):
        if v in max_clique_nodes:
            node_colors_max_clique[i] = (0.9, 0.2, 0.2) # 醒目紅色

    plt.figure(figsize=(10, 8))

    # 繪製網路,強調最大團體節點
    nx.draw_networkx(
        G_karate_demo, pos=karate_pos,
        node_color=node_colors_max_clique, node_size=300, alpha=0.9,
        edge_color="#333333", width=0.5, alpha=0.4, style='dashed' # 繪製所有邊
    )

    nx.draw_networkx_labels(G_karate_demo, karate_pos, font_size=10, font_weight='bold')

    plt.title("Zachary Karate Club Network - Maximal Clique Highlighted", fontsize=16)
    plt.axis('off')
    plt.show()

    print("最大團體視覺化完成,標示出網路中最密集連接的節點群組。")

else:
    print("\n無法視覺化最大團體,因為未找到團體或網路為空。")
@startuml
!define DISABLE_LINK
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

start

:迭代式社群劃分與團體結構分析;:精確控制社群數量;
note right
Girvan-Newman 迭代特性
使用 itertools.islice() 提取分割
範例: 獲取 4 個社群 (第三個分割)
視覺化: 顯示更細分的社群結構
end note

:團體 (Cliques) 的識別與分析;
note right
團體定義: 完全連通的節點子集
團體意義: 網路最密集區域, 社群核心
關聯網路轉換: 投影機制
NetworkX 函數: nx.find_cliques()
結果: 迭代器 (所有團體)
end note

:最大團體 (Maximal Clique) 的識別與視覺化;
note right
最大團體: 包含最多節點的團體
查找方法: max(cliques, key=len)
視覺化: 突出顯示最大團體節點
end note

:總結與展望;
note right
Girvan-Newman 的靈活性
團體分析的價值
為後續網路結構分析奠定基礎
end note

stop

@enduml

看圖說話:

此圖示總結了「迭代式社群劃分與團體結構分析」的內容,重點在於探討如何精確控制 Girvan-Newman 演算法的社群劃分數量,以及引入團體 (Cliques) 的概念進行網路結構分析。流程開頭首先聚焦於「迭代式社群劃分與團體結構分析」,透過「分割」結構,詳細闡述了「精確控制社群數量」(說明了 Girvan-Newman 的「迭代特性」,介紹了「使用 itertools.islice() 提取分割」,並給出了「範例:獲取 4 個社群」),接著探討了「團體 (Cliques) 的識別與分析」(定義了「團體」,闡述了其「意義」,並介紹了「NetworkX 函數」),並說明了「最大團體 (Maximal Clique) 的識別與視覺化」(定義了「最大團體」,介紹了「查找方法」,並說明了「視覺化」方式),最後以「總結與展望」作結,強調了「Girvan-Newman 的靈活性」和「團體分析的價值」,並指出其「為後續網路結構分析奠定基礎」。