igraph 上手教程——使用 Python 開展社會(huì)網(wǎng)絡(luò)分析和可視化

原文地址: https://blog.mariozzj.cn/posts/52d95125/

國(guó)內(nèi)鏡像: https://cnblog.mariozzj.cn/posts/52d95125/

原官方教程(英文):https://igraph.org/python/tutorial/0.9.7/tutorial.html


igraph

igraph 是一個(gè)開源免費(fèi)網(wǎng)絡(luò)分析工具集合,在效率和便捷性上表現(xiàn)較好。之前所學(xué)和網(wǎng)絡(luò)上的教程大多基于 R 語(yǔ)言,而實(shí)際上 igraph 為 R坷随、Python灵再、Mathematica 和 C/C++ 均有支持被廓,可以前往 [igraph 官網(wǎng)](igraph – Network analysis software) 了解鲜棠。

之前使用過 R 包的 igraph,但是我的 R 語(yǔ)言實(shí)在學(xué)藝不精滚婉,之后也沒怎么用到過。在我的數(shù)據(jù)分析場(chǎng)景下帅刀,Python 可能相對(duì)于熟悉一些让腹,且可以聯(lián)動(dòng)實(shí)現(xiàn)數(shù)據(jù)處理和分析,感覺非常方便扣溺。

igraph 的 Python 包的使用文檔正在完善中骇窍,網(wǎng)絡(luò)上幾經(jīng)查找并沒有完全完整的中文版本手冊(cè),所以我本著學(xué)習(xí)這個(gè)程序包的目的順便將其最新(0.9.7)的英文版本操作手冊(cè)的概覽教程翻譯下來锥余,主要來源于 Tutorial (igraph.org) 腹纳,如有翻譯不地道的地方也請(qǐng)各位指正!整體閱讀下來感覺手冊(cè)寫的還是比較簡(jiǎn)明驱犹、全面的嘲恍。

安裝 igraph

使用 pip 安裝 igraph

pip install python-igraph

可以繼續(xù)安裝 pycairo 用于支持網(wǎng)絡(luò)的可視化

pip install pycairo

啟動(dòng) Python 運(yùn)行如下代碼,檢查是否安裝成功:

import igraph as ig
petersen = ig.Graph.Famous("petersen")
ig.plot(petersen)
案例 - Petersen 網(wǎng)絡(luò)圖

如安裝無誤着绷,展示的是著名的 Petersen 圖蛔钙。

igraph 使用教程

從零搭建圖

引入 igraph 包后,可以自行創(chuàng)建一個(gè)圖:

>>> g = ig.Graph()
>>> g
<igraph.Graph at 0x294fbc389a0>
>>> print(g)
IGRAPH U--- 0 0 --

在這里荠医,g 是一個(gè) Graph 實(shí)例吁脱。打印出來的結(jié)果中的兩個(gè)數(shù)字是節(jié)點(diǎn)數(shù)和連邊數(shù)。如果我們打印上一節(jié)的 Petersen 圖彬向,會(huì)這么返回:

>>> print(petersen)
IGRAPH U--- 10 15 --
+ edges:
 0 --  1  4  5    3 --  2  4  8    6 --  1  8  9    9 --  4  6  7
 1 --  0  2  6    4 --  0  3  9    7 --  2  5  9
 2 --  1  3  7    5 --  0  7  8    8 --  3  5  6

這里還給出了具體的連邊信息兼贡,Petersen 圖中有 10 個(gè)節(jié)點(diǎn)和 15 條連邊,每個(gè)節(jié)點(diǎn)都與三個(gè)節(jié)點(diǎn)相連娃胆。

我們可以調(diào)用 add_vertices(num) 方法為我們創(chuàng)建的圖增加 num 個(gè)節(jié)點(diǎn)遍希,使用 add_edges([pairs]) 方法增加連邊。pairs 為兩個(gè)節(jié)點(diǎn)編號(hào)組成的元組里烦,可以輸入一至多個(gè)凿蒜,如

>>> g.add_vertices(6)
>>> g.add_edges([(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)])
>>> print(g)
IGRAPH U--- 6 7 --
+ edges:
0--1 1--2 0--2 2--3 3--4 4--5 3--5

目前的這個(gè)網(wǎng)絡(luò)中有 6 個(gè)節(jié)點(diǎn)和 7 條連邊禁谦。在構(gòu)建的網(wǎng)絡(luò)中,節(jié)點(diǎn) ID 是由 0 開始的連續(xù)整數(shù)废封,添加連邊時(shí)的編號(hào)對(duì)就對(duì)應(yīng)著這些 ID州泊。而連邊其實(shí)也是有 ID 的,也是由 0 開始的連續(xù)整數(shù)漂洋。節(jié)點(diǎn)和連邊 ID 的連續(xù)性不會(huì)改變遥皂,所以如果我們刪除節(jié)點(diǎn)和連邊,可能會(huì)造成部分節(jié)點(diǎn)和連邊 ID 的變化刽漂。

如果兩個(gè)節(jié)點(diǎn)之間存在連邊演训,我們可以使用 get_eid()方法獲取他們的連邊的編號(hào):

>>> g.get_eid(2,3)
3

可以使用 delete_vertices() 方法刪除圖中的節(jié)點(diǎn),可以使用 delete_edges() 刪除圖中的連邊贝咙,括號(hào)內(nèi)的參數(shù)為一至多個(gè)節(jié)點(diǎn)/連邊的 ID样悟。

>>> g.delete_edges(3)
>>> ig.summary(g)
IGRAPH U--- 6 6 --

summary() 方法可以只展示圖的基本信息,相比于 print() 可以避免大圖中大量連邊占用輸出颈畸。

生成圖

