Public
Edited
May 3
Insert cell
Insert cell
chart = {

const treeData = {
name: "reviews",
children: data.map(d => ({
name: d.name,
children: [
{ name: `Rating: ${d.rating}` },
{ name: `Reviews: ${d.reviews}` },
{ name: `Type: ${d.type}` }
]
}))
};


const root = tree(d3.hierarchy(treeData)
.sort((a, b) => a.data.name.toLowerCase().localeCompare(b.data.name.toLowerCase())));


const ratings = treeData.children.map(d => {
const ratingNode = d.children.find(c => c.name.startsWith("Rating:"));
return ratingNode ? parseFloat(ratingNode.name.split("Rating: ")[1]) : null;
}).filter(r => r !== null);

const CircleAlpha = d3.scaleLinear()
.domain([d3.min(ratings), d3.max(ratings)])
.range([0.3, 1]);


const typeColor = d3.scaleOrdinal()
.domain(["Bar", "Café", "Restaurant", "School", "Park"])
.range(d3.schemeSet2);

root.descendants().forEach(d => {
const typeNode = d.data.children?.find(c => c.name.startsWith("Type:"));
const ratingNode = d.data.children?.find(c => c.name.startsWith("Rating:"));
const type = typeNode ? typeNode.name.split("Type: ")[1] : null;
const rating = ratingNode ? parseFloat(ratingNode.name.split("Rating: ")[1]) : null;

d.color = type ? typeColor(type) : "#ccc";
d.opacity = rating ? CircleAlpha(rating) : 0.5;
});


const svg = d3.create("svg");



svg.append("g")
.attr("fill", "none")
.attr("stroke", "#aaa")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5)
.selectAll("path")
.data(root.links())
.join("path")
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(d => d.y))
.attr("stroke", d => d.target.color);

svg.append("g")
.selectAll("circle")
.data(root.descendants())
.join("circle")
.attr("transform", d => `rotate(${d.x * 180 / Math.PI - 90}) translate(${d.y},0)`)
.attr("fill", d => d.color)
.attr("fill-opacity", d => d.opacity)
.attr("r", 4);

svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.selectAll("text")
.data(root.descendants())
.join("text")
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
rotate(${d.x >= Math.PI ? 180 : 0})
`)
.attr("dy", "0.31em")
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
.attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke", "white");

return svg.attr("viewBox", autoBox).node();
}
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
color = d3.scaleOrdinal()
.domain(["Acrobatie", "Jonglerie et magie","Clowns","Dressage"])
.range(d3.schemeCategory10)
Insert cell
// Set the color of each node by recursively inheriting.
function setColor(d) {
var name = d.data.name;
d.color = setColor.domain().indexOf(name) >= 0 ? setColor(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
//define circle opacities by using getMinMaxvalues()

data = FileAttachment("Fixed_Yelp_Data.json").json()
Insert cell
width = 975
Insert cell
radius = width / 2
Insert cell
tree = d3.cluster().size([2 * Math.PI, radius - 100])
Insert cell
d3 = require("d3@6")
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