Public
Edited
Mar 7, 2024
Importers
1 star
Insert cell
Insert cell
drawChart(dataWithRandomY)
Insert cell
Insert cell
Insert cell
drawChart(dataWithAdjustedY)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dataWithRandomY = {
const random = makeRng();
return data.map((d) => ({ x: xScale(d), y: yScale(random()) }));
}
Insert cell
dataWithAdjustedY = spacedJitter(
data,
(d) => xScale(d),
[yScale(0), yScale(1)],
{ random: makeRng(), iterations, edgeSpacing }
)
Insert cell
// To use this function outside Observable, copy this function and the functions in the following
// three cells into your .js file.
function spacedJitter(
data,
getX,
yRange,
{ random = Math.random, iterations = 20, edgeSpacing = 0 } = {}
) {
const yStart = yRange[0];
const ySize = yRange[1] - yRange[0];
const _data = data.map((d, i) => ({
x: getX(d),
y: yStart + random() * ySize,
index: i,
datum: d
}));

_data.sort((a, b) => a.x - b.x);

for (let i = 0; i < iterations; i++) {
for (let j = 0; j < _data.length; j++) {
const index = getRandomInt(0, _data.length, random); // choose a random point to try to move
const d = _data[index];
const distanceFromNearestPoint = distance(
index,
d.y,
yRange,
edgeSpacing,
_data
);
const candidateY = yStart + random() * ySize;
if (
distance(index, candidateY, yRange, edgeSpacing, _data) >
distanceFromNearestPoint
) {
d.y = candidateY;
}
}
}
_data.sort((a, b) => a.index - b.index);
for (const d of _data) {
delete d.index;
}
return _data;
}
Insert cell
function distance(dIndex, yVal, yRange, edgeSpacing, data) {
const closestEdgeDist = Math.min(
Math.abs(yVal - yRange[0]),
Math.abs(yVal - yRange[1])
);
let closestDist =
edgeSpacing === 0 ? Infinity : closestEdgeDist / edgeSpacing;
const yThis = yVal;
closestDist = updateClosestDistance(closestDist, dIndex, yThis, 1, data);
closestDist = updateClosestDistance(closestDist, dIndex, yThis, -1, data);
return closestDist;
}
Insert cell
function updateClosestDistance(closestDist, dIndex, yThis, step, data) {
const xThis = data[dIndex].x;
const startIndex = dIndex + step;
const endIndex = step === 1 ? data.length : -1;
for (let i = startIndex; i !== endIndex; i += step) {
const other = data[i];
const xOther = other.x;
if (Math.abs(xThis - xOther) > closestDist) {
break;
}
const yOther = other.y;
const xDiff = xThis - xOther;
const yDiff = yThis - yOther;
if (yDiff * yDiff >= closestDist * closestDist) continue; // An optimisation
const dist = Math.hypot(xDiff, yDiff);
if (dist < closestDist) closestDist = dist;
}
return closestDist;
}
Insert cell
function getRandomInt(min, max, random = Math.random) {
// based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
return Math.floor(random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
}
Insert cell
xScale = d3
.scaleLinear()
.domain(d3.extent(data))
.range([dims.marginLeft, dims.width - dims.marginRight])
Insert cell
yScale = d3
.scaleLinear()
.domain([-0.2, 1.2])
.range([dims.height - dims.marginBottom, dims.marginTop])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
quartiles = simpleStatistics.quantile(data, [0.25, 0.5, 0.75])
Insert cell
data = {
const rawData = (await FileAttachment("data.json").json())
.map((d) => d.datum.value)
.filter((d) => d < 14);

if (numberOfPoints < rawData.length) {
return rawData.slice(0, numberOfPoints);
} else {
const initialLength = rawData.length;
const random = makeRng();
while (rawData.length < numberOfPoints) {
rawData.push(
rawData[Math.floor(random() * initialLength)] - 0.1 + random() / 5
); // Random element of array from https://stackoverflow.com/a/5915122
}
}

return rawData;
}
Insert cell
simpleStatistics = require("simple-statistics")
Insert cell
makeRng = function () {
function sfc32(a, b, c, d) {
return function () {
a |= 0;
b |= 0;
c |= 0;
d |= 0;
var t = (((a + b) | 0) + d) | 0;
d = (d + 1) | 0;
a = b ^ (b >>> 9);
b = (c + (c << 3)) | 0;
c = (c << 21) | (c >>> 11);
c = (c + t) | 0;
return (t >>> 0) / 4294967296;
};
}

let seed = 1337 ^ 0xdeadbeef; // 32-bit seed with optional XOR value
// Pad seed with Phi, Pi and E.
// https://en.wikipedia.org/wiki/Nothing-up-my-sleeve_number
let _rand = sfc32(0x9e3779b9, 0x243f6a88, 0xb7e15162, seed);
for (let i = 0; i < 15; i++) _rand();
return _rand;
}
Insert cell
<style>
path.domain {
display: none
}
</style>
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