Public
Edited
May 5, 2023
1 star
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
simColor = SOMSim(this, conColor, {data: SOMColorData({random: false}), timeConstant: 1000, learningFactor: 0.1, distanceFactor: 20})
Insert cell
Insert cell
Insert cell
Insert cell
simGrid = SOMSim(this, conGrid, {rows: 10, columns: 10, depth: 2, data: SOMGridData({random: false}), timeConstant: 1000, learningFactor: 0.25, distanceFactor: 5})
Insert cell
Insert cell
Insert cell
Insert cell
simAnimal = SOMSim(this, conAnimal, {rows: 10, columns: 10, depth: 13, data: SOMAnimalData(), timeConstant: 1000, learningFactor: 0.25, distanceFactor: 5})
Insert cell
Insert cell
Insert cell
Insert cell
function SOMSim(sim, controls, {rows = 40, columns = 40, depth = 3, data = [], timeConstant = 1000, learningFactor = 0.1, distanceFactor = 40} = {}) {
if (controls.reset || !sim) {
// (Re)set the simulation
sim = {};

// Learning parameters
sim.timeConstant = timeConstant;
sim.learningFactor = learningFactor;
sim.distanceFactor = distanceFactor;
// Setup the SOM
sim.rows = rows;
sim.columns = columns;
sim.depth = depth;
sim.som = math.random([sim.rows, sim.columns, sim.depth], 0.05, 0.95);
// Setup the Inputs
sim.data = data;
sim.inputs = data.length;
// Setup the step count
sim.step = 0;
} else {
// Update the step count
sim.step++;

// Pick random input
const datum = sim.data[math.randomInt(0, sim.inputs)];
// Find best matching unit
const best = bestMatchNode(datum, sim.som);
// Update weights
const stepUpdate = Math.exp(-sim.step / sim.timeConstant);
const stepUpdate2 = Math.exp(-sim.step / (sim.timeConstant * 2));
const distanceRate = distanceFactor * stepUpdate;
const learningRate = learningFactor * stepUpdate2;
for (let row = 0; row < sim.rows; row++) {
for (let column = 0; column < sim.columns; column++) {
const nodeDistance = euclideanDistance([best.row, best.column], [row, column]);
const influence = Math.exp(-Math.pow(nodeDistance, 2) / (2 * Math.pow(distanceRate, 2)));
for (let depth = 0; depth < sim.depth; depth++) {
sim.som[row][column][depth] += learningRate * influence * (datum[depth] - sim.som[row][column][depth]);
}
}
}
}
return sim;
}
Insert cell
function bestMatchNode(datum, som) {
// Find best matching unit
let bestRow = 0;
let bestColumn = 0;
let bestDistance = datum.length;
for (let row = 0; row < som.length; row++) {
for (let column = 0; column < som[0].length; column++) {
const distance = euclideanDistance(datum, som[row][column]);
if (distance < bestDistance) {
bestDistance = distance;
bestRow = row;
bestColumn = column;
}
}
}
return {row: bestRow, column: bestColumn};
}
Insert cell
function bestMatchDatum(node, data) {
// Find best matching unit
let bestIndex = 0;
let bestDistance = node.length;
for (let index = 0; index < data.length; index++) {
const distance = euclideanDistance(node, data[index]);
if (distance < bestDistance) {
bestDistance = distance;
bestIndex = index;
}
}
return bestIndex;
}
Insert cell
function euclideanDistance (x, y) {
const vectorSize = x.length
let result = 0;
let diff = 0;
for (let i = 0; i < vectorSize; i++) {
diff = x[i] - y[i];
result += diff * diff;
}
return Math.sqrt(result)
}
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

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