Skip to content

Instantly share code, notes, and snippets.

@tlfrd
Last active January 1, 2020 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tlfrd/13e827fe101cfe14e83b4c3c44571f8f to your computer and use it in GitHub Desktop.
Save tlfrd/13e827fe101cfe14e83b4c3c44571f8f to your computer and use it in GitHub Desktop.
Column Chart
license: mit

An example of a column chart visualising NSS scores per question at Imperial.

This is part of a series of visualisations called My Visual Vocabulary which aims to recreate every visualisation in the FT's Visual Vocabulary from scratch using D3.

<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
text {
font-family: monospace;
}
.outline {
fill: none;
}
.below-bar {
fill: #d7191c;
}
.eq-bar {
fill: #c9c9c9;
}
.above-bar {
fill: #2c7bb6;
}
.legend-label {
font-size: 11px;
}
</style>
</head>
<body>
<script>
var margin = {top: 100, right: 200, bottom: 100, left: 100};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
function getValues(d) {
for (var i = 1; i <= 27; i++) {
d[i] = +d[i];
}
return d;
}
var englandMode = true;
var x = d3.scaleBand()
.range([0, width])
.padding(0.2);
var y = d3.scaleLinear()
.range([height, 0]);
d3.csv("nss-results.csv", getValues, function(error, results) {
if (error) throw error;
var englandAverage = results[results.length - 2],
imperialAverage = results[results.length - 1];
results = results.slice(0, -2);
results.forEach(d => {
var eng = [],
imp = [];
for (var i = 1; i <= 27; i++) {
eng[i - 1] = (d[i] - englandAverage[i]).toPrecision(2);
imp[i - 1] = (d[i] - imperialAverage[i]).toPrecision(2);
}
d.englandAvgDiff = eng;
d.imperialAvgDiff = imp;
});
var resultsByQuestion = d3.range(27);
resultsByQuestion = resultsByQuestion.map(d => c = {
belowEng: 0,
equalEng: 0,
aboveEng: 0,
belowImp: 0,
equalImp: 0,
aboveImp: 0
});
results.forEach(d => {
d.englandAvgDiff.forEach((x, i) => {
if (x > 0) resultsByQuestion[i].aboveEng++;
if (x < 0) resultsByQuestion[i].belowEng++;
if (x == 0) resultsByQuestion[i].equalEng++;
})
d.imperialAvgDiff.forEach((x, i) => {
if (x > 0) resultsByQuestion[i].aboveImp++;
if (x < 0) resultsByQuestion[i].belowImp++;
if (x == 0) resultsByQuestion[i].equalImp++;
})
});
x.domain(d3.range(27));
y.domain([0, results.length]);
var title = svg.append("g")
.attr("transform", "translate(" + [width / 2, -30] + ")");
title.append("text")
.attr("text-anchor", "middle")
.text("No. of Departments w/ Scores Above or Below England Avg Per Question");
var legend = svg.append("g")
.attr("transform", "translate(" + [width + margin.right / 5, 0] + ")");
legend.selectAll("rect")
.data(["above-bar", "eq-bar", "below-bar"])
.enter().append("rect")
.attr("class", d => d)
.attr("y", (d, i) => (height / 3) * i)
.attr("height", d => height / 3)
.attr("width", x.bandwidth());
legend.append("rect")
.attr("class", "outline")
.attr("height", height)
.attr("width", x.bandwidth());
legend.selectAll("text")
.data(["Above", "Equal", "Below"])
.enter().append("text")
.attr("class", "legend-label")
.attr("y", (d, i) => (height / 3) * i)
.attr("x", x.bandwidth())
.attr("dx", 10)
.attr("dy", height / 6)
.text(d => d)
var yAxisGroup = svg.append("g")
.attr("class", "y-axis-group");
var yAxis = yAxisGroup.append("g")
.attr("class", "y-axis")
.call(d3.axisLeft(y).tickSize(0));
yAxis.attr("transform", "translate(" + [-5, 0] + ")");
yAxis.select(".domain").style("opacity", 0);
var xAxisGroup = svg.append("g")
.attr("class", "x-axis-group")
.attr("transform", "translate(" + [0, height] + ")")
var xAxis = xAxisGroup.append("g")
.attr("class", "x-axis")
.call(d3.axisBottom(x).tickSize(0).tickFormat(d => d + 1));
xAxis.attr("transform", "translate(" + [0, 5] + ")")
xAxis.select(".domain").style("opacity", 0);
var xLabel = xAxisGroup.append("text")
.attr("transform", "translate(" + [width / 2, margin.bottom / 2] + ")")
.attr("text-anchor", "middle")
.text("Question Number")
var barGroups = svg.append("g")
.attr("class", "bar-groups")
.selectAll("g")
.data(resultsByQuestion)
.enter().append("g")
.attr("class", "bar-group")
.attr("transform", (d, i) => "translate(" + [x(i), 0] + ")")
var aboveBars = barGroups.append("rect")
.attr("class", "above-bar")
.attr("y", d => y(d.belowEng + d.equalEng + d.aboveEng))
.attr("height", d => height - y(d.belowEng + d.equalEng + d.aboveEng))
.attr("width", x.bandwidth());
var eqBars = barGroups.append("rect")
.attr("class", "eq-bar")
.attr("y", d => y(d.equalEng + d.belowEng))
.attr("height", d => height - y(d.equalEng + d.belowEng))
.attr("width", x.bandwidth());
var belowBars = barGroups.append("rect")
.attr("class", "below-bar")
.attr("y", d => y(d.belowEng))
.attr("height", d => height - y(d.belowEng))
.attr("width", x.bandwidth());
var outlines = barGroups.append("rect")
.attr("class", "outline")
.attr("height", d => y(0))
.attr("width", x.bandwidth());
d3.select("svg").on("click", switchBetween);
function switchBetween() {
function newY(d) {
var aY, eY, bY;
if (englandMode) {
aY = y(d.belowImp + d.equalImp + d.aboveImp),
eY = y(d.equalImp + d.belowImp),
bY = y(d.belowImp);
} else {
aY = y(d.belowEng + d.equalEng + d.aboveEng),
eY = y(d.equalEng + d.belowEng),
bY = y(d.belowEng);
}
return [aY, eY, bY];
}
aboveBars
.transition()
.attr("y", d => newY(d)[0])
.attr("height", d => height - newY(d)[0]);
eqBars
.transition()
.attr("y", d => newY(d)[1])
.attr("height", d => height - newY(d)[1]);
belowBars
.transition()
.attr("y", d => newY(d)[2])
.attr("height", d => height - newY(d)[2]);
if (englandMode) {
title.select("text")
.text("No. of Departments w/ Scores Above or Below Imperial Avg Per Question");
} else {
title.select("text")
.text("No. of Departments w/ Scores Above or Below England Avg Per Question");
}
englandMode = !englandMode;
}
});
</script>
</body>
department 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
Medicine 0.88 0.82 0.91 0.80 0.75 0.82 0.93 0.73 0.76 0.56 0.55 0.85 0.74 0.72 0.76 0.77 0.75 0.88 0.93 0.91 0.78 0.91 0.93 0.80 0.72 0.75 0.90
Other Med 0.88 0.74 0.89 0.86 0.80 0.77 0.70 0.50 0.68 0.35 0.59 0.73 0.68 0.59 0.35 0.53 0.59 0.71 0.84 0.85 0.64 0.88 0.92 0.83 0.64 0.56 0.70
Biology 0.87 0.84 0.89 0.82 0.83 0.81 0.71 0.75 0.62 0.71 0.48 0.85 0.66 0.67 0.72 0.86 0.75 0.88 0.82 0.86 0.52 0.89 0.87 0.69 0.62 0.47 0.81
Other Bio 0.90 0.78 0.83 0.82 0.83 0.86 0.80 0.66 0.64 0.70 0.52 0.82 0.58 0.62 0.67 0.86 0.86 0.78 0.85 0.86 0.51 0.89 0.88 0.74 0.74 0.51 0.83
Chemistry 0.84 0.75 0.89 0.75 0.80 0.74 0.82 0.51 0.56 0.59 0.53 0.75 0.60 0.52 0.52 0.74 0.67 0.85 0.84 0.89 0.57 0.78 0.79 0.63 0.48 0.53 0.73
Phys & Astro 0.82 0.73 0.86 0.71 0.71 0.68 0.60 0.47 0.43 0.46 0.56 0.82 0.56 0.62 0.57 0.70 0.70 0.78 0.81 0.87 0.45 0.76 0.82 0.54 0.50 0.40 0.67
Geology 0.99 0.97 0.96 0.93 0.93 0.94 0.97 0.93 0.87 0.97 0.94 0.97 0.97 0.99 0.99 0.91 0.99 1.00 0.96 1.00 0.89 0.94 0.99 0.99 0.97 0.86 0.97
Maths & Stats 0.87 0.76 0.83 0.79 0.85 0.70 0.62 0.57 0.69 0.53 0.55 0.87 0.68 0.68 0.76 0.73 0.81 0.91 0.82 0.91 0.59 0.70 0.88 0.78 0.67 0.52 0.78
Computing 0.82 0.79 0.94 0.82 0.85 0.87 0.91 0.54 0.79 0.54 0.63 0.90 0.78 0.64 0.74 0.74 0.87 0.92 0.89 0.93 0.71 0.98 0.95 0.85 0.80 0.66 0.86
Gen Eng 0.92 0.91 0.97 0.86 0.91 0.97 0.85 0.57 0.74 0.77 0.66 0.91 0.80 0.69 0.86 0.82 0.85 0.80 0.81 0.91 0.85 0.94 0.92 0.94 0.83 0.57 0.97
Mech Eng 0.91 0.83 0.93 0.87 0.91 0.90 0.88 0.77 0.81 0.78 0.83 0.90 0.87 0.88 0.85 0.83 0.92 0.85 0.88 0.89 0.74 0.99 0.99 0.84 0.89 0.57 0.86
Aero Eng 0.71 0.67 0.81 0.75 0.72 0.77 0.74 0.65 0.67 0.43 0.57 0.86 0.73 0.59 0.70 0.70 0.75 0.90 0.87 0.87 0.57 0.96 0.86 0.61 0.57 0.53 0.78
E&E Eng 0.88 0.81 0.92 0.85 0.88 0.89 0.86 0.75 0.71 0.60 0.65 0.86 0.69 0.68 0.80 0.79 0.88 0.89 0.88 0.97 0.72 0.96 0.92 0.80 0.85 0.66 0.86
Civ Eng 0.93 0.81 0.94 0.88 0.86 0.89 0.93 0.69 0.75 0.87 0.69 0.96 0.90 0.82 0.90 0.90 0.90 0.96 0.96 0.94 0.85 0.97 0.94 0.92 0.79 0.77 0.94
Chem Eng 0.86 0.79 0.88 0.94 0.88 0.87 0.86 0.75 0.70 0.61 0.68 0.95 0.74 0.74 0.84 0.82 0.95 0.80 0.84 0.86 0.70 0.96 0.94 0.85 0.92 0.58 0.84
Materials 0.85 0.78 0.85 0.73 0.91 0.90 0.73 0.54 0.69 0.65 0.69 0.79 0.71 0.73 0.66 0.82 0.77 0.94 0.89 0.98 0.72 0.91 0.85 0.80 0.65 0.71 0.87
Business 0.79 0.64 0.71 0.67 0.69 0.76 0.76 0.57 0.67 0.50 0.55 0.79 0.57 0.59 0.62 0.69 0.79 0.81 0.83 0.88 0.56 0.88 0.80 0.69 0.66 0.59 0.66
England Average 0.89 0.83 0.85 0.82 0.84 0.85 0.82 0.72 0.73 0.73 0.74 0.86 0.79 0.75 0.71 0.79 0.77 0.83 0.87 0.86 0.72 0.86 0.84 0.76 0.62 0.58 0.84
Imperial Average 0.87 0.80 0.89 0.82 0.83 0.83 0.82 0.66 0.69 0.62 0.62 0.87 0.73 0.70 0.74 0.78 0.81 0.87 0.87 0.91 0.68 0.90 0.90 0.78 0.73 0.61 0.84
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment