123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- /**
- * This plugin computes HITS statistics (Authority and Hub measures) for each node of the graph.
- * It adds to the graph model a method called "HITS".
- *
- * Author: Mehdi El Fadil, Mango Information Systems
- * License: This plugin for sigma.js follows the same licensing terms as sigma.js library.
- *
- * This implementation is based on the original paper J. Kleinberg, Authoritative Sources in a Hyperlinked Environment (http://www.cs.cornell.edu/home/kleinber/auth.pdf), and is inspired by implementation in Gephi software (Patick J. McSweeney <pjmcswee@syr.edu>, Sebastien Heymann <seb@gephi.org>, Dual-licensed under GPL v3 and CDDL)
- * https://github.com/Mango-information-systems/gephi/blob/fix-hits/modules/StatisticsPlugin/src/main/java/org/gephi/statistics/plugin/Hits.java
- *
- * Bugs in Gephi implementation should not be found in this implementation.
- * Tests have been put in place based on a test plan used to test implementation in Gephi, cf. discussion here: https://github.com/jacomyal/sigma.js/issues/309
- * No guarantee is provided regarding the correctness of the calculations. Plugin author did not control the validity of the test scenarii.
- *
- * Warning: tricky edge-case. Hubs and authorities for nodes without any edge are only reliable in an undirected graph calculation mode.
- *
- * Check the code for more information.
- *
- * Here is how to use it:
- *
- * > // directed graph
- * > var stats = s.graph.HITS()
- * > // returns an object indexed by node Id with the authority and hub measures
- * > // like { "n0": {"authority": 0.00343, "hub": 0.023975}, "n1": [...]*
- *
- * > // undirected graph: pass 'true' as function parameter
- * > var stats = s.graph.HITS(true)
- * > // returns an object indexed by node Id with the authority and hub measures
- * > // like { "n0": {"authority": 0.00343, "hub": 0.023975}, "n1": [...]
- */
- (function() {
- 'use strict';
- if (typeof sigma === 'undefined')
- throw 'sigma is not declared';
- /**
- * This method takes a graph instance and returns authority and hub measures computed for each node. It uses the built-in
- * indexes from sigma's graph model to search in the graph.
- *
- * @param {boolean} isUndirected flag informing whether the graph is directed or not. Default false: directed graph.
- * @return {object} object indexed by node Ids, containing authority and hub measures for each node of the graph.
- */
- sigma.classes.graph.addMethod(
- 'HITS',
- function(isUndirected) {
- var res = {}
- , epsilon = 0.0001
- , hubList = []
- , authList = []
- , nodes = this.nodes()
- , nodesCount = nodes.length
- , tempRes = {}
- if (!isUndirected)
- isUndirected = false
- for (var i in nodes) {
-
- if (isUndirected) {
- hubList.push(nodes[i])
- authList.push(nodes[i])
- }
- else {
- if (this.degree(nodes[i].id, 'out') > 0)
- hubList.push(nodes[i])
-
- if (this.degree(nodes[i].id, 'in') > 0)
- authList.push(nodes[i])
- }
-
- res[nodes[i].id] = { authority : 1, hub: 1 }
- }
- var done
-
- while (true) {
- done = true
- var authSum = 0
- , hubSum = 0
-
- for (var i in authList) {
-
- tempRes[authList[i].id] = {authority : 1, hub:0 }
-
- var connectedNodes = []
- if (isUndirected)
- connectedNodes = this.allNeighborsIndex[authList[i].id]
- else
- connectedNodes = this.inNeighborsIndex[authList[i].id]
-
- for (var j in connectedNodes) {
- if (j != authList[i].id)
- tempRes[authList[i].id].authority += res[j].hub
- }
-
- authSum += tempRes[authList[i].id].authority
-
- }
-
- for (var i in hubList) {
-
- if (tempRes[hubList[i].id])
- tempRes[hubList[i].id].hub = 1
- else
- tempRes[hubList[i].id] = {authority: 0, hub : 1 }
-
- var connectedNodes = []
-
- if (isUndirected)
- connectedNodes = this.allNeighborsIndex[hubList[i].id]
- else
- connectedNodes = this.outNeighborsIndex[hubList[i].id]
-
- for (var j in connectedNodes) {
- if (j != hubList[i].id)
- tempRes[hubList[i].id].hub += res[j].authority
- }
-
- hubSum += tempRes[hubList[i].id].hub
-
- }
-
- for (var i in authList) {
- tempRes[authList[i].id].authority /= authSum
-
- if (Math.abs((tempRes[authList[i].id].authority - res[authList[i].id].authority) / res[authList[i].id].authority) >= epsilon)
- done = false
- }
-
- for (var i in hubList) {
- tempRes[hubList[i].id].hub /= hubSum
-
- if (Math.abs((tempRes[hubList[i].id].hub - res[hubList[i].id].hub) / res[hubList[i].id].hub) >= epsilon)
- done = false
- }
- res = tempRes
-
- tempRes = {}
- if (done)
- break
-
- }
- return res
- }
- )
- }).call(window)
|