Public
Edited
May 20, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const filter = data.filter((d) => d.i < pntCount);

return Plot.plot({
width: width,
height: (width * 9) / 16,
x: { grid: true, nice: true },
y: { grid: true, nice: true },
color: {
legend: true,
scheme: "Category10"
},
marks: [
Plot.density(filter, {
x: "i",
y: (d) => "C " + d.cluster,
stroke: (d) => "Cluster " + d.cluster,
strokeWidth: 0.5,
opacity: 0.8,
bandwidth: 4
}),
Plot.dot(filter, {
x: "i",
y: (d) => "C " + d.cluster,
r: 2,
fill: (d) => "Cluster " + d.cluster
// opacity: 0.5
}),
Plot.frame()
]
});
}
Insert cell
data
Insert cell
clustering = (k = 5) => {
return new Promise((resolve, reject) => {
kmeans.clusterize(
data.map((d) => {
return { x: d.x, y: d.y };
}),
{ k: k },
(err, res) => {
console.log("----- Results -----");
console.log(`Iterations: ${res.iterations}`);
console.log("Clusters: ");
console.log(res.clusters);
// res.clusters.map(({ vectorIds }, cluster) =>
// vectorIds.map((idx) => (data[idx].cluster = cluster))
// );
resolve(res);
}
);
});
}
Insert cell
data.map((d) => {
return { x: d.x, y: d.y };
})
Insert cell
data = {
// Setup
const points = 20000,
r = 1;

// Data
const data = [],
rndX = d3.randomUniform(boundingBox.xRange[0], boundingBox.xRange[1]),
rndY = d3.randomUniform(boundingBox.yRange[0], boundingBox.yRange[1]),
rndPnt = () => {
return { x: rndX(), y: rndY() };
};

// Append points
var pnt = rndPnt(),
newPnt,
pntCount = 1;

// while (!d3.geoContains(Geography.nation, [pnt.x, pnt.y])) {
while (!d3.polygonContains(boundingBox.polygon, [pnt.x, pnt.y])) {
pnt = rndPnt();
}

data.push(Object.assign({ r, i: 0 }, pnt));

for (let i = 0; i < points; ++i) {
pnt = rndPnt();

newPnt = addNewPnt(pnt, data);
if (!newPnt) continue;

++pntCount;
data.push(Object.assign({ r, i: pntCount }, newPnt.newPnt));
}

//
Object.assign(data, { boundingBox, rndPnt, pntCount });

return data;
}
Insert cell
pnt = data.rndPnt()
Insert cell
addNewPnt = (pnt, data) => {
const threshold = pntSpace,
nearestPnt =
data[
d3.minIndex(data, (p) => {
const d = computeDistance(p, pnt);
p.distance = d;
return d;
})
],
{ distance } = nearestPnt;

if (distance < threshold) return undefined;

var x,
y,
k = threshold / distance;

x = nearestPnt.x + (pnt.x - nearestPnt.x) * k;
y = nearestPnt.y + (pnt.y - nearestPnt.y) * k;

if (!d3.geoContains(Geography.nation, [x, y])) return undefined;

const newPnt = { x, y };

return { nearestPnt, newPnt, pnt };
}
Insert cell
function computeDistance(a, b) {
return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}
Insert cell
{
const { nation, states, countries, statemesh, countrymesh } = Geography;

return Plot.plot({
projection: "albers",
marks: [Plot.geo(nation)]
});
}
Insert cell
d3.geoContains(Geography.nation, [-110, 32])
Insert cell
boundingBox = {
const multipoly = Geography.nation.features[0].geometry.coordinates[0][0];

return {
xRange: d3.extent(multipoly, (d) => d[0]),
yRange: d3.extent(multipoly, (d) => d[1]),
polygon: multipoly
};
}
Insert cell
Geography = {
const us = await FileAttachment("us-counties-10m.json").json();

const nation = topojson.feature(us, us.objects.nation),
states = topojson.feature(us, us.objects.states),
counties = topojson.feature(us, us.objects.counties),
statemesh = topojson.mesh(us, us.objects.states, (a, b) => a !== b),
countrymesh = topojson.mesh(us, us.objects.counties, (a, b) => a !== b);

return { us, nation, states, counties, statemesh, countrymesh };
}
Insert cell
d3 = require("d3")
Insert cell
kmeans = require("https://bundle.run/kmeans-engine@1.5.0")
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