Public
Edited
Jun 16, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
var j,
count,
targetCategory,
capture = document.getElementById("capture");

for (let step = 0; step < steps; ++step) {
count = 0;
for (let i = 0; i < num; ++i) {
if (isAngry(i)) {
targetCategory = checkNeighbors(i)[0][0];
j = rndIdx();
while (data[j].category !== targetCategory) j = rndIdx();
exchange(i, j);
count += 1;
}
}

capture.innerHTML = `
<p>Step: ${step} | exchange: ${count}</p>
`;

yield plotDataCanvas();

if (step === steps - 1 || count === 0) {
yield plotData();
break;
}
}

return plotData();
}
Insert cell
plotDataCanvas = () => {
const canvas = document.getElementById("canvas"),
width = 800,
height = 600,
xScale = d3.scaleLinear().domain([0, 1]).range([0, width]).nice(),
yScale = d3.scaleLinear().domain([1, 0]).range([0, height]).nice();

canvas.setAttribute("width", 800);
canvas.setAttribute("height", 600);

const context = canvas.getContext("2d");

const r = 3;

{
context.rect(0, 0, width, height);
context.fillStyle = "white";
context.fill();
}

function drawCategory(category) {
context.beginPath();
data
.filter((d) => d.category === category)
.map((d) => {
context.moveTo(xScale(d.x), yScale(d.y));
context.arc(xScale(d.x), yScale(d.y), r, 0, Math.PI * 2);
});

context.fillStyle = colorScheme[category];
context.fill();
}

for (let category = 0; category < categories; ++category) {
drawCategory(category);
}
}
Insert cell
colorScheme = d3.schemeCategory10
Insert cell
plotData = () => {
return Plot.plot({
width: 800,
height: 600,
aspectRatio: 1.0,
color: { legend: true, scheme: "Category10" },
x: { nice: true },
y: { nice: true },
marks: [
Plot.voronoi(data, {
x: "x",
y: "y",
fill: "category",
stroke: "white",
strokeWidth: 0.3,
opacity: 0.5
}),
Plot.dot(data, {
x: "x",
y: "y",
fill: "category",
opacity: "movings",
r: 2
}),
Plot.frame()
]
});
}
Insert cell
/**
* Exchange the category between i- and j-th points.
*/
exchange = (i, j) => {
const { category, movings } = data[i];

Object.assign(data[i], { category: data[j].category, movings: movings + 1 });
Object.assign(data[j], { category });
}
Insert cell
/**
* Determine if the i-the point is angry.
* It is angry if its category is not of simple-dominance in its neighbourhood.
*/
isAngry = (i) => {
const neighbors = searchlight(i), // [...delaunay.neighbors(i)],
{ category } = data[i],
others = neighbors.filter((j) => data[j].category === category);

return others.length * categories < neighbors.length;
}
Insert cell
/**
* Search the i-th point for its neighbours,
* it is a searchlight method.
*/
searchlight = (idx, order = neighbourOrder) => {
var neighbours = [idx],
edge = [idx],
newEdge;

for (let i = 0; i < order; ++i) {
newEdge = [];
edge.map((j) => {
newEdge = newEdge.concat([...delaunay.neighbors(j)]);
});

edge = newEdge.filter((d) => neighbours.indexOf(d) < 0);

neighbours = neighbours.concat(newEdge);
}

return [...new Set(neighbours)].filter((d) => d !== idx);
}
Insert cell
searchlight(0)
Insert cell
[...new Set([1, 1, 3].concat([4, 5]))]
Insert cell
checkNeighbors(0)
Insert cell
/**
* Checkout the neighbours of the i-th point,
* the neighbours are grouped by their category,
* and the counts are used to sort the categories.
*/
checkNeighbors = (i) =>
[...d3.group(searchlight(i), (d) => data[d].category)].sort(
(a, b) => b[1].length - a[1].length
)
Insert cell
delaunay = d3.Delaunay.from(data.map((d) => [d.x, d.y]))
Insert cell
data = {
button;
// neighbourOrder;

const data = [];

for (let i = 0; i < num; ++i) {
data.push(generatePnt(i));
}

return data;
}
Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
/**
* Generate random point
*/
generatePnt = (i = 0) => {
const x = rnd(),
y = rnd(),
category = rndCategory(),
movings = 0;

return { i, x, y, category, movings };
}
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