Published
Edited
Feb 12, 2020
Insert cell
md`# Horizontal grouped bar chart`
Insert cell
chart = {
const svg = d3.select(DOM.svg(width, height))
const barGroups = svg.append('g')
.selectAll('g')
.data(data)
.join('g')
.attr('transform', d => `translate(0, ${ y0(d[groupKey]) })`)
let bars = barGroups.selectAll('rect')
.data(d => keys.map(key => ({ key, value: d[key] })))
.join('rect')
.attr('x', margin.left)
.attr('y', d => y1(d.key))
.attr('width', d => x(d.value))
.attr('height', d => y1.bandwidth())
.attr('fill', d => color(d.key))

svg.append('g')
.call(xAxis)
svg.append('g')
.call(yAxis)

return svg.node()
}
Insert cell
y0 = d3.scaleBand()
.domain(data.map(d => d[groupKey]))
.rangeRound([ margin.top, height - margin.bottom ])
.paddingInner(0.1)
Insert cell
y1 = d3.scaleBand()
.domain(keys)
.rangeRound([ 0, y0.bandwidth() ])
.padding(0.05)
Insert cell
x = d3.scaleLinear()
.domain([ 0, highestCount ]).nice()
.rangeRound([ margin.left, width - margin.right ])
Insert cell
xAxis = g => g
.attr('transform', `translate(0, ${ height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g => g.select('.domain').remove())
Insert cell
yAxis = g => g
.attr('transform', `translate(${ margin.left, 0})`)
.call(d3.axisLeft(y0).ticks(null, 's'))
.call(g => g.select('.domain').remove())
.call(g => g.select('.tick:last-of-type text').clone())
.attr('x', 0)
.attr('text-anchor', 'end')
.attr('font-weight', 'bold')
.attr('font-size', 10)
.selectAll('text')
.attr('y', 20)
.attr('x', margin.left - 10)
.attr('height', 10)
.attr('transform', 'rotate(315)')


Insert cell
color = d3.scaleOrdinal()
.range(["#98abc5", "#6b486b", "#a05d56", "#ff8c00"])
Insert cell
highestCount = d3.max(data.flatMap(d => [ d.primary, d.contrast, d.benchMark]))
Insert cell
groupKey = data.columns[0]
Insert cell
keys = data.columns.slice(1)
Insert cell
data = Object.assign(_data, { columns: [ 'question', 'primary', 'contrast', 'benchmark' ], y: 'Count'})

Insert cell
dataKey = (key) => _.toLower(key)
Insert cell
_data = [
{
question: 'question 1',
primary: 12,
contrast: 7,
benchmark: undefined,
},
{
question: 'question 2',
primary: 6,
contrast: 0,
benchmark: 4
},
{
question: 'question 3',
primary: undefined,
contrast: 2,
benchmark: undefined
}
]

Insert cell
height = data.length * 60 + _.uniq(data.map(r => r.region)).length * 25
// 20 / bar + 10 between groups
Insert cell
margin = ({ top: 30, right: 10, bottom: 20, left: 50 })
Insert cell
d3 = require('d3@5')
Insert cell
_ = require('lodash')
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