package flare.analytics.graph { import flare.animate.Transitioner; import flare.util.Property; import flare.vis.data.Data; import flare.vis.data.DataList; import flare.vis.data.NodeSprite; import flare.vis.operator.Operator; /** * Calculates betweenness centrality measures for nodes in a graph. * The algorithm used is due to Ulrik Brandes, as published in the * * Journal of Mathematical Sociology, 25(2):163-177, 2001. */ public class BetweennessCentrality extends Operator { private var _bc:Property = Property.$("props.centrality"); /** The property in which to store the centrality score. This property * is used to annotate nodes with their betweenness centrality score. * The default value is "props.centrality". */ public function get centralityField():String { return _bc.name; } public function set centralityField(f:String):void { _bc = Property.$(f); } /** Flag indicating the type of links to follow in the graph. The * default is NodeSprite.GRAPH_LINKS. */ public var links:int = NodeSprite.GRAPH_LINKS; /** * Creates a new BetweennessCentrality operator. */ public function BetweennessCentrality() { } /** @inheritDoc */ public override function operate(t:Transitioner=null):void { calculate(visualization.data); } /** * Calculates the betweenness centrality values for the given data set. * @param data the data set for which to compute centrality measures */ public function calculate(data:Data):void { var nodes:DataList = data.nodes; var N:int = nodes.length, i:int; var n:NodeSprite, v:NodeSprite, w:NodeSprite; var si:Score, sv:Score, sw:Score; nodes.visit(function(n:NodeSprite):void { _bc.setValue(n, 0); n.props._score = new Score(); }); for (i=0; i 0) { stack.push(v = queue.shift()); si = v.props._score; v.visitNodes(function(w:NodeSprite):void { var sv:Score = si, sw:Score = w.props._score; if (sw.distance < 0) { queue.push(w); sw.distance = sv.distance + 1; } if (sw.distance == sv.distance + 1) { sw.paths += sv.paths; sw.predecessors.push(v); } }, links); } while (stack.length > 0) { w = stack.pop(); sw = w.props._score; for each (v in sw.predecessors) { sv = v.props._score; sv.dependency += (sv.paths/sw.paths) * (1+sw.dependency); } if (w !== n) sw.centrality += sw.dependency; } } nodes.visit(function(n:NodeSprite):void { _bc.setValue(n, n.props._score.centrality); delete n.props._score; }); } } // end of class BetweennessCentrality } import flare.util.Arrays; /** Helper class for storing intermediate centrality computations */ class Score { public var dependency:Number = 0; public var distance:Number = -1; public var paths:Number = 0; public var predecessors:Array = []; public var centrality:Number = 0; public function reset():void { Arrays.clear(predecessors); dependency = 0; distance = -1; paths = 0; } }