igraph 包中有多種圖生成器乌奇,大體上可以分為兩種:確定性(Deterministic)和隨機(jī)性(Stochastic)圖生成器。如果調(diào)用時(shí)輸入相同的參數(shù)眯娱,確定性圖生成器創(chuàng)建出來的圖是完全相同的礁苗,而隨機(jī)性圖生成器則會(huì)生成不同的圖。

確定性圖生成器包括創(chuàng)建樹圖徙缴、正則格(Regular Lattice)试伙、環(huán)(Ring)、擴(kuò)展弦環(huán)(Extended Chordal Ring)等著名圖于样。如Graph.Tree() 方法可以創(chuàng)建樹圖:

>>> g = ig.Graph.Tree(127, 2)
>>> ig.summary(g)
IGRAPH U--- 127 126 --
>>> g2 = ig.Graph.Tree(127, 2)
>>> g2.get_edgelist() == g.get_edgelist()
True
>>> g2.get_edgelist()[0:2]
[(0, 1), (0, 2)]

創(chuàng)建了樹圖 g疏叨,共計(jì) 127 個(gè)節(jié)點(diǎn),除葉子節(jié)點(diǎn)外每個(gè)節(jié)點(diǎn)都有 2 個(gè)孩子節(jié)點(diǎn)穿剖,所以共有 126 條連邊蚤蔓。后面使用相同參數(shù)創(chuàng)建 g2,可發(fā)現(xiàn) gg2 連邊完全相同糊余。這里用到的 get_edgelist() 方法會(huì)返回連邊元組組成的列表秀又。

隨機(jī)性圖生成器可以創(chuàng)建 ER 隨機(jī)圖(Erd?s-Rényi Random Network)、Barabási-Albert 網(wǎng)絡(luò)模型贬芥、隨機(jī)幾何圖形(Geometric Random graphs)等吐辙。如Graph.GRG() 方法可以創(chuàng)建隨機(jī)幾何圖:

>>> g = ig.Graph.GRG(100, 0.2)
>>> ig.summary(g)
IGRAPH U---- 100 516 --
+ attr: x (v), y (v)

在創(chuàng)建隨機(jī)幾何圖 GRG(n,d) 的兩個(gè)參數(shù)中,n 指定了節(jié)點(diǎn)數(shù)量蘸劈,d 則為連邊距離閾值昏苏,所有點(diǎn)在單元空間中隨機(jī)分布,距離小于 d 的則建立連邊,這樣一種建圖規(guī)則會(huì)導(dǎo)致即使給出相同的參數(shù)贤惯,創(chuàng)建出來的圖也是不一樣的:

>>> g2 = ig.Graph.GRG(100, 0.2)
>>> g.get_edgelist() == g2.get_edgelist()
False
>>> g.isomorphic(g2)
False

同樣參數(shù)創(chuàng)建的 g2g 的邊-節(jié)點(diǎn) ID 關(guān)系不同洼专。使用isomorphic() 方法能夠判定兩個(gè)圖是否同構(gòu)(isomorphic),很顯然從結(jié)果看救巷,這次實(shí)驗(yàn)中二者也不是同構(gòu)的壶熏。

在 GRG 的 summary 中句柠,我們看到了圖的參數(shù)(attr)被打印出來浦译,接下來將繼續(xù)介紹。

設(shè)置與獲取屬性

igraph 使用 ID 區(qū)分不同的節(jié)點(diǎn)和連邊溯职,之前提到過這些 ID 是由 0 開始的連續(xù)的整數(shù)精盅,當(dāng)我們刪除節(jié)點(diǎn)和連邊時(shí)這種連續(xù)性不會(huì)被破壞,所以執(zhí)行刪除操作時(shí)可能涉及到其他節(jié)點(diǎn)/連邊 ID 的變化谜酒。

假設(shè)我們使用 igraph 做社會(huì)網(wǎng)絡(luò)分析叹俏,節(jié)點(diǎn)代表人,連邊代表人之間的社會(huì)聯(lián)系僻族,一種可能的建立節(jié)點(diǎn) ID 和人物名對(duì)應(yīng)關(guān)系的方法是創(chuàng)建一個(gè) Python 列表將 ID 和姓名對(duì)應(yīng)起來粘驰,這種方式的缺陷是這個(gè)列表需要隨網(wǎng)絡(luò)的調(diào)整而同步調(diào)整,這樣或許有些麻煩述么。

igraph 支持為節(jié)點(diǎn)蝌数、連邊、圖對(duì)象添加屬性度秘。首先顶伞,我們創(chuàng)建一個(gè)簡(jiǎn)單的社會(huì)網(wǎng)絡(luò):

>>> g = ig.Graph([(0,1), (0,2), (2,3), (3,4), (4,2), (2,5), (5,0), (6,3), (5,6)])

假設(shè)我們要為節(jié)點(diǎn)(人)添加姓名、年齡剑梳、性別屬性唆貌,同時(shí)對(duì)于連邊(社會(huì)聯(lián)系)添加是否為正式聯(lián)系的標(biāo)注屬性。對(duì)于 igraph 包中的每個(gè) Graph 對(duì)象垢乙,可以調(diào)用其成員變量 vses 锨咙,即節(jié)點(diǎn)序列 VertexSeq(Vertice Sequence)和連邊序列 EdgeSeq(Edge Sequence),可以將其當(dāng)作 Python 中的字典(dict)對(duì)象使用:

>>> g.vs
<igraph.VertexSeq object at 0x1b23b90>
>>> g.vs["name"] = ["Alice", "Bob", "Claire", "Dennis", "Esther", "Frank", "George"]
>>> g.vs["age"] = [25, 31, 18, 47, 22, 23, 50]
>>> g.vs["gender"] = ["f", "m", "f", "m", "f", "m", "m"]
>>> g.es["is_formal"] = [False, False, True, True, True, False, True, False, False]

當(dāng)你將 vs/es 當(dāng)作字典使用時(shí)追逮,就是將屬性值分配給圖中的所有節(jié)點(diǎn)/連邊酪刀。當(dāng)然我們也可以將 vs/es 當(dāng)作 Python 的列表(list)使用,給出索引值獲取特定節(jié)點(diǎn)/連邊羊壹,對(duì)于獲取到的節(jié)點(diǎn)/連邊蓖宦,也可以(像字典一樣使用)修改它的屬性:

>>> g.es[0]
igraph.Edge(<igraph.Graph object at 0x4c87a0>,0,{'is_formal': False})
>>> g.es[0].attributes()
{'is_formal': False}
>>> g.es[0]["is_formal"] = True
>>> g.es[0]
igraph.Edge(<igraph.Graph object at 0x4c87a0>,0,{'is_formal': True})

從示例可以看出,es 對(duì)象是邊序列油猫,其中的每一個(gè)元素都是 Edge 對(duì)象稠茂,打印出來的值是該邊所屬的圖、邊 ID、和屬性組成的字典睬关。Edge 對(duì)象有一些有用的屬性诱担,如:source,展示邊的源節(jié)點(diǎn)电爹;target蔫仙,展示邊的目標(biāo)節(jié)點(diǎn);index丐箩,獲得邊 ID摇邦;tuple,獲得源節(jié)點(diǎn)和目標(biāo)節(jié)點(diǎn)組成的元組屎勘;以及這里用到的 attributes() 施籍,獲得有邊的所有屬性及值組成的字典。

Graph.es 對(duì)象一般是一個(gè)圖中所有邊的集合概漱,索引值 i 一般都會(huì)返回 ID 為 i 的邊丑慎,對(duì) Graph.vs 也同理。但是需要注意瓤摧,并不是所有的邊序列對(duì)象都是某個(gè)圖的所有邊的集合竿裂,對(duì)節(jié)點(diǎn)序列對(duì)象也是如此,在后文會(huì)介紹特例照弥。

之前提到過腻异,不僅對(duì)于節(jié)點(diǎn)和邊,圖對(duì)象也可以設(shè)置參數(shù):

>>> g["date"] = "2009-01-10"
>>> print(g["date"])
2009-01-10

最后产喉,如果需要?jiǎng)h除屬性捂掰,可以使用 Python 關(guān)鍵字 del,像刪除字典中鍵值對(duì)一樣刪除屬性:

>>> g.vs[3]["foo"] = "bar"
>>> g.vs["foo"]
[None, None, None, 'bar', None, None, None]
>>> del g.vs["foo"]
>>> g.vs["foo"]
Traceback (most recent call last):
  File "<stdin>", line 25, in <module>
KeyError: 'Attribute does not exist'

圖的結(jié)構(gòu)性指標(biāo)計(jì)算

igraph 提供許多計(jì)算圖的結(jié)構(gòu)性指標(biāo)的方法曾沈,本節(jié)僅選取部分進(jìn)行介紹这嚣,將繼續(xù)沿用上一節(jié)我們建立的圖。

大多數(shù)人能想到的最簡(jiǎn)單的指標(biāo)就是節(jié)點(diǎn)的度數(shù)塞俱。節(jié)點(diǎn)的度等于與該節(jié)點(diǎn)建立連邊的節(jié)點(diǎn)數(shù)姐帚,在有向圖中度還分入度(指向該節(jié)點(diǎn)的連邊數(shù))和出度(由該節(jié)點(diǎn)發(fā)出的連邊數(shù)),這些指標(biāo) igraph 都可以計(jì)算障涯,例如要計(jì)算度罐旗,可以使用 degree() 方法。

>>> g.degree()
[3, 1, 4, 3, 2, 3, 2]

如果要在有向圖中計(jì)算入度和出度唯蝶,只需調(diào)用時(shí)添加 mode 參數(shù)九秀,如 g.degree(mode="in")g.degree(mode="out")粘我。也可以添加一個(gè)或多個(gè)節(jié)點(diǎn) ID鼓蜒,以獲取特定節(jié)點(diǎn)的度痹换。

>>> g.degree(6)
2
>>> g.degree([2,3,4])
[4, 3, 2]

