Public
Edited
Nov 28, 2023
Insert cell
Insert cell
chart = {

const root = tree(d3.hierarchy(data)
//Sorting of words. Uncomment the adopted one.
//the following line is for sorting the topic words by alphabetical order.
//.sort((a, b) => a.data.name.toLowerCase().localeCompare (b.data.name.toLowerCase()))
//the following line is for sorting the topic words by their weightings.
.sort((a, b) => d3.descending(a.data.value, b.data.value))
);
const links = root.links();
const nodes = root.descendants();

setColor(root);
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);

const link = svg.append("g")
.attr("fill", "none")
.attr("stroke", "grey")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(links)
.join("path")
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(d => d.y))
.each(function(d) { d.target.linkNode = this; })
.attr("stroke", d => d.target.color);
const node = svg.append("g")
.selectAll("circle")
.data(nodes)
.join("circle")
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
`)
.attr("fill", d => d.children ? color(d.data.name) : color(d.data.group))
//uncomment the following line to show circles with different opacities
//.attr("fill-opacity", d => d.children ? 1 : CircleAlpha(d.data.value))
.attr("r", d => d.children ? 4 : 0);
const titre = svg.append("g")
.selectAll("text")
.data(nodes)
.join("text")
.attr("font-family", "sans-serif")
.attr("font-size", "0.65em")
//.attr("stroke-linejoin", "round")
//.attr("stroke-width", 3)
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
rotate(${d.x >= Math.PI ? 180 : 0})
`)
.attr("dy", "0em")
.attr("dx", "1.2em")
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
// this line modifies the text direction of the anchor
//.attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
.attr("visibility", d => d.children ? null : "hidden")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke", "rgba(255,255,255,1)");

const affiche = svg.append("g")
.selectAll("image")
.data(nodes)
.join("image")
.attr("width", 40)
.attr("height", 50)
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
rotate(${d.x >= Math.PI ? 180 : 0})
`)
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
.attr("y", d => d.y < Math.PI === !d.children ? 6 : -6)
.attr("href", d => d.data.name);

affiche;
node.append("title")
.text(d => d.data.name);
titre
.attr("x", d => d.x)
.attr("y", d => d.y);
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);
return svg.node();
}
Insert cell
soccer = FileAttachment("soccer.json").json()
Insert cell
height = 1500
Insert cell
md `## Radial Dendrogram Color Setting
Contents of the domain part of "color" variables are the names of topics. There are 16 topics and this is the domain of the following cell is "Topic 00" to "Topic 15".`
Insert cell
//get the min and max values of the topic word weightings. These values are obtained during the process of topic modeling. So, it is also possible to get the values from python and change the domain parts of CirSize and CircleAlpha manually.
toppicData = data.children.map(d => d.name)
Insert cell
color = d3.scaleOrdinal()
.domain(toppicData)
.range(d3.schemeCategory10)
Insert cell
// Set the color of each node by recursively inheriting.
function setColor(d) {
var name = d.data.name;
d.color = color.domain().indexOf(name) >= 0 ? color(name) : d.parent ? d.parent.color : null;
if (d.children) d.children.forEach(setColor);
}
Insert cell
function autoBox() {
document.body.appendChild(this);
const {x, y, width, height} = this.getBBox();
document.body.removeChild(this);
return [x, y, width, height];
}
Insert cell
md `## Data File for the Radial Dendrogram
The data file is in json format. The json file can be generated by following the python tutorial introduced in the introduction of this radial dendrogram.`
Insert cell
data = FileAttachment("buts.json").json()
Insert cell
//get the min and max values of the topic word weightings. These values are obtained during the process of topic modeling. So, it is also possible to get the values from python and change the domain parts of CirSize and CircleAlpha manually.
function getMinMaxvalues() {
let mappedData = data.children.map(d => d.children)
var allthedata = []
for (let i = 0; i < mappedData.length; i++) {
let j = i * 2;
allthedata[j] = d3.min(mappedData[i].map(v => v.value));
allthedata[j+1] = d3.max(mappedData[i].map(v => v.value));
}
return allthedata;
}
Insert cell
//define circle sizes by using getMinMaxvalues(). It is also possible to use the values obtained from python. In this case, it is not needed to define getMinMaxvalues().
CirSize = d3.scaleSqrt().domain([d3.min(getMinMaxvalues()),d3.max(getMinMaxvalues())]).range([1,8])
Insert cell
//define circle opacities by using getMinMaxvalues()
CircleAlpha = d3.scaleLinear().domain([d3.min(getMinMaxvalues()),d3.max(getMinMaxvalues())]).range([0.3,1])
Insert cell
width = 1500
Insert cell
radius = width / 2
Insert cell
tree = d3.cluster().size([2 * Math.PI, radius - 100])
Insert cell
d3 = require("d3@6")
Insert cell
wikidata_hierarchies1 = FileAttachment("wikidata_hierarchies(1).json").json()
Insert cell
buts = FileAttachment("buts.json").json()
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more