Public
Edited
Oct 24, 2023
6 forks
Importers
65 stars
Insert cell
Insert cell
Insert cell
values1 = distribution("Uniform") // 1000 values distributed with d3.randomUniform
Insert cell
Insert cell
Insert cell
Insert cell
bin1 = d3.bin()
Insert cell
Insert cell
buckets1 = bin1(values1)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
bin2 = d3.bin()
Insert cell
buckets2 = bin2(values2)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const values = Array.from({ length: 100 }, (d, i) => ({
label: i,
p: Math.random()
})),
bin = d3.bin().value(d => d.p);
return bin(values);
}
Insert cell
Insert cell
Insert cell
bin3 = d3.bin().domain([0, max3])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
bin3.domain()(values3)
Insert cell
Insert cell
d3.bin().domain()(values3)
Insert cell
Insert cell
bin4 = {
const scale = d3
.scaleLinear()
.domain(d3.extent(values3))
.nice(); // 😃
return d3.bin().domain(scale.domain());
}
Insert cell
Insert cell
bin4.domain()()
Insert cell
Insert cell
Insert cell
bin5 = d3.bin().thresholds([0, 2, 5, 10, 15])
Insert cell
Insert cell
Insert cell
Insert cell
bin6 = d3.bin().thresholds(nthresholds)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
textbin = d3
.bin()
.thresholds((data, min, max) => [...new Set(data.map(d => d[0]))].sort())
Insert cell
textbin(
`Après la faculté de penser, celle de communiquer ses pensées à ses semblables est l'attribut le plus frappant qui distingue l'homme de la brute`
.toLowerCase()
.split(/[\s']/)
)
Insert cell
Insert cell
simple = require("simple-statistics@7")
Insert cell
Insert cell
bin25 = d3.bin()
.thresholds(data => simple.ckmeans(data, chunks).map(l => d3.min(l)))
Insert cell
Insert cell
Insert cell
Insert cell
bin26 = d3.bin()
.thresholds((data, min, max) =>
d3.range(chunks).map(t => min + (t / chunks) * (max - min))
)
Insert cell
Insert cell
Insert cell
Insert cell
max = 18
Insert cell
x = d3.scaleLinear()
.domain([0, max])
.range([30, width - 30])
.clamp(false)
Insert cell
function draw_buckets(bin, values) {
const svg = this || DOM.svg(width, 100);

const buckets = bin(values);

const binColor = d3
.scaleThreshold()
.domain(buckets.map(d => d.x0))
.range(colors);

d3.select(svg)
.selectAll("rect")
.data(buckets)
.join("rect")
.attr("y", d => 10)
.attr("height", 100 - 2 * 10)
.attr("x", d => (x(d.x0) + 1) | 0)
.attr("width", d => (x(d.x1) | 0) - (x(d.x0) | 0) - 2)
.attr("stroke", d => binColor(d.x0))
.attr("stroke-width", 1)
.attr("stroke-dasharray", d => (d.length === 0 ? "1 5" : null))
.attr("fill", "none");

draw_values(svg, values);

d3.select(svg)
.selectAll("circle")
.attr("fill", binColor)
.attr("stroke", binColor);

d3.select(svg)
.selectAll("text")
.data(buckets.filter(d => d.length > 0))
.join("text")
.attr("x", d => (x(d.x0) + 3) | 0)
.attr("y", 86)
.attr("fill", "black")
.attr("font-size", 9)
.text(d =>
x(d.x1) - x(d.x0) < 50
? d.length
: d.length > 1
? `${d.length} items`
: d.length === 1
? "1 item"
: "empty bin"
);

return svg;
}
Insert cell
function draw_values(svg, values) {
// We sort the data for meaningful transitions.
const current = values.slice().sort();

const prev = (svg && svg.values) || current;

svg.values = current;

d3.select(svg)
.selectAll("circle")
.data(current)
.join("circle")
.attr("r", 2)
.attr("stroke", "#588")
.attr("fill", "lightblue")
.attr("fill-opacity", 0.3)
.attr("cx", (_, i) => x(prev[i]))
.attr("cy", (_, i) => y(i))
.transition()
.duration(500)
.attr("cx", x);

return svg;
}
Insert cell
colors = ["black"]
.concat(d3.schemeCategory10)
.concat(d3.schemePaired)
.concat(d3.schemePastel1)
.concat(d3.schemePastel2)
Insert cell
function draw_histogram_from_buckets(buckets, x, opts = {}) {
const width = opts.width || 300,
height = opts.height || 200,
margin = opts.margin || { top: 20, right: 20, bottom: 30, left: 40 },
svg = d3.select(DOM.svg(width, height)),
maxBins = d3.max(buckets, d => d.length),
data = buckets.flat(),
count = data.length,
y = d3
.scaleLinear()
.domain([0, maxBins])
.nice()
.range([height - margin.bottom, margin.top]),
frequency = opts.relative
? d3
.scaleLinear()
.domain([0, maxBins / count])
.nice()
.range([height - margin.bottom, margin.top])
: y,
xAxis = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
.call(g =>
g
.append("text")
.attr("x", width - margin.right)
.attr("y", -4)
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text(opts.xText)
);

const binColor = d3
.scaleThreshold()
.domain(buckets.map(d => d.x0))
.range(colors);

svg
.append("g")
.selectAll("rect")
.data(buckets)
.join("rect")
.attr("fill", opts.fill || (d => binColor(d.x0)))
.attr("x", d => x(d.x0) + 1)
.attr("width", d => Math.max(0, x(d.x1) - x(d.x0) - 1))
.attr("y", d => y(d.length))
.attr("height", d => y(0) - y(d.length));

svg.append("g").call(xAxis);

if (opts.title)
svg
.append("g")
.append("text")
.text(opts.title)
.style("fill", "#000")
.attr("font-weight", "bold")
.style("font-size", 14)
.style("text-anchor", "end")
.attr("x", width - 30)
.attr("y", 10);

const labels = svg
.append("g")
.selectAll("text")
.data(buckets.filter(d => d.length > 0))
.join("text")
.attr("x", d => ((x(d.x0) + x(d.x1)) / 2) | 0)
.attr("y", d => y(d.length) - 2)
.style("fill", "black")
.style("font-size", 10)
.style("text-anchor", "middle");
if (opts.relative) {
const format = d3.format(".1%");
labels.text(d => format(d.length / count));
} else
labels.text(d =>
x(d.x1) - x(d.x0) < 50
? d.length
: d.length > 1
? `${d.length} items`
: d.length === 1
? "1 item"
: "empty bucket"
);

return svg.node();
}
Insert cell
y = {
const frac = x => x - (x | 0);
return i => 50 + 25 * frac(952 * Math.sin((i + 0.5) * 876));
}
Insert cell
randomDistributions = {
const r20 = d3.randomNormal(max * 0.3, 2),
r21 = d3.randomNormal(max * 0.75, 1),
r4 = d3.randomExponential(0.3);
return [
{ label: "Uniform", random: d3.randomUniform(0.5, max - 0.3) },
{ label: "Normal", random: d3.randomNormal(max / 2, (1.4 * max) / 18) },
{ label: "Two blobs", random: () => (Math.random() > 0.3 ? r20() : r21()) },
{ label: "Skewed right", random: d3.randomLogNormal() },
{ label: "Skewed left", random: () => 18 - r4() }
];
}
Insert cell
function distribution(label) {
return select_distribution(label).value
}
Insert cell
function select_distribution(
label,
description = "Random distribution",
length = 1000
) {
const form = html`<form>${randomDistributions.map(
(o, i) =>
`<label style="display: inline-block; margin: 5px 10px 3px 0; font-size: 0.85em;"><input name=radio type=radio value=${i}${
o.label === label ? " checked" : ""
}>${o.label}</label>`
)}
${
description
? `<div style="font-size: 0.85rem; font-style: italic;">${description}</div>`
: ""
}</form>`;
form.oninput = () => {
form.value = Float32Array.from(
{ length },
randomDistributions[+form.radio.value].random
);
};
form.onchange = () => {
// Safari…
form.value = form.radio.value;
form.dispatchEvent(new CustomEvent("input"));
};
form.oninput();
return form;
}
Insert cell
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