這樣一種填入一個(gè)或多個(gè)節(jié)點(diǎn) ID 的方式適用于大多數(shù) igraph 中結(jié)構(gòu)性指標(biāo)的計(jì)算方法。除了填入 ID都弹,當(dāng)然還可以填入 VertexSeqEdgeSeq 實(shí)例娇豫,下一篇文章會(huì)予以介紹。有一些指標(biāo)相比于使用整個(gè)圖畅厢,使用部分節(jié)點(diǎn)和連邊計(jì)算的結(jié)果是沒有意義的冯痢,這種情況下可能就不支持這樣的輸入,但是可以對(duì)結(jié)果進(jìn)行索引來獲取部分結(jié)果(如 Graph.evcent()

除了度數(shù)框杜,igraph 還可以計(jì)算中心性指標(biāo)浦楣,包括節(jié)點(diǎn)和邊的中介中心性(方法:Graph.betweenness()Graph.edge_betweenness() )或者 Google 的 PageRank (Graph.pagerank())等霸琴。這里介紹邊中介中心性的計(jì)算椒振。邊中介中心性是網(wǎng)絡(luò)中所有最短路徑中經(jīng)過該邊的路徑的數(shù)目。

>>> g.edge_betweenness()
[6.0, 6.0, 4.0, 2.0, 4.0, 3.0, 4.0, 3.0. 4.0]

計(jì)算得到所有邊的中介中心性梧乘,然后我們可以綜合運(yùn)用之前的方法,找到那些中介中心性較高的邊:

>>> ebs = g.edge_betweenness()
>>> max_eb = max(ebs)
>>> [g.es[idx].tuple for idx, eb in enumerate(ebs) if eb == max_eb]
[(0, 1), (0, 2)]

大多數(shù)結(jié)構(gòu)性指標(biāo)計(jì)算可以就部分節(jié)點(diǎn)/連邊得出庐杨,即可以用 VertexSeq选调,EdgeSeqVertex灵份,Edge 對(duì)象計(jì)算:

>>> g.vs.degree()
[3, 1, 4, 3, 2, 3, 2]
>>> g.es.edge_betweenness()
[6.0, 6.0, 4.0, 2.0, 4.0, 3.0, 4.0, 3.0. 4.0]
>>> g.vs[2].degree()
4

使用參數(shù)查找特定節(jié)點(diǎn)和連邊

選擇節(jié)點(diǎn)和連邊

假設(shè)在某個(gè)社會(huì)網(wǎng)絡(luò)中仁堪,你需要找出具有最大點(diǎn)度或中介中心度的人,除了使用之前給出的方式結(jié)合一些你可能掌握的 Python 方法填渠,igraph 提供了更簡(jiǎn)單的方式:

>>> g.vs.select(_degree=g.maxdegree())["name"]
["Alice", "Bob"]

這種表達(dá)乍一看有些奇怪弦聂,我們可以一步步拆解來理解。select()VertexSeq 的一個(gè)方法氛什,它的唯一目標(biāo)是通過節(jié)點(diǎn)的指標(biāo)來過濾節(jié)點(diǎn)集合莺葫,通過添加位置參數(shù)或關(guān)鍵字參數(shù)可以實(shí)現(xiàn)。一般來說位置參數(shù)沒有顯式定義的名稱(如_degree)枪眉,在關(guān)鍵字參數(shù)前讀取捺檬。

  • 如果首個(gè)位置參數(shù)為 None,會(huì)返回一個(gè)空序列:

    >>> seq = g.vs.select(None)
    >>> len(seq)
    0
    
  • 如果首個(gè)位置參數(shù)為可調(diào)用對(duì)象(如函數(shù)贸铜、綁定方法等)堡纬,這個(gè)對(duì)象會(huì)逐一被節(jié)點(diǎn)序列 vs 中的節(jié)點(diǎn)調(diào)用。若函數(shù)返回結(jié)果為 True蒿秦,當(dāng)前節(jié)點(diǎn)會(huì)被包含在結(jié)果中烤镐,否則被排除在結(jié)果之外。

    >>> graph = Graph.Full(10)
    >>> only_odd_vertices = graph.vs.select(lambda vertex: vertex.index % 2 == 1)
    >>> len(only_odd_vertices)
    5
    
  • 如果首個(gè)位置參數(shù)是可迭代的對(duì)象(如列表棍鳖、生成器等)炮叶,該對(duì)象的返回值必須為整數(shù),且這些整數(shù)會(huì)作為當(dāng)前節(jié)點(diǎn)序列(并不一定是整個(gè)圖的所有節(jié)點(diǎn))的索引值使用,僅有處于這些索引值位置的節(jié)點(diǎn)會(huì)進(jìn)入結(jié)果集悴灵。返回值中的浮點(diǎn)數(shù)扛芽、字符串和無效的節(jié)點(diǎn) ID 值會(huì)被忽略:

    >>> seq = graph.vs.select([2, 3, 7])
    >>> len(seq)
    3
    >>> [v.index for v in seq]
    [2, 3, 7]
    >>> seq = seq.select([0, 2])         # filtering an existing vertex set
    >>> [v.index for v in seq]
    [2, 7]
    >>> seq = graph.vs.select([2, 3, 7, "foo", 3.5])
    >>> len(seq)
    3
    
  • 如果首個(gè)位置參數(shù)為整數(shù),所有余下的位置參數(shù)都應(yīng)是整數(shù)积瞒,這些參數(shù)被當(dāng)作索引值使用川尖,處于這些索引值位置的節(jié)點(diǎn)會(huì)進(jìn)入結(jié)果集。

    >>> seq = graph.vs.select(2,3,7)
    >>> len(seq)
    3
    

除了位置參數(shù)之外茫孔,select() 方法支持一系列關(guān)鍵字參數(shù)(具有顯式定義的參數(shù)名):

關(guān)鍵字參數(shù) 含義
name_eq 參數(shù)/屬性必須等于給出的參數(shù)值
name_ne 參數(shù)/屬性必須不等于給出的參數(shù)值
name_lt 參數(shù)/屬性必須小于給出的參數(shù)值
name_le 參數(shù)/屬性必須小于等于給出的參數(shù)值
name_gt 參數(shù)/屬性必須大于給出的參數(shù)值
name_ge 參數(shù)/屬性必須大于等于給出的參數(shù)值
name_in 參數(shù)/屬性必須在給出的參數(shù)值之中叮喳,參數(shù)值須為序列
name_notin 參數(shù)/屬性必須不在給出的參數(shù)值之中,參數(shù)值須為序列

例如缰贝,下面這一行代碼能返回 vsage 小于 30 的記錄:

>>> g.vs.select(age_lt=30)

當(dāng)然馍悟,select() 也可以以簡(jiǎn)略形式調(diào)用,下面這行與上面那行等價(jià):

>>> g.vs(age_lt=30)

不能寫作 g.vs(age < 30) 剩晴,因?yàn)閰?shù)列表中只有 = 是被允許的锣咒。

有時(shí)可能會(huì)出現(xiàn)已定義屬性與結(jié)構(gòu)指標(biāo)值名相同的情況,如點(diǎn)度(degree)和你定義的 degree 屬性赞弥,為解決歧義問題毅整,如需將結(jié)構(gòu)指標(biāo)名用于篩選節(jié)點(diǎn),必須在指標(biāo)名前加 _绽左。

>>> g.vs(_degree_gt=2)

除了度以外悼嫉,還有一些特殊的結(jié)構(gòu)指標(biāo)名用于篩選節(jié)點(diǎn):

  • 使用 _source_from 用于邊篩選,篩選那些來源節(jié)點(diǎn)為特定節(jié)點(diǎn)的邊拼窥,如篩選來源為 ID 為 2 的邊:

    >>> g.es.select(_source=2)
    
  • 使用 _target_to 用于邊篩選戏蔑,篩選那些目標(biāo)節(jié)點(diǎn)為特定節(jié)點(diǎn)的邊,用法同上鲁纠。

  • _within 參數(shù)傳入 VertexSeq 或索引值序列(列表或集合)总棵,選取包含在給出的節(jié)點(diǎn)(索引)序列中的節(jié)點(diǎn)。

    >>> g.es.select(_within=[2,3,4])
    >>> g.es.select(_within=g.vs[2:5])
    
  • _between 參數(shù)傳入兩個(gè)由 VertexSeq 或包含索引值的列表房交,或 Vertex 對(duì)象組成的元組彻舰,選取那些從一組的節(jié)點(diǎn)發(fā)出,指向另一組的節(jié)點(diǎn)的邊集合候味。

    >>> men = g.vs.select(gender="m")
    >>> women = g.vs.select(gender="f")
    >>> g.es.select(_between=(men,women))
    

使用某些屬性尋找單一節(jié)點(diǎn)或連邊

有時(shí)我們會(huì)用許多屬性限定在圖中尋找某一個(gè)特定的節(jié)點(diǎn)或連邊刃唤。如果這樣的條件下有多個(gè)結(jié)果返回,我們可能不關(guān)心返回哪一個(gè)結(jié)果白群,或者我們預(yù)先知道這樣的條件下只會(huì)返回一個(gè)結(jié)果尚胞,比如我們用 name 屬性查找某個(gè)姓名為指定值的節(jié)點(diǎn)。

VertexSeqEdgeSeq 對(duì)象提供 find() 方法供以上場(chǎng)景使用帜慢。與 select() 不同的是笼裳,find() 僅返回結(jié)果集的第一個(gè)結(jié)果(如果沒有結(jié)果則報(bào)錯(cuò))唯卖。比如:

claire = g.vs.find(name="Claire")
>>> type(claire)
igraph.Vertex
>>> claire.index
2

如果沒有這樣的結(jié)果則會(huì)報(bào)錯(cuò):

>>> g.vs.find(name="Joe")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: no such vertex

使用 name 找節(jié)點(diǎn)

相比于 ID,節(jié)點(diǎn)的名字似乎更容易被記憶躬柬。igraph 對(duì) name 屬性做了特殊處理拜轨,將其設(shè)置為“索引”,這樣用 name 也可以快速地查找到節(jié)點(diǎn)允青,而且為了讓使用更簡(jiǎn)單橄碾,name 可以作為“索引”在所有 ID 可以出現(xiàn)的地方出現(xiàn),包含列表等情況颠锉。

比如計(jì)算某一節(jié)點(diǎn)的度:

>>> g.degree("Dennis")
3
>>> g.vs.find("Dennis").degree()
3

igraph 在后臺(tái)維持著 ID 和 name 的對(duì)應(yīng)關(guān)系法牲,當(dāng)圖改變時(shí)這個(gè)對(duì)應(yīng)關(guān)系也會(huì)對(duì)應(yīng)調(diào)整。和 ID 不同琼掠,igraph 允許重復(fù)的 name 拒垃,但是當(dāng)你在使用 name 尋找某個(gè) name 與其他節(jié)點(diǎn)重復(fù)的節(jié)點(diǎn)時(shí),igraph 只會(huì)返回他們之中的一個(gè)瓷蛙,如果要尋找其他的節(jié)點(diǎn)可能只能用其他辦法了悼瓮。

圖與鄰接矩陣

鄰接矩陣是表示圖的一種方式,在鄰接矩陣中速挑,行列均為節(jié)點(diǎn)編號(hào)谤牡,對(duì)應(yīng)的值表明節(jié)點(diǎn)之間是否有連邊。如矩陣中的第 i 行第 j 列的值表明節(jié)點(diǎn) i 和節(jié)點(diǎn) j 是否有連邊姥宝。要獲取圖的鄰接矩陣,可以使用 get_adjacency() 方法恐疲。

>>> g.get_adjacency()
Matrix([[0, 1, 1, 0, 0, 1, 0], [1, 0, 0, 0, 0, 0, 0], [1, 0, 0, 1, 1, 1, 0], [0, 0, 1, 0, 1, 0, 1], [0, 0, 1, 1, 0, 0, 0], [1, 0, 1, 0, 0, 0, 1], [0, 0, 0, 1, 0, 1, 0]])
>>> print(g.get_adjacency())
[[0, 1, 1, 0, 0, 1, 0]
 [1, 0, 0, 0, 0, 0, 0]
 [1, 0, 0, 1, 1, 1, 0]
 [0, 0, 1, 0, 1, 0, 1]
 [0, 0, 1, 1, 0, 0, 0]
 [1, 0, 1, 0, 0, 0, 1]
 [0, 0, 0, 1, 0, 1, 0]]

