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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more