Public
Edited
May 3
Insert cell
Insert cell
socDolphins = FileAttachment("soc-dolphins.mtx")
Insert cell
dolphon.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
data = {
const raw = await FileAttachment("dolphon.csv").text();

const lines = raw
.split("\n")
.map(l => l.trim())
.filter(l => l.length > 0);

// For graph representation i subtracted one so we have it all in an array structure
const links = [];
for (const line of lines) {
const [aRaw, bRaw] = line.split(",");
const a = parseInt(aRaw.trim(), 10) - 1;
const b = parseInt(bRaw.trim(), 10) - 1;
if (!isNaN(a) && !isNaN(b)) {
links.push({ source: a, target: b });
}
}

const uniqueIDs = Array.from(new Set(links.flatMap(l => [l.source, l.target])));

const nodes = uniqueIDs.map(id => ({ id, name: `Dolphin ${id}` }));

return { nodes, links };
}

Insert cell

// I was struggling with this function and used gpt help to achieve bits and pieces of it. Honestly it was all over the place so im not sure what exactly went right and it started to work
function drag(simulation) {
function dragstarted(event, d) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}

function dragended(event, d) {
if (!event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}

return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}

Insert cell
chart = {
const width = 1000;
const height = 1000;

const links = data.links.map(d => Object.create(d));
const nodes = data.nodes.map((d, i) => Object.create({ ...d, id: i }));

const degreeMap = new Map();
links.forEach(link => {
degreeMap.set(link.source, (degreeMap.get(link.source) || 0) + 1);
degreeMap.set(link.target, (degreeMap.get(link.target) || 0) + 1);
});

nodes.forEach(node => {
node.degree = degreeMap.get(node.id) || 0;
});

const color = d3.scaleSequential(d3.interpolateBlues)
.domain([0, d3.max(nodes, d => d.degree)]);

const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id).distance(80))
.force("charge", d3.forceManyBody().strength(-50))
.force("center", d3.forceCenter(width / 2, height / 2));

const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

const link = svg.append("g")
.attr("stroke", "#aaa")
.attr("stroke-opacity", 0.3)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", 1.5);

const node = svg.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1)
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("r", d => 4 + d.degree / 2)
.attr("fill", d => color(d.degree))
.call(drag(simulation));

node.append("title")
.text(d => `Dolphin ${d.id}, Degree: ${d.degree}`);

node.on("mouseover", (event, d) => {
link.style("stroke-opacity", l => (l.source === d || l.target === d ? 1 : 0.05));
node.style("opacity", n => n === d || links.some(l => (l.source === d && l.target === n) || (l.target === d && l.source === n)) ? 1 : 0.3);
}).on("mouseout", () => {
link.style("stroke-opacity", 0.3);
node.style("opacity", 1);
});

const texts = svg.selectAll(".label")
.data(nodes.filter(d => d.degree > 6))
.join("text")
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.attr("dx", 10)
.attr("dy", "0.35em")
.text(d => `#${d.id}`);

simulation.on("tick", () => {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);

node
.attr("cx", d => d.x)
.attr("cy", d => d.y);

texts
.attr("x", d => d.x)
.attr("y", d => d.y);
});

invalidation.then(() => simulation.stop());

return svg.node();
}

Insert cell
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