Public
Edited
Mar 4, 2024
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
spacedJitterChart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

addSharedThings(svg);

svg
.append("g")
.attr("fill", "#cb1a1a")
.attr("stroke", "#cb1a1a")
.selectAll("circle")
.data(dataWithSpacedJitter)
.join("circle")
.attr("cx", (d) => d.x)
.attr("cy", (d) => y(d.datum.region) + d.y + radius * 2)
.attr("r", radius)
.attr("fill-opacity", fillOpacity / 100)
.attr("stroke-opacity", strokeOpacity / 100)
.attr("stroke-width", circleStrokeWidth)
.append("title")
.text((d) => d.datum.msoanm + " " + d.datum.pct);

addRegionMeans(svg);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// A function to add things like the x axis to all charts
addSharedThings = function(svg) {
// grey rectangles
svg
.append("g")
.attr("fill", "#f5f5f5")
.selectAll("rect")
.data(y.domain())
.join("rect")
.attr("x", d => x(0))
.attr("y", d => y(d))
.attr("width", x(1) - x(0))
.attr("height", y.bandwidth);

// region labels
svg
.append("g")
.attr("fill", "#444")
.selectAll("text")
.data(y.domain())
.join("text")
.attr("x", d => x(0) + 5)
.attr("y", d => y(d) + 20)
.text(d => d);

// x axis
svg.append("g").call(xAxis);
}
Insert cell
addRegionMeans = function(svg) {
svg
.append("g")
.attr("stroke", "#222")
.selectAll("line")
.data(regionAverages)
.join("line")
.attr("x1", d => x(d.mean))
.attr("x2", d => x(d.mean))
.attr("y1", d => y(d.region))
.attr("y2", d => y(d.region) + y.bandwidth())
.attr("stroke-width", 2);
}
Insert cell
dataWithSpacedJitter = {
const dataByRegion = {};
const random = sfc32(1, 2, 3, 4); // use a deterministic random number generator for reproducibility
for (const d of data) {
if (!(d.region in dataByRegion)) dataByRegion[d.region] = [];
dataByRegion[d.region].push(d);
}
const result = [];
for (const region in dataByRegion) {
result.push(
...spacedJitter(
dataByRegion[region],
(d) => x(d.pct),
[0, y.bandwidth() - radius * 4],
{ random }
)
);
}
return result;
}
Insert cell
data = {
var data = rawData.map(d => ({ ...d }));
var bins = {};
var random = sfc32(1, 2, 3, 4); // use a deterministic random number generator for reproducibility
data.forEach(d => {
d.random = random();
d.pctRound = (Math.round(d.pct * numBands) / numBands) * 100;
var binName = d.region + d.pctRound;
if (binName in bins) {
d.y = bins[binName]++;
} else {
d.y = 0;
bins[binName] = 1;
}
});
return data;
}
Insert cell
regionAverages = {
var regionSums = {};
var regionPops = {};
data.forEach(d => {
if (!(d.region in regionSums)) {
regionSums[d.region] = 0;
regionPops[d.region] = 0;
}
regionSums[d.region] += d.pct * d.pop;
regionPops[d.region] += +d.pop;
});
return Object.keys(regionSums).map(d => ({
region: d,
mean: regionSums[d] / regionPops[d]
}));
}
Insert cell
rawData = d3.csvParse(await FileAttachment("vaccines.csv").text());
Insert cell
format = x.tickFormat(20, data.format)
Insert cell
x = d3
.scaleLinear()
.domain([0, 1])
.range([margin.left, width - margin.right])
Insert cell
y = d3
.scaleBand()
.domain(
[...regionAverages].sort((a, b) => a.mean - b.mean).map(d => d.region)
)
// .domain(data.map(d => d.region))
.rangeRound([margin.top, height - margin.bottom])
.padding(0.07)
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(width / 80, data.format))
.call(g => g.select(".domain").remove())
Insert cell
margin = ({ top: 30, right: 10, bottom: 10, left: 10 })
Insert cell
d3 = require("d3@6")
Insert cell
// Random number generator
// https://stackoverflow.com/a/47593316
function sfc32(a, b, c, d) {
let rng = function() {
a >>>= 0;
b >>>= 0;
c >>>= 0;
d >>>= 0;
var t = (a + b) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
d = (d + 1) | 0;
t = (t + d) | 0;
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
for (let i = 0; i < 10; i++) {
rng();
}
return rng;
}
Insert cell
import { spacedJitter } from "@jtrim-ons/avoiding-overlaps-in-jitter-plots"
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