Public
Edited
Jan 19, 2023
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
setup = {
const margin = { top: 120, right: 0, bottom: 10, left: 120 }
const leafSet = new Set();
graph.links.map(d => leafSet.add(d.source));
const nodeIds = Array.from(leafSet);
const parentSet = new Set();
graph.links.map(d => parentSet.add(d.target));
const parentIds = Array.from(parentSet);

const height = nodeIds.length * 10
const width = parentIds.length * 10
const y = d3
.scaleBand()
.domain(nodeIds)
.range([0, height]),
x = d3
.scaleBand()
.domain(parentIds)
.range([0, width]),
z = d3
.scaleLinear()
.domain([0, 4])
.clamp(true),
c = d3.scaleOrdinal(d3.range(10), d3.schemeCategory10);

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);

const g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");

g.append("rect")
.style("fill", "#eee")
.attr("width", width)
.attr("height", height);

const matrix = graph.links
.flatMap(({ source, target, value }) => [
[source, target, value]//,
//[target, source, value]
])
//.concat(nodeIds.map(i => [i, i]));

const labels = g
.append("g")
.style("font-size", "8px")
.style("font-family", "sans-serif");

const columns = labels
.append("g")
.selectAll()
.data(parentIds)
.join("g")
.attr("transform", "rotate(-90)")
.append("g");
columns
.append("line")
.attr("x2", -height)
.style("stroke", "white");
columns
.append("text")
.attr("dx", 2)
.attr("dy", y.bandwidth() / 2 + 2)
.text(i => graph.nodes[i].name);
const rows = labels
.append("g")
.selectAll()
.data(nodeIds)
.join("g");
rows
.append("line")
.attr("x2", width)
.style("stroke", "white");
rows
.append("text")
.attr("text-anchor", "end")
.attr("dx", -2)
.attr("dy", x.bandwidth() / 2 + 2)
.text(i => graph.nodes[i].name);

const rects = g
.append("g")
.attr("transform", "translate(1,1)")
.selectAll()
.data(matrix)
.join("rect")
.attr("width", x.bandwidth() - 2)
.attr("height", y.bandwidth() - 2)
.attr("fill", ([s, t]) =>
graph.nodes[s].group === graph.nodes[t].group
? c(graph.nodes[t].group)
: "black"
)
.attr("fill-opacity", ([, , v]) => z(v));

let prev;

update(graph.nodes.map(d => +d.id));

function update(permutation) {
x.domain(permutation.filter(function(d) {return parentSet.has(d)}));
y.domain(permutation.filter(function(d) {return leafSet.has(d)}));

const delayx = prev ? i => x(i) * 4 : 0;
const delayy = prev ? i => y(i) * 4 : 0;
const delay2 = prev ? ([i]) => y(i) * 4 : 0;
const duration = prev ? 1500 : 0;

columns
.transition()
.delay(delayx)
.duration(duration)
.attr("transform", i => `translate(0, ${x(i)})`);
rows
.transition()
.delay(delayy)
.duration(duration)
.attr("transform", i => `translate(0, ${y(i)})`);
rects
.transition()
//.delay(delay2)
.duration(duration)
.attr("x", ([, t]) => x(t))
.attr("y", ([s]) => y(s));

prev = permutation;
return permutation;
}

return Object.assign(svg.node(), { update });
}
Insert cell
current = setup.update(permutations[sorting]())
Insert cell
Insert cell
permutations = orders(graph, {distance})
Insert cell
orders = ({ nodes, links }, { distance = "manhattan" }) => {
const n = nodes.length;

const graph = reorder.graph().nodes(nodes).links(links).init();

let dist_adjacency;

const leafOrder = reorder
.optimal_leaf_order()
.distance(reorder.distance[distance]);

function computeLeaforder() {
const adjacency = reorder.graph2mat(graph);
return leafOrder(adjacency);
}

function computeLeaforderDist() {
if (!dist_adjacency) dist_adjacency = reorder.graph2valuemats(graph);
return reorder.valuemats_reorder(dist_adjacency, leafOrder);
}

function computeBarycenter() {
const barycenter = reorder.barycenter_order(graph);
return reorder.adjacent_exchange(graph, ...barycenter);
}

function computeRCM() {
return reorder.reverse_cuthill_mckee_order(graph);
}

function computeSpectral() {
return reorder.spectral_order(graph);
}

const orders = {
none: () => d3.range(n),
name: () =>
d3.range(n).sort((a, b) => d3.ascending(nodes[a].name, nodes[b].name)),
count: () => d3.range(n).sort((a, b) => nodes[b].count - nodes[a].count),
group: () =>
d3
.range(n)
.sort(
(a, b) =>
d3.ascending(nodes[a].group, nodes[b].group) ||
d3.ascending(nodes[a].name, nodes[b].name)
),
leafOrder: computeLeaforder,
leafOrderDist: computeLeaforderDist,
barycenter: computeBarycenter,
rcm: computeRCM,
spectral: computeSpectral
};

return orders;
}
Insert cell
orderingsOptions = [
"none",
"name",
"count",
"group",
"leafOrder",
"leafOrderDist",
"barycenter",
"rcm",
"spectral"
]
Insert cell
distanceOptions = ({
euclidean: "Euclidean (L2)",
manhattan: "Manhattan (L1)",
minkowski: "Minkowski",
chebyshev: "Chebyshev",
hamming: "Hamming",
jaccard: "Jaccard",
braycurtis: "Braycurtis"
})
Insert cell
reorder = require("reorder.js@2.2.4")
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