Skip to content

Instantly share code, notes, and snippets.

@karen-izuka
Last active February 18, 2020 19:08
Show Gist options
  • Save karen-izuka/6c1a91be29850b275bb7e56b5feb6a24 to your computer and use it in GitHub Desktop.
Save karen-izuka/6c1a91be29850b275bb7e56b5feb6a24 to your computer and use it in GitHub Desktop.
Stacked to Multiple Bars
Year F M
1945 1247736 1345618
1950 1632719 1790549
1955 1891872 2059007
1960 1970544 2132359
1965 1704807 1860956
1970 1702018 1859245
1975 1399587 1562138
1980 1601771 1784390
1985 1672079 1847650
1990 1851571 2052946
1995 1730231 1903185
2000 1789157 1962969
2005 1822321 1995746
2010 1751853 1915942
2015 1758448 1909804
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Birth Rates</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>United States Birth Rates (1945 - 2015)</h1>
<h2>Select Radio Buttons to Stack ⇆ Unstack</h2>
<form>
<label><input type="radio" name="mode" value="multiples" checked> Multiples</label>
<label><input type="radio" name="mode" value="stacked"> Stacked</label>
</form>
<div id="chart"></div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="index.js"></script>
</body>
</html>
//static variables
const margin = {top: 25, right: 25, bottom: 25, left:25};
const width = 1500 - margin.left - margin.right;
const height = 600 - 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 x = d3.scaleBand().range([0, width]).padding(0.2);
const y0 = d3.scaleBand().range([height, 0]);
const y1 = d3.scaleLinear();
const xAxis = g => g
.call(d3.axisBottom(x).tickSizeOuter(0));
const format = d3.format(',d');
//load data
const load = async () => {
const data = await d3.csv('data.csv', ({Year, F, M}) => ({Year: Year, F: +F, M: +M}));
chart(data);
}
//render chart
const chart = data => {
const keys = data.columns.slice(1);
const stack = d3.stack()
.keys(keys)
(data);
x.domain(data.map(d => d.Year));
y0.domain(keys);
y1.domain([0, d3.max(stack, d => d3.max(d, d => d[1]-d[0]))]).range([y0.bandwidth(), 0]);
const level = svg.selectAll('.level')
.data(stack)
.enter()
.append('g')
.attr('class', 'level')
.attr('id', d => d.key)
.attr('transform', d => `translate(0, ${y0(d.key)})`);
const yLabel = level.append('text')
.text(d => d.key)
.attr('class', 'yLabel')
.attr('x', -5)
.attr('y', (d, i) => (y1(d[0][1] - d[0][0]) + y0.bandwidth())/2)
.attr('text-anchor', 'end');
const rect = level.selectAll('rect')
.data(d => d)
.join('rect')
.attr('class', 'rect')
.attr('x', d => x(d.data.Year))
.attr('y', d => y1((d[1]-d[0])))
.attr('width', x.bandwidth())
.attr('height', d => y0.bandwidth() - y1((d[1]-d[0])));
const barLabel = level.selectAll('.barLabel')
.data(d => d)
.join('text')
.text(d => `${format((d[1]-d[0])/1000)}K`)
.attr('class', 'barLabel')
.attr('x', d => x(d.data.Year) + x.bandwidth()/2)
.attr('y', d => y1(d[1]-d[0]) + 25)
.attr('text-anchor', 'middle');
const gx = level.filter((d, i) => !i)
.append('g')
.attr('class', 'axis')
.attr('transform', `translate(0, ${y0.bandwidth()})`)
.call(xAxis);
$('input').on('change', change);
}
function change() {
if (this.value === 'multiples') {
transitionMultiplies();
} else {
transitionStacked();
}
}
const transitionMultiplies = () => {
const t = svg.transition().duration(1000);
const g = t.selectAll('.level')
.attr('transform', d => `translate(0, ${y0(d.key)})`);
g.selectAll('rect')
.attr('y', d => y1((d[1]-d[0])));
g.selectAll('.barLabel')
.attr('y', d => y1(d[1]-d[0]) + 25);
g.selectAll('.yLabel')
.attr('y', (d, i) => (y1(d[0][1] - d[0][0]) + y0.bandwidth())/2);
}
const transitionStacked = () => {
const t = svg.transition().duration(1000);
const g = t.selectAll('.level')
.attr('transform', `translate(0, ${y0(y0.domain()[0])})`);
g.selectAll('rect')
.attr('y', d => y1(d[1]));
g.selectAll('.barLabel')
.attr('y', d => y1(d[1]) + 25);
g.selectAll('.yLabel')
.attr('y', (d, i) => y1(d[0][0]) - (y0.bandwidth() - y1((d[0][1]-d[0][0])))/2);
}
load();
body {
text-align: center;
}
form {
display: inline-block;
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
padding: 5px;
}
h1 {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h2 {
font-family: Arial, Helvetica, sans-serif;
font-size: 14px;
text-align: center;
}
svg {
display: block;
margin: auto;
}
.axis text {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
.barLabel {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
.yLabel {
font-family: Arial, Helvetica, sans-serif;
font-size: 12px;
}
#F .rect {
fill: #fde0dd;
}
#M .rect {
fill: #deebf7;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment