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繪製圖形的Cairo庫。這個庫同樣在上面提供的非官方網址上可以找到,下載對應系統的版本,並進行pip安裝即可。這裡不再詳細介紹。
前提工作做好,接下里進行網路社群圖的繪製。如果不熟悉igraph包的使用,可以參見官網的使用手冊。裡面也有關於網路圖繪製的介紹。下面給出相關程式碼:
# Python 2.7
程式碼寫的稍微有些繁瑣,主要是為了之後C++程式碼的呼叫方便。函式需要三個引數,均為檔案路徑名。第一個檔案是複雜網路的鄰接矩陣,我們可以使用Graph.Read_Adjacency()來直接讀取,不過這個方法預設讀取的是有向網路,而我使用的均是無向網路,所以需要使用Graph.to_undirected()將其轉換為無向網路。第二個檔案為演算法的聚類結果檔案,檔案是由1-k個整數標籤表示社群檢測結果,k表示一共檢測到k個社群。第三個檔案表示真實的網路劃分結果,對於有些網路,我們往往並不知道真實的網路劃分結果,這裡是一個可選檔案。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")
接著,繪製網路的視覺化效果引數。使用字典可以直接將視覺化引數設定好,這裡我們用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繪製復
雜網絡圖的很少。於是寫下這一篇部落格,希望能為研究複雜網路的小夥伴提供一些思路。