0%

Stacked Bar

不同发色、不同眼睛颜色学生数量。

设计思路

表格给出的是一个 4×4 的矩阵,可以以列为横轴、行为纵轴,画出堆叠柱形图(方案1)。

也可以反过来,以行为横轴、以列为纵轴,画出堆叠柱形图(方案2)。

方案

方案1

HTML

<html>
<head>
<title>chart 1</title>
</head>
<body>
<p>The eye-color statics of different-hair-colored students</p>
<meta charset="utf-8">
<style>
.bar { fill: steelblue; }
.axis path { display: none;}
</style>

<script src="https://d3js.org/d3.v4.js"></script>
<svg width="350" height="400"></svg>
<script>
var svg=d3.select("svg"),
margin={top:20, right:20, bottom: 40, left:40},
width=+svg.attr("width")-margin.left-margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x=d3.scaleBand()
.rangeRound([0,width])
.padding(0.1)
.align(0.1)

var y=d3.scaleLinear()
.rangeRound([height,0]);

var z=d3.scaleOrdinal()
.range(["#b75454","#0080ff","#a8b461","#4ca64c"])

var stack=d3.stack();

d3.csv("https://raw.githubusercontent.com/Co10/d3_files/master/CSV_files/00/eye_hair_color_data.csv",type,function(data){
x.domain(data.map(function(d){return d.Hair_color;}));
y.domain([0,d3.max(data,function(d){return d.total;})]).nice();
z.domain(data.columns.slice(1));

stack.keys(data.columns.slice(1));

g.selectAll(".serie")
.data(stack(data))
.enter().append("g")
.attr("class","serie")
.attr("fill",function(d){return z(d.key);})
.selectAll("rect")
.data(function(d){return d;})
.enter().append("rect")
.attr("x",function(d){return x(d.data.Hair_color);})
.attr("y",function(d){return y(d[1]);})
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());

g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));

g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "s"))
.append("text")
.attr("x", 1)
.attr("y", y(y.ticks(10).pop())-10)
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "#000")
.text("Number");
g.append("text")
.attr("x",240)
.attr("y",370)
.style("font-size","12px")
.text("Hair Color")
g.append("text")
.attr("x",237)
.attr("y",0)
.style("font-size","12px")
.text("Eye Color")

var legend = g.selectAll(".legend")
.data(data.columns.slice(1).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; })
.style("font", "10px sans-serif");

legend.append("rect")
.attr("x", width - 18)
.attr("width", 16)
.attr("height", 16)
.attr("fill", z);

legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return d; });

})

function type(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}

var p =d3.select("body").selectAll("p");
p.style("color","#1979a9").style("font-size","28px");

</script>
</body>
</html>

方案2

HTML

<html>
<head>
<title>chart 1</title>
</head>
<body>
<p>The hair-color statics of different-eye-colored students</p>
<meta charset="utf-8">
<style>
.bar { fill: steelblue; }
.axis path { display: none;}
</style>

<script src="https://d3js.org/d3.v4.js"></script>
<svg width="350" height="400"></svg>
<script>
var svg=d3.select("svg"),
margin={top:20, right:20, bottom: 40, left:40},
width=+svg.attr("width")-margin.left-margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var x=d3.scaleBand()
.rangeRound([0,width])
.padding(0.1)
.align(0.1)

var y=d3.scaleLinear()
.rangeRound([height,0]);

var z=d3.scaleOrdinal()
.range(["#141619","#A52A2A","#E50000","#FAF0BE"])

var stack=d3.stack();

d3.csv("https://raw.githubusercontent.com/Co10/d3_files/master/CSV_files/00/hair_eye_color_data.csv",type,function(data){
x.domain(data.map(function(d){return d.Eye_color;}));
y.domain([0,d3.max(data,function(d){return d.total;})]).nice();
z.domain(data.columns.slice(1));

stack.keys(data.columns.slice(1));

g.selectAll(".serie")
.data(stack(data))
.enter().append("g")
.attr("class","serie")
.attr("fill",function(d){return z(d.key);})
.selectAll("rect")
.data(function(d){return d;})
.enter().append("rect")
.attr("x",function(d){return x(d.data.Eye_color);})
.attr("y",function(d){return y(d[1]);})
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth());

g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));

g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10, "s"))
.append("text")
.attr("x", 1)
.attr("y", y(y.ticks(10).pop())-10)
.attr("dy", "0.35em")
.attr("text-anchor", "start")
.attr("fill", "#000")
.text("Number");
g.append("text")
.attr("x",250)
.attr("y",370)
.style("font-size","12px")
.text("Eye Color")
g.append("text")
.attr("x",235)
.attr("y",0)
.style("font-size","12px")
.text("Hair Color")

var legend = g.selectAll(".legend")
.data(data.columns.slice(1).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; })
.style("font", "10px sans-serif");

legend.append("rect")
.attr("x", width - 18)
.attr("width", 16)
.attr("height", 16)
.attr("fill", z);

legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.attr("text-anchor", "end")
.text(function(d) { return d; });

})

function type(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}

var p =d3.select("body").selectAll("p");
p.style("color","#1979a9").style("font-size","28px");

</script>
</body>
</html>

实现方法

通过 d3.stack 实现堆叠。

总结

两张图对比,不管是方案1还是方案2,横向对比都能看出不同眼色(发色)数量多少,纵向可明显看出发色(眼色)数量大小。

除此之外,方案1能看出棕色发色学生最多,棕眼、蓝眼相比绿、绿褐的柱形图明显较高,综合可看出棕眼、蓝眼学生数量较多,绿褐次之,绿最少。

同样,方案2能看出棕、蓝眼学生数量相当且较多,发色依然可以通过色块大小、数量看出棕发学生数量最多,红发学生数量最少。

不管是方案1还是方案2,每个单一矩形都代表着同一发色、同一眼色的学生数量,可以看出数量最多的三大块:棕发棕眼 > 金发蓝眼 > 棕发蓝眼。

参考模板

https://www.d3-graph-gallery.com/graph/barplot_stacked_basicWide.html