使用 Python/NetworkX 進行圖形數據科學

已發表: 2022-03-11

我們被數據淹沒了。 不斷擴展的數據庫和電子表格充滿了隱藏的業務洞察力。 當數據如此之多時,我們如何分析數據並得出結論? 圖(網絡,而不是條形圖)提供了一種優雅的方法。

我們經常使用表格來概括地表示信息。 但是圖使用了一種專門的數據結構:一個節點代表一個元素,而不是一個表格行。 一條連接兩個節點以指示它們的關係。

這種圖數據結構使我們能夠從獨特的角度觀察數據,這就是為什麼從分子生物學到社會科學的各個領域都使用圖數據科學的原因:

左邊是一個蛋白質相互作用圖,上面有許多大小和顏色不同的點,以及它們之間的不同顏色的線。大多數點(節點)形成一個大的中央集群,但有些點僅在邊緣成對、三胞胎或四胞胎連接,與主集群斷開連接。右側是 Twitter 交互圖,其中節點為亞像素大小,大致分為三組:由小污點和大部分灰色的小點組成的輕雲;以及圍繞前兩組的外部灰色模糊環之前的白色緩衝區。
左圖來源:TITZ、Bjorn 等人。 “梅毒螺旋體的二元蛋白質相互作用組……” PLoS One, 3, no. 5(2008 年)。

右圖來源:ALBANESE、Federico 等人。 “在 Twitter 上使用文本挖掘和圖形機器學習預測不斷變化的個人。” (2020 年 8 月 24 日):arXiv:2008.10749 [cs.SI]

那麼開發人員如何利用圖數據科學呢? 讓我們轉向最常用的數據科學編程語言:Python。

Python中的“圖論”圖入門

Python 開發人員可以使用多個圖形數據庫,例如 NetworkX、igraph、SNAP 和 graph-tool。 除了優缺點,它們在處理和處理 Python 圖形數據結構方面具有非常相似的接口。

我們將使用流行的 NetworkX 庫。 它易於安裝和使用,並支持我們將使用的社區檢測算法。

使用 NetworkX 創建新圖很簡單:

 import networkx as nx G = nx.Graph()

但是G還不是一個圖,沒有節點和邊。

如何將節點添加到圖形

我們可以通過將Graph()的返回值與.add_node() (或.add_nodes_from()用於列表中的多個節點)鏈接起來,將節點添加到網絡中。 我們還可以通過將字典作為參數傳遞給節點添加任意特徵或屬性,正如我們在node 4node 5中展示的那樣:

 G.add_node("node 1") G.add_nodes_from(["node 2", "node 3"]) G.add_nodes_from([("node 4", {"abc": 123}), ("node 5", {"abc": 0})]) print(G.nodes) print(G.nodes["node 4"]["abc"]) # accessed like a dictionary

這將輸出:

 ['node 1', 'node 2', 'node 3', 'node 4', 'node 5'] 123

但是節點之間沒有邊,它們是孤立的,數據集並不比一個簡單的表好。

如何將邊添加到圖形

與節點技術類似,我們可以使用.add_edge()將兩個節點的名稱作為參數(或.add_edges_from()用於列表中的多個邊),並且可以選擇包含屬性字典:

 G.add_edge("node 1", "node 2") G.add_edge("node 1", "node 6") G.add_edges_from([("node 1", "node 3"), ("node 3", "node 4")]) G.add_edges_from([("node 1", "node 5", {"weight" : 3}), ("node 2", "node 4", {"weight" : 5})])

NetworkX 庫支持這樣的圖,其中每條邊都可以有一個權重。 例如,在一個社交網絡圖中,節點是用戶,邊是交互,權重可以表示給定用戶對之間發生了多少交互——這是一個高度相關的指標。

NetworkX 在使用G.edges時列出所有邊,但不包括它們的屬性。 如果我們想要邊屬性,我們可以使用G[node_name]來獲取連接到節點的所有內容,或者使用G[node_name][connected_node_name]來獲取特定邊的屬性:

 print(G.nodes) print(G.edges) print(G["node 1"]) print(G["node 1"]["node 5"])

這將輸出:

 ['node 1', 'node 2', 'node 3', 'node 4', 'node 5', 'node 6'] [('node 1', 'node 2'), ('node 1', 'node 6'), ('node 1', 'node 3'), ('node 1', 'node 5'), ('node 2', 'node 4'), ('node 3', 'node 4')] {'node 2': {}, 'node 6': {}, 'node 3': {}, 'node 5': {'weight': 3}} {'weight': 3}

但是以這種方式閱讀我們的第一個圖表是不切實際的。 值得慶幸的是,有一個更好的表示。

如何從圖(和加權圖)生成圖像

可視化圖表是必不可少的:它可以讓我們快速清晰地看到節點之間的關係和網絡結構。

只需快速調用nx.draw(G)

用黑線連接它們的六個紅點。四個形成一個四邊形,其中一個角連接到其餘兩個。

讓我們通過調用nx.draw()使較重的邊緣相應地變粗:

 weights = [1 if G[u][v] == {} else G[u][v]['weight'] for u,v in G.edges()] nx.draw(G, width=weights)

我們為失重邊緣提供了默認厚度,如結果所示:

與上一張圖像類似,但點位置略有偏移,兩條線突出(一條粗三倍,另一條粗五倍)。

我們的方法和圖算法即將變得更加複雜,因此下一步是使用更知名的數據集。

使用電影《星球大戰:第四集》中的數據進行圖形數據科學

為了更容易解釋和理解我們的結果,我們將使用這個數據集。 節點代表重要角色,而邊緣(此處未加權)表示場景中的共同出現。

注意:數據集來自 Gabasova, E. (2016)。 星球大戰社交網絡。 DOI:https://doi.org/10.5281/zenodo.1411479。

首先,我們將使用nx.draw(G_starWars, with_labels = True)可視化數據:

一個更繁忙的圖表,有 19 個藍點(每個都標有一個大寫的字符名稱),其中許多藍點之間的線條均勻粗線。

通常一起出現的字符,如 R2-D2 和 C-3PO,看起來緊密相連。 相比之下,我們可以看到達斯維德並沒有與歐文分享場景。

Python NetworkX 可視化佈局

為什麼每個節點都位於上圖中的位置?

這是默認spring_layout算法的結果。 它模擬彈簧的力,吸引連接的節點並排斥斷開的節點。 這有助於突出顯示連接良好的節點,這些節點最終位於中心。

NetworkX 有其他佈局,它們使用不同的標準來定位節點,例如circular_layout

 pos = nx.circular_layout(G_starWars) nx.draw(G_starWars, pos=pos, with_labels = True)

結果:

在節點和邊的存在方面完全相同的圖,但藍點形成一個圓圈。 (注意:並非橢圓中的每一對相鄰點都共享一條邊。)

這種佈局是中性的,因為節點的位置不取決於其重要性——所有節點都被平等地表示。 (圓形佈局還可以幫助可視化單獨的連接組件——在任何兩個節點之間都有路徑的子圖——但在這裡,整個圖是一個大的連接組件。)

我們看到的兩種佈局都有一定程度的視覺混亂,因為邊緣可以自由地穿過其他邊緣。 但是像spring_layout這樣的另一種力導向算法 Kamada-Kawai 定位節點以最小化系統的能量。

這減少了邊緣交叉,但要付出代價:它比其他佈局慢,因此不強烈推薦用於具有許多節點的圖形。

這個有一個專門的繪圖功能:

 nx.draw_kamada_kawai(G_starWars, with_labels = True)

這會產生這種形狀:

又是同一張圖。它看起來更像第一個,但藍點分佈更均勻,重疊邊緣更少。

在沒有任何特殊干預的情況下,該算法將主要角色(如盧克、萊婭和 C-3PO)置於中心,將不太突出的角色(如卡米和多多納將軍)置於邊緣。

用特定佈局可視化圖表可以給我們一些有趣的定性結果。 儘管如此,定量結果是任何數據科學分析的重要組成部分,因此我們需要定義一些指標。

節點分析:Degree 和 PageRank

現在我們可以清楚地可視化我們的網絡,我們可能會對節點的特徵感興趣。 有多個指標描述節點的特徵,在我們的示例中,描述了字符的特徵。

一個節點的一個基本指標是它的度數:它有多少條邊。 星球大戰角色節點的程度衡量他們與多少其他角色共享場景。

degree()函數可以計算一個字符或整個網絡的度數:

 print(G_starWars.degree["LUKE"]) print(G_starWars.degree)

兩個命令的輸出:

 15 [('R2-D2', 9), ('CHEWBACCA', 6), ('C-3PO', 10), ('LUKE', 15), ('DARTH VADER', 4), ('CAMIE', 2), ('BIGGS', 8), ('LEIA', 12), ('BERU', 5), ('OWEN', 4), ('OBI-WAN', 7), ('MOTTI', 3), ('TARKIN', 3), ('HAN', 6), ('DODONNA', 3), ('GOLD LEADER', 5), ('WEDGE', 5), ('RED LEADER', 7), ('RED TEN', 2)]

根據程度從高到低對節點進行排序可以用一行代碼完成:

 print(sorted(G_starWars.degree, key=lambda x: x[1], reverse=True))

輸出:

 [('LUKE', 15), ('LEIA', 12), ('C-3PO', 10), ('R2-D2', 9), ('BIGGS', 8), ('OBI-WAN', 7), ('RED LEADER', 7), ('CHEWBACCA', 6), ('HAN', 6), ('BERU', 5), ('GOLD LEADER', 5), ('WEDGE', 5), ('DARTH VADER', 4), ('OWEN', 4), ('MOTTI', 3), ('TARKIN', 3), ('DODONNA', 3), ('CAMIE', 2), ('RED TEN', 2)]

作為一個整體,度數沒有考慮到各個邊緣的細節。 給定的邊是連接到其他孤立的節點還是連接到與整個網絡連接的節點? 谷歌的 PageRank 算法聚合這些信息來衡量一個節點在網絡中的“重要性”。

PageRank 度量可以解釋為代理從一個節點隨機移動到另一個節點。 連接更好的節點有更多的路徑通過它們,因此代理會更頻繁地訪問它們。

這樣的節點會有更高的 PageRank,我們可以用 NetworkX 庫來計算:

 pageranks = nx.pagerank(G_starWars) # A dictionary print(pageranks["LUKE"]) print(sorted(pageranks, key=lambda x: x[1], reverse=True))

這將打印盧克的等級和按等級排序的字符:

 0.12100659993223405 ['OWEN', 'LUKE', 'MOTTI', 'DODONNA', 'GOLD LEADER', 'BIGGS', 'CHEWBACCA', 'LEIA', 'BERU', 'WEDGE', 'RED LEADER', 'RED TEN', 'OBI-WAN', 'DARTH VADER', 'CAMIE', 'TARKIN', 'HAN', 'R2-D2', 'C-3PO']

歐文是PageRank最高的人物,超過了度數最高的盧克。 分析:歐文雖然不是與其他角色分享場景最多的角色,但他是與盧克本人、R2-D2、C-3PO等許多重要角色分享場景的角色。

相比之下,排名第三的角色 C-3PO 是 PageRank 最低的角色。 儘管 C-3PO 有很多聯繫,但其中很多都帶有不重要的角色。

要點:使用多個指標可以更深入地了解圖形節點的不同特徵。

社區檢測算法

在分析網絡時,分離社區可能很重要:彼此高度連接但與社區外節點連接最少的節點組。

為此有多種算法。 它們中的大多數都可以在無監督機器學習算法中找到,因為它們為節點分配了一個標籤,而無需它們之前已經被標記過。

最著名的之一是標籤傳播。 在其中,每個節點都以一個唯一的標籤開始,在一個社區中。 節點的標籤根據相鄰節點的大部分標籤進行迭代更新。

標籤在網絡中擴散,直到所有節點與其大多數鄰居共享一個標籤。 彼此緊密連接的節點組最終具有相同的標籤。

使用 NetworkX 庫,運行這個算法只需要三行 Python:

 from networkx.algorithms.community.label_propagation import label_propagation_communities communities = label_propagation_communities(G_starWars) print([community for community in communities])

輸出:

 [{'R2-D2', 'CAMIE', 'RED TEN', 'RED LEADER', 'OBI-WAN', 'DODONNA', 'LEIA', 'WEDGE', 'HAN', 'OWEN', 'CHEWBACCA', 'GOLD LEADER', 'LUKE', 'BIGGS', 'C-3PO', 'BERU'}, {'DARTH VADER', 'TARKIN', 'MOTTI'}]

在這個集合列表中,每個集合代表一個社區。 熟悉這部電影的讀者會注意到,該算法成功地將“好人”與“壞人”區分開來,在不使用任何真實(社區)標籤或元數據的情況下有意義地區分角色。

在 Python 中使用圖數據科學的智能洞察

我們已經看到,開始使用圖形數據科學工具比聽起來更簡單。 一旦我們使用 Python 中的 NetworkX 庫將數據表示為圖形,幾行簡短的代碼就可以說明問題。 我們可以通過社區檢測算法可視化我們的數據集,測量和比較節點特徵,並明智地聚類節點。

擁有使用 Python 從網絡中提取結論和見解的技能,使開發人員能夠與數據科學服務管道中常見的工具和方法集成。 從搜索引擎到航班調度再到電氣工程,這些方法很容易適用於廣泛的環境。

圖數據科學推薦讀物

社區檢測算法
趙洋、雷內·阿爾格斯海默和克勞迪奧·泰松。 “人工網絡社區檢測算法的比較分析。” 科學報告,6,沒有。 30750 (2016)。

圖深度學習
托馬斯·基普夫。 “圖卷積網絡”。 2016 年 9 月 30 日。

圖數據科學的應用
Albanese、Federico、Leandro Lombardi、Esteban Feuerstein 和 Pablo Balenzuela。 “在 Twitter 上使用文本挖掘和圖形機器學習預測不斷變化的個人。” (2020 年 8 月 24 日):arXiv:2008.10749 [cs.SI]。

科恩,埃利爾。 “PyData 特拉維夫聚會:Node2vec。” YouTube。 2018 年 11 月 22 日。視頻,21:09。 https://www.youtube.com/watch?v=828rZgV9t1g。