前言:
社交網絡分析是一個常常會遇到的業務問題,故而筆者也一致在不斷嘗試不同的社交網絡分析工具。之前使用過python networkx, igraph C library, 今日再介紹一款C++環境下的分析工具: Boost Graph Library(下文簡稱BGL), 該庫據說可輕鬆處理百萬級別節點規模的網絡. 業界對該款工具的評語有:
1. large, stable, efficient
2. Lots of algorithms, graph types
3. Peer-reviewed code with many users, nightly regression testing, etc.
4. Performance comparable to FORTRAN.
本文介紹下該工具的入門操作,如何創建一個網絡,並設置節點屬性,以及如何讀取節點屬性。
1. 準備工作
解壓後,按以下步驟安裝
1. sh ./bootstrap.sh
2. ./b2
2. 創建圖
一般情況下,我們會把網絡中的邊存放在文件中,然後希望讀取該文件後,自動創建相應的圖(igraph就是這樣處理的),但是悲劇的是,在BGL中,死活沒找到直接讀文件的接口,於是只能手工逐行讀取了。比如, 我們用如下格式準備好一個圖中邊的列表文件:
v1 v2
v1 v3
v3 v4
現在要做的是,逐行讀入,給網絡添加節點和邊。同時,要注意可能存在重複添加的情況,要做好重複性檢測。完整代碼如下:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/regex.hpp>
#include <fstream>
using namespace std;
using namespace boost;
int main(int argc, char* argv[]){
if(argc != 2){
cout << "Usage: " << argv[0] << " <inputEdgeListFile> " << endl;
exit(-1);
}
typedef adjacency_list<vecS,vecS,undirectedS,property<vertex_name_t,string> > Graph;
Graph g;
typedef graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef graph_traits<Graph>::edge_descriptor edge_descriptor;
typedef graph_traits<Graph>::vertices_size_type vertices_size_type;
typedef graph_traits<Graph>::edges_size_type edges_size_type;
string inputEdgeListFile = argv[1];
ifstream infile(inputEdgeListFile.c_str());
string startNodeName, endNodeName;
map<string, vertex_descriptor> name2vertex;
while(infile >> startNodeName >> endNodeName){
vertex_descriptor s,e;
if(name2vertex.find(startNodeName) == name2vertex.end()){
s = add_vertex(g);
put(vertex_name,g,s,startNodeName);
name2vertex[startNodeName] = s;
}else{
s = name2vertex[startNodeName];
}
if(name2vertex.find(endNodeName) == name2vertex.end()){
e = add_vertex(g);
put(vertex_name,g,e,endNodeName);
name2vertex[endNodeName] = e;
}else{
e = name2vertex[endNodeName];
}
//test if edge (s,e) exists
pair<edge_descriptor,bool> edgeFlag = edge(s,e,g);
if( ! edgeFlag.second){
add_edge(s,e,g);
}
}
vertices_size_type n = num_vertices(g);
cout << "圖G中節點總數爲:" << n << endl;
edges_size_type m = num_edges(g);
cout << "圖G中邊總數爲:" << m << endl;
//如果需訪問圖中每個節點的屬性,可如下操作
typedef boost::graph_traits<Graph>::vertex_iterator vertex_iterator;
std::pair<vertex_iterator, vertex_iterator> vi;
for(vi = vertices(g);vi.first != vi.second; ++ vi.first){
cout << get(vertex_name,g,*vi.first) << endl;
}
return 0;
}
筆者測試了一個節點規模爲12W, 邊規模爲15W的圖,用上面程序執行創建圖的時間消耗是2秒左右。將這個文件中的邊反覆copy幾份,使文件中行數增長到142W,這時再讀取,一來需要遍歷的行數增加,二來去重的工作量增加很多,這時執行的時間消耗是15.91s,性能還是蠻不錯的。
3. 後記
- 注意
typedef adjacency_list<vecS,vecS,undirectedS,property<vertex_name_t,string> > Graph;
- 如果需要遍歷圖中節點或邊,BGL提供了迭代器方法,如 vertices(g)返回是一個pair類型,第一個參數是指向首節點的迭代器,第二個參數是指向末節點的迭代器。如果需要提取屬性特徵,則使用get(特徵名,圖對象,節點)的方法。
typedef boost::graph_traits<Graph>::vertex_iterator vertex_iterator; std::pair<vertex_iterator, vertex_iterator> vi; for(vi = vertices(g);vi.first != vi.second; ++ vi.first){ cout << get(vertex_name,g,*vi.first) << endl; }
- 總體上,個人感覺BGL的入門成本比networkx, igraph都要高一些,一來官方文檔寫得不夠通俗,二來市面上能找到的相關文章也較少。同時,BGL的易用性上,也較後兩者略不不足,提供的內置算法,也不如後兩者豐富。