Public
Edited
Apr 17
Insert cell
Insert cell
Insert cell
Insert cell
ForceGraph(graph, {
nodeId: d => d.id,
nodeTitle: d => `title: ${d.title}\n\nAbstract: ${d.abstract}`,
nodeLabel: d => `${d.first_author} ${d.year}`,
// nodeRadius: d => Math.sqrt(d.citationCount),
width: 1200, height: 1050, invalidation
})
Insert cell
Insert cell
Insert cell
graph = FileAttachment("test@8.json").json()
Insert cell
// Copyright 2021 Observable, Inc.
// Released under the ISC license.
// https://observablehq.com/@d3/force-directed-graph
function ForceGraph({nodes, links}, {
nodeId = d => d.id,
nodeTitle,
nodeLabel = d => d.year,
nodeRadius = 4,
linkSource = ({source}) => source,
linkTarget = ({target}) => target,
width = 640, height = 400,
invalidation} = {}) {
// Compute values.
const N = d3.map(nodes, nodeId).map(intern);
const LS = d3.map(links, linkSource).map(intern);
const LT = d3.map(links, linkTarget).map(intern);
const L = d3.map(nodes, nodeLabel).map(intern);
if (nodeTitle === undefined) nodeTitle = (_, i) => N[i];
const T = nodeTitle == null ? null : d3.map(nodes, nodeTitle);
const NR = typeof nodeRadius !== "function" ? null : d3.map(nodes, nodeRadius);
// Replace the input nodes and links with mutable objects for the simulation.
nodes = d3.map(nodes, (_, i) => ({id: N[i], label: L[i]}));
links = d3.map(links, (_, i) => ({source: LS[i], target: LT[i]}));
const simulation = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody().strength(-350))
.force("x", d3.forceX())
.force("y", d3.forceY())
.on("tick", ticked);

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; height: intrinsic;");
svg.append('defs').append('marker')
.attr("id",'arrowhead')
.attr("viewBox", "0 -5 10 10")
.attr('refX',15)
.attr('refY',-0.5)
.attr('orient','auto')
.attr("markerWidth", 4)
.attr("markerHeight", 4)
.append('path')
.attr("d", "M0,-5L10,0L0,5");
const link = svg.append("g")
.attr("fill", "none")
.attr("stroke-width", 1.5)
.attr("stroke", "#999")
.selectAll("path")
.data(links)
.join("path")
.attr('marker-end','url(#arrowhead)');
const node = svg.append("g")
.selectAll("g")
.data(nodes)
.join("g")
.call(drag(simulation));

node.append("circle")
.attr("stroke", "white")
.attr("stroke-width", 1.5)
.attr("r", typeof nodeRadius !== "function" ? nodeRadius : 4);
node.append("text")
.attr("x", 8)
.attr("y", "0.31em")
.text(d => d.label)
if (NR) node.attr("r", d => d.size);
if (T) node.append("title").text(({index: i}) => T[i]);

// Handle invalidation.
if (invalidation != null) invalidation.then(() => simulation.stop());
function ticked() {
link.attr("d", linkArc);
node
.attr("transform", d => `translate(${d.x},${d.y})`)
.attr("cx", d => d.x)
.attr("cy", d => d.y);
}
function intern(value) {
return value !== null && typeof value === "object" ? value.valueOf() : value;
}

return svg.node();
}
Insert cell
citation_r = d3.scaleLog();
Insert cell
Insert cell
Insert cell
import {mdPlus} from "@tmcw/bonus-markdown-flavor"
Insert cell
import {tidy, mutate} from "@pbeshai/tidyjs"
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