我在复制描述性统计数据方面取得了相当大的进展,这是可以在here上看到的蜂群风格的视觉效果。为了更方便起见,我还将图片包括在这里:

从我的代码片段中,您将看到除了金字塔样式堆叠之外,我拥有该可视化的所有装饰。
var margins = {top:20, bottom:300, left:30, right:100};
var height = 150;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var xScale = d3.scaleLinear()
.range([0, width]);
var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 14.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 16.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 10.0, 'inlf': 1},
{'age': 35.0, 'educ': 11, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 16.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 13.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 17.0, 'inlf': 1},
{'age': 42.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 11, 'inlf': 1},
{'age': 59.0, 'educ': 16.0, 'inlf': 1},
{'age': 36.0, 'educ': 13.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 16.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 10.0, 'inlf': 1},
{'age': 51, 'educ': 14.0, 'inlf': 1},
{'age': 30.0, 'educ': 17.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 57.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 16.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 8.0, 'inlf': 1},
{'age': 52.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 34.0, 'educ': 17.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 40.0, 'educ': 14.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 8.0, 'inlf': 1},
{'age': 47.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 36.0, 'educ': 8.0, 'inlf': 1},
{'age': 46.0, 'educ': 17.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 9.0, 'inlf': 1},
{'age': 34.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 49.0, 'educ': 17.0, 'inlf': 1},
{'age': 32.0, 'educ': 15.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 6.0, 'inlf': 1},
{'age': 60.0, 'educ': 14.0, 'inlf': 1},
{'age': 50.0, 'educ': 12.0, 'inlf': 1},
{'age': 56.0, 'educ': 14.0, 'inlf': 1},
{'age': 51, 'educ': 9.0, 'inlf': 1},
{'age': 54.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 13.0, 'inlf': 1},
{'age': 46.0, 'educ': 9.0, 'inlf': 1},
{'age': 46.0, 'educ': 15.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 32.0, 'educ': 13.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 13.0, 'inlf': 0},
{'age': 37.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 16.0, 'inlf': 0},
{'age': 42.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 13.0, 'inlf': 0},
{'age': 46.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 17.0, 'inlf': 0},
{'age': 55.0, 'educ': 14.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 10, 'inlf': 0},
{'age': 49.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 17.0, 'inlf': 0},
{'age': 47.0, 'educ': 10.0, 'inlf': 0},
{'age': 54.0, 'educ': 13.0, 'inlf': 0},
{'age': 40, 'educ': 10, 'inlf': 0},
{'age': 43.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 35.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 54.0, 'educ': 16.0, 'inlf': 0},
{'age': 35.0, 'educ': 8.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 55.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 13.0, 'inlf': 0},
{'age': 45.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 14.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 50.0, 'educ': 12.0, 'inlf': 0},
{'age': 58.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 17.0, 'inlf': 0},
{'age': 40, 'educ': 14.0, 'inlf': 0},
{'age': 50, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 9.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 14.0, 'inlf': 0},
{'age': 40.0, 'educ': 16.0, 'inlf': 0}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8","#003366"]);
xScale.domain(d3.extent(data, function(d) { return d.educ; }));
colorScale.domain(d3.extent(data, function(d) {return d.age; }));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(1))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 75 : height + 100
}))
.force("collide", d3.forceCollide(4))
.stop();
for (var i = 0; i < 120; ++i) simulation.tick();
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
var circles = graphGroup.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(12))
.attr('y', height-10)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(11.7))
.attr('y', height+30)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");.cells path {
fill: none;
pointer-events: all;
}
.cells :hover circle {
fill: red;
}
text {
font-size: 17px;
font-family: TW Cen MT;
}
.axis path, .axis line {
fill: none;
stroke: none;
}<script src="https://d3js.org/d3.v5.min.js"></script>
现在,堆叠的形状看起来像是一种椭圆。我还没有找到改变默认形状的方法。
问题
如何修改圆形堆积的默认形状,使其类似于金字塔?它不一定非得是一个完美的金字塔,只是在底部追求更多,在顶部追求更少。我可以预见,对于d.educ=12的数据点,事情可能会特别棘手,因为它是高度饱和的。
发布于 2019-09-26 10:40:59
如果不一定要完美,可以通过对模拟施加硬约束并调整力来实现此效果。
硬约束
要形成硬约束(如具有天花板或地板),可以在每个记号后手动移动点。
例如,如果要为height - 15的顶点形成一个底板,并为height + 45的底点形成一个上限,则可以更改:
for (var i = 0; i < 120; ++i) simulation.tick();至:
for (var i = 0; i < 120; ++i) {
simulation.tick();
data.forEach(function (d) {
if (d.inlf) {
d.y = Math.min(d.y, height - 15);
} else {
d.y = Math.max(d.y, height + 45);
}
});
}强制设置
在此之后,您可能希望发挥forceX和forceY的优势。我还在forceCollide中添加了.iterations,以防您以后需要调整它。这些设置似乎对我来说没什么问题:
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(0.05))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 15 : height + 45
}).strength(0.1))
.force("collide", d3.forceCollide(4).iterations(1))
.stop();对于弱力或重叠圆,可能需要增加刻度迭代次数。在本例中,我将其增加到240。
for (var i = 0; i < 120; ++i) {
simulation.tick();
// ...至:
for (var i = 0; i < 240; ++i) {
simulation.tick();
// ...初始位置
正如评论中指出的,圆圈不是他们应该在的地方!这是由于较弱的力没有将它们从初始位置移动到足够远。为了解决这个问题,我们可以将初始位置设置在我们想要的位置附近。
data.forEach(function (d) {
d.x = xScale(d.educ);
d.y = d.inlf ? height - 15 : height + 45;
});结果
在向上移动这些点之后,总的来说,它看起来如下所示:
var margins = {top:20, bottom:300, left:30, right:100};
var height = 150;
var width = 900;
var totalWidth = width+margins.left+margins.right;
var totalHeight = height+margins.top+margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate("+margins.left+","+margins.top+")");
var xScale = d3.scaleLinear()
.range([0, width]);
var data = [{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 14.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 16.0, 'inlf': 1},
{'age': 54.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 10.0, 'inlf': 1},
{'age': 35.0, 'educ': 11, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 35.0, 'educ': 12.0, 'inlf': 1},
{'age': 42.0, 'educ': 16.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 13.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 43.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 17.0, 'inlf': 1},
{'age': 42.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 11, 'inlf': 1},
{'age': 59.0, 'educ': 16.0, 'inlf': 1},
{'age': 36.0, 'educ': 13.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 45.0, 'educ': 16.0, 'inlf': 1},
{'age': 42.0, 'educ': 11, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 10.0, 'inlf': 1},
{'age': 51, 'educ': 14.0, 'inlf': 1},
{'age': 30.0, 'educ': 17.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 57.0, 'educ': 12.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 16.0, 'inlf': 1},
{'age': 45.0, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 30.0, 'educ': 12.0, 'inlf': 1},
{'age': 46.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 37.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 8.0, 'inlf': 1},
{'age': 52.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 16.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 34.0, 'educ': 17.0, 'inlf': 1},
{'age': 55.0, 'educ': 14.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 1},
{'age': 40.0, 'educ': 14.0, 'inlf': 1},
{'age': 43.0, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 8.0, 'inlf': 1},
{'age': 47.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 36.0, 'educ': 8.0, 'inlf': 1},
{'age': 46.0, 'educ': 17.0, 'inlf': 1},
{'age': 34.0, 'educ': 12.0, 'inlf': 1},
{'age': 41, 'educ': 12.0, 'inlf': 1},
{'age': 51, 'educ': 12.0, 'inlf': 1},
{'age': 33.0, 'educ': 12.0, 'inlf': 1},
{'age': 52.0, 'educ': 12.0, 'inlf': 1},
{'age': 58.0, 'educ': 9.0, 'inlf': 1},
{'age': 34.0, 'educ': 10.0, 'inlf': 1},
{'age': 31, 'educ': 12.0, 'inlf': 1},
{'age': 48.0, 'educ': 12.0, 'inlf': 1},
{'age': 32.0, 'educ': 12.0, 'inlf': 1},
{'age': 49.0, 'educ': 17.0, 'inlf': 1},
{'age': 32.0, 'educ': 15.0, 'inlf': 1},
{'age': 58.0, 'educ': 12.0, 'inlf': 1},
{'age': 50.0, 'educ': 6.0, 'inlf': 1},
{'age': 60.0, 'educ': 14.0, 'inlf': 1},
{'age': 50.0, 'educ': 12.0, 'inlf': 1},
{'age': 56.0, 'educ': 14.0, 'inlf': 1},
{'age': 51, 'educ': 9.0, 'inlf': 1},
{'age': 54.0, 'educ': 17.0, 'inlf': 1},
{'age': 59.0, 'educ': 13.0, 'inlf': 1},
{'age': 46.0, 'educ': 9.0, 'inlf': 1},
{'age': 46.0, 'educ': 15.0, 'inlf': 1},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 32.0, 'educ': 13.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 13.0, 'inlf': 0},
{'age': 37.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 48.0, 'educ': 16.0, 'inlf': 0},
{'age': 42.0, 'educ': 12.0, 'inlf': 0},
{'age': 33.0, 'educ': 13.0, 'inlf': 0},
{'age': 46.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 44.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 17.0, 'inlf': 0},
{'age': 55.0, 'educ': 14.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 10, 'inlf': 0},
{'age': 49.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 17.0, 'inlf': 0},
{'age': 47.0, 'educ': 10.0, 'inlf': 0},
{'age': 54.0, 'educ': 13.0, 'inlf': 0},
{'age': 40, 'educ': 10, 'inlf': 0},
{'age': 43.0, 'educ': 12.0, 'inlf': 0},
{'age': 30, 'educ': 16.0, 'inlf': 0},
{'age': 47.0, 'educ': 17.0, 'inlf': 0},
{'age': 35.0, 'educ': 12.0, 'inlf': 0},
{'age': 45.0, 'educ': 16.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 54.0, 'educ': 16.0, 'inlf': 0},
{'age': 35.0, 'educ': 8.0, 'inlf': 0},
{'age': 30, 'educ': 12.0, 'inlf': 0},
{'age': 55.0, 'educ': 12.0, 'inlf': 0},
{'age': 34.0, 'educ': 12.0, 'inlf': 0},
{'age': 38.0, 'educ': 13.0, 'inlf': 0},
{'age': 45.0, 'educ': 10, 'inlf': 0},
{'age': 47.0, 'educ': 12.0, 'inlf': 0},
{'age': 39.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 14.0, 'inlf': 0},
{'age': 33.0, 'educ': 12.0, 'inlf': 0},
{'age': 50.0, 'educ': 12.0, 'inlf': 0},
{'age': 58.0, 'educ': 12.0, 'inlf': 0},
{'age': 49.0, 'educ': 17.0, 'inlf': 0},
{'age': 40, 'educ': 14.0, 'inlf': 0},
{'age': 50, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 9.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 46.0, 'educ': 12.0, 'inlf': 0},
{'age': 36.0, 'educ': 12.0, 'inlf': 0},
{'age': 53.0, 'educ': 14.0, 'inlf': 0},
{'age': 40.0, 'educ': 16.0, 'inlf': 0}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8","#003366"]);
xScale.domain(d3.extent(data, function(d) { return d.educ; }));
colorScale.domain(d3.extent(data, function(d) {return d.age; }));
data.forEach(function (d) {
d.x = xScale(d.educ);
d.y = d.inlf ? height - 15 : height + 45;
});
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) {
return xScale(d.educ);
}).strength(0.05))
.force("y", d3.forceY(function(d) {
return d.inlf ? height - 15 : height + 45
}).strength(0.1))
.force("collide", d3.forceCollide(4).iterations(1))
.stop();
for (var i = 0; i < 240; ++i) {
simulation.tick();
data.forEach(function (d) {
if (d.inlf) {
d.y = Math.min(d.y, height - 15);
} else {
d.y = Math.max(d.y, height + 45);
}
});
}
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
var circles = graphGroup.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height-5)
.attr('y2', height-5)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(12))
.attr('y', height-10)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#b8cce4")
.style('stroke-width',"5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height+35)
.attr('y2', height+35)
.style('stroke',"#4f81b9")
.style('stroke-width',"5px");
graphGroup.append('rect')
.attr('x',xScale(11.7))
.attr('y', height+30)
.attr('width', 5)
.attr('height',10)
.style('fill', "#f6d18b");.cells path {
fill: none;
pointer-events: all;
}
.cells :hover circle {
fill: red;
}
text {
font-size: 17px;
font-family: TW Cen MT;
}
.axis path, .axis line {
fill: none;
stroke: none;
}<script src="https://d3js.org/d3.v5.min.js"></script>
发布于 2019-09-29 03:52:53
来得有点晚,但如果你想要一个更坚固的堆叠金字塔,我会放弃力模拟。下面是一个使用自定义金字塔堆叠算法的自定义实现:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script>
var margins = {
top: 20,
bottom: 300,
left: 30,
right: 100
};
var height = 150;
var width = 900;
var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;
var svg = d3.select('body')
.append('svg')
.attr('width', totalWidth)
.attr('height', totalHeight);
var graphGroup = svg.append('g')
.attr('transform', "translate(" + margins.left + "," + margins.top + ")");
var xScale = d3.scaleLinear()
.range([0, width]);
var data = [{
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 14.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 37.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 11,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 11,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 12.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 59.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 17.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 11,
'inlf': 1
}, {
'age': 59.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 36.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 42.0,
'educ': 11,
'inlf': 1
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 51,
'educ': 14.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 57.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 31,
'educ': 16.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 16.0,
'inlf': 1
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 30.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 37.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 31,
'educ': 16.0,
'inlf': 1
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 40.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 41,
'educ': 12.0,
'inlf': 1
}, {
'age': 36.0,
'educ': 8.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 41,
'educ': 12.0,
'inlf': 1
}, {
'age': 51,
'educ': 12.0,
'inlf': 1
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 52.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 9.0,
'inlf': 1
}, {
'age': 34.0,
'educ': 10.0,
'inlf': 1
}, {
'age': 31,
'educ': 12.0,
'inlf': 1
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 49.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 32.0,
'educ': 15.0,
'inlf': 1
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 6.0,
'inlf': 1
}, {
'age': 60.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 50.0,
'educ': 12.0,
'inlf': 1
}, {
'age': 56.0,
'educ': 14.0,
'inlf': 1
}, {
'age': 51,
'educ': 9.0,
'inlf': 1
}, {
'age': 54.0,
'educ': 17.0,
'inlf': 1
}, {
'age': 59.0,
'educ': 13.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 9.0,
'inlf': 1
}, {
'age': 46.0,
'educ': 15.0,
'inlf': 1
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 44.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 48.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 32.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 34.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 37.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 48.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 42.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 10,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 44.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 17.0,
'inlf': 0
}, {
'age': 55.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 49.0,
'educ': 10,
'inlf': 0
}, {
'age': 49.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 38.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 10.0,
'inlf': 0
}, {
'age': 54.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 40,
'educ': 10,
'inlf': 0
}, {
'age': 43.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 30,
'educ': 16.0,
'inlf': 0
}, {
'age': 47.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 35.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 54.0,
'educ': 16.0,
'inlf': 0
}, {
'age': 35.0,
'educ': 8.0,
'inlf': 0
}, {
'age': 30,
'educ': 12.0,
'inlf': 0
}, {
'age': 55.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 34.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 38.0,
'educ': 13.0,
'inlf': 0
}, {
'age': 45.0,
'educ': 10,
'inlf': 0
}, {
'age': 47.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 39.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 33.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 50.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 58.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 49.0,
'educ': 17.0,
'inlf': 0
}, {
'age': 40,
'educ': 14.0,
'inlf': 0
}, {
'age': 50,
'educ': 12.0,
'inlf': 0
}, {
'age': 53.0,
'educ': 9.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 46.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 36.0,
'educ': 12.0,
'inlf': 0
}, {
'age': 53.0,
'educ': 14.0,
'inlf': 0
}, {
'age': 40.0,
'educ': 16.0,
'inlf': 0
}];
var colorScale = d3.scaleLinear()
.range(["#e7eef8", "#003366"]);
xScale.domain(d3.extent(data, function(d) {
return d.educ;
}));
colorScale.domain(d3.extent(data, function(d) {
return d.age;
}));
// bin the data based on top bottom and x-position
var bins = d3.nest()
.key(function(d) { return [(d.inlf ? 1 : 0), d.educ] })
.entries(data);
// calculate positions (build the pyramid)
var r = 3;
bins.forEach( (bin) => {
var ds = bin.values;
var br = 1, n = 1;
while (br < ds.length){
br += n; n += 1;
}
n -= 1
var c = 0; rn = n; g = 0;
var s = r * 2 + 3;
ds.forEach((d)=>{
d.y = (rn - n) * s;
d.x = (c * s);
d.g = g;
c += 1;
if (c == n){
c = 0;
n -= 1;
g += 1
}
});
});
// re-bin by age and row so that we can traslate the rows
var binsAndRow = d3.nest()
.key(function(d) { return [(d.inlf ? 1 : 0), d.educ]; })
.key(function(d) { return d.g; })
.entries(data);
// draw circles
var x = graphGroup.selectAll("g")
.data(binsAndRow)
.enter()
.append("g")
.attr("class", (d) => "bin " + "b" + d.key)
.selectAll("g")
.data( (d) => {
return d.values
})
.enter()
.append("g")
.attr("class", (d) => "row " + "r" + d.key)
.selectAll("circle")
.data( (d) => {
return d.values
})
.enter()
.append("circle")
.attr("r", 3)
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style('fill', function(d) {return colorScale(d.age)});
// position the pyramids
graphGroup
.selectAll(".bin")
.attr("transform", function(d){
var bb = this.getBBox(),
xPos = (xScale(d.key.split(",")[1]) - bb.width/2),
isFlip = d.key.startsWith("1"),
yPos = isFlip ? height - 20 : height + 50,
t = "translate(" + xPos + "," + yPos + ")";
if (isFlip) t+= "scale(1,-1)";
return t;
})
.selectAll(".row")
.attr("transform", function(d) {
var pbb = this.parentNode.getBBox();
var bb = this.getBBox();
return "translate(" + ((pbb.width - bb.width)/2) + "," + 0 + ")";
});
graphGroup.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(17))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#000");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(12))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#b8cce4")
.style('stroke-width', "5px");
graphGroup.append('line')
.attr('x1', xScale(12))
.attr('x2', xScale(14))
.attr('y1', height - 5)
.attr('y2', height - 5)
.style('stroke', "#4f81b9")
.style('stroke-width', "5px");
graphGroup.append('rect')
.attr('x', xScale(12))
.attr('y', height - 10)
.attr('width', 5)
.attr('height', 10)
.style('fill', "#f6d18b");
graphGroup.append('line')
.attr('x1', xScale(5))
.attr('x2', xScale(11))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#b8cce4")
.style('stroke-width', "5px");
graphGroup.append('line')
.attr('x1', xScale(11))
.attr('x2', xScale(12))
.attr('y1', height + 35)
.attr('y2', height + 35)
.style('stroke', "#4f81b9")
.style('stroke-width', "5px");
graphGroup.append('rect')
.attr('x', xScale(11.7))
.attr('y', height + 30)
.attr('width', 5)
.attr('height', 10)
.style('fill', "#f6d18b");
</script>
</body>
</html>
https://stackoverflow.com/questions/58009457
复制相似问题