Public
Edited
Jul 13, 2022
1 fork
16 stars
Insert cell
Insert cell
chart = Plot.plot({
marks: [
Plot.rect(
data,
Plot.stackX({
x: "count",
y1: 0,
y2: 1,
fill: (_, i) => i,
stroke: "white",
strokeWidth: 0.5
})
),
Plot.dot(
data,
stackDisperseX({
x: "count",
y: d3.randomUniform.source(d3.randomLcg(42))(0.02, 1 - 0.02),
fill: "white",
r: 1.5,
repeat: "positive"
})
),
Plot.text(
data,
Plot.stackX1({
x: "count",
y: 0,
rotate: -90,
textAnchor: "end",
dy: 8,
dx: 5,
text: "name",
fontWeight: "bold"
})
),
Plot.text(
data,
Plot.stackX1({
x: "count",
y: 0,
rotate: -90,
textAnchor: "end",
dy: 8,
dx: 18,
text: (d) => `${d.positive} of ${d.count}`,
fill: "#888"
})
)
],
x: { axis: null },
y: { axis: null },
marginBottom: 110,
color: { interpolate: (t) => d3.rgb(10, 225 - t * 150, 255 - t * 150) },
width
})
Insert cell
Insert cell
function stackDisperseX({ y, ...options }) {
// random within margins
const random = d3.randomUniform.source(d3.randomLcg(41))(0.1, 1 - 0.1);

// extend options: add stackX and a lazy channel
// note: we pass y directly (Plot.stackX would otherwise consume it)
const [x, setX] = Plot.column("x");
options = { ...Plot.stackX(options), x, y };

// compose the options with a transform
return Plot.transform(options, function (data, facets) {
// call the initial transform (including stackX) and retrieve the stacks’ start (X1) and end (X2)
options.transform(data, facets);
const X1 = options.x1.transform();
const X2 = options.x2.transform();

// extract the repeat numbers for each data point
const N = Plot.valueof(data, options.repeat);
// create a new data structure indicating the new length
data = { length: d3.sum(N) };
// instantiate the lazy channel to an array with the new data length
const X = new Float32Array(data.length);
setX(X);

// loop over each facet, then over each initial data point
// and fill the channels with random positions x between X1 and X2
let k = 0;
facets = facets.map((facet) => {
const index = [];
for (const i of facet) {
const interval = d3.interpolate(X1[i], X2[i]);
for (let j = 0; j < N[i]; j++) {
X[k] = interval(random());
index.push(k);
k++;
}
}
return index;
});

return { data, facets };
});
}
Insert cell
Insert cell
Inputs.table(data)
Insert cell
// Plot plugin?
// Plot = addDisperseX(Plot_)
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