Published
Edited
Mar 9, 2018
1 fork
1 star
Insert cell
Insert cell
svg = html`<svg width=${config.width} height=${config.height}></svg>`
Insert cell
Insert cell
data = d3.csv("https://gist.githubusercontent.com/clhenrick/60b8de7fe07c0c6122cff4979feef1fe/raw/5f30c13b504b443bd4c027add21e79fbf1d8fa8d/pivot.csv",
(d) => ({
year: +d.year,
'african american': +d['African American'],
asian: +d.Asian,
latino: +d['Latino'],
white: +d['White']
}))
Insert cell
Insert cell
d3 = require("d3@next")
Insert cell
Insert cell
config = {
const height = 600;
const margin = {
top: 10,
bottom: 60,
left: 65,
right: 10
};
const innerHeight = height - margin.top - margin.bottom;
const innerWidth = width - margin.left - margin.right;
return {
width, // note: `width` is a global constant that references the width of the notebook
height,
innerWidth,
innerHeight,
margin
};
}
Insert cell
Insert cell
scales = {
// reference some values from our config and format blocks
const {innerWidth, innerHeight} = config;
const {series} = format;
// set up the x scale
const xScale = d3.scaleLinear()
.domain(d3.extent(data, d => d.year))
.range([0, innerWidth]);
// set up the y scale
// note: referencing the series property returned by format() even though it's defined below!
const yScale = d3.scaleLinear()
.domain([
d3.min(series, series => d3.min(series, d => d[0])),
d3.max(series, series => d3.max(series, d => d[1]))
])
.range([innerHeight, 0]);
// color scale
const cScale = d3.scaleOrdinal(d3.schemePastel2);

return {
xScale,
yScale,
cScale
};
}
Insert cell
Insert cell
format = {
// create an array of strings for our data's ethnicity categories
// we only want the column names for the ethnicity categories, not the "year"
const keys = Object.keys(data[0]).slice(1);
// use d3's stack layout to transform our data so that it plays nicely with our area path generator later
const stack = d3.stack()
.keys(keys)
.order(d3.stackOrderDescending); // so that the largest grouping is stacked below the others
const series = stack(data);
return {
series
};
}
Insert cell
Insert cell
axises = {
// reference our x and y scales from earlier
const {xScale, yScale} = scales
// bottom axis generator
const xAxis = d3.axisBottom()
.scale(xScale)
.ticks(10)
.tickFormat(d3.format(''));

// left axis generator
const yAxis = d3.axisLeft()
.scale(yScale)
.tickValues([0, 0.25, 0.5, 0.75, 1])
.tickFormat(d3.format(',.0%'));
return {
xAxis,
yAxis
};
}
Insert cell
Insert cell
main = {
const {margin, innerHeight, innerWidth} = config;
const {xScale, yScale, cScale} = scales;
const {xAxis, yAxis} = axises;
const {series} = format;
// main svg group
const main = d3.select(svg).append('g')
.attr('class', 'main')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// x axis
main.append('g')
.attr('class', 'axis x')
.attr('transform', `translate(0, ${innerHeight})`)
.call(xAxis);
// x axis label
d3.select('g.axis.x')
.append('text')
.attr('class', 'label')
.attr('x', innerWidth / 2)
.attr('y', 35)
.text('Year')
.style('text-anchor', 'middle');
// y axis
main.append('g')
.attr('class', 'axis y')
.call(yAxis);

// y axis label
d3.select('.y.axis').append('text')
.attr('class', 'label')
.attr('x', -innerHeight / 2 + 25)
.attr('y', -50)
.attr('transform', `rotate(-90 0 0)`)
.text('Ethnicity')
.style('text-anchor', 'middle');
return main;
}
Insert cell
Insert cell
Insert cell
{
const {margin} = config;
const {xScale, yScale, cScale} = scales;
const {series} = format;
// main svg group
const main = d3.select(svg).append('g')
.attr('class', 'main')
.attr('transform', `translate(${margin.left}, ${margin.top})`);

// define an area path generator
const area = d3.area()
.x(d => xScale(d.data.year))
.y0(d => yScale(d[0]))
.y1(d => yScale(d[1]))

// create the stacked area paths
main.selectAll('.area')
.data(series)
.enter().append('path')
.attr('class', 'area')
.attr('fill', d => cScale(d.key))
.attr('d', area);
}
Insert cell
Insert cell
html`<style>
text.label {
fill: #333;
font-size: 1.5em;
}
</style>`
Insert cell
Insert cell
final = {
const {margin, innerHeight, innerWidth} = config;
const {xScale, yScale, cScale} = scales;
const {xAxis, yAxis} = axises;
const {series} = format;
// main svg group
const main = d3.select(svg).append('g')
.attr('class', 'main')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
// x axis
main.append('g')
.attr('class', 'axis x')
.attr('transform', `translate(0, ${innerHeight})`)
.call(xAxis);
// x axis label
d3.select('g.axis.x')
.append('text')
.attr('class', 'label')
.attr('x', innerWidth / 2)
.attr('y', 35)
.text('Year')
.style('text-anchor', 'middle');
// generate y axis
main.append('g')
.attr('class', 'axis y')
.call(yAxis);

// y axis label
d3.select('.y.axis').append('text')
.attr('class', 'label')
.attr('x', -innerHeight / 2 + 25)
.attr('y', -50)
.attr('transform', `rotate(-90 0 0)`)
.text('Ethnicity')
.style('text-anchor', 'middle');
// define an area path generator
const area = d3.area()
.x(d => xScale(d.data.year))
.y0(d => yScale(d[0]))
.y1(d => yScale(d[1]))

// create the stacked area paths
main.selectAll('.area')
.data(series)
.enter().append('path')
.attr('class', 'area')
.attr('fill', d => cScale(d.key))
.attr('d', area);

return main
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more