從輸出可以看出腊满,ID 為 2 的節(jié)點(diǎn)( [1, 0, 0, 1, 1, 1, 0] )和 ID 為 0、3培己、4碳蛋、5 的節(jié)點(diǎn)相連,而和 ID 為 1省咨、6 的節(jié)點(diǎn)之間沒有連邊肃弟。

布局與繪圖

之前的介紹中,圖是一個(gè)相對(duì)抽象的數(shù)學(xué)概念零蓉,我們沒有將其映射至 2D 或 3D 空間笤受。如果我們想將圖可視化,我們首先需要將節(jié)點(diǎn)對(duì)應(yīng)到二維或三維空間內(nèi)敌蜂。圖繪制(Graph Drawing)——圖理論的一個(gè)分支箩兽,嘗試運(yùn)用多種圖布局算法解決這個(gè)映射問題。igraph 引入了部分布局算法章喉,并且可以結(jié)合 Cairo 包(本文開始時(shí)安裝過)將其繪制為 PDF汗贫、PNG身坐、SVG 等形式。

布局算法

布局方法是 Graph 對(duì)象的成員方法落包,一般都以 layout_ 起頭:

方法全名 簡(jiǎn)稱 算法描述
layout_circle circle , circular 將節(jié)點(diǎn)放在一個(gè)圓環(huán)上
layout_drl drl 常用于大圖部蛇,分散遞歸布局
layout_fruchterman_reingold fr Fruchterman-Reingold 力導(dǎo)向布局
layout_fruchterman_reingold_3d fr3d , fr_3d Fruchterman-Reingold 力導(dǎo)向布局(3D 版)
layout_kamada_kawai kk Kamda-Kawai 力導(dǎo)向布局
layout_kamada_kawai_3d kk3d , kk_3d Kamda-Kawai 力導(dǎo)向布局(3D 版)
layout_lgl large , lgl , large_graph 常用于大圖,大圖布局
layout_random random 將節(jié)點(diǎn)完全隨機(jī)放置
layout_random_3d random_3d 將節(jié)點(diǎn)完全隨機(jī)放置在三維空間
layout_reingold_tilford rt , tree Reingold-Tilford 樹狀布局咐蝇,對(duì)類樹狀圖有用
layout_reingold_tilford_circular rt_citcular , tree 極坐標(biāo)轉(zhuǎn)換后 Reingold-Tilford 樹狀布局涯鲁,對(duì)類樹狀圖有用
layout_sphere sphere , spherical , circular_3d 將節(jié)點(diǎn)放在球面

布局方法可以直接被調(diào)用,也可以使用通用的 layout() 方法:

>>> layout = g.layout_kamada_kawai()
>>> layout = g.layout("kamada_kawai")

layout() 方法的首個(gè)參數(shù)必須為布局算法的簡(jiǎn)稱(如上表)嘹害,余下的位置參數(shù)和關(guān)鍵字參數(shù)都為布局算法使用撮竿,例如下面兩行等價(jià)語(yǔ)句:

>>> layout = g.layout_reingold_tilford(root=[2])
>>> layout = g.layout("rt", [2])

布局方法返回 Layout 對(duì)象,這個(gè)對(duì)象像一個(gè)列表組成的列表笔呀,每個(gè)列表對(duì)應(yīng)著節(jié)點(diǎn)在 2D 或 3D 空間中的位置坐標(biāo)幢踏。Layout 對(duì)象包含一些有用的方法,可以批量轉(zhuǎn)換许师、縮放房蝉、旋轉(zhuǎn)坐標(biāo)。當(dāng)然微渠,其主要的作用還是將數(shù)據(jù)傳遞給 plot() 方法搭幻,獲得 2D 圖繪制。

使用布局繪圖

我們可以用之前的社會(huì)網(wǎng)絡(luò)繪圖逞盆,采用 Kamada-Kawai 布局:

>>> layout = g.layout("kk")
>>> ig.plot(g, layout=layout)

這將繪制出和下圖近似的網(wǎng)絡(luò)圖(實(shí)際展現(xiàn)出的樣子會(huì)隨機(jī)變化):

Kamada-Kawai 布局社會(huì)網(wǎng)絡(luò)圖

如果想使用 matplotlib 作為繪圖引擎檀蹋,需要使用 target 參數(shù)創(chuàng)建軸:

>>> import matplotlib.pyplot as plt
>>> fig, ax = plt.subplots()
>>> ig.plot(g, layout=layout, target=ax)

目前還不是很美觀,我們可以將人名標(biāo)注在節(jié)點(diǎn)附近云芦,將節(jié)點(diǎn)按照性別上色俯逾。圖中的節(jié)點(diǎn)標(biāo)簽?zāi)J(rèn)來源于 label 參數(shù),節(jié)點(diǎn)的顏色由 color 標(biāo)簽決定舅逸,所以可以調(diào)整后重新繪制:

>>> g.vs["label"] = g.vs["name"]
>>> color_dict = {"m": "blue", "f": "pink"}
>>> g.vs["color"] = [color_dict[gender] for gender in g.vs["gender"]]
>>> ig.plot(g, layout=layout, bbox=(300, 300), margin=20)
>>> # ig.plot(g, layout=layout, bbox=(300, 300), margin=20, target=ax) # matplotlib 版本
美化 - Kamada-Kawai 布局社會(huì)網(wǎng)絡(luò)圖

我們重用了之前的布局對(duì)象桌肴,但是我們也注明了我們需要一個(gè)較小的畫布(300 × 300 像素)和 20 像素的邊距使得標(biāo)簽?zāi)軌蛲耆故尽?/p>

除了在節(jié)點(diǎn)和連邊的屬性中設(shè)置,你當(dāng)然也可以在 plot() 時(shí)傳入?yún)?shù)達(dá)到相同效果:

