UNPKG

11.9 kBSource Map (JSON)View Raw
1{"version":3,"file":"chinese-whispers.es6.umd.js","sources":["../dist/src/version.js","../dist/src/chinese-whispers.js"],"sourcesContent":["/**\n * This file was auto generated from scripts/generate-version.sh\n */\nexport const VERSION = '0.3.9';\n//# sourceMappingURL=version.js.map","/**\n * Chinese Whispers Algorithm for Node.js\n *\n * https://github.com/huan/chinese-whispers\n * License: Apache-2.0\n * Author: Huan LI <zixia@zixia.net>\n *\n * Inspired by:\n * - http://blog.csdn.net/liyuan123zhouhui/article/details/70312716\n * - http://alexloveless.co.uk/data/chinese-whispers-graph-clustering-in-python/\n * - https://github.com/uhh-lt/chinese-whispers\n * - https://github.com/anvaka/ngraph.cw\n *\n */\nimport { EventEmitter } from 'events';\nexport { VERSION } from './version';\nconst jsnx = require('jsnetworkx');\nconst { knuthShuffle } = require('knuth-shuffle');\nexport class ChineseWhispers extends EventEmitter {\n constructor(options) {\n super();\n this.weightFunc = options.weightFunc;\n this.epochs = options.epochs || 15;\n this.threshold = options.threshold;\n this.on('change', () => this.changeCounter++);\n }\n on(event, listener) {\n super.on(event, listener);\n return this;\n }\n emit(event, ...args) {\n return super.emit(event, ...args);\n }\n cluster(dataList) {\n // this.dataList = dataList\n this.graph = this.buildNetwork(dataList, this.weightFunc, this.threshold);\n // initial epoch\n this.emit('epoch', this.graph, -1);\n // run Chinese Whispers\n // I default to 10 iterations. This number is usually low.\n // After a certain number (individual to the data set) no further clustering occurs\n let epochs = this.epochs;\n while (epochs--) {\n this.changeCounter = 0;\n this.iterate();\n this.emit('epoch', this.graph, this.changeCounter);\n }\n const clusterList = this.buildClusterList(this.graph);\n return clusterList;\n }\n iterate() {\n const nodeList = this.graph.nodes();\n // I randomize the nodes to give me an arbitrary start point\n knuthShuffle(nodeList); // orignal array modified\n for (const node of nodeList) {\n this.relabelNode(node);\n }\n }\n relabelNode(node) {\n const newLabel = this.recalcLabel(node);\n const nodeAttr = this.graph.node.get(node);\n if (nodeAttr.label !== newLabel) {\n // set the label of target node to the winning local label\n this.emit('change', node, nodeAttr.label, newLabel);\n nodeAttr.label = newLabel;\n // console.log('set node ' + this.data[node] + ' label to ' + this.data[parseInt(newLabel)])\n }\n }\n recalcLabel(node) {\n const nxNode = this.graph.get(node);\n const neighborList = this.graph.neighbors(node);\n if (neighborList.length === 0) {\n const attr = this.graph.node.get(node);\n return attr.label; // return self label if no neighbors\n }\n // console.log('neighbors of node ' + this.data[node]\n // + ' is: ', neighborList.map((i: number) => this.data[i]))\n const labelWeightMap = {};\n // do an inventory of the given nodes neighbours and edge weights\n for (const neighbor of neighborList) {\n const neighborAttr = this.graph.node.get(neighbor);\n const label = neighborAttr.label;\n if (!(label in labelWeightMap)) {\n labelWeightMap[label] = 0;\n }\n const edgeAttr = nxNode.get(neighbor);\n const weight = edgeAttr.weight;\n // console.log('### ', weight, ' ###')\n labelWeightMap[label] += weight;\n }\n // find the label with the highest edge weight sum\n let max = 0;\n // In javascript the keys of object can only be strings\n // - https://stackoverflow.com/a/41870625/1123955\n let maxLabel = '-1';\n // for (const label in labelWeightMap) {\n // console.log('labelWeight node: ' + this.data[node] + ' - '\n // + this.data[parseInt(label)] + ' is: ', labelWeightMap[label])\n // }\n for (const label in labelWeightMap) {\n if (labelWeightMap[label] > max) {\n max = labelWeightMap[label];\n maxLabel = label;\n }\n }\n return maxLabel;\n }\n buildClusterList(G) {\n const clusterList = [];\n const labelIndexMap = {};\n let index = 0;\n for (const node of G.nodes()) {\n const label = G.node.get(node).label;\n if (!(label in labelIndexMap)) {\n labelIndexMap[label] = index++;\n }\n const labelIndex = labelIndexMap[label];\n if (!(labelIndex in clusterList)) {\n clusterList[labelIndex] = [];\n }\n clusterList[labelIndex].push(node);\n }\n return clusterList;\n }\n buildNetwork(dataList, weightFunc, threshold) {\n const nodeList = Object.keys(dataList).map(k => parseInt(k, 10)); // [0, 1, 2, ..., data.length - 1]\n // const nodeList: CWNode[] = [...data.keys()] // [0, 1, 2, ..., data.length - 1]\n const edgeList = [];\n for (let i = 0; i < dataList.length; i++) {\n for (let j = i + 1; j < dataList.length; j++) {\n const weight = weightFunc(dataList[i], dataList[j]);\n if (threshold && threshold > weight) {\n // console.log('threshold: ', threshold, ' weight: ', weight)\n // skip this edge because it's weight is below threshold\n this.emit('edge');\n }\n else {\n const edge = [i, j, { weight }];\n edgeList.push(edge);\n this.emit('edge', edge);\n }\n }\n }\n // initialize the graph\n const G = new jsnx.Graph();\n // Add nodes\n G.addNodesFrom(nodeList);\n // CW needs an arbitrary, unique label for each node before initialisation\n // Here I use the ID of the node since I know it's unique\n // You could use a random number or a counter or anything really\n for (const n of G.nodes()) {\n const nodeAttr = G.node.get(n);\n nodeAttr.label = n;\n }\n // add edges\n G.addEdgesFrom(edgeList);\n // console.log(edgeList)\n return G;\n }\n}\nexport default ChineseWhispers;\n//# sourceMappingURL=chinese-whispers.js.map"],"names":["EventEmitter"],"mappings":"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"}
\No newline at end of file