Published
Edited
Nov 26, 2020
2 forks
Importers
20 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function nodes_sorted() {
const nodes = order_by_distance(data.nodes);

// largest group must come first: reverse if it's not the case
const order = nodes.map(d => data.nodes.indexOf(d)),
n = nodes.length;

var sum = 0;
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
sum += (A[order[i]][order[j]] < 0.2 ? 1 : 0) * Math.sign(n - (i + j));
}
}
if (sum < 0) nodes.reverse();

return nodes;
}
Insert cell
function makevis() {
return chart(sorted ? nodes_sorted() : nodes, value);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
value = {
const index = d3.group(links, d => d.source, d => d.target);
return ({ source, target }) => {
const li = index.get(source);
if (li) {
const lj = li.get(target);
if (lj) return lj[0].value;
}
return -1;
};
}
Insert cell
Insert cell
A = {
const A = Array.from(data.nodes).map((_, i) =>
Array.from(data.nodes, (_, j) => (i === j ? 0 : 2))
),
m = [...new Set(data.nodes.map(d => d.id)).values()];
data.links
.filter(l => l.value > 0.001)
.forEach(l => {
const i = m.indexOf(l.source),
j = m.indexOf(l.target),
v = Math.pow(
1.0 - l.value,
1
) /* * (nodes[i].group == nodes[j].group ? 1 : 1) */;
if (i > -1 && j > -1) {
A[i][j] = v;
A[j][i] = v;
}
});
return A;
}
Insert cell
B = {
const B = A.map(d => d.slice());

for (let i = 0; i < B.length; i++) {
for (let j = 0; j < B.length; j++) {
B[i][j] = A[i][j];
for (let k = 0; k < B.length; k++) {
B[i][j] +=
Math.max(Math.abs(A[i][k] - A[j][k]) - alpha, 0) / 3 / nodes.length;
}
}
}

return B;
}
Insert cell
Insert cell
Insert cell
mutable debug = undefined
Insert cell
function chart(nodes, accessor) {
const height = width;
var w = width - margin.left - margin.right;
var h = width - margin.top - margin.bottom;

const ids = nodes.map(d => d.id),
matrix = ids.map(source =>
ids.map(target => ({ source, target, val: accessor({ source, target }) }))
);

function colored(d) {
const val = d.val;
if (val === "UNAVAILABLE") return "lime";
if (val === "NA") return "#ccc";
if (d.source == d.target) return "#333";
if (+val > 0) return color(+val);
return "white";
}

var x = d3
.scaleBand()
.rangeRound([0, w])
.paddingInner(0.1)
.align(0)
.domain(ids);
var svgDOM = d3
.select(DOM.svg(width, height))
.attr("class", "matrixdiagram")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom);

svgDOM.append("defs").html(css);

var svg = svgDOM.append("g");

var row = svg
.selectAll("g.row")
.data(matrix)
.enter()
.append("g")
.attr("class", "row")
.attr("transform", d => `translate(0,${x(d[0].source)})`)
.each(makeRow);

row
.append("text")
.attr("class", "label")
.attr("x", -4)
.attr("y", x.bandwidth() / 2)
.attr("dy", "0.32em")
.text(d => d[0].source);

var column = svg
.selectAll("g.column")
.data(matrix)
.enter()
.append("text")
.attr("class", "column label")
.attr("transform", d => `translate(${x(d[0].source)},0)rotate(-90)`)
.attr("x", 4)
.attr("y", x.bandwidth() / 2)
.attr("dy", "0.32em")
.text(d => d[0].source);

function makeRow(rowData) {
var cell = d3
.select(this)
.selectAll("rect")
.data(rowData)
.enter()
.append("rect")
.attr("x", d => x(d.target))
.attr("width", x.bandwidth())
.attr("height", x.bandwidth())
.style("fill-opacity", 1)
.style("fill", colored)
.on("mouseover", d => {
row
.filter(e => e[0].source === d.source)
.style("fill", "#d62333")
.style("font-weight", "bold");
column
.filter(e => e[0].source === d.target)
.style("fill", "#d62333")
.style("font-weight", "bold");
})
.on("mouseout", () => {
row.style("fill", null).style("font-weight", null);
column.style("fill", null).style("font-weight", null);
});
cell
.append("title")
.text(d =>
d.source === d.target
? d.source
: `${d.source} ↔︎ ${d.target} (${d.val})`
);
}

const zoom = d3
.zoom()
.on("zoom", ({ transform }) => svg.attr("transform", transform));

return svgDOM
.call(zoom)
.call(zoom.transform, d3.zoomIdentity.translate(margin.left, margin.top))
.node();
}
Insert cell
css = {
const w = width - margin.left - margin.right,
n = data.nodes.length,
r = (0.75 * w) / n;
return `<style>
svg.matrixdiagram {
font: ${r}px sans-serif;
}
svg.matrixdiagram .label {
fill: #999;
font-size: ${r}px;
text-anchor: end;
}
svg.matrixdiagram .column.label {
text-anchor: start;
}
svg.matrixdiagram rect {
fill: #eee;
stroke: #d62333;
stroke-width: 0;
}
svg.matrixdiagram rect:hover {
stroke-width: 1px;
}
</style>`;
}
Insert cell
Insert cell
color = {
function domain(color) {
return color.domain(truedomain ? [0, 1] : [0, d3.max(links, d => d.value)]);
}

switch (colorI) {
case "diverging":
return domain(d3.scaleSequential(t => d3.interpolateRdYlBu(1 - t)));
break;
case "plasma":
return domain(d3.scaleSequential(t => d3.interpolatePlasma(1 - t)));
break;
case "reds":
return domain(d3.scaleSequential(d3.interpolateReds));
break;
}
}
Insert cell
import {legend as colorLegend} from "@d3/color-legend"
Insert cell
margin = {
const r =
d3.max(data.nodes, d => d.id.length) *
(width / (20 + data.nodes.length) / 2.5);
return {
top: 10 + r,
left: 10 + r,
right: 10,
bottom: 10
};
}
Insert cell
Insert cell
Insert cell
d3 = require("d3@6")
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more