1. 程式人生 > >Python利用igraph繪製複雜網路聚類(社群檢測)結果圖

Python利用igraph繪製複雜網路聚類(社群檢測)結果圖

        前言:研究生期間主要做複雜網路聚類,也稱為社群檢測。臨畢業前,老師讓之前發表的論文裡的演算法程式碼C化,並寫出介面進行視覺化。由於之前雖然做過視覺化,但基本上都是將聚類結果匯入到pajek或者gephi這類專門的軟體裡進行繪製的。想要將社群檢測結果實時的進行繪製並且要通過C\C++直接繪製,確實沒有什麼頭緒。後來,通過瀏覽部落格知道igaph這個包可以使用,由於想要熟悉下python,於是選擇了Python的igraph包,而放棄使用C語言的igraph包。這樣,我就可以將演算法產生的聚類結果直接匯入Python程式碼,並利用Python的igraph包進行網路劃分結果的繪製。然後,再用C++呼叫Python程式碼,並將Python產生的聚類結果圖直接載入到MFC中,這樣就能實現實時地對社群檢測結果進行繪製。

        一. 利用Python進行網路聚類圖的繪製

        當然我們需要實現安裝Python和igraph包。本人使用的系統是win7系統,安裝的軟體是anaconda,內含Python2.7版本,基本上需要的東西anaconda都整合好了,一鍵安裝很是方便。

        其次,我們需要安裝python-igraph包。這是官方網址。直接使用pip安裝會出現問題(Windows系統本身的問題),因此本人選用的非官方網址的安裝包,見這裡。這個網址在官網也提供了,一定要選對對應的版本。Python3.6以上的版本好像並沒有對應的igraph包,因此安裝的時候會出現平臺不支援的提示。然後在dos系統環境下pip install 安裝包

,即可實現igraph的安裝。

        接下來,我們還需要安裝支援igraph繪製圖形的Cairo庫。這個庫同樣在上面提供的非官方網址上可以找到,下載對應系統的版本,並進行pip安裝即可。這裡不再詳細介紹。

        前提工作做好,接下里進行網路社群圖的繪製。如果不熟悉igraph包的使用,可以參見官網的使用手冊。裡面也有關於網路圖繪製的介紹。下面給出相關程式碼:

# Python 2.7
from igraph import *
from PIL import Image

colors_type = ["yellow", "red", "green", "coral", "alice blue", "cyan", "pink", 
"gray", "blue", "green yellow", "orange", "light blue", "hot pink", "light green", "gold"]

def PlotNetworks(net_file, detected_label, real_label = "Unknown Type"):
	## read files
	network = Graph.Read_Adjacency(net_file)
	Graph.to_undirected(network)
	f1 = open(detected_label)
	line = f1.readline()
	line = line.strip()
	str_line = line.split('\t')
	dlabel = [int(ele) for ele in str_line]
	network.vs["dlabel"] = dlabel
	if(real_label != "Unknown Type"):
		f2 = open(real_label)
		line = f2.readline()
		line = line.strip()
		str_line = line.split('\t')
		rlabel = [int(ele) for ele in str_line]
		network.vs["rlabel"] = rlabel
	
	# plot networks
	nnodes = len(network.vs)
	network.vs["name"] = [str(i+1) for i in range(nnodes)]

	layout = network.layout("drl")
	visual_style = {}
	if(nnodes < 100):
		visual_style["vertex_size"] = 22
	else:
		visual_style["vertex_size"] = 18
	visual_style["vertex_label"] = network.vs["name"]
	visual_style["layout"] = layout
	visual_style["bbox"] = (500,500)
	visual_style["margin"] = 20
	visual_style["edge_curved"] = 0.3
	visual_style["vertex_color"] = [colors_type[i-1] for i in network.vs["dlabel"]]
	plot(network, "social_network1.png", **visual_style)
	figure1 = Image.open("social_network1.png")
	figure1.save("social_network1.bmp")
	if(real_label != "Unknown Type"):
		visual_style["vertex_color"] = [colors_type[i-1] for i in network.vs["rlabel"]]
		plot(network, "social_network2.png", **visual_style)
		figure2 = Image.open("social_network2.png")
		figure2.save("social_network2.bmp")
        程式碼寫的稍微有些繁瑣,主要是為了之後C++程式碼的呼叫方便。函式需要三個引數,均為檔案路徑名。第一個檔案是複雜網路的鄰接矩陣,我們可以使用Graph.Read_Adjacency()來直接讀取,不過這個方法預設讀取的是有向網路,而我使用的均是無向網路,所以需要使用Graph.to_undirected()將其轉換為無向網路。第二個檔案為演算法的聚類結果檔案,檔案是由1-k個整數標籤表示社群檢測結果,k表示一共檢測到k個社群。第三個檔案表示真實的網路劃分結果,對於有些網路,我們往往並不知道真實的網路劃分結果,這裡是一個可選檔案。

