d3.js 中每一层都有多个元素的金字塔
Pyramid with multiple elements at each level in d3.js
我必须使用 d3.js 从数据创建金字塔可视化。附上示例视图。我正在寻求帮助解决 2 个挑战。
我了解如何通过下面给出的示例创建金字塔,但我不明白如何添加多个元素(如图所示,具有 A、B、C 等值)每个级别
http://bl.ocks.org/ronakrrb/73e9204a66e2a9c1fee8
我想为最低级别的一些元素附加树状结构。
感谢任何帮助。
我不能用树代替金字塔,因为每一层的元素之间没有父子关系
示例演示数据:
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
]
这是一种方法 - 这是第一个 非常 粗略的传球。如果你说方向对了,我可以改进和完善:
(我需要改进间距的发生方式,我还需要将团队成员添加到字母 A 中,并且需要对其进行总体清理等)
20 年 6 月 11 日更新 - 清理间距并在字母周围添加方框,在数字周围添加圆圈。
var width = 700,
height = 450,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#255aee","#3a6fff","#4f84ff","rgb(101,154,302)","rgb(122,175,323)", "rgb(144,197,345)", "rgb(165,218,366)"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
//*
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
];
//*/
const grouped = Object.values(data.reduce((aggObj, item) => {
if (!aggObj[item.level]){
aggObj[item.level] = {level: item.level, names: []};
}
aggObj[item.level].names.push({name: item.Name, team: item.team});
return aggObj;
}, {}));
//console.log(grouped);
var pyramid = d3.pyramid()
.size([width,height])
.value(function(d) { return d.level; });
var line = d3.svg.line()
.interpolate('linear-closed')
.x(function(d,i) { return d.x; })
.y(function(d,i) { return d.y; });
var g = svg.selectAll(".pyramid-group")
.data(pyramid(grouped)) //swap for grouped data
.enter().append("g")
.attr("class", "pyramid-group");
g.append("path")
.attr("d", function (d){ return line(d.coordinates); })
.style("fill", function(d) { return color(d.level); });
var textBoxes = g.selectAll(".textBoxes")
.data((d) => {
const helperObj = {};
helperObj.coordinates = d.coordinates;
helperObj.level = d.level;
const len = d.names.length;
helperObj.len = d.names.length;
if(d.coordinates.length === 4) {
const left = d.coordinates[0].x;
const right = d.coordinates[3].x;
const topLeft = d.coordinates[1].x;
const topRight = d.coordinates[2].x;
const start = left + ((topLeft - left) / 2);
const end = right - ((right - topRight) / 2);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
const yCenter = (((d.coordinates[0].y-d.coordinates[1].y)/2)+d.coordinates[1].y) + 5;
helperObj.yCenter = yCenter;
helperObj.yDiff = (d.coordinates[0].y-d.coordinates[1].y);
} else {
const left = d.coordinates[1].x;
const right = d.coordinates[2].x;
const start = left + ((right - left) / 4);
const end = right - ((right - left) / 4);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
helperObj.yCenter = (d.coordinates[0].y + d.coordinates[1].y)/2 + 10;
helperObj.yDiff = (d.coordinates[1].y-d.coordinates[0].y) * 0.9;
}
return d.names.map((e,i) => {
const name = e.name;
const team = e.team || [];
const teamLen = team.length;
return {...helperObj, i, name, team, teamLen};
})
})
.enter().append("g");
textBoxes.append("rect")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter - (d.yDiff/8);
},
"x": function (d,i) {
return d.start + ((i+0.05) * d.sep);
},
"width": function (d,i) {
return d.sep * 0.9;
},
"height": function (d,i) {
return (d.yDiff/8) * 3;
},
"fill": function (d,i) {
return "#e0f4ff";
},
"rx": function (d,i) {
return d.sep * 0.1;
}
});
textBoxes.append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter;
},
"x": function (d,i) {
return d.start + ((i+0.5) * d.sep);
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "hanging")
.style("font-size", "x-large")
.text(function(d) { return d.name; });
var teamMembers = textBoxes.selectAll(".teamMembers")
.data((d) => {
const itemCopy = {...d};
const teamCopy = [...d.team];
delete itemCopy.team;
return d.team.map(e => ({...itemCopy, teamMember: e}));
});
teamMembers.enter().append("line")
.attr({
"y1": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x1": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"y2": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/6);
},
"x2": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + (0.5 * (0.7*d.sep));
},
"stroke": function (d,i) {
//console.log(d)
return "black";
},
"stroke-width": function (d,i) {
return "2";
}
});
teamMembers.enter().append("circle")
.attr({
"cy": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"cx": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"r": function (d,i) {
return d.sep * 0.05;
}
});
teamMembers.enter().append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"fill": function (d,i) {
return "white";
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", "large")
.text(function(d) { return d.teamMember; });
d3.select("body").append("table")
.attr({
"id" : "footer",
"width": width + "px"
})
d3.select("body #footer").append("tr")
.attr({
"class" : "PykCharts-credits",
"id" : "credit-datasource"
})
.append("td")
.style("text-align","left")
.html("<span style='pointer-events:none;'>Credits: </span><a href='http://pykcharts.com' target='_blank'>"+ "Pykcharts" +"</a>");
<html>
<head>
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://s3-ap-southeast-1.amazonaws.com/charts.pykih.com/gists/pyramid.js"></script>
</body>
</html>
输出:
我必须使用 d3.js 从数据创建金字塔可视化。附上示例视图。我正在寻求帮助解决 2 个挑战。
我了解如何通过下面给出的示例创建金字塔,但我不明白如何添加多个元素(如图所示,具有 A、B、C 等值)每个级别 http://bl.ocks.org/ronakrrb/73e9204a66e2a9c1fee8
我想为最低级别的一些元素附加树状结构。
感谢任何帮助。 我不能用树代替金字塔,因为每一层的元素之间没有父子关系
示例演示数据:
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
]
这是一种方法 - 这是第一个 非常 粗略的传球。如果你说方向对了,我可以改进和完善:
(我需要改进间距的发生方式,我还需要将团队成员添加到字母 A 中,并且需要对其进行总体清理等)
20 年 6 月 11 日更新 - 清理间距并在字母周围添加方框,在数字周围添加圆圈。
var width = 700,
height = 450,
radius = Math.min(width, height) / 2;
var color = d3.scale.ordinal()
.range(["#255aee","#3a6fff","#4f84ff","rgb(101,154,302)","rgb(122,175,323)", "rgb(144,197,345)", "rgb(165,218,366)"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
//*
const data = [
{ Name: "G", level: 1},
{ Name: "F", level: 2},
{ Name: "DE", level: 2},
{ Name: "C", level: 3},
{ Name: "B", level: 3},
{ Name: "A", level: 3, team: [1,2,3]}
];
//*/
const grouped = Object.values(data.reduce((aggObj, item) => {
if (!aggObj[item.level]){
aggObj[item.level] = {level: item.level, names: []};
}
aggObj[item.level].names.push({name: item.Name, team: item.team});
return aggObj;
}, {}));
//console.log(grouped);
var pyramid = d3.pyramid()
.size([width,height])
.value(function(d) { return d.level; });
var line = d3.svg.line()
.interpolate('linear-closed')
.x(function(d,i) { return d.x; })
.y(function(d,i) { return d.y; });
var g = svg.selectAll(".pyramid-group")
.data(pyramid(grouped)) //swap for grouped data
.enter().append("g")
.attr("class", "pyramid-group");
g.append("path")
.attr("d", function (d){ return line(d.coordinates); })
.style("fill", function(d) { return color(d.level); });
var textBoxes = g.selectAll(".textBoxes")
.data((d) => {
const helperObj = {};
helperObj.coordinates = d.coordinates;
helperObj.level = d.level;
const len = d.names.length;
helperObj.len = d.names.length;
if(d.coordinates.length === 4) {
const left = d.coordinates[0].x;
const right = d.coordinates[3].x;
const topLeft = d.coordinates[1].x;
const topRight = d.coordinates[2].x;
const start = left + ((topLeft - left) / 2);
const end = right - ((right - topRight) / 2);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
const yCenter = (((d.coordinates[0].y-d.coordinates[1].y)/2)+d.coordinates[1].y) + 5;
helperObj.yCenter = yCenter;
helperObj.yDiff = (d.coordinates[0].y-d.coordinates[1].y);
} else {
const left = d.coordinates[1].x;
const right = d.coordinates[2].x;
const start = left + ((right - left) / 4);
const end = right - ((right - left) / 4);
const sep = (end - start) / len;
helperObj.start = start;
helperObj.end = end;
helperObj.sep = sep;
helperObj.yCenter = (d.coordinates[0].y + d.coordinates[1].y)/2 + 10;
helperObj.yDiff = (d.coordinates[1].y-d.coordinates[0].y) * 0.9;
}
return d.names.map((e,i) => {
const name = e.name;
const team = e.team || [];
const teamLen = team.length;
return {...helperObj, i, name, team, teamLen};
})
})
.enter().append("g");
textBoxes.append("rect")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter - (d.yDiff/8);
},
"x": function (d,i) {
return d.start + ((i+0.05) * d.sep);
},
"width": function (d,i) {
return d.sep * 0.9;
},
"height": function (d,i) {
return (d.yDiff/8) * 3;
},
"fill": function (d,i) {
return "#e0f4ff";
},
"rx": function (d,i) {
return d.sep * 0.1;
}
});
textBoxes.append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter;
},
"x": function (d,i) {
return d.start + ((i+0.5) * d.sep);
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "hanging")
.style("font-size", "x-large")
.text(function(d) { return d.name; });
var teamMembers = textBoxes.selectAll(".teamMembers")
.data((d) => {
const itemCopy = {...d};
const teamCopy = [...d.team];
delete itemCopy.team;
return d.team.map(e => ({...itemCopy, teamMember: e}));
});
teamMembers.enter().append("line")
.attr({
"y1": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x1": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"y2": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/6);
},
"x2": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + (0.5 * (0.7*d.sep));
},
"stroke": function (d,i) {
//console.log(d)
return "black";
},
"stroke-width": function (d,i) {
return "2";
}
});
teamMembers.enter().append("circle")
.attr({
"cy": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"cx": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"r": function (d,i) {
return d.sep * 0.05;
}
});
teamMembers.enter().append("text")
.attr({
"y": function (d,i) {
//console.log(d)
return d.yCenter + (d.yDiff/3);
},
"x": function (d,i) {
return d.start + ((d.i+0.15) * d.sep) + ((i+0.5) * ((0.7*d.sep)/d.teamLen));
},
"fill": function (d,i) {
return "white";
}
})
.style("text-anchor", "middle")
.style("alignment-baseline", "middle")
.style("font-size", "large")
.text(function(d) { return d.teamMember; });
d3.select("body").append("table")
.attr({
"id" : "footer",
"width": width + "px"
})
d3.select("body #footer").append("tr")
.attr({
"class" : "PykCharts-credits",
"id" : "credit-datasource"
})
.append("td")
.style("text-align","left")
.html("<span style='pointer-events:none;'>Credits: </span><a href='http://pykcharts.com' target='_blank'>"+ "Pykcharts" +"</a>");
<html>
<head>
<style>
body {
font: 10px sans-serif;
}
.arc path {
stroke: #fff;
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://s3-ap-southeast-1.amazonaws.com/charts.pykih.com/gists/pyramid.js"></script>
</body>
</html>
输出: