Published
Edited
Oct 14, 2019
2 stars
Insert cell
Insert cell
chart = {
const svg = d3.create('svg')
.attr('viewBox', [0, 0, width, height])

svg.append('g')
.call(xAxis)
svg.append('g')
.call(yAxis)
const seriesG = svg.append('g')
.selectAll('g')
.data(series)
.join('g')
seriesG.append('path')
.attr('fill', 'none')
.attr('stroke', d => z(d[0].key))
.attr('stroke-width', 1.5)
.attr('d', d3.line()
.x(d => x(d.date))
.y(d => y(d.value)))
seriesG.append('g')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('stroke-linecap', 'round')
.attr('stroke-linejoin', 'round')
.attr('text-anchor', 'middle')
.selectAll('text')
.data(d => d)
.join('text')
.text(d => d.value)
.attr('dy', '0.35em')
.attr('x', d => x(d.date))
.attr('y', d => y(d.value))
.call(text => text.filter((d, i, data) => i === data.length - 1)
.append('tspan')
.attr('font-weight', 'bold')
.text(d => ` ${d.key}`))
.clone(true).lower()
.attr('fill', 'none')
.attr('stroke', 'white')
.attr('stroke-width', 6)
return svg.node()
}
Insert cell
Insert cell
Insert cell
margin = ({
top: 30,
right: 50,
bottom: 30,
left: 30
})
Insert cell
height = 500 / 2
Insert cell
labelPadding = 3
Insert cell
x = d3.scaleUtc()
.domain([data[0].date, data[data.length - 1].date])
.range([margin.left, width - margin.right])
Insert cell
xAxis = g => g
.attr('transform', `translate(0, ${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0))
Insert cell
y = d3.scaleLinear()
.domain([0, d3.max(series, s => d3.max(s, d => d.value))])
.range([height - margin.bottom, margin.top])
Insert cell
yAxis = g => g
.attr('transform', `translate(${margin.left}, 0)`)
.call(d3.axisLeft(y))
.call(g => g.select('.domain').remove())
.call(g => g.select('.tick:last-of-type text').clone()
.attr('x', 3)
.attr('text-anchor', 'start')
.attr('class', 'title')
.attr('font-weight', 'bold')
//.text('Q2 Usual Weekly Earnings Data: bls.gov 2019 population survey')
)
.call(g => g.select('.tick:last-of-type').attr('class', 'tick-bold'))
.call(g => g.selectAll('.tick-bold *:not(.title)').remove())
.call(g => g.selectAll('.tick:not(.tick-bold)').remove())

Insert cell
z = d3.scaleOrdinal(data.columns.slice(1), d3.schemeCategory10)
Insert cell
Insert cell
maleEarnings = (await d3.csv('https://raw.githubusercontent.com/adg29/datasets/master/bls.gov/weekly-earnings/2019-median-male-population.csv', d3.autoType))
Insert cell
femaleEarnings = (await d3.csv('https://raw.githubusercontent.com/adg29/datasets/master/bls.gov/weekly-earnings/2019-median-female-population.csv', d3.autoType))
Insert cell
maleDataTransformed = maleEarnings.reduce(transformCSVData, [])
Insert cell
femaleDataTransformed = femaleEarnings.reduce(transformCSVData, [])
Insert cell
transformCSVData = (quarterly, d) => {
let yearlyQuarters = [quarterToRender].map(quarter => {
let date = getQuarterRange(d.Year, quarter)
return {
date: new Date(date.end.format('Y-MM-DD')),
value: d[`Qtr${quarter}`],
quarter
}
}).filter(d => d.value)
return quarterly.concat(yearlyQuarters)
}
Insert cell
aggregate = femaleDataTransformed.map( (d, i) => {
return {
date: d.date,
female: d.value,
male: maleDataTransformed[i].value,
quarter: d.quarter
}
})
Insert cell
data = Object.assign( aggregate , {columns: ['date', 'male', 'female']})
Insert cell
series = data.columns.slice(1).map(key => data.map(({[key]: value, date, quarter}) => ({key, date, value, quarter})))
Insert cell
md`# Dependencies`
Insert cell
d3 = require('https://d3js.org/d3.v5.js')
Insert cell
moment = require('moment')
Insert cell
import {getQuarterRange} from "@adg29/line-chart-with-tooltip-median-weekly-earnings"
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