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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more