Public
Edited
Sep 16, 2023
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
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
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
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
Insert cell
chart_beeswarm = {
// init plot + get relevant data and keyframe
const svg = d3.select(DOM.svg(chart_param.width, chart_param.height)),
nodes = node_data.map(d => Object.create(d)),
kf = keyframe_beeswarm;

// draw horizontal axis of beeswarm plot
svg
.append("line")
.attr("x1", x(0))
.attr("x2", x(1))
.attr("y1", y(0.5))
.attr("y2", y(0.5))
.style("stroke", "#bbb")
.style("stroke-dasharray", 4);

// create simulation using:
// (1) x force to keep each node in it's original x position
// (2) y force pulling all nodes towards axis
// (3) collide force to keep nodes separated
const sim = d3
.forceSimulation(nodes)
.force("x", d3.forceX(d => d.x))
.force("y", d3.forceY(y(0.5)).strength(0.1))
.force(
"collide",
d3.forceCollide().radius(d => (beeswarm_w_r ? d.r : 8) + 1)
)
.stop()
.tick(kf);

// draw nodes (with radius based on user input and no edges)
const [node, edge] = draw_nodes(svg, nodes, null, beeswarm_w_r, false);

// update each node upon simulation tick
sim.on("tick", () => {
node.attr("cx", d => d.x).attr("cy", d => d.y);
});

return svg.node();
}
Insert cell
bee2 = {
const svg = d3.select(DOM.svg(chart_param.width, chart_param.height)),
kf = keyframe_beeswarm;
const rescale = (arr,newMin,newMax) => {
let mx = Math.max.apply(Math,arr);
let mn = Math.min.apply(Math,arr);
return arr.map(v => (newMax-newMin)*(v-mn)/(mx-mn)+newMin);
};
const r = 24;
const N = 30;
const nodes = rescale([...Array(N)].map(() => d3.randomNormal(0.5,0.125)()),0,1).map(v => Object.create({x: v, y:y(d3.randomUniform(1)())}));
// draw a data axis
const xaxis = svg
.append("g")
.attr("Class","X-axis")
.attr("transform",`translate(0,${chart_param.height - chart_param.margin.bottom - chart_param.margin.top})`)
.call(d3.axisBottom(x));
// draw horizontal axis of beeswarm plot
svg
.append("line")
.attr("x1", x(0))
.attr("x2", x(1))
.attr("y1", y(0.5))
.attr("y2", y(0.5))
.style("stroke", "#bbb")
.style("stroke-dasharray", 4);
// create simulation using:
// (1) x force to keep each node in it's original x position
// (2) y force pulling all nodes towards axis
// (3) collide force to keep nodes separated
const sim = d3
.forceSimulation(nodes)
.force("x", d3.forceX(d => x(d.x)).strength(4))
.force("y", d3.forceY(y(0.5)).strength(0.5))
.force(
"collide",
d3.forceCollide().radius(d => (r+1)*1.05).strength(2)
)
.alphaDecay(0.01)
.alpha(0.05);

// draw the circles
const circ = svg.selectAll(".circ")
.data(nodes).enter()
.append("circle")
.classed("circ", true)
.attr("cx", d => x(d.x))
.attr("cy", d => d.y)
.attr("r", d => r)
.attr("stroke", "red")
.attr("stroke-width", "3")
.attr("fill","none");
// try with text
let sigfig = d3.format(".3");
const node = svg.selectAll(".node")
.data(nodes).enter()
.append("text")
.classed("node",true)
.attr("x", d => x(d.x))
.attr("y", d => d.y) // already scaled in generator
.attr("text-anchor", "middle")
.attr("stroke","red")
.attr("stroke-width", "1px")
.attr("alignment-baseline", "middle")
.attr("font-family","monospace")
.attr("font-size", `${r/2 - 1}px`)
.text(d => sigfig(d.x));
// stroke="red"
// stroke-width="3"
// fill="none"
// update each node upon simulation tick
sim.on("tick", () => {
node
.attr("x", d => d.x)
.attr("y", d => d.y);
circ
.attr("cx", d => d.x)
.attr("cy", d => d.y);
});

return svg.node();
}
Insert cell
bintest = {
const rescale = (arr,newMin,newMax) => {
let mx = Math.max.apply(Math,arr);
let mn = Math.min.apply(Math,arr);
return arr.map(v => (newMax-newMin)*(v-mn)/(mx-mn)+newMin);
};
const N = 30;
const nodes = rescale([...Array(N)].map(() => d3.randomNormal(0.5,0.125)()),0,1).map((v,i) => Object.create({x: v, y:y(d3.randomUniform(1)()), id:i}));
const binGen = d3.bin()
.value(d => d.x)
.domain(d3.extent(nodes.map(v => v.x)))
.thresholds(x.ticks(10));
let buckets = binGen(nodes);
return buckets;
}
Insert cell
d3.extent([...Array(10)].map(() => d3.randomNormal(0.5,0.15)())).map( (v,i,arr) => {
let dx = d3.pairs(arr,(a,b) => b-a);
return (parseFloat(i)-1)*dx + v;
});
Insert cell
[...y.domain()]
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

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