接著,繪製網路的視覺化效果引數。使用字典可以直接將視覺化引數設定好,這裡我們用visual_style來表示。相關引數均可以在上述提供的官方手冊找到。其中著重介紹下visual_style["layout"]這個引數,它是一種網路的佈局演算法,熟悉pajek軟體的同學對這個引數應該也有所瞭解。其中“drl”是大圖的分散式遞迴佈局演算法,常用的還有“kk”,“fr”等,這些都能使自己的網路節點佈局合理,比較美觀。由於這些引數都是非確定型的繪圖方式,也就是說,每次節點的佈局都會有所差異。更多引數見官網手冊。

這裡的color_style給了很多顏色,因為後期要繪製美國大學生足球隊網路,需要至少12種顏色,這裡Wikipedia提供了多達上百種的顏色,只需要將顏色的首字母小寫即可。最後需要說明一下,由於我的MFC程式碼裡需要呼叫bmp影象,而plot不能儲存為bmp格式,這裡多了一步將png轉換為bmp格式的過程。

        下面給出MFC介面繪製社群檢測效果圖,影象繪製如下:

        1、Zachary's karate network(即跆拳道網路):


2、dolphin social network


3、American football network

這裡為了突出檢測圖和真實圖的區別,故意選擇了效果一般時的引數。勿介意(手動滑稽)。。。

二、MFC呼叫Python程式碼

        前面已經給出了繪製社群檢測效果圖的程式碼。這裡簡單介紹下怎樣使用C++程式碼呼叫Python程式碼。這篇【部落格】基本實現了vs配置使用C++呼叫python程式碼,這裡需要強調幾點:第五步應該在第三步之前進行實施,因為我配置完第五步之後,發現原先配置好的第三步又變回了原始配置,所以我們可以先配置第五步再接著配置第三步;此外,由於我們安裝的是anaconda,所以我們需要在anaconda的相關檔案下找到include目錄和libs目錄,並進行配置而非Python27目錄,其他不需要改變。這樣我們就可以配置好vs環境。

下面程式碼實現了C++呼叫Python程式碼:

void callPython(char *str1, char *str2, char *str3)
{
Py_Initialize();

PyObject *pModule = NULL;
PyObject *pFunc = NULL;
pModule = PyImport_ImportModule("PlotNetworks");
pFunc = PyObject_GetAttrString(pModule, "PlotNetworks");
PyObject *pArgs = PyTuple_New(3);
PyTuple_SetItem(pArgs, 0, Py_BuildValue("s", str1));
PyTuple_SetItem(pArgs, 1, Py_BuildValue("s", str2));
PyTuple_SetItem(pArgs, 2, Py_BuildValue("s", str3));
PyEval_CallObject(pFunc, pArgs);
Py_Finalize();
}

str1, str2, str3就是我們Python程式碼裡所需要的三個檔案,PyImport_ImportModule()呼叫的是我們的python檔名,下面的函式呼叫“PlotNetworks”

是python程式碼內的函式名。這裡的pArgs對應Python裡的一個元組,下面的引數“s”表示該引數是字串型別。通過pArgs可以實現將引數傳遞給pFunc。

這篇部落格】可以進行參考。最後,在使用MFC繪製網路圖的時候,發現了一個問題。即當我第二次選擇檔案時,在點選聚類就會出現問題,提示顯示

python呼叫那一塊出現問題。找了一些資料,發現Py_Initialize()和Py_Finalize()在一個程式中不能多次使用,原因是Py_Initialize初始化的佔用的記憶體並

不能被Py_Finalize()完全釋放(大概是這個意思)。於是我將這兩個函式分開來,一個放在MFC視窗的初始化中,一個放在關閉視窗的訊息函式中,最

終解決了這個問題。

結束語:寫這個部落格的原因一方面為了記錄下這幾天的工作,另一方面發現網上寫R使用igraph包繪製複雜網路圖的部落格較多,對於Python繪製復

雜網絡圖的很少。於是寫下這一篇部落格,希望能為研究複雜網路的小夥伴提供一些思路。