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

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