color_dict = {"m": "blue", "f": "pink"}
>>> plot(g, layout=layout, vertex_color=[color_dict[gender] for gender in g.vs["gender"]])

如果我們想將節(jié)點(diǎn)屬性數(shù)據(jù)和繪圖完全分離開來琉历,這是一種推薦的方式坠七。當(dāng)然,我們也可以完全用一個(gè)字典來存儲(chǔ)上述布局參數(shù)旗笔,最后使用 ** 操作符將樣式參數(shù)一并傳入:

>>> visual_style = {}
>>> visual_style["vertex_size"] = 20
>>> visual_style["vertex_color"] = [color_dict[gender] for gender in g.vs["gender"]]
>>> visual_style["vertex_label"] = g.vs["name"]
>>> visual_style["edge_width"] = [1 + 2 * int(is_formal) for is_formal in g.es["is_formal"]]
>>> visual_style["layout"] = layout
>>> visual_style["bbox"] = (300, 300)
>>> visual_style["margin"] = 20
>>> plot(g, **visual_style)
美化2 - Kamada-Kawai 布局社會(huì)網(wǎng)絡(luò)圖

節(jié)點(diǎn)和邊的繪圖布局參數(shù)

上一節(jié)看到了很多節(jié)點(diǎn)和邊的布局參數(shù)彪置,傳入這些參數(shù)能夠覆蓋 igraph 繪圖的默認(rèn)設(shè)置參數(shù)。實(shí)際上换团,有大量的參數(shù)可以用于調(diào)整布局:

節(jié)點(diǎn)繪圖布局參數(shù)

參數(shù)名 關(guān)鍵參數(shù) 用處
color vertex_color 節(jié)點(diǎn)的顏色
font vertex_font 節(jié)點(diǎn)字體
label vertex_label 節(jié)點(diǎn)標(biāo)簽
label_angel vertex_label_angle 節(jié)點(diǎn)標(biāo)簽擺放在節(jié)點(diǎn)的相對(duì)方向角度悉稠,例如 0 度時(shí)是正右方
label_color vertex_label_color 節(jié)點(diǎn)標(biāo)簽的顏色
label_dist vertex_label_dist 節(jié)點(diǎn)標(biāo)簽距離節(jié)點(diǎn)的距離,與節(jié)點(diǎn)大小相關(guān)
label_size vertex_label_size 節(jié)點(diǎn)標(biāo)簽的字體大小
order vertex_order 繪制節(jié)點(diǎn)的順序艘包;序號(hào)較小的節(jié)點(diǎn)會(huì)被優(yōu)先繪制
shape vertex_shape 節(jié)點(diǎn)的形狀的猛;可用形狀有:rectangle(正方形)耀盗、circle(圓形)、hidden(隱藏)卦尊、triangle-up(三角形)叛拷、triangle-down(倒三角),可以查看 drawing_shapes 獲取更多形狀
size vertex_size 節(jié)點(diǎn)的大衅袢础(單位:像素)

連邊繪圖布局參數(shù)

參數(shù)名 關(guān)鍵參數(shù) 用處
color edge_color 邊的顏色
curved edge_curved 邊的曲率忿薇;0 代表直邊,負(fù)數(shù)時(shí)邊向順時(shí)針方向彎曲躏哩,正數(shù)時(shí)邊向逆時(shí)針方向彎曲署浩,True 視為 0.5,False 視為 0扫尺;使用該參數(shù)可以讓多條邊不互相遮擋筋栋,可以查看 plot()autocurve 參數(shù)
font edge_font 邊的字體
arrow_size edge_arrow_size 有向圖中,邊的箭頭的大姓ぁ(相對(duì)于 15 像素)
arrow_width edge_arrow_width 有向圖中弊攘,邊的箭頭的寬度(相對(duì)于 10 像素)
width edge_width 邊的寬度(單位:像素)

plot() 的通用關(guān)鍵字參數(shù)

用于 plot() 的關(guān)鍵字參數(shù)如下:

關(guān)鍵字參數(shù) 用處
autocurve 決定繪圖時(shí)多邊是否自動(dòng)彎曲;10 000 條以下邊的情況默認(rèn)為 True姑曙,其余為 False
bbox 畫布的邊界框(Bounding BOX)大小襟交,須為指定寬度和高度的元組;默認(rèn)為 (600,600)
layout 采用的布局伤靠〉酚颍可以是 Layout 實(shí)例,包含坐標(biāo)元組的列表宴合,或者是布局算法的名稱竟宋;默認(rèn)為 auto,根據(jù)圖的大小和連接程度自動(dòng)決定布局
margin 畫布的 上/右/下/左 邊距纺讲,須為數(shù)組或列表轰坊;如果填寫的元素少于 4 個(gè),其中的元素會(huì)被重復(fù)使用

繪圖時(shí)指定顏色

igraph 可以用以下方式指定顏色(用于邊、節(jié)點(diǎn)篡腌、標(biāo)簽參數(shù))

X11 顏色名

可以查看維基百科中的 X11 顏色名 獲得完整列表,或者查看 igraph.drawing.colors.knows_colors 字典的鍵坤邪,在這里不是大小寫敏感灸撰,所以填入 DarkBluedarkblue 效果相同。

CSS 樣式顏色字符串

可按以下樣式形成字符串填入(RGB 分別代表紅粗梭、綠争便、藍(lán))

  • #RRGGBB,六位十六進(jìn)制断医,每種顏色從 0 至 255(16 × 16)滞乙。如 #0088ff 奏纪。
  • #RGB ,三位十六進(jìn)制斩启,每種顏色從 0 至 15序调,如 #08f
  • rgb(R, G, B)兔簇,可填入 0 至 255 的十進(jìn)制整數(shù)或百分比发绢。如 rgb(0, 127, 255)rgb(0%, 50%, 100%)

