使用C++ Boost Graph Library 進行社交網絡分析入門篇

前言:

     社交網絡分析是一個常常會遇到的業務問題,故而筆者也一致在不斷嘗試不同的社交網絡分析工具。之前使用過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中比較靈活的一點就是允許用戶自主定義各種節點和邊屬性。
  • 如果需要遍歷圖中節點或邊,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的易用性上,也較後兩者略不不足,提供的內置算法,也不如後兩者豐富。

發佈了42 篇原創文章 · 獲贊 91 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章