Public
Edited
Jan 27, 2023
Fork of Baby Names
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart(await query(
WITH male AS (
SELECT name, SUM(number) AS number
FROM \`bigquery-public-data.usa_names.usa_1910_current\`
WHERE gender = 'M'
GROUP BY 1
),
female AS (
SELECT name, SUM(number) AS number
FROM \`bigquery-public-data.usa_names.usa_1910_current\`
WHERE gender = 'F'
GROUP BY 1
),
unisex AS (
SELECT m.name, ABS(m.number - f.number), m.number + f.number AS t
FROM male m
JOIN female f on f.name = m.name
WHERE m.number + f.number > 10000
ORDER BY 2 ASC, 3 DESC
)
SELECT name, gender, CAST(year AS STRING) as year, SUM(number) AS number
FROM \`bigquery-public-data.usa_names.usa_1910_current\`
WHERE name IN (
SELECT name FROM unisex LIMIT 8
)
GROUP BY 1, 2, 3
ORDER BY name, gender, year DESC
))
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const rowHeight = 45
const bands = 4
const padding = { top: 20, right: 10, bottom: 20, left: 10 }
const grouped = d3.group(data, d => d.name, d => d.gender)
const height = grouped.size * rowHeight
const x = d3.scaleTime()
.domain(yearExtent.map(y => new Date(y, 0, 1)))
.range([0, width - padding.left - padding.right])
const y = d3.scaleSqrt()
.domain([0, d3.max(data, d => d.number) / bands])
.range([rowHeight, 0])
const color = d3.scaleOrdinal(d3.schemeAccent)
const area = d3.area()
.x(d => x(new Date(d.year, 0, 1)))
.y0(y(0))
.y1(d => y(d.number))

const svg = DOM.svg(width + padding.left + padding.right,
height + padding.top + padding.bottom)
// clip path
const clip = DOM.uid('clip')
d3.select(svg).append('clipPath')
.attr('id', clip.id)
.append('rect')
.attr('width', width)
.attr('height', rowHeight)

const chart = d3.select(svg).append('g')
.attr('transform', `translate(${padding.left}, ${padding.top})`)
.style('user-select', 'none')
.style('-moz-user-select', 'none')
.style('-webkit-user-select', 'none')
// axes
const axes = chart.selectAll('g.axis').data([0, height])
.enter().append('g')
.attr('transform', d => `translate(0, ${d})`)
.each(function (d) {
d3.select(this).call(d === 0 ? d3.axisTop(x) : d3.axisBottom(x))
})
// names
const names = chart.selectAll('g.name').data(Array.from(grouped))
.enter().append('g')
.attr('transform', (d, i) => `translate(0, ${i * rowHeight})`)
.attr('clip-path', clip)

// genders
const genders = names.selectAll('g.gender').data(d => Array.from(d[1]))
.enter().append('g')
for (let i = 0; i < bands; i++)
genders.append('path')
.attr('fill', d => color(d[0]))
.attr('opacity', 1/bands)
.attr('transform', `translate(0, ${i * rowHeight})`)
.attr('d', d => area(d[1]))
// name
names.append('text')
.attr('y', rowHeight - 5)
.text(d => d[0])
// values, brush
const rule = chart.append('g')
rule.append('rect')
.attr('width', 1)
.attr('height', height)
const brushTicks = rule.selectAll('text').data([-8.5, height + 17])
.enter().append('text')
.attr('y', d => d)
.attr('text-anchor', 'middle')
.attr('font-size', 12)
const opacity = d3.scalePow([0, 10], [0, 1]).exponent(2).clamp(true)
const values = genders.append('text')
.attr('text-anchor', 'end')
.attr('fill', d => color(d[0]))
.attr('y', d => d[0] === 'F' ? rowHeight - 5 : rowHeight - 25)
brush(x(x.domain()[1]))
d3.select(svg)
.on('mousemove', () => brush(d3.mouse(chart.node())[0]))
.on('touchmove', () => brush(d3.touches(chart.node())[0][0]))
function brush(px) {
const year = x.invert(px).getFullYear()
rule
.attr('transform', `translate(${px}, 0)`)
brushTicks
.text(year)
axes.selectAll('text')
.attr('opacity', d => opacity(Math.abs(d.getFullYear() - year)))
values
.attr('x', px - 5)
.text(d => {
const found = d[1].find(v => v.year == year) // TODO: bisect
return found ? format(found.number) : ''
})
}

return svg
}
Insert cell
format = d3.format(',')
Insert cell
yearExtent = {
const result = await query('SELECT MIN(year) AS min, MAX(year) AS max FROM \`bigquery-public-data.usa_names.usa_1910_current\`')
return [result[0].min, result[0].max]
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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