Unlisted
Edited
Oct 4, 2024
Insert cell
Insert cell
{
const context = DOM.context2d(width, 120),
W = d3.sum(weights),
x = d3.scaleLinear([0, 1], [10, width-10]);

context.lineWidth = 2;
context.strokeStyle = "white";

const f = weightedQuantileF(values, weights);
function draw(v) {
let s = x(0),
t = 0;

v = f(v).value;
context.fillStyle = color(v);
context.fillRect(0, 0, width, 120);

let rectangle;
for (const [value, weight] of d3
.zip(values, weights)
.sort((a, b) => d3.ascending(a[0], b[0]))) {
let s1 = x((t += weight / W));
context.fillStyle = color(value);
context.fillRect(s, 10, s1 - s + 1, 100);

if (v === value) rectangle = [Math.floor(s), 11, Math.floor(s1 - s), 98]

s = s1;
}
context.strokeRect(...rectangle);
}
draw(.5);
return d3
.select(context.canvas)
.on("pointermove click", (event) => draw(x.invert(d3.pointer(event)[0])))
.node();
}
Insert cell
color = d3.scaleSequential(d3.interpolateTurbo)
Insert cell
values = Array.from({ length: 15 }, Math.random)
Insert cell
weights = Array.from(values, () => .1 + Math.random())
Insert cell
weightedQuantile(values, weights, 0.333)
Insert cell
weightedQuantileF(values, weights).invert(0.43)
Insert cell
weightedQuantile([0, 1], [1, 2], 1 / 3)
Insert cell
weightedQuantileIndex([0, 1], [1, 2], 1 / 3)
Insert cell
weightedQuantile([0], [1, 2], 1)
Insert cell
weightedQuantileIndex([0], [1, 2], 1)
Insert cell
weightedQuantiles(values, weights, d3.ticks(0, 1, 10))
Insert cell
weightedQuantiles(d3.range(100).map(i => ({value:i, weight: 1+i})), d => d.weight, d3.ticks(0, 1, 10), d => d.value)
Insert cell
function weightedQuantileF(data, weights, accessor) {
// values is an iterable (todo: use *numbers* from d3-array)
const values = accessor ? Float64Array.from(data, (d, i) => accessor(d, i, data)) : Float64Array.from(data);
// weights is an accessor function or an iterable
weights = typeof weights === "function" ? Float64Array.from(data, weights) : Float64Array.from(weights);
const indices = Int32Array.from(values, (_, i) => i)
.sort((i, j) => d3.ascending(values[i], values[j])),
cs = d3.cumsum(indices, i => weights[i]);

const wq = (q) => {
const r = cs[cs.length - 1] * q,
i = d3.bisectLeft(cs, r),
mix = r == cs[i] && i < values.length - 1,
select = mix ? [indices[i], indices[i + 1]] : [indices[i], indices[i]],
value = mix ? (values[select[0]] + values[select[1]]) / 2 : values[select[0]];
return { value, select };
}

wq.invert = (value) => (cs[d3.bisector((i) => values[i]).left(indices, value)-1] ?? 0) / cs[cs.length - 1];

return wq;
}
Insert cell
function weightedQuantile(values, weights, q, accessor) {
return weightedQuantileF(values, weights, accessor)(q).value;
}
Insert cell
function weightedQuantileIndex(values, weights, q, accessor) {
return weightedQuantileF(values, weights, accessor)(q).select;
}
Insert cell
function weightedQuantiles(values, weights, q, accessor) {
return q.map(weightedQuantileF(values, weights, accessor)).map(r => r.value);
}
Insert cell
Insert cell
Plot.plot({
marks: [
Plot.lineY(d3.ticks(0, 1, 1000), {
x: (t) => t,
y: ((f) => (t) => f(t).value)(weightedQuantileF(values, weights)),
stroke: "blue"
}),
Plot.lineY(d3.ticks(0, 1, 1000), {
x: weightedQuantileF(values, weights).invert,
y: (t) => t,
stroke: "red",
mixBlendMode: "multiply"
})
]
})
Insert cell
Insert cell
d3.quantile([0,1], 0.3)
Insert cell
weightedQuantile([0,1], () => 1, 0.3)
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