study-buddy / app /agents /d3 /templates /network_adjacency_matrix.py
GitHub Actions
deploy d092bea3608b7a29952f16357fda39b7a29e399b
2e818da
Raw
History Blame Contribute Delete
4.64 kB
"""Adjacency matrix template — network family.
d3-graph-gallery.com has no dedicated adjacency-matrix example (no
`/graph/matrix_basic.html` or sibling page — confirmed 404, and the site's
network/arc/chord section pages link only to force graphs, arc diagrams,
and chord diagrams). The closest real sibling on that site for a grid of
colored cells is the general heatmap example —
https://d3-graph-gallery.com/graph/heatmap_style.html (d3 v4: d3.scaleBand()
row/column axes, d3.scaleSequential(d3.interpolateInferno) cell coloring,
cells loaded from a remote CSV via d3.csv()). Here the row/column bands are
keyed by `data.nodes` ids (not a remote CSV), and cell values come from an
NxN matrix built dynamically from `data.links` (same source/target-id
lookup approach as network_chord.py) rather than a hardcoded/loaded table.
"""
from __future__ import annotations
from app.agents.d3.registry import D3Template, register
from app.agents.d3.templates._graph import GraphData, GraphNode, GraphLink
_HTML = """<!-- Adapted from d3-graph-gallery.com (Yan Holtz) — https://d3-graph-gallery.com/graph/heatmap_style.html (no adjacency-matrix example exists on this site; closest sibling grid-of-cells chart substituted, see module docstring) -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v7.min.js"></script>
<style>
html, body { margin: 0; padding: 0; width: 100vw; height: 100vh; background: #FAF7F2; color: #1A3557; font-family: Georgia, 'Libre Caslon Text', serif; overflow: hidden; }
#my_dataviz { width: 100vw; height: 100vh; }
.chart-title { fill: #1A3557; font-size: 18px; text-anchor: middle; }
.axis text { fill: #1A3557; font-size: 11px; }
.axis path, .axis line { display: none; }
.cell { stroke: #FAF7F2; }
</style>
</head>
<body>
<div id="my_dataviz"></div>
<script>
const data = __DATA__;
const margin = {top: 110, right: 30, bottom: 20, left: 110},
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
const svg = d3.select("#my_dataviz")
.append("svg")
.attr("viewBox", `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
.attr("preserveAspectRatio", "xMidYMid meet")
.style("width", "100%")
.style("height", "100%")
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svg.append("text")
.attr("class", "chart-title")
.attr("x", width / 2)
.attr("y", -70)
.text(data.title);
const ids = data.nodes.map(d => d.id);
const index = new Map(ids.map((id, i) => [id, i]));
const n = ids.length;
const matrix = Array.from({length: n}, () => new Array(n).fill(0));
data.links.forEach(link => {
const i = index.get(link.source);
const j = index.get(link.target);
if (i === undefined || j === undefined) return;
matrix[i][j] += link.value;
matrix[j][i] += link.value;
});
const x = d3.scaleBand().range([0, width]).domain(ids).padding(0.05);
const y = d3.scaleBand().range([0, height]).domain(ids).padding(0.05);
svg.append("g")
.attr("class", "axis")
.call(d3.axisTop(x).tickSize(0))
.selectAll("text")
.attr("transform", "translate(6,-8) rotate(-30)")
.style("text-anchor", "start");
svg.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).tickSize(0));
const maxValue = d3.max(matrix.flat()) || 1;
const color = d3.scaleSequential(d3.interpolateBlues).domain([0, maxValue]);
const cells = [];
matrix.forEach((row, i) => row.forEach((v, j) => cells.push({ row: ids[i], col: ids[j], value: v })));
svg.selectAll(".cell")
.data(cells)
.join("rect")
.attr("class", "cell")
.attr("x", d => x(d.col))
.attr("y", d => y(d.row))
.attr("width", x.bandwidth())
.attr("height", y.bandwidth())
.attr("rx", 3)
.style("fill", d => d.value === 0 ? "#EFE9DF" : color(d.value));
</script>
</body>
</html>
"""
golden_sample = GraphData(
title="Demo",
nodes=[
GraphNode(id="A", group="x"),
GraphNode(id="B", group="x"),
GraphNode(id="C", group="y"),
],
links=[
GraphLink(source="A", target="B", value=2),
GraphLink(source="B", target="C", value=1),
],
)
register(
D3Template(
id="network_adjacency_matrix",
family="network",
title="Adjacency matrix",
when_to_use="Show which named entities connect to which, and how strongly, as a grid of colored cells.",
data_requirements="A list of nodes (id, optional group) and weighted links (source id, target id, value).",
schema=GraphData,
html_template=_HTML,
golden_sample=golden_sample,
)
)