| import { euclidean } from "../metrics/index.js"; |
| import { Heap } from "../datastructure/index.js"; |
| import { distance_matrix, Matrix } from "../matrix/index.js"; |
| |
| |
| |
| |
| |
| export class KNN { |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| constructor(elements=null, metric=euclidean) { |
| this._metric = metric; |
| this._elements = elements instanceof Matrix ? elements : Matrix.from(elements); |
| const N = this._elements.shape[0]; |
| if (metric === "precomputed") { |
| this._D = this._elements.clone(); |
| } else { |
| this._D = distance_matrix(this._elements, metric); |
| } |
| this.KNN = []; |
| for (let row = 0; row < N; ++row) { |
| const distances = this._D.row(row); |
| const H = new Heap(null, d => d.value, "min"); |
| for (let j = 0; j < N; ++j) { |
| H.push({ |
| value: distances[j], |
| index: j, |
| }); |
| } |
| this.KNN.push(H); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| search(t, k = 5) { |
| const metric = this._metric; |
| const KNN = this.KNN; |
| let H; |
| if (Array.isArray(t)) { |
| if (this._metric == "precomputed") { |
| throw "Search by query element is only possible when not using a precomputed distance matrix!" |
| } |
| const elements = this._elements; |
| const N = KNN.length; |
| let nearest_element_index = null; |
| let nearest_dist = Infinity; |
| for (let i = 0; i < N; ++i) { |
| const element = elements.row(i); |
| const dist = metric(t, element); |
| if (dist < nearest_dist) { |
| nearest_element_index = i; |
| nearest_dist = dist; |
| } |
| } |
| H = KNN[nearest_element_index]; |
| } else if (Number.isInteger(t)) { |
| H = KNN[t] |
| } |
| |
| let result = [] |
| for (let i = 0; i < k; ++i) { |
| result.push(H.pop()) |
| } |
| result.forEach(res => H.push(res.element)) |
| return result |
| } |
| } |