GraphFrames分析豆瓣用户及小组

上篇已经介绍了通过Spark Graphx构建图,在构图过程中要准备一个Long类型的字段作为节点的VertexID,而对于上篇中用到的数据集,显然增加了工作量(将oid[String类型]转换为Long)。
GraphFrames能够避免这样的操作。

GraphFrames

GraphFrames是以DataFrame为基础的构图组件。
(GraphFrames is a package for Apache Spark which provides DataFrame-based Graphs. )
它的目标是保留GraphX功能的同时充分利用Spark DataFrames的优势来扩展GraphX的功能。其扩展功能包括motif 预测,DataFrame序列化以及高性能的图查询。
( It aims to provide both the functionality of GraphX and extended functionality taking advantage of Spark DataFrames. This extended functionality includes motif finding, DataFrame-based serialization, and highly expressive graph queries.)

利用GraphFrames进行构图

主要流程和上篇没太大差异。主要是利用GraphFrames构图时,节点DataFrames需要指明某列为id列(可以使String类型),边DataFrames需要指明src、dst列(可以使String类型)。代码如下:

    //将RDD转为DataFrame
    val personsdf = sqlContext.createDataFrame(personsRDD).toDF("id", "name", "no", "groupno", "vertextype")
    val groupsdf = sqlContext.createDataFrame(groupsRDD).toDF("id", "name", "tags", "groupno", "vertextype").dropDuplicates("groupno")

    val groupsds = groupsdf.select("id", "groupno", "tags")
    val relation = personsdf.join(groupsds, personsdf("groupno") === groupsds("groupno")).toDF("src","personname","personno","groupno","personvertextype","dst","groupno","tags").select("src","dst","tags")

    val personsds = personsdf.dropDuplicates("id", "no").toDF("id", "name", "tags", "groupno", "vertextype")
    val unionds = personsds.union(groupsdf)

    val graph = GraphFrame(unionds, relation)

linkurious.js

linkurious.js是纯Javascript开发的js库。它利用Sigma.js进行结构数据的解析及可视化,其提供Canvas、WebGL、SVG三种渲染模式来渲染图的节点和边。
(Linkurious.js is developed in pure Javascript. It uses Sigma.js for its graph data structure and visualization engine, which provides both Canvas, WebGL and SVG renderers for nodes and edges. )

利用linkurious.js进行图的可视化

首先,需要将DataFrames输出成gexf格式的数据结构文件

    def graphFrametoGexf(g: GraphFrame) : String = {
      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
        "<gexf xmlns=\"http://www.gephi.org/gexf\" xmlns:viz=\"http://www.gephi.org/gexf/viz\">\n" +
        "  <graph mode=\"static\">\n" +
        "    <nodes>\n" +
        g.vertices.rdd.map(v => if(v.get(4)=="p")
        {"      <node id=\"" + v.get(0) + "\" label=\"" +
          v.get(1) + "\">\n        <viz:color b=\"51\" g=\"51\" r=\"255\"/>\n      </node>\n"} else
        {"      <node id=\"" + v.get(0) + "\" label=\"" +
          v.get(1) + "\">\n        <viz:color b=\"255\" g=\"51\" r=\"51\"/>\n      </node>\n"
        }).collect.mkString +
        "    </nodes>\n" +
        "    <edges>\n" +
        g.edges.rdd.map(e =>
        "    <edge source=\"" + e.get(0) +
          "\" target=\"" + e.get(1) + "\" label=\"" + e.get(2) +
          "\" />\n").collect.mkString +
        "    </edges>\n" +
        "  </graph>\n" +
        "</gexf>"

    val strgexf = graphFrametoGexf(graph)
    //println(strgexf)
    val writer = new PrintWriter(new File("D:\\Tomcat7.0\\webapps\\linkurious.js\\examples\\data\\4444.gexf"))
    writer.print(strgexf)
    writer.close()

其次,利用linkurious.js进行可视化

sigma.parsers.gexf('data/4444.gexf', {
  // container: 'graph-container',
  renderer: {
    container: document.getElementById('graph-container'),
    type: 'canvas'
  },
  settings: {
    edgeColor: 'default',
    defaultEdgeColor: '#ccc',
    animationsTime: 5000,
    drawLabels: true,
    labelThreshold: 5,
    scalingMode: 'inside',
    batchEdgesDrawing: true,
    hideEdgesOnMove: true,
    sideMargin: 1
  }
}, function(s) {
  s.graph.nodes().forEach(function (n) {
    if (!s.graph.degree(n.id)) {
      s.graph.dropNode(n.id);
    }
    else {
      n.x = Math.random();
      n.y = Math.random();
    }
  });
  s.refresh();

  // Configure the ForceLink algorithm:
  var fa = sigma.layouts.configForceLink(s, {
    worker: true,
    autoStop: true,
    background: true,
    scaleRatio: 30,
    gravity: 3,
    easing: 'cubicInOut'
  });

  // Bind the events:
  fa.bind('start stop', function(e) {
    console.log(e.type);
    document.getElementById('layout-notification').style.visibility = '';
    if (e.type == 'start') {
      document.getElementById('layout-notification').style.visibility = 'visible';
    }
  });

  // Start the ForceLink algorithm:
  sigma.layouts.startForceLink();
});

测试

利用200个用户及400个组进行构图测试,显示效果流畅。(注意:节点名字不能包括Unicode字符,否则linkurious.js无法解析)


关系图.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容