Public
Edited
Aug 28, 2024
Insert cell
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
workbook = FileAttachment("申万行业分类@1.xlsx").xlsx()
Insert cell
source_data=workbook.sheet('Sheet1')
Insert cell
viewof targetLevel=Inputs.radio([1,2,3],{label:"折叠到第几层:",value:3})
Insert cell
Insert cell
Insert cell
Insert cell
//可以设置,但不能更新
//data.children=origin_data.children.filter((d,i)=>i<=3)
Insert cell
Insert cell
tidyTree = {
//d
let tree = data => d3.tree()
.size([width, width])
.separation((a, b) => (a.parent == b.parent ? 1 : 3))
(d3.hierarchy(data));
const svg = d3.select(DOM.svg(width, width))
.style("width", "100%")
.style("height", "auto")
.style("padding", "10px")
.style("box-sizing", "border-box")
.style("font", "12px sans-serif");
const g = svg.append("g");
const color=d3.schemeCategory10
const linkgroup = g.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
//.attr("stroke",(d,i)=>d.color)
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5);

const nodegroup = g.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3);

function newdata (animate = true) {
let root = tree(data);
let links_data = root.links();
let links = linkgroup
.selectAll("path")
.data(links_data, d => d.source.data.name+"_"+d.target.data.name)
links.exit().remove();
let newlinks = links
.enter()
.append("path");

let t = d3.transition()
.duration(animate ? 400 : 0)
.ease(d3.easeLinear)
.on("end", function() {
const box = g.node().getBBox();
svg.transition().duration(1000).attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);
});
let alllinks = linkgroup.selectAll("path")
alllinks
.transition(t)
.attr("d", d3.linkHorizontal()
.x(d => d.y)
.y(d => d.x));


let nodes_data = root.descendants().reverse();
let nodes = nodegroup
.selectAll("g")
.data(nodes_data, function (d) {
if (d.parent) {
return d.parent.data.name+d.data.name;
}
return d.data.name});
nodes.exit().remove();

let newnodes = nodes
.enter().append("g");
let allnodes = animate ? nodegroup.selectAll("g").transition(t) : nodegroup.selectAll("g");
allnodes
.attr("transform", d => `
translate(${d.y},${d.x})
`);
newnodes.append("circle")
.attr("r", 4.5)
.on ("click", function (d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
d.data.children = altChildren;
d.data.altChildren = children;
newdata ();
});
nodegroup.selectAll("g circle").attr("fill", function (d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
//return d.children || (children && (children.length > 0 || altChildren.length > 0)) ? "#555" : "#999" } );
return d.data.color})


newnodes.append("text")
.attr("dy", "0.31em")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke", "white");
nodegroup.selectAll("g text")
.attr("x", d => !d.children ? 6 : -6)
.attr("text-anchor", d => !d.children ? "start" : "end")
//.attr("transform", d => d.x >= Math.PI ? "rotate(180)" : null);

}

//if (isCollapse) data.children.forEach(collapse);
collapse(data,targetLevel,0)
//data.children.forEach(d=>collapse(d,targetLevel,0))
newdata (false);
document.body.appendChild(svg.node());

const box = g.node().getBBox();
//box.width = box.height = Math.max(box.width, box.height)*1.2;
svg.remove()
.attr("width", box.width)
.attr("height", box.height)
.attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);

return svg.node();
}
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