Skip to content

Instantly share code, notes, and snippets.

@karen-izuka
Last active February 18, 2020 18:43
Show Gist options
  • Save karen-izuka/3c526353dd781a695843c7264e775c64 to your computer and use it in GitHub Desktop.
Save karen-izuka/3c526353dd781a695843c7264e775c64 to your computer and use it in GitHub Desktop.
Stacked Nested Bar Chart
year quarter year_quarter a b
2017 Q1 2017_Q1 5 10
2017 Q2 2017_Q2 7 12
2017 Q3 2017_Q3 8 13
2017 Q4 2017_Q4 10 8
2018 Q1 2018_Q1 9 12
2018 Q2 2018_Q2 13 8
2018 Q3 2018_Q3 14 6
2018 Q4 2018_Q4 12 7
2019 Q1 2019_Q1 7 13
2019 Q2 2019_Q2 6 10
2019 Q3 2019_Q3 15 7
2019 Q4 2019_Q4 8 12
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello!</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Example Chart</h1>
<div id="chart"></div>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="index.js"></script>
</body>
</html>
//load data
const load = async () => {
const data = await d3.csv('data.csv', ({year, quarter, year_quarter, a, b}) => ({
year: year, quarter: quarter, year_quarter: year_quarter, a: +a, b: +b
}));
chart(data);
}
const chart = data => {
//prep data
const groups = d3.map(data, d => d.year_quarter).keys();
const subgroups = data.columns.slice(3);
const stack = d3.stack()
.keys(subgroups)
(data);
//setup svg
const margin = {top: 25, right: 25, bottom: 50, left: 25};
const width = 1250 - margin.left - margin.right;
const height = 450 - margin.top - margin.bottom;
const svg = d3.select('#chart')
.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})`);
const format = d3.format('$,d');
//setup scales
const x0 = d3.scaleBand()
.domain(data.map(d => d.year))
.range([0, width])
.padding(0.1);
const x1 = d3.scaleBand()
.domain(data.map(d => d.quarter))
.range([0, x0.bandwidth()])
.padding(0.2);
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.a + d.b)])
.range([height, 0]);
const z = d3.scaleOrdinal()
.domain(subgroups)
.range(['#BCCBDE', '#C2DDE6']);
//setup axes
const xAxis0 = g => g
.call(d3.axisBottom(x0).tickSize(0))
.call(g => g.select('.domain').remove());
const xAxis1 = g => g
.call(d3.axisBottom(x1))
.call(g => g.select('.domain').remove());
//draw
const g = svg.append('g')
.selectAll('g')
.data(stack)
.join('g')
.attr('class', d => `${d.key}`)
.attr('fill', d => z(d.key));
const bar = g.selectAll('rect')
.data(d => d)
.join('rect')
.attr('class', 'rect')
.attr('x', d => x0(d.data.year) + x1(d.data.quarter))
.attr('y', d => y(d[1]))
.attr('width', x1.bandwidth())
.attr('height', d => y(d[0]) - y(d[1]))
.attr('stroke', '#FFFFFF')
.attr('stroke-width', 3);
const text = g.selectAll('.label')
.data(d => d)
.join('text')
.text(d => `${format(d[1] - d[0])}`)
.attr('class', 'label')
.attr('x', d => x0(d.data.year) + x1(d.data.quarter) + x1.bandwidth()/2)
.attr('y', d => y(d[1]) + 20)
.attr('fill', 'black')
.attr('text-anchor', 'middle');
const total = svg.selectAll('.total')
.data(data)
.join('text')
.text(d => `${format(d.a + d.b)}`)
.attr('class', 'total')
.attr('x', d => x0(d.year) + x1(d.quarter) + x1.bandwidth()/2)
.attr('y', d => y(d.a + d.b) - 10)
.attr('text-anchor', 'middle');
//draw axes
const gx0 = svg.append('g')
.attr('class', 'gx0')
.attr('transform', `translate(0, ${height + 35})`)
.call(xAxis0);
const gx1 = svg.selectAll('.gx1')
.data(x0.domain())
.join('g')
.attr('class', 'gx1')
.attr('transform', d => `translate(${x0(d)}, ${height})`)
.call(xAxis1);
}
load();
h1 {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
text-rendering: optimizeLegibility;
}
svg {
display: block;
margin: auto;
}
.gx0 {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
shape-rendering: crispEdges;
text-rendering: optimizeLegibility;
}
.gx1 {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
shape-rendering: crispEdges;
text-rendering: optimizeLegibility;
}
.label {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
text-rendering: optimizeLegibility;
}
.total {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
text-rendering: optimizeLegibility;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment