xxxxxxxxxx
<html>
<head>
<title>Clustering, betweenness centrality and eigenvector centrality</title>
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="/emeeks/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js" type="text/javascript"></script>
<style>
body {
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
font-size: 14px;
line-height: 20px;
color: #333;
background-color: #fff;
}
div#canvas {
background-color: white;
border: 1px solid #999;
border-radius: 5px;
position: relative;
}
div#canvas .description {
color: #777;
position: absolute;
bottom: 0;
left: 0;
right: 0;
background-color: #EEE;
border-top: 1px solid #999;
border-bottom-left-radius: inherit;
border-bottom-right-radius: inherit;
padding: 5px;
}
svg.rule {
opacity: 1;
}
.edge line {
stroke: rgb(153, 153, 153);
stroke-width: 1.5px;
pointer-events: none;
}
marker {
fill: rgb(99,99,99);
stroke: rgb(153, 153, 153);
}
.node circle {
stroke-width: 2;
stroke: rgb(153, 153, 153);
fill: rgb(153, 153, 153);
cursor: pointer;
}
</style>
</head>
<body>
<div id="canvas">
<span class="description">Karate club graph</span>
<svg class="jsnx rule"></svg>
</div>
</body>
<footer>
<script>
var rule_graph_file = "rule_graph.json",
data_error_file = "data_error.csv";
var canvas_width = 640,
canvas_height = 480,
svg_rule_width = 0.75 * canvas_width,
svg_rule_height = canvas_height - 30.23;
d3.select("div#canvas")
.style("width", canvas_width + 'px')
.style("height", canvas_height + 'px')
.select("svg.rule")
.style("width", svg_rule_width + 'px')
.style("height", svg_rule_height + 'px');
function encode_rule(rule) {
rule = rule.replace("==", "_eq_");
rule = rule.replace("!=", "_neq_");
rule = rule.replace(">", "_g_");
rule = rule.replace(">=", "_ge_");
rule = rule.replace("<", "_l_");
rule = rule.replace("<=", "_le_");
vov = rule.split("_");
return {
'rule': rule,
'variable': vov[0],
'operator': vov[1],
// 'value': Number(vov[2]),
'value': vov[2]
};
}
function random_int(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
d3.json(rule_graph_file, function(error, graph) {
if (error) throw error;
graph.nodes.forEach(function(n) {
r = encode_rule(n.id);
n.rule = r.rule;
n.variable = r.variable;
n.operator = r.operator;
n.value = r.value;
});
var nodeHash = graph.nodes.reduce(function(map, n) {
map[n.id] = n;
return map;
}, {});
graph.links.forEach(function(e, i) {
e.id = i;
e.source = nodeHash[e.source];
e.target = nodeHash[e.target];
e.weight = 5;
});
create_rule_network(graph.nodes, graph.links);
});
function create_rule_network(nodes, edges) {
var rule_force = d3.layout.force()
.nodes(nodes)
.links(edges)
.size([svg_rule_width, svg_rule_height])
.charge(-300)
.chargeDistance(100)
.gravity(0.05)
.on("tick", update_rule_network)
.on('end', function() {
console.log('Layout finished. Start processing data...');
process_data_error(data_error_file, nodes, edges);
});
var rule_drag = rule_force.drag();
var svg = d3.select("svg.rule"),
g = svg.append("g");
svg.append("svg:defs").selectAll("marker")
.data(["end"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.attr("class", "arrow");
g.append("g").attr("class", "edges")
.selectAll("g.edge")
.data(edges, function (d) {return d.id})
.enter()
.append("g")
.attr("class", "edge")
.append("line")
.attr("marker-end", "url(#end)");
var g_nodes = g.append("g").attr("class", "nodes")
.selectAll("g.node")
.data(nodes)
.enter()
.append("g")
.attr("class", function (d) {return "node " + d.rule})
.attr("variable", function (d) {return d.variable})
.attr("operator", function (d) {return d.operator})
.attr("value", function (d) {return d.value})
.call(rule_drag);
g_nodes.append("circle")
.attr("r", 6);
g_nodes.append("text")
.style("text-anchor", "middle")
.attr("y", 3)
.style("stroke-width", "1px")
.style("stroke-opacity", 0.75)
.style("stroke", "white")
.style("font-size", "8px")
.text(function (d) {return d.id})
.style("pointer-events", "none");
g_nodes.append("text")
.style("text-anchor", "middle")
.attr("y", 3)
.style("font-size", "8px")
.text(function (d) {return d.id})
.style("pointer-events", "none");
rule_force.start();
}
function update_rule_network(e) {
d3.select("svg.rule").selectAll("line")
.attr("x1", function (d) {return d.source.x})
.attr("y1", function (d) {return d.source.y})
.attr("x2", function (d) {return d.target.x})
.attr("y2", function (d) {return d.target.y});
d3.select("svg.rule").selectAll("g.node")
.attr("transform", function (d) {return "translate(" + d.x + "," + d.y + ")"});
}
function process_data_error(data_error_file, nodes, edges) {
d3.csv(data_error_file, function(error, data_error) {
data_error.forEach(function(d, i) {
for(k in d) {
if (d[k] == "") d[k] = "NULL";
// d[k] = Number(d[k]);
}
if(i<=10) {
console.log("+++++ PROCESSING RECORD #" + (i+1) + " +++++");
// console.log("Data " + JSON.stringify(d));
process_record(d, nodes, edges);
}
});
});
}
function process_record(data, nodes, edges) {
var num_curr_error = Number.MAX_SAFE_INTEGER,
num_prev_error = num_curr_error;
var trial_no = 1;
do {
console.log("++ TRIAL #" + trial_no)
num_prev_error = num_curr_error;
set_hit_flag(data, nodes, edges);
var centrality = calculate_centrality(data, nodes, edges),
num_curr_error = centrality['num_error'],
max_centrality_nodes = centrality['max_centrality_nodes'];
console.log(" => Num error : " + num_curr_error);
console.log(" => Max centrality : " + max_centrality_nodes.join(', '));
var data_before = JSON.parse(JSON.stringify(data));
data_before = max_centrality_nodes.reduce(function(arr, n) {
arr.push(n + "=" + data_before[n]);
return arr;
}, []);
console.log(" => Data before revision : " + data_before.join(","));
if (num_curr_error < num_prev_error) {
data = revise_data(data, nodes, edges, max_centrality_nodes);
var data_after = JSON.parse(JSON.stringify(data));
data_after = max_centrality_nodes.reduce(function(arr, n) {
arr.push(n + "=" + data_after[n]);
return arr;
}, []);
console.log(" => Data after revision : " + data_after.join(","));
}
trial_no += 1;
} while(num_curr_error > 0 && num_curr_error < num_prev_error);
}
function set_hit_flag(data, nodes, edges) {
nodes.forEach(function(n) {
var el = d3.selectAll("svg.rule .node." + n.rule),
el_data = el.data()[0];
el.select("circle").style("fill", "rgb(255, 165, 0)");
el_data.color = "O";
if(n.id.startsWith("G") && n.id.endsWith("-in")) {
el.select("circle").style("fill", "rgb(255, 192, 203)");
el_data.color = "P";
el.data([el_data]).enter();
} else if(n.id.startsWith("G") && n.id.endsWith("-out")) {
el.select("circle").style("fill", "rgb(0, 0, 255)");
el_data.color = "B";
el.data([el_data]).enter();
} else if(n.id.startsWith("C")) {
el.select("circle").style("fill", "rgb(255, 255, 255)");
el_data.color = "W";
el.data([el_data]).enter();
}
});
var valids = [];
for(v in data) {
var el_data = find_valid_node(v, data[v]);
if (el_data != null) {
valids.push([el_data.id, data[v]])
var el = d3.select('.node.' + el_data.rule);
el.select("circle").style("fill", "rgb(0, 128, 0)");
el.data(el.data(), function(d) {
d.color = "G";
return d;
}).enter();
// Set flag it's group too
var group_els = d3.selectAll(".node." + el_data.group + "-in," +
".node." + el_data.group + "-out");
group_els.select("circle").style("fill", "rgb(0, 128, 0)");
group_els.data(group_els.data(), function(d) {
d.color = "G";
return d;
}).enter();
}
}
// console.log("Valid nodes" + valids);
}
function find_valid_node(v, str_value) {
var nodes = d3.selectAll(".node[variable=" + v + "]");
if (nodes[0].length > 0) {
for (var _d=0; _d<nodes.data().length; _d++) {
var d = nodes.data()[_d];
var val = Number(str_value),
rule_val = Number(d.value);
if (d.value == "NULL" && str_value == "NULL") return d;
if (d.operator == "eq") {
if (d.value.indexOf("-") !== -1) {
var min_max = d.value.split("-");
try {
var min = Number(min_max[0]),
max = Number(min_max[1]);
if (val >= min && val <= max) return d;
} catch (err) {}
}
else if (d.value == str_value) return d;
}
if (val != NaN && rule_val != NaN) {
if (d.operator == "g") {
if (val > rule_val) return d;
} else if (d.operator == "ge") {
if (val >= rule_val) return d;
} else if (d.operator == "l") {
if (val < rule_val) return d;
} else if (d.operator == "le") {
if (val <= rule_val) return d;
}
}
}
}
return null;
}
function calculate_centrality(data, nodes, edges) {
var num_error = 0,
centrality_nodes_data = {},
hyper_edges = {};
var sel_nodes = nodes.filter(function(n) {
return n.id.startsWith("C");
});
// console.log(sel_nodes.map(function(n) { return n.rule }));
var in_edges = edges.reduce(function(map, n) {
if(! (n.target.rule in map)) {
map[n.target.rule] = [];
}
map[n.target.rule].push(n.source);
return map;
}, {});
var out_edges = edges.reduce(function(map, n) {
if(! (n.source.rule in map)) {
map[n.source.rule] = [];
}
map[n.source.rule].push(n.target);
return map;
}, {});
sel_nodes.forEach(function(n) {
var source_variables = [],
target_variables = [],
is_target_error = false;
if (in_edges[n.rule] != null) {
in_edges[n.rule].forEach(function(i_n) {
if (i_n.color == "G") {
if (i_n.rule.startsWith("G")) {
in_edges[i_n.rule].forEach(function(i_m) {
source_variables.push(i_m.variable);
});
} else {
source_variables.push(i_n.variable);
}
}
});
}
if (out_edges[n.rule] != null) {
out_edges[n.rule].forEach(function(o_n) {
if (o_n.rule.startsWith("G")) {
out_edges[o_n.rule].forEach(function(o_m) {
target_variables.push(o_m.variable);
});
} else {
target_variables.push(o_n.variable);
}
if (o_n.color == "G") {
is_target_error = true;
}
});
}
// console.log(n.rule, source_variables, target_variables);
if (source_variables.length != 0 && !is_target_error) {
var nd = d3.selectAll(".node." + n.rule);
// console.log("Error rule " + nd.data()[0].id);
nd.select("circle").style("fill", "rgb(255, 0, 0)");
nd.data(nd.data(), function(d) {
d.color = "R";
return d;
});
num_error += 1;
var hyper_edge = [];
source_variables.forEach(function(sv) {
centrality_nodes_data[sv] = {id: sv};
target_variables.forEach(function(tv) {
centrality_nodes_data[tv] = {id: tv};
if(! hyper_edge.includes(sv)) {
hyper_edge.push(sv);
}
if(! hyper_edge.includes(tv)) {
hyper_edge.push(tv);
}
});
});
hyper_edges[n.rule] = hyper_edge;
}
if (source_variables.length == 0 && is_target_error) {
var nd = d3.selectAll(".node." + n.rule);
// console.log("Inconsistent rule " + nd.data()[0].id);
nd.select("circle").style("fill", "rgb(0,255,255)");
nd.data(nd.data(), function(d) {
d.color = "C";
return d;
});
}
});
// hypergraph
// console.log("Hyperedges : " + JSON.stringify(hyper_edges));
var max_centrality_nodes = [],
c_hyper_edges = JSON.parse(JSON.stringify(hyper_edges));
while(Object.keys(hyper_edges).length > 0) {
var key = Object.keys(hyper_edges)[0],
hyper_edge = hyper_edges[key],
keys_to_remove = [];
hyper_edge.forEach(function(n) {
if (! (max_centrality_nodes.includes(n))) {
max_centrality_nodes.push(n);
}
Object.keys(hyper_edges).forEach(function(k) {
var he = hyper_edges[k];
if (he.includes(n)) {
if (! (keys_to_remove.includes(k))) {
keys_to_remove.push(k);
}
}
});
});
keys_to_remove.forEach(function(k) {
delete hyper_edges[k]
});
}
// console.log("Max centrality nodes : " + JSON.stringify(max_centrality_nodes));
var max = -1,
selected_nodes = [];
max_centrality_nodes.forEach(function(n) {
var count = 0;
Object.keys(c_hyper_edges).forEach(function(k) {
var he = c_hyper_edges[k];
if (he.includes(n)) {
count += 1;
}
});
// console.log(n, count)
if (count > max) {
selected_nodes = [];
selected_nodes.push(n);
max = count;
} else if (count == max) {
selected_nodes.push(n);
}
});
max_centrality_nodes = selected_nodes;
// console.log("Max centrality nodes 2 : " + JSON.stringify(max_centrality_nodes));
return {
'num_error': num_error,
'max_centrality_nodes': max_centrality_nodes
};
}
function revise_data(data, nodes, edges, max_centrality_nodes) {
// console.log(JSON.stringify(max_centrality_nodes))
var revision_candidates = [];
max_centrality_nodes.forEach(function(cn) {
var nds = d3.selectAll("[variable=" + cn + "]"),
nds_data = nds.data();
nds_data.forEach(function(d) {
revision_candidates.push(d.rule);
});
});
// console.log(JSON.stringify(max_centrality_nodes),
// JSON.stringify(revision_candidates))
if (revision_candidates.length > 0) {
var sel_cdt_idx = random_int(0, revision_candidates.length-1),
selected_node_rule = revision_candidates[sel_cdt_idx],
selected_node = d3.select('.node.' + selected_node_rule),
selected_node_data = selected_node.data()[0];
console.log(" => Node to be revised : " + selected_node_data.id);
selected_node.select("circle").style("fill", "rgb(255,0,255)");
selected_node.data(selected_node.data(), function(d) {
d.color = "M";
return d;
});
// console.log("=> Make revision for variable " + selected_node_data.variable);
var val = selected_node_data.value;
if (val.indexOf("-") !== -1) {
val = val.split("-")[0];
val = Number(val);
} else {
val = Number(val);
}
if (selected_node_data.operator == "g") {
val += val;
} else if (selected_node_data.operator == "l") {
val -= val;
}
console.log(" => Value of variable " + selected_node_data.variable +
" is revised from " + data[selected_node_data.variable] +
" to " + val)
data[selected_node_data.variable] = val;
}
return data;
}
</script>
</footer>
</html>
Updated missing url https://bl.ocks.org/emeeks/raw/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js to /emeeks/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
https://bl.ocks.org/emeeks/raw/9915de8989e2a5c34652/7d22d8e4f05e944ced10408ae64579beff3c0858/jsnetworkx.js