RGB 列表

樣例:[255, 128, 0]垄琐,(255, 128, 0)边酒,"255, 128, 0"

保存繪圖

igraph 可以將繪圖保存為文件狸窘,這樣可以用于諸如出版等場(chǎng)景墩朦。方法也很簡(jiǎn)單,只需要將目標(biāo)文件名作為附加參數(shù)附在圖對(duì)象后面即可朦前。支持的格式由擴(kuò)展引擎決定介杆,igraph 可以保存 Cairo 支持的所有格式,如 SVG韭寸,PDF 和 PNG 等春哨。SVG 和 PDF 文件可以被轉(zhuǎn)換為 .ps.eps 格式,PNG 可以被轉(zhuǎn)換為 .tif 恩伺。

>>> ig.plot(g, "social_network.pdf", **visual_style)

igraph 與外部包聯(lián)動(dòng)

igraph 提供許多方法從外部讀取圖和保存圖:

格式 簡(jiǎn)稱 讀取方法 寫入方法
鄰接列表/LGL lgl Graph.Read_Lgl() Graph.write_lgl()
鄰接矩陣 adajacency Graph.Read_Adjacency() Graph.write_adjacency()
DL dl Graph.Read_DL() 暫無
邊列表 edgelist赴背,edgesedge Graph.Read_Edgelist() Graph.write_edgelist()
GraphViz graphviz晶渠,dot 暫無 Graph.write_dot()
GML gml Graph.Read_GML() Graph.write_gml
GraphML graphml Graph.Read_GraphML() Graph.write_graphml()
Gzipped GraphML graphmlz Graph.Read_GraphMLz() Graph.write_graphmlz()
LEDA leda 暫無 Graph.write_leda()
標(biāo)記邊列表/NCOL ncol Graph.Read_Ncol() Graph.write_ncol()
Pajek 格式 pajek凰荚,net Graph.Read_Pajek() Graph.write_pajek()
壓制圖 pickle Graph.Read_Pickle() Graph.write_pickle()

例如,可以下載著名的 扎卡里空手道俱樂部問題案例圖文件褒脯,解壓后將其加載至 igraph便瑟。因?yàn)樗?GraphML 文件,需要使用對(duì)應(yīng)的方法:

>>> karate = ig.Graph.Read_GraphML("zachary.graphml")
>>> ig.summary(karate)
IGRAPH UNW- 34 78 -- Zachary's karate club network

如果想要將其轉(zhuǎn)換為其他格式番川,比如 Pajek 支持的格式到涂,可以繼續(xù)轉(zhuǎn)化保存:

>>> karate.write_pajek("zachary.net")

需要注意,不同的格式均有其限制颁督,比如有的格式不支持存儲(chǔ)參數(shù)践啄。最佳的存儲(chǔ) igraph 圖的格式可能是 GraphML、GML沉御,或者如果你沒有參數(shù)屿讽,NCOL 和邊列表也可以(NCOL 支持節(jié)點(diǎn)名和邊權(quán)重)。如果你只是想暫時(shí)保存圖吠裆,之后還要在 igraph 中使用伐谈,可以使用 pickle 格式保證獲得相同的圖表烂完。

還有兩個(gè)幫助方法:load() 是一個(gè)通用的讀取方法,能夠嘗試從文件類型擴(kuò)展中自動(dòng)識(shí)別對(duì)應(yīng)類型并讀锐没椤窜护;Graph.save() 能以文件類型擴(kuò)展中默認(rèn)指定格式保存圖片,當(dāng)然也可以用 format 參數(shù)填入簡(jiǎn)稱覆蓋默認(rèn)保存類型非春。

>>> karate = load("zachary.graphml")
>>> karate.save("zachary.net")
>>> karate.save("zachary.my_extension", format="gml")

結(jié)語(yǔ)

本篇僅僅從表面介紹了 igraph 包的一些功能柱徙,作者將繼續(xù)完善完整的操作手冊(cè)∑骊迹可以查看 API 手冊(cè) 以獲取完整的 igraph 類护侮、函數(shù)、方法使用信息储耐。 Graph 類是一個(gè)好的開始羊初,如果遇到難懂的部分,可以在 討論組 中尋求幫助什湘。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末长赞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子闽撤,更是在濱河造成了極大的恐慌得哆,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哟旗,死亡現(xiàn)場(chǎng)離奇詭異贩据,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)闸餐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門饱亮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人舍沙,你說我怎么就攤上這事近上。” “怎么了拂铡?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵戈锻,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我和媳,道長(zhǎng),這世上最難降的妖魔是什么哈街? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任留瞳,我火速辦了婚禮,結(jié)果婚禮上骚秦,老公的妹妹穿的比我還像新娘她倘。我一直安慰自己璧微,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布硬梁。 她就那樣靜靜地躺著前硫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荧止。 梳的紋絲不亂的頭發(fā)上屹电,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音跃巡,去河邊找鬼危号。 笑死,一個(gè)胖子當(dāng)著我的面吹牛素邪,可吹牛的內(nèi)容都是我干的外莲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼兔朦,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼偷线!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起沽甥,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤声邦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后安接,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翔忽,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年盏檐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歇式。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胡野,死狀恐怖材失,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情硫豆,我是刑警寧澤龙巨,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站熊响,受9級(jí)特大地震影響旨别,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜汗茄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一秸弛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦递览、人聲如沸叼屠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)镜雨。三九已至,卻和暖如春儿捧,著一層夾襖步出監(jiān)牢的瞬間荚坞,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工纯命, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留西剥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓亿汞,卻偏偏與公主長(zhǎng)得像瞭空,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疗我,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容