Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F61934705
correlationNetwork.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, May 9, 22:05
Size
24 KB
Mime Type
text/x-c
Expires
Sat, May 11, 22:05 (2 d)
Engine
blob
Format
Raw Data
Handle
17580099
Attached To
R5853 genocrunch-2.1
correlationNetwork.js
View Options
function correlationNetwork(id, legend_id, json, W = 600, H = 600, font_family = "verdana, arial, helvetica, sans-serif", color_palette = d3.schemeCategory10) {
// Size
var margin = {top: 10, right: 10, bottom: 40, left: 10},
width = W - margin.left - margin.right,
height = H - margin.top - margin.bottom;
// Restrictions
drag_size_limit = 80;
// Colors, symbols and scaling
var colors = d3.scaleOrdinal(color_palette),
link_color = [["#999999", "Pos. correlation"],
["#ff3385", "Neg. correlation"]],
symbols = d3.scaleOrdinal([d3.symbolCircle,
d3.symbolSquare,
d3.symbolTriangle,
d3.symbolStar,
d3.symbolDiamond,
d3.symbolCross]),
legend_svg_symsize = 15,
rRange = [6, 24],
size_legend_data = [15, 10, 5],
wRange = [0.1*rRange[0], 0.9*rRange[0]],
sizeScale = d3.scaleLinear()
.range(rRange),
widthScale = d3.scaleLinear()
.range(wRange),
attractionScale = d3.scaleLinear()
.range([100, 50]),
repulsionScale = d3.scaleLinear()
.range([50, 100]),
chargeScale = d3.scaleLinear()
.range([-100, -200]),
sizeOptions = [{"text":"", "value":"", "title":""},
{"text":"Degree", "value":"degree", "title":"Node degree"},
{"text":"Mean abundance", "value":"mean", "title":"Mean abundance"},
{"text":"Max abundance", "value":"max", "title":"Max abundance"},
{"text":"Min abundance", "value":"min", "title":"Min abundance"}
];
oRange = [0.1, 0.3],
p_value_legend_data = [[0.001, "***"],
[0.01, "**"],
[0.05, "*"],
[1, "ns"],
["NA", "na"]],
stat_settings = {"range":[0.2, 0.6, 0.8, 1],
"domain":[1, 0.05, 0.01, 0.001],
"text":["ns", "*", "**", "***"]},
opacity = d3.scaleLinear()
.range(stat_settings.range)
.domain(stat_settings.domain),
colorBarSize = 10;
// General functions
function getSizeExtrema(json, fun, extrema="max", absolute=false) {
var values = json.map(function(d){
if (absolute) {
return Math.abs(d[fun]);
}
return d[fun];
})
if (extrema == "max") {
return Math.max.apply(null, values);
} else if (extrema == "min") {
return Math.min.apply(null, values);
};
};
function getNodeDegree(links, nodes) {
var degree = {};
links.forEach(function (d, i) {
if (Object.keys(degree).indexOf(d.target.toString()) == -1) {
degree[d.target.toString()] = 1;
} else {
degree[d.target.toString()] = degree[d.target.toString()]+1;
};
if (Object.keys(degree).indexOf(d.source.toString()) == -1) {
degree[d.source.toString()] = 1;
} else {
degree[d.source.toString()] = degree[d.source.toString()]+1;
};
});
nodes.forEach(function (d) {
if (Object.keys(degree).indexOf(d.id) != -1) {
d["degree"] = degree[d.id];
} else {
d["degree"] = 0; // Just to avoid scale domain to start at 0...
}
});
};
function unique(value, index, self) {
return self.indexOf(value) === index;
}
// Buttons
var buttons = d3.select("#d3-buttons")
buttons.html("");
//$.getJSON(data, function(json) {
//////////////// Simulation ////////////////
var simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY())
.force("x", d3.forceX());
var ticked = function() {
link
.attr("x1", function(d) { return Math.max(0, Math.min(d.source.x, width)); })
.attr("y1", function(d) { return Math.max(0, Math.min(d.source.y, height)); })
.attr("x2", function(d) { return Math.max(0, Math.min(d.target.x, width)); })
.attr("y2", function(d) { return Math.max(0, Math.min(d.target.y, height)); });
node
.attr("transform", function(d) {
return "translate(" + Math.max(0, Math.min(d.x, width)) + "," + Math.max(0, Math.min(d.y, height)) + ")"; });
nodeLabel
.attr("transform", function(d, i) {
return "translate(" + Math.max(0, Math.min(d.x, width)) + "," + Math.max(0, Math.min(d.y, height)) + ")"; });
};
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.25).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
//////////////// Draw links and nodes ////////////////
var legendContainer = d3.select("#"+legend_id).append("div")
.attr('class', 'columns-2')
var svgContainer = d3.select("#"+id)
.style("height", (height + margin.top + margin.bottom)+"px")
var svg = svgContainer.append("svg")
.attr("id", "svg-figure")
.attr("class", "svg-figure network-well")
.attr("width", (width + margin.left + margin.right)+"px")
.attr("height",(height + margin.top + margin.bottom)+"px")
.style("pointer-events", "all")
.call(d3.zoom()
.scaleExtent([1, 4])
.duration(1000)
.translateExtent([[margin.left, margin.top], [width + margin.right, height + margin.top + margin.bottom]])
.on("zoom", zoomed));
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function zoomed() {
g.attr("transform", d3.event.transform);
};
// Draw links
var link = g.append("g")
.selectAll();
// Draw Nodes
var node = g.append("g")
.selectAll("g");
var nodeLabel = g.append("g")
.selectAll("g");
// Add legend
var legend = legendContainer.append("div")
.attr("id", "svg-legend")
.style("font-family", font_family)
var colorLegend = legend.append("ul")
.style("list-style-type", "none")
.style("padding-top", "25px")
.selectAll("ul");
legend.append("div")
.style("border-top", "solid #ccc 1px")
var symbolLegend = legend.append("ul")
.style("list-style-type", "none")
.selectAll("ul");
legend.append("div")
.style("border-top", "solid #ccc 1px")
var linkLegend = legend.append("ul")
.style("list-style-type", "none")
.selectAll("ul");
//////////////// NODES COLORS ////////////////
var setSymbolColor = function() {
var selected_model = $('#modelSelect').val(),
selected_effect = $('#effectSelect').val(),
color_domain = json.nodes.map(function (d){return d.stat[selected_model][selected_effect]['highest-mean'];}).filter(unique);
if (selected_model != '')
colors.domain(color_domain);
// Update symbols color
d3.selectAll(".coloured-symbol")
.style("fill", function (d){
if (selected_model != '')
return colors(d.stat[selected_model][selected_effect]['highest-mean']);
return colors('');
})
.attr("fill-opacity", function (d){
if (selected_model != '')
return opacity(d.stat[selected_model][selected_effect]['p-value']);
return 1;
});
// Update color legend
colorLegend = colorLegend.data([]);
colorLegend.exit().remove();
colorLegend = colorLegend
.data(color_domain)
.enter().append("li")
.attr("id", function(d) { return d;})
.attr("class", "legend legend-no-interaction")
.attr("selected", 0)
.attr("title", function(d) { return d;})
colorLegendSpan = colorLegend.append("span")
var legend_svg_width = legend_svg_symsize*p_value_legend_data.length;
colorLegendSpanSvg = colorLegendSpan.append("svg")
.attr("width", legend_svg_width+"px")
.attr("height", legend_svg_symsize+"px")
.style("margin-right", "5px")
.style("overflow", "visible")
for (var i = 0; i < p_value_legend_data.length; i++) {
// (V)(°,,,°)(V)
colorLegendSpanSvg.append("g")
.attr("transform", "translate("+(i*legend_svg_width/p_value_legend_data.length)+", -2)")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", legend_svg_symsize)
.text(function(d, j){
if (j == 0 && color_domain.length > 0)
return p_value_legend_data[i][1];
})
colorLegendSpanSvg.append("rect")
.attr("transform", "translate("+(i*legend_svg_width/p_value_legend_data.length)+", 0)")
.attr("width", legend_svg_symsize)
.attr("height", legend_svg_symsize)
.attr("stroke", "none")
.attr("fill", function (d, j){
if (p_value_legend_data[i][0] == "NA") {
return "lightgrey";
};
return colors(d);
})
.attr("fill-opacity", function (d, j){
if (p_value_legend_data[i][0] == "NA") {
return 1;
};
return opacity(p_value_legend_data[i][0]);
})
};
colorLegendSpan.append("span")
.html(function(d) { return d;})
};
//////////////// NODES SHAPE AND SIZE ////////////////
var setSymbolSize = function() {
var selected_size_factor = $("#sizeSelect").val(),
symbols_domain = json.nodes.map(function (d){return d['data-type'];}).filter(unique),
size = {};
symbols.domain(symbols_domain);
if (selected_size_factor != '') {
for (var i = 0; i < symbols_domain.length; i++) {
size[symbols_domain[i]] = {value:json.nodes.filter(function (d){return d['data-type'] == symbols_domain[i];}).map(function(d){return Number(d[selected_size_factor]);})};
size[symbols_domain[i]]['min'] = Math.min.apply(null, size[symbols_domain[i]]['value']);
size[symbols_domain[i]]['max'] = Math.max.apply(null, size[symbols_domain[i]]['value']);
}
}
// Set symbols size
d3.selectAll(".symbol")
.transition().duration(400)
.attr("d", d3.symbol()
.type(function (d){ return symbols(d['data-type']);})
.size(function (d){
if (selected_size_factor != '') {
sizeScale.domain([size[d['data-type']]['min'], size[d['data-type']]['max']]);
d.r = sizeScale(d[selected_size_factor]);
} else {
d.r = 20;
}
return d.r*d.r;
}))
.attr('radius', function(d){return d.r;});
// Set size-dependent simulation variables
var symbol_data = [];
d3.selectAll(".symbol").each(function(){return symbol_data.push(d3.select(this).attr("radius"));});
var force_size_factor = (Math.log2(symbol_data.length+1)+1)/2;
chargeScale.domain([Math.min.apply(null, symbol_data), Math.max.apply(null, symbol_data)]);
if (force_size_factor == 0) {
force_size_factor = 1;
};
simulation
.force("collide", d3.forceCollide()
.radius(function (d){
return d.r;
})
.iterations(2)
.strength(1/force_size_factor))
.force("charge", d3.forceManyBody()
.strength(function (d){
return chargeScale(d.r)/force_size_factor;
}));
// Update symbol legend
symbolLegend = symbolLegend.data([]);
symbolLegend.exit().remove();
symbolLegend = symbolLegend
.data(symbols_domain)
.enter().append("li")
.attr("id", function(d) { return d;})
.attr("class", "legend legend-no-interaction")
.attr("selected", 0)
.attr("title", function(d) { return d;})
symbolLegendSpan = symbolLegend.append("span")
var legend_svg_width = legend_svg_symsize*size_legend_data.length;
symbolLegendSpanSvg = symbolLegendSpan.append("svg")
.attr("width", legend_svg_width+"px")
.attr("height", legend_svg_symsize+"px")
.style("margin-right", "5px")
.style("overflow", "visible")
for (var i = 0; i < size_legend_data.length; i++) {
// (V)(°,,,°)(V)
symbolLegendSpanSvg.append("path")
.attr("transform", "translate("+(i*legend_svg_width/size_legend_data.length)+", "+legend_svg_symsize/2+")")
.attr("d", d3.symbol()
.type(function (d){ return symbols(d);})
.size(size_legend_data[i]*size_legend_data[i]))
.attr("stroke", "#333")
.attr("fill-opacity", 0)
};
symbolLegendSpan.append("span")
.html(function(d) { return d;})
}
//////////////// RESTART ////////////////
var restart = function() {
var weight_thres = [$("#pThresRange").val(), $("#nThresRange").val()],
weight_p_value_thres = $("#weightPvalThres").val(),
link_data = JSON.parse(JSON.stringify(json.links.filter(function(d){
if (d.weight >= 0) {
return (d.weight >= weight_thres[0] && d['p-value'] <= weight_p_value_thres);
}
return (d.weight < -weight_thres[1] && d['p-value'] <= weight_p_value_thres);
})));
getNodeDegree(link_data, json.nodes);
var kept_node = link_data.map(function(d){return d['source'];}).concat(link_data.map(function(d){return d['target'];})).filter(unique),
node_data = JSON.parse(JSON.stringify(json.nodes.filter(function(d){return kept_node.indexOf(d.id) != -1;}))),
link_width = {weight:link_data.map(function(d){return d.weight;})};
link_width['min'] = Math.min.apply(null, link_width.weight.map(Math.abs));
link_width['max'] = Math.max.apply(null, link_width.weight.map(Math.abs));
widthScale.domain([link_width['min'], link_width['max']]);
attractionScale.domain([link_width['min'], link_width['max']]);
// Update links
link = link.data([]);
link.exit().remove();
link = link
.data(link_data);
link = link.enter()
.append("line")
.attr("class", "link-line")
.attr("stroke-linecap", "round")
.attr("stroke", function (d) {
if (Number(d.weight) >= 0) {
return link_color[0][0];
} else {
return link_color[1][0];
};
})
.attr("stroke-width", function (d) {
return widthScale(Math.abs(d.weight));
});
// Update nodes
node = node.data([]);
node.exit().remove();
node = node
.data(node_data);
node = node.enter()
.append("g")
.append("path")
.attr("class", "coloured-symbol symbol")
.attr("stroke", "white");
// Update nodes labels
nodeLabel = nodeLabel.data([]);
nodeLabel.exit().remove();
nodeLabel = nodeLabel
.data(node_data);
nodeLabel = nodeLabel.enter()
.append("g")
.attr('class', 'node-label');
nodeLabel.append("path")
.attr("class", "symbol")
.attr("stroke", "#333333")
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
nodeLabel.append("text")
.text(function (d) {
var label = d.name.split(";");
if (label.length > 1)
return label[label.length-2] +";"+ label[label.length-1];
return label[0];
})
.attr("text-anchor", "start")
.attr("font-family", font_family)
.attr("display", "none")
.attr("selected", false);
// Add interactive option if not too many nodes
//if (node_data.length < drag_size_limit) {
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
nodeLabel.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
//};
setSymbolSize();
setSymbolColor();
displayLabels();
showLabels();
// Restart simulation
simulation = simulation
.nodes(node_data)
.on("tick", ticked);
var data_size_factor = 1+node_data.length*node_data.length;
simulation
.force("link", d3.forceLink()
.id(function(d) { return d.id;})
.distance(function(d) {
if (d.weight >= 0) {
return attractionScale(Math.abs(d.weight))/data_size_factor;
} else {
return repulsionScale(Math.abs(d.weight))/data_size_factor;
};
})
.strength(function(d) {
if (d.weight >= 0) {
return repulsionScale(Math.abs(d.weight))/100;
} else {
return attractionScale(Math.abs(d.weight))/100;
};
}));
simulation.force("link")
.links(link_data);
simulation.alpha(0.5).restart();
// Update links legend
linkLegend = linkLegend.data([]);
linkLegend.exit().remove();
linkLegend = linkLegend
.data(link_color)
.enter().append("li")
.attr("id", function(d) { return d[1];})
.attr("class", "legend legend-no-interaction")
.attr("selected", 0)
.attr("title", function(d) { return d[1];})
linkLegendSpan = linkLegend.append("span")
linkLegendSpanSvg = linkLegendSpan.append("svg")
.attr("width", "25px")
.attr("height", "10px")
.style("margin-right", "5px")
.style("overflow", "visible")
linkLegendSpanSvg.append("rect")
.attr("width", 25)
.attr("height", 5)
.attr("y", 2.5)
.attr("stroke", "none")
.attr("fill", function(d){return d[0]})
linkLegendSpan.append("span")
.html(function(d, i) { return ['Coeff. > ', 'Coeff. < -'][i]+weight_thres[i];})
}
// Nodes labels functions
function displayLabels () {
var selected_label = $(".node-label");
selected_label.on("mouseenter", function() {
d3.select(this.childNodes[1]).attr("display", "inline");
d3.select(this.childNodes[0]).attr("stroke-opacity", 1);
});
selected_label.on("mouseleave", function() {
if (this.childNodes[1].getAttribute("selected") == "false") {
d3.select(this.childNodes[1]).attr("display", "none");
};
d3.select(this.childNodes[0]).attr("stroke-opacity", 0);
});
selected_label.on("click", function() {
if (this.childNodes[1].getAttribute("selected") == "false") {
d3.select(this.childNodes[1]).attr("display", "inline");
d3.select(this.childNodes[1]).attr("selected", true);
} else {
d3.select(this.childNodes[1]).attr("display", "none");
d3.select(this.childNodes[1]).attr("selected", false);
}
});
};
// Model update function
var updateEffect = function() {
var keys = Object.keys(json.legend[$('#modelSelect').val()])
new_data = [...Array(keys.length)];
for (var i = 0; i < keys.length; i++) {
new_data[i] = {label:keys[i], value:keys[i]}
}
$('#effectSelect').multiselect('dataprovider', new_data)
.multiselect("refresh");
setSymbolColor();
}
// Display labels button
var showLabels = function() {
var label_text = d3.selectAll(".node-label").select(function(){ return this.childNodes[1];});
if ($("#labelButton").is(':checked')) {
label_text.attr("display", "inline");
label_text.attr("selected", true);
} else {
label_text.attr("display", "none");
label_text.attr("selected", false);
};
};
// Search in labels
var searchLabels = function() {
$("#labelButton").attr("checked", false);
var key = $("#searchInput").val().toUpperCase();
if (key != '') {
var selected = d3.selectAll(".node-label").filter(function(){return this.__data__.name.toUpperCase().indexOf(key.toUpperCase()) != -1 });
non_selected = d3.selectAll(".node-label").filter(function(){return this.__data__.name.toUpperCase().indexOf(key.toUpperCase()) == -1 });
selected.select(function(){ return this.childNodes[1];}).attr("display", "inline");
selected.select(function(){ return this.childNodes[1];}).attr("selected", true);
non_selected.select(function(){ return this.childNodes[1];}).attr("display", "none");
non_selected.select(function(){ return this.childNodes[1];}).attr("selected", false);
} else {
to_free = d3.selectAll(".node-label");
to_free.select(function(){return this.childNodes[1];}).attr("display", "none");
to_free.select(function(){return this.childNodes[1];}).attr("selected", false);
};
};
// Label button and search
appendLabelCheckBox(buttons, "Show labels", "Labels", "labelButton", showLabels)
appendSearchInput(buttons, "Search", "searchInput", searchLabels);
// Select model
var modelSelect = buttons.append("div")
.attr("title", "Chose model")
.attr("class", "form-group")
modelSelect.append("label")
.html("Color (model)")
modelSelect.append("div")
.attr("class", "multiselect-btn-container figtool-multiselect-btn-container")
.append("select")
.attr("id", "modelSelect")
.attr("class", "form-control multiselect figtool-multiselect")
.selectAll("option")
.data(Object.keys(json.legend))
.enter().append("option")
.text(function (d){ return d;});
$('#modelSelect').on('change', updateEffect);
// Select effect
var effectSelect = buttons.append("div")
.attr("title", "Chose effect")
.attr("class", "form-group")
effectSelect.append("label")
.html("Color (effect)")
effectSelect.append("div")
.attr("class", "multiselect-btn-container figtool-multiselect-btn-container")
.append("select")
.attr("id", "effectSelect")
.attr("class", "form-control multiselect figtool-multiselect")
.selectAll("option")
.data(Object.keys(json.legend[$('#modelSelect').val()]))
.enter().append("option")
.text(function (d){ return d;});
$('#effectSelect').on('change', setSymbolColor);
// Select size
var sizeSelect = buttons.append("div")
.attr("title", "Chose size variable")
.attr("class", "form-group")
sizeSelect.append("label")
.html("Radius")
sizeSelect.append("div")
.attr("class", "multiselect-btn-container figtool-multiselect-btn-container")
.append("select")
.attr("id", "sizeSelect")
.attr("class", "form-control multiselect figtool-multiselect")
.selectAll("option")
$('#sizeSelect').on('change', setSymbolSize);
// Button for link weight threshold
var weightPvalThres = buttons.append("div")
.attr("title", "Set a p-value cutoff for links.")
.attr("class", "form-group")
weightPvalThres.append("label")
.html("Links p-value cutoff")
weightPvalThres.append("input")
.attr("id", "weightPvalThres")
.attr("type", "number")
.attr("class", "form-control form-number-field")
.attr("min", 0)
.attr("max", 1)
.attr("step", 0.001)
.attr("value", 1)
.on("change", restart);
var pThresRange = buttons.append("span")
.attr("title", "Cut-off for positive correlations.")
pThresRange.append("label")
.append("p")
.html("Pos. corr. cut-off <span style='white-space: nowrap'>(<i style='color:"+link_color[0][0]+"' class='fa fa-window-minimize icon-sim-link'></i>).</span>")
pThresRange.append("input")
.attr("id", "pThresRange")
.attr("type", "range")
.attr("class", "full-width")
.attr("min", 0)
.attr("max", 1)
.attr("step", 0.05)
.on("change", restart);
$("#pThresRange").val('0.75')
var nThresRange = buttons.append("span")
.attr("title", "Cut-off for negative correlations.")
nThresRange.append("label")
.append("p")
.html("Neg. corr. cut-off <span style='white-space: nowrap'>(<i style='color:"+link_color[1][0]+"' class='fa fa-window-minimize icon-sim-link'></i>).</span>")
nThresRange.append("input")
.attr("id", "nThresRange")
.attr("type", "range")
.attr("class", "full-width")
.attr("min", 0)
.attr("max", 1)
.attr("step", 0.05)
.on("change", restart);
$("#nThresRange").val('0.75')
setMultiselect('.figtool-multiselect');
//resizeMultiselect('#d3-buttons', 1, '#d3-buttons', false);
$('#sizeSelect').multiselect('dataprovider', sizeOptions)
.multiselect("refresh");
restart();
//});
};
Event Timeline
Log In to Comment