UNPKG

entropyx

Version:

A simple data mining library, written in TypeScript

113 lines 4.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SammonMapping = void 0; const distance_1 = require("@/base/distance"); const seedrandom_1 = require("seedrandom"); class SammonMapping { constructor(options = {}) { this.maxIterations = options.maxIterations ?? 20; this.learningRate = options.learningRate ?? 0.3; this.projectionDimension = options.projectionDimension ?? 2; this.distanceFn = options.distanceFn ?? distance_1.Distance.euclidean; if (typeof options.randomSeed === 'number') { this.random = (0, seedrandom_1.alea)(options.randomSeed.toString()); } else { this.random = Math.random; } } fit(data) { const n = data.length; if (n === 0) { return { projection: [] }; } const distStar = this.createDistanceMatrix(data); const C = this.sumOfDistances(distStar); let projected = this.initializeProjection(n, this.projectionDimension); for (let iter = 0; iter < this.maxIterations; iter++) { const distProj = this.createDistanceMatrix(projected); const nextProjection = Array.from({ length: n }, () => new Array(this.projectionDimension).fill(0)); for (let p = 0; p < n; p++) { for (let q = 0; q < this.projectionDimension; q++) { const numerator = this.firstDerivative(projected, distProj, distStar, p, q, C); const denominator = this.secondDerivative(projected, distProj, distStar, p, q, C); const oldCoord = projected[p][q]; const step = this.learningRate * (numerator / (Math.abs(denominator) + 1e-12)); nextProjection[p][q] = oldCoord - step; } } projected = nextProjection; } return { projection: projected }; } firstDerivative(projected, distProj, distStar, p, q, C) { let sum = 0; const n = projected.length; for (let j = 0; j < n; j++) { if (j === p) continue; const dStar = distStar[p][j]; const dProj = distProj[p][j]; if (dStar === 0 || dProj === 0) continue; const term1 = (dStar - dProj) / (dStar * dProj); const term2 = projected[p][q] - projected[j][q]; sum += term1 * term2; } return (-2 / C) * sum; } secondDerivative(projected, distProj, distStar, p, q, C) { let sum = 0; const n = projected.length; for (let j = 0; j < n; j++) { if (j === p) continue; const dStar = distStar[p][j]; const dProj = distProj[p][j]; if (dStar === 0 || dProj === 0) continue; const base = 1 / (dStar * dProj); const diff = dStar - dProj; const coordDiff = projected[p][q] - projected[j][q]; const c = (coordDiff * coordDiff) / dProj; const d = 1 + diff / dProj; sum += base * (diff - c * d); } return (-2 / C) * sum; } createDistanceMatrix(data) { const n = data.length; const distances = Array.from({ length: n }, () => new Array(n).fill(0)); for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { const d = this.distanceFn(data[i], data[j]); distances[i][j] = d; distances[j][i] = d; } } return distances; } sumOfDistances(matrix) { let sum = 0; const n = matrix.length; for (let i = 0; i < n; i++) { for (let j = i + 1; j < n; j++) { sum += matrix[i][j]; } } return sum; } initializeProjection(n, d) { const projection = []; for (let i = 0; i < n; i++) { const row = []; for (let j = 0; j < d; j++) { row.push(this.random()); } projection.push(row); } return projection; } } exports.SammonMapping = SammonMapping; //# sourceMappingURL=sammon-mapping.js.map