@nshiab/simple-data-analysis-core 2.0.8 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/class/SimpleTable.d.ts +72 -0
- package/esm/class/SimpleTable.d.ts.map +1 -1
- package/esm/class/SimpleTable.js +68 -0
- package/esm/helpers/appendColumnBatches.d.ts +3 -0
- package/esm/helpers/appendColumnBatches.d.ts.map +1 -0
- package/esm/helpers/appendColumnBatches.js +21 -0
- package/esm/helpers/buildUmapGraph.d.ts +24 -0
- package/esm/helpers/buildUmapGraph.d.ts.map +1 -0
- package/esm/helpers/buildUmapGraph.js +155 -0
- package/esm/helpers/cacheDatabase.d.ts.map +1 -1
- package/esm/helpers/cacheDatabase.js +2 -1
- package/esm/helpers/fitUmapCurve.d.ts +2 -0
- package/esm/helpers/fitUmapCurve.d.ts.map +1 -0
- package/esm/helpers/fitUmapCurve.js +66 -0
- package/esm/helpers/initialUmapCoordinates.d.ts +2 -0
- package/esm/helpers/initialUmapCoordinates.d.ts.map +1 -0
- package/esm/helpers/initialUmapCoordinates.js +5 -0
- package/esm/helpers/optimizeUmapLayout.d.ts +7 -0
- package/esm/helpers/optimizeUmapLayout.d.ts.map +1 -0
- package/esm/helpers/optimizeUmapLayout.js +93 -0
- package/esm/helpers/prepareUmapLayoutOptions.d.ts +14 -0
- package/esm/helpers/prepareUmapLayoutOptions.d.ts.map +1 -0
- package/esm/helpers/prepareUmapLayoutOptions.js +23 -0
- package/esm/helpers/readScalarNumber.d.ts +3 -0
- package/esm/helpers/readScalarNumber.d.ts.map +1 -0
- package/esm/helpers/readScalarNumber.js +4 -0
- package/esm/helpers/umapRandom.d.ts +2 -0
- package/esm/helpers/umapRandom.d.ts.map +1 -0
- package/esm/helpers/umapRandom.js +10 -0
- package/esm/methods/cache.js +1 -1
- package/esm/methods/loadArray.d.ts.map +1 -1
- package/esm/methods/loadArray.js +3 -20
- package/esm/methods/umap.d.ts +5 -0
- package/esm/methods/umap.d.ts.map +1 -0
- package/esm/methods/umap.js +156 -0
- package/esm/methods/updateWithJS.js +9 -21
- package/llm.md +80 -1
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/script/class/SimpleTable.d.ts +72 -0
- package/script/class/SimpleTable.d.ts.map +1 -1
- package/script/class/SimpleTable.js +68 -0
- package/script/helpers/appendColumnBatches.d.ts +3 -0
- package/script/helpers/appendColumnBatches.d.ts.map +1 -0
- package/script/helpers/appendColumnBatches.js +24 -0
- package/script/helpers/buildUmapGraph.d.ts +24 -0
- package/script/helpers/buildUmapGraph.d.ts.map +1 -0
- package/script/helpers/buildUmapGraph.js +161 -0
- package/script/helpers/cacheDatabase.d.ts.map +1 -1
- package/script/helpers/cacheDatabase.js +2 -1
- package/script/helpers/fitUmapCurve.d.ts +2 -0
- package/script/helpers/fitUmapCurve.d.ts.map +1 -0
- package/script/helpers/fitUmapCurve.js +69 -0
- package/script/helpers/initialUmapCoordinates.d.ts +2 -0
- package/script/helpers/initialUmapCoordinates.d.ts.map +1 -0
- package/script/helpers/initialUmapCoordinates.js +11 -0
- package/script/helpers/optimizeUmapLayout.d.ts +7 -0
- package/script/helpers/optimizeUmapLayout.d.ts.map +1 -0
- package/script/helpers/optimizeUmapLayout.js +99 -0
- package/script/helpers/prepareUmapLayoutOptions.d.ts +14 -0
- package/script/helpers/prepareUmapLayoutOptions.d.ts.map +1 -0
- package/script/helpers/prepareUmapLayoutOptions.js +29 -0
- package/script/helpers/readScalarNumber.d.ts +3 -0
- package/script/helpers/readScalarNumber.d.ts.map +1 -0
- package/script/helpers/readScalarNumber.js +7 -0
- package/script/helpers/umapRandom.d.ts +2 -0
- package/script/helpers/umapRandom.d.ts.map +1 -0
- package/script/helpers/umapRandom.js +13 -0
- package/script/methods/cache.js +1 -1
- package/script/methods/loadArray.d.ts.map +1 -1
- package/script/methods/loadArray.js +2 -19
- package/script/methods/umap.d.ts +5 -0
- package/script/methods/umap.d.ts.map +1 -0
- package/script/methods/umap.js +162 -0
- package/script/methods/updateWithJS.js +9 -21
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type prepareUmapLayoutOptions from "./prepareUmapLayoutOptions.js";
|
|
2
|
+
export default function optimizeUmapLayout(graph: {
|
|
3
|
+
source: Uint32Array;
|
|
4
|
+
target: Uint32Array;
|
|
5
|
+
weight: Float64Array;
|
|
6
|
+
}, initial: Float64Array, options: ReturnType<typeof prepareUmapLayoutOptions>): Float64Array;
|
|
7
|
+
//# sourceMappingURL=optimizeUmapLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimizeUmapLayout.d.ts","sourceRoot":"","sources":["../../src/helpers/optimizeUmapLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,wBAAwB,MAAM,+BAA+B,CAAC;AAS1E,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACxC,KAAK,EAAE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,EACzE,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,UAAU,CAAC,OAAO,wBAAwB,CAAC,GACnD,YAAY,CA2Fd"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import umapRandom from "./umapRandom.js";
|
|
2
|
+
// Internal interface. Graph entries are directed, in a stable order; the fuzzy
|
|
3
|
+
// union graph supplies both orientations. Coordinates are interleaved x,y.
|
|
4
|
+
// Inputs are borrowed read-only. State is O(edges + vertices), never O(n²).
|
|
5
|
+
// Implements reference Euclidean UMAP SGD: scheduled attractive edges, sampled
|
|
6
|
+
// repulsion, clipped gradients, symmetric attractive updates, linear decay.
|
|
7
|
+
// Algorithm: https://umap-learn.readthedocs.io/en/latest/how_umap_works.html
|
|
8
|
+
export default function optimizeUmapLayout(graph, initial, options) {
|
|
9
|
+
const { epochs, seed, learningRate, negativeSamples, curve: [a, b] } = options;
|
|
10
|
+
const count = initial.length / 2;
|
|
11
|
+
if (!Number.isInteger(count) || count < 3 || count > 0xffffffff) {
|
|
12
|
+
throw new Error("Expected interleaved coordinates for at least three vertices.");
|
|
13
|
+
}
|
|
14
|
+
for (const value of initial) {
|
|
15
|
+
if (!Number.isFinite(value)) {
|
|
16
|
+
throw new Error("Initial coordinates must be finite.");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const { source, target, weight } = graph;
|
|
20
|
+
if (!weight.length || source.length !== weight.length ||
|
|
21
|
+
target.length !== weight.length)
|
|
22
|
+
throw new Error("Invalid graph column lengths.");
|
|
23
|
+
let maximum = 0;
|
|
24
|
+
for (let edge = 0; edge < weight.length; edge++) {
|
|
25
|
+
if (source[edge] >= count || target[edge] >= count ||
|
|
26
|
+
source[edge] === target[edge])
|
|
27
|
+
throw new Error("Graph contains an invalid vertex or self edge.");
|
|
28
|
+
if (!Number.isFinite(weight[edge]) || weight[edge] < 0 || weight[edge] > 1)
|
|
29
|
+
throw new Error("Graph weights must be between zero and one.");
|
|
30
|
+
maximum = Math.max(maximum, weight[edge]);
|
|
31
|
+
}
|
|
32
|
+
if (!maximum)
|
|
33
|
+
throw new Error("Graph must contain a positive edge.");
|
|
34
|
+
const period = new Float64Array(weight.length);
|
|
35
|
+
const nextPositive = new Float64Array(weight.length);
|
|
36
|
+
const nextNegative = new Float64Array(weight.length);
|
|
37
|
+
// Reference pruning uses 500 when explicitly requesting at most ten epochs.
|
|
38
|
+
const threshold = maximum / (epochs > 10 ? epochs : 500);
|
|
39
|
+
for (let edge = 0; edge < weight.length; edge++) {
|
|
40
|
+
period[edge] = weight[edge] > 0 && weight[edge] >= threshold
|
|
41
|
+
? epochs / ((weight[edge] / maximum) * epochs)
|
|
42
|
+
: Infinity;
|
|
43
|
+
nextPositive[edge] = period[edge];
|
|
44
|
+
nextNegative[edge] = period[edge] / negativeSamples;
|
|
45
|
+
}
|
|
46
|
+
const coordinates = initial.slice();
|
|
47
|
+
const random = umapRandom(seed ^ 0x7f4a7c15);
|
|
48
|
+
const clip = (gradient) => Math.max(-4, Math.min(4, gradient));
|
|
49
|
+
let alpha = learningRate;
|
|
50
|
+
for (let epoch = 0; epoch < epochs; epoch++) {
|
|
51
|
+
for (let edge = 0; edge < weight.length; edge++) {
|
|
52
|
+
if (nextPositive[edge] > epoch)
|
|
53
|
+
continue;
|
|
54
|
+
const u = source[edge] * 2, v = target[edge] * 2;
|
|
55
|
+
let dx = coordinates[u] - coordinates[v], dy = coordinates[u + 1] - coordinates[v + 1];
|
|
56
|
+
let squared = dx * dx + dy * dy;
|
|
57
|
+
if (squared > 0) {
|
|
58
|
+
const attraction = -2 * a * b * Math.pow(squared, b - 1) /
|
|
59
|
+
(1 + a * Math.pow(squared, b));
|
|
60
|
+
const gx = clip(attraction * dx) * alpha, gy = clip(attraction * dy) * alpha;
|
|
61
|
+
coordinates[u] += gx;
|
|
62
|
+
coordinates[u + 1] += gy;
|
|
63
|
+
coordinates[v] -= gx;
|
|
64
|
+
coordinates[v + 1] -= gy;
|
|
65
|
+
}
|
|
66
|
+
nextPositive[edge] += period[edge];
|
|
67
|
+
const negativePeriod = period[edge] / negativeSamples;
|
|
68
|
+
const draws = Math.floor((epoch - nextNegative[edge]) / negativePeriod);
|
|
69
|
+
for (let draw = 0; draw < draws; draw++) {
|
|
70
|
+
const other = Math.floor(random() * count) * 2;
|
|
71
|
+
dx = coordinates[u] - coordinates[other];
|
|
72
|
+
dy = coordinates[u + 1] - coordinates[other + 1];
|
|
73
|
+
squared = dx * dx + dy * dy;
|
|
74
|
+
// Coincident points get zero repulsive gradient, as in current Python
|
|
75
|
+
// UMAP; this intentionally differs from UMAP-JS's constant +4 fallback.
|
|
76
|
+
if (squared > 0) {
|
|
77
|
+
const repulsion = 2 * b /
|
|
78
|
+
((0.001 + squared) * (1 + a * Math.pow(squared, b)));
|
|
79
|
+
coordinates[u] += clip(repulsion * dx) * alpha;
|
|
80
|
+
coordinates[u + 1] += clip(repulsion * dy) * alpha;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
nextNegative[edge] += draws * negativePeriod;
|
|
84
|
+
}
|
|
85
|
+
alpha = learningRate * (1 - epoch / epochs);
|
|
86
|
+
}
|
|
87
|
+
for (const value of coordinates) {
|
|
88
|
+
if (!Number.isFinite(value)) {
|
|
89
|
+
throw new Error("UMAP optimization produced a non-finite coordinate.");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return coordinates;
|
|
93
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default function prepareUmapLayoutOptions(options?: {
|
|
2
|
+
epochs?: number;
|
|
3
|
+
seed?: number;
|
|
4
|
+
minDistance?: number;
|
|
5
|
+
learningRate?: number;
|
|
6
|
+
negativeSamples?: number;
|
|
7
|
+
}): {
|
|
8
|
+
epochs: number;
|
|
9
|
+
seed: number;
|
|
10
|
+
learningRate: number;
|
|
11
|
+
negativeSamples: number;
|
|
12
|
+
curve: [number, number];
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=prepareUmapLayoutOptions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepareUmapLayoutOptions.d.ts","sourceRoot":"","sources":["../../src/helpers/prepareUmapLayoutOptions.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,wBAAwB,CAC9C,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CACrB;;;;;;EA2BP"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fitUmapCurve from "./fitUmapCurve.js";
|
|
2
|
+
// Resolve and validate settings before database work, fitting the curve once.
|
|
3
|
+
export default function prepareUmapLayoutOptions(options = {}) {
|
|
4
|
+
const { epochs = 200, seed = 42, minDistance = 0.1, learningRate = 1, negativeSamples = 5, } = options;
|
|
5
|
+
for (const [key, value] of Object.entries({ epochs, negativeSamples })) {
|
|
6
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
7
|
+
throw new Error(`${key} must be a positive safe integer.`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) {
|
|
11
|
+
throw new Error("seed must be an integer between 0 and 4294967295.");
|
|
12
|
+
}
|
|
13
|
+
if (!Number.isFinite(learningRate) || learningRate <= 0) {
|
|
14
|
+
throw new Error("learningRate must be finite and positive.");
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
epochs,
|
|
18
|
+
seed,
|
|
19
|
+
learningRate,
|
|
20
|
+
negativeSamples,
|
|
21
|
+
curve: fitUmapCurve(minDistance),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readScalarNumber.d.ts","sourceRoot":"","sources":["../../src/helpers/readScalarNumber.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAGzD,wBAA8B,gBAAgB,CAC5C,UAAU,EAAE,gBAAgB,EAC5B,GAAG,EAAE,MAAM,mBAGZ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"umapRandom.d.ts","sourceRoot":"","sources":["../../src/helpers/umapRandom.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,IAAI,EAAE,MAAM,gBAQ9C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Mulberry32: separate initialization and sampling streams, stable per seed.
|
|
2
|
+
export default function umapRandom(seed) {
|
|
3
|
+
let state = seed >>> 0;
|
|
4
|
+
return () => {
|
|
5
|
+
state = (state + 0x6D2B79F5) | 0;
|
|
6
|
+
let value = Math.imul(state ^ state >>> 15, 1 | state);
|
|
7
|
+
value ^= value + Math.imul(value ^ value >>> 7, 61 | value);
|
|
8
|
+
return ((value ^ value >>> 14) >>> 0) / 4294967296;
|
|
9
|
+
};
|
|
10
|
+
}
|
package/esm/methods/cache.js
CHANGED
|
@@ -7,7 +7,7 @@ import serializeCacheInputs, { getCacheInputLabel, } from "../helpers/serializeC
|
|
|
7
7
|
import { createTableGenerationId, peekTableGeneration, restoreTableGeneration, } from "../helpers/tableGeneration.js";
|
|
8
8
|
import { loadCacheDatabase, writeCacheDatabase, } from "../helpers/cacheDatabase.js";
|
|
9
9
|
import { assertCacheTableMutation, cacheTableDependenciesMatch, captureCacheTableDependencies, changedCacheTableDependenciesMessage, recordCacheTableAccess, unchangedCacheTableDependenciesMessage, } from "../helpers/cacheTableDependencies.js";
|
|
10
|
-
const CACHE_FORMAT_VERSION = "duckdb-
|
|
10
|
+
const CACHE_FORMAT_VERSION = "duckdb-v5";
|
|
11
11
|
export default async function cache(table, compute, options = {}) {
|
|
12
12
|
// If cache() is itself called from another cache computation, the outer
|
|
13
13
|
// computation depends on the table produced here.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadArray.d.ts","sourceRoot":"","sources":["../../src/methods/loadArray.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"loadArray.d.ts","sourceRoot":"","sources":["../../src/methods/loadArray.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,WAAW,EACjB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAKvD,MAAM,CAAC,OAAO,UAAU,SAAS,CAC/B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EAAE,EAClC,OAAO,GAAE,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAM,QAiBnE;AAED,KAAK,aAAa,GAAG;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAgB,YAAY,CAC1B,IAAI,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,EAAE,EAClC,WAAW,GAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,EAC3C,0BAA0B,UAAQ,GACjC,aAAa,CAsHf;AAED,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,WAAW,EACxB,QAAQ,EAAE,aAAa,iBA+DxB"}
|
package/esm/methods/loadArray.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { arrayValue,
|
|
1
|
+
import { arrayValue, DuckDBDateValue, DuckDBTimestampTZValue, DuckDBTimestampValue, DuckDBTimeValue, } from "@duckdb/node-api";
|
|
2
|
+
import appendColumnBatches from "../helpers/appendColumnBatches.js";
|
|
2
3
|
import prepareGeometry from "../helpers/prepareGeometry.js";
|
|
3
4
|
import prepareJSON from "../helpers/prepareJSON.js";
|
|
4
5
|
import geometryFromJSON from "../helpers/geometryFromJSON.js";
|
|
@@ -136,25 +137,7 @@ export async function executePreparedArray(simpleTable, prepared) {
|
|
|
136
137
|
}
|
|
137
138
|
try {
|
|
138
139
|
await simpleTable.sdb.customQuery(`CREATE ${needsConversion ? "TEMP" : "OR REPLACE"} TABLE ${quoteIdentifier(staged)}(${keys.map((key, i) => `${quoteIdentifier(key)} ${storageTypes[i]}`).join(", ")})`);
|
|
139
|
-
|
|
140
|
-
.createAppender(staged);
|
|
141
|
-
try {
|
|
142
|
-
const duckDBTypes = storageTypes.map((d) => parseDuckDBType(d));
|
|
143
|
-
// The maximum capacity of a DuckDB data chunk is 2048 rows.
|
|
144
|
-
const chunkSize = 2000;
|
|
145
|
-
for (let start = 0; start < rowCount; start += chunkSize) {
|
|
146
|
-
const end = Math.min(start + chunkSize, rowCount);
|
|
147
|
-
const dataChunk = DuckDBDataChunk.create(duckDBTypes, end - start);
|
|
148
|
-
for (let i = 0; i < keys.length; i++) {
|
|
149
|
-
dataChunk.setColumnValues(i, columnsData[i].slice(start, end));
|
|
150
|
-
}
|
|
151
|
-
appender.appendDataChunk(dataChunk);
|
|
152
|
-
}
|
|
153
|
-
appender.flushSync();
|
|
154
|
-
}
|
|
155
|
-
finally {
|
|
156
|
-
appender.closeSync();
|
|
157
|
-
}
|
|
140
|
+
await appendColumnBatches(simpleTable.connection, staged, storageTypes.map((type) => parseDuckDBType(type)), rowCount, (column, start, end) => columnsData[column].slice(start, end));
|
|
158
141
|
if (!needsConversion)
|
|
159
142
|
return;
|
|
160
143
|
// CTAS is atomic: conversion must finish before the destination is replaced.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"umap.d.ts","sourceRoot":"","sources":["../../src/methods/umap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC;AAavD,KAAK,OAAO,GAAG,WAAW,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE/D,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,KAAK,EAAE,WAAW,EAClB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,OAAY,QAStB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import queueOp from "../helpers/queueOp.js";
|
|
2
|
+
import quoteIdentifier from "../helpers/quoteIdentifier.js";
|
|
3
|
+
import queryDB from "../helpers/queryDB.js";
|
|
4
|
+
import mergeOptions from "../helpers/mergeOptions.js";
|
|
5
|
+
import buildUmapGraph from "../helpers/buildUmapGraph.js";
|
|
6
|
+
import prepareUmapLayoutOptions from "../helpers/prepareUmapLayoutOptions.js";
|
|
7
|
+
import readScalarNumber from "../helpers/readScalarNumber.js";
|
|
8
|
+
import optimizeUmapLayout from "../helpers/optimizeUmapLayout.js";
|
|
9
|
+
import initialUmapCoordinates from "../helpers/initialUmapCoordinates.js";
|
|
10
|
+
import appendColumnBatches from "../helpers/appendColumnBatches.js";
|
|
11
|
+
import { DOUBLE, INTEGER } from "@duckdb/node-api";
|
|
12
|
+
export default function umap(table, column, options = {}) {
|
|
13
|
+
const settings = structuredClone(options);
|
|
14
|
+
queueOp(table, {
|
|
15
|
+
kind: "barrier",
|
|
16
|
+
method: "umap()",
|
|
17
|
+
parameters: { column, options: settings },
|
|
18
|
+
execute: () => execute(table, column, settings),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function execute(table, column, options) {
|
|
22
|
+
const { neighbors = 15, metric = "euclidean" } = options;
|
|
23
|
+
if (!Number.isSafeInteger(neighbors) || neighbors < 2) {
|
|
24
|
+
throw new Error("neighbors must be a safe integer of at least 2.");
|
|
25
|
+
}
|
|
26
|
+
const layoutOptions = prepareUmapLayoutOptions(options);
|
|
27
|
+
if (!["euclidean", "cosine"].includes(metric)) {
|
|
28
|
+
throw new Error("metric must be euclidean or cosine.");
|
|
29
|
+
}
|
|
30
|
+
const types = await table.getTypes();
|
|
31
|
+
const columns = Object.keys(types);
|
|
32
|
+
const resolve = (name) => {
|
|
33
|
+
const found = columns.find((c) => c.toLowerCase() === name.toLowerCase());
|
|
34
|
+
if (found === undefined)
|
|
35
|
+
throw new Error(`Column ${name} does not exist.`);
|
|
36
|
+
return found;
|
|
37
|
+
};
|
|
38
|
+
const vector = resolve(column);
|
|
39
|
+
if (columns.some((c) => ["umapx", "umapy"].includes(c.toLowerCase()))) {
|
|
40
|
+
throw new Error("UMAP output columns umapX and umapY must not already exist.");
|
|
41
|
+
}
|
|
42
|
+
if (!/^(FLOAT|DOUBLE|TINYINT|SMALLINT|INTEGER|BIGINT|HUGEINT|UTINYINT|USMALLINT|UINTEGER|UBIGINT|UHUGEINT|DECIMAL\(\d+,\d+\))\[\d*\]$/
|
|
43
|
+
.test(types[vector])) {
|
|
44
|
+
throw new Error("UMAP requires a one-dimensional numeric LIST or ARRAY column.");
|
|
45
|
+
}
|
|
46
|
+
const connection = table.connection;
|
|
47
|
+
const [sourceOid, sourceTemporary] = (await connection.runAndReadAll(`SELECT table_oid,temporary FROM duckdb_tables()
|
|
48
|
+
WHERE lower(table_name)=lower($1) AND schema_name=current_schema()
|
|
49
|
+
AND database_name IN (current_database(),'temp')
|
|
50
|
+
ORDER BY temporary DESC LIMIT 1`, [table.name])).getRowsJS()[0] ?? [];
|
|
51
|
+
if (typeof sourceOid !== "number" && typeof sourceOid !== "bigint") {
|
|
52
|
+
throw new Error("UMAP requires a materialized source table.");
|
|
53
|
+
}
|
|
54
|
+
const q = quoteIdentifier;
|
|
55
|
+
const suffix = crypto.randomUUID().replaceAll("-", "");
|
|
56
|
+
const name = (part) => `__sda_umap_${suffix}_${part}`;
|
|
57
|
+
const ordinal = q(name("ordinal"));
|
|
58
|
+
const source = q(table.name), snapshot = q(name("snapshot"));
|
|
59
|
+
const layoutName = name("layout"), layout = q(layoutName);
|
|
60
|
+
const names = {
|
|
61
|
+
rows: q(name("rows")),
|
|
62
|
+
knn: q(name("knn")),
|
|
63
|
+
search: q(name("search")),
|
|
64
|
+
hnsw: q(name("hnsw")),
|
|
65
|
+
candidates: q(name("candidates")),
|
|
66
|
+
scales: q(name("scales")),
|
|
67
|
+
directed: q(name("directed")),
|
|
68
|
+
graph: q(name("graph")),
|
|
69
|
+
};
|
|
70
|
+
const scratch = [
|
|
71
|
+
snapshot,
|
|
72
|
+
layout,
|
|
73
|
+
...Object.entries(names)
|
|
74
|
+
.filter(([key]) => key !== "hnsw").map(([, value]) => value),
|
|
75
|
+
];
|
|
76
|
+
try {
|
|
77
|
+
// Retain payloads in native DuckDB types. A private ordinal distinguishes
|
|
78
|
+
// duplicate rows and is independent of any user column named rowid.
|
|
79
|
+
await connection.run(`CREATE TEMP TABLE ${snapshot} AS
|
|
80
|
+
SELECT *, row_number() OVER () - 1 AS ${ordinal} FROM ${source}`);
|
|
81
|
+
const count = await readScalarNumber(connection, `SELECT count(*) FROM ${snapshot}`);
|
|
82
|
+
if (count < 3 || count > 0x7fffffff) {
|
|
83
|
+
throw new Error("UMAP requires between 3 and 2147483647 rows.");
|
|
84
|
+
}
|
|
85
|
+
if (await readScalarNumber(connection, `SELECT count(*) FROM ${snapshot}
|
|
86
|
+
WHERE ${q(vector)} IS NULL OR len(${q(vector)})=0
|
|
87
|
+
OR list_count(${q(vector)}) != len(${q(vector)})
|
|
88
|
+
OR NOT list_bool_and(list_transform(${q(vector)}, x -> isfinite(x::DOUBLE)))`)) {
|
|
89
|
+
throw new Error("Vectors must be nonempty, non-null and contain finite numbers only.");
|
|
90
|
+
}
|
|
91
|
+
if (await readScalarNumber(connection, `SELECT count(DISTINCT len(${q(vector)})) FROM ${snapshot}`) !== 1) {
|
|
92
|
+
throw new Error("Vectors must have equal dimensions.");
|
|
93
|
+
}
|
|
94
|
+
const dimensions = await readScalarNumber(connection, `SELECT len(${q(vector)}) FROM ${snapshot} LIMIT 1`);
|
|
95
|
+
await connection.run(`CREATE TEMP TABLE ${names.rows} AS
|
|
96
|
+
SELECT (row_number() OVER (ORDER BY ${ordinal})-1)::INTEGER AS vertex,
|
|
97
|
+
${ordinal} AS ordinal, ${q(vector)}::DOUBLE[${dimensions}] AS vec FROM ${snapshot}`);
|
|
98
|
+
if (await readScalarNumber(connection, `SELECT count(*) FROM ${names.rows}
|
|
99
|
+
WHERE NOT isfinite(array_inner_product(vec,vec)) OR array_inner_product(vec,vec)>1e300
|
|
100
|
+
${metric === "cosine" ? "OR array_inner_product(vec,vec)=0" : ""}`)) {
|
|
101
|
+
throw new Error("Vector norms overflow, or cosine distance received a zero vector.");
|
|
102
|
+
}
|
|
103
|
+
const graph = await buildUmapGraph(connection, {
|
|
104
|
+
count,
|
|
105
|
+
dimensions,
|
|
106
|
+
neighbors: Math.min(neighbors, count - 1),
|
|
107
|
+
}, {
|
|
108
|
+
metric,
|
|
109
|
+
search: count <= 1000 ? "exact" : "hnsw",
|
|
110
|
+
}, names);
|
|
111
|
+
// Release index and vector scratch before allocating optimizer state.
|
|
112
|
+
for (const relation of [
|
|
113
|
+
names.search,
|
|
114
|
+
names.candidates,
|
|
115
|
+
names.knn,
|
|
116
|
+
names.directed,
|
|
117
|
+
names.scales,
|
|
118
|
+
names.graph,
|
|
119
|
+
]) {
|
|
120
|
+
await connection.run(`DROP TABLE IF EXISTS ${relation}`);
|
|
121
|
+
}
|
|
122
|
+
await connection.run(`ALTER TABLE ${names.rows} DROP COLUMN vec`);
|
|
123
|
+
const coordinates = optimizeUmapLayout(graph, initialUmapCoordinates(count, layoutOptions.seed), layoutOptions);
|
|
124
|
+
await connection.run(`CREATE TEMP TABLE ${layout} (vertex INTEGER,x DOUBLE,y DOUBLE)`);
|
|
125
|
+
await appendColumnBatches(connection, layoutName, [INTEGER, DOUBLE, DOUBLE], count, (column, start, end) => Array.from({ length: end - start }, (_, row) => column === 0
|
|
126
|
+
? start + row
|
|
127
|
+
: coordinates[2 * (start + row) + column - 1]));
|
|
128
|
+
// Publish in one statement: failed fits never add partial columns. Existing
|
|
129
|
+
// native indexes must be restored because CREATE OR REPLACE drops them.
|
|
130
|
+
const indexes = (await connection.runAndReadAll("SELECT sql FROM duckdb_indexes() WHERE table_oid=$1", [sourceOid])).getRowsJS().map((row) => String(row[0]));
|
|
131
|
+
await connection.run("BEGIN TRANSACTION");
|
|
132
|
+
try {
|
|
133
|
+
await queryDB(table, `CREATE OR REPLACE ${sourceTemporary ? "TEMP " : ""}TABLE ${source} AS
|
|
134
|
+
SELECT ${columns.map((c) => `s.${q(c)}`).join(",")}, l.x AS "umapX", l.y AS "umapY"
|
|
135
|
+
FROM ${snapshot} s JOIN ${names.rows} r ON s.${ordinal}=r.ordinal
|
|
136
|
+
JOIN ${layout} l ON r.vertex=l.vertex ORDER BY s.${ordinal}`, mergeOptions(table, {
|
|
137
|
+
table: table.name,
|
|
138
|
+
method: "umap()",
|
|
139
|
+
parameters: { column, options },
|
|
140
|
+
noClean: true,
|
|
141
|
+
}));
|
|
142
|
+
for (const sql of indexes)
|
|
143
|
+
await connection.run(sql);
|
|
144
|
+
await connection.run("COMMIT");
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
await connection.run("ROLLBACK");
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
for (const relation of scratch) {
|
|
153
|
+
await connection.run(`DROP TABLE IF EXISTS ${relation}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -4,7 +4,7 @@ import quoteIdentifier from "../helpers/quoteIdentifier.js";
|
|
|
4
4
|
import { retainRegisteredTables } from "../helpers/tableRegistry.js";
|
|
5
5
|
import queueOp from "../helpers/queueOp.js";
|
|
6
6
|
import { prepareArray } from "./loadArray.js";
|
|
7
|
-
import
|
|
7
|
+
import appendColumnBatches from "../helpers/appendColumnBatches.js";
|
|
8
8
|
import readMutationRows from "../helpers/readMutationRows.js";
|
|
9
9
|
import parseDuckDBType from "../helpers/parseDuckDBType.js";
|
|
10
10
|
export default function updateWithJS(simpleTable, dataModifier, options = {}) {
|
|
@@ -133,26 +133,14 @@ async function executeUpdateWithJS(simpleTable, dataModifier, options) {
|
|
|
133
133
|
parseDuckDBType(added.types[newKeys.indexOf(key)]));
|
|
134
134
|
await simpleTable.sdb.customQuery(`CREATE OR REPLACE TABLE ${quoteIdentifier(scratch.name)} (${keys.map((key, i) => `${quoteIdentifier(key)} ${columnTypes[i]}`)
|
|
135
135
|
.join(", ")})`);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
? geometryData.get(key).slice(start, end)
|
|
145
|
-
: source.types.has(key)
|
|
146
|
-
? modified.slice(start, end).map((row) => source.toNative(row[key], columnTypes[i]))
|
|
147
|
-
: added.columnsData[newKeys.indexOf(key)].slice(start, end));
|
|
148
|
-
}
|
|
149
|
-
appender.appendDataChunk(chunk);
|
|
150
|
-
}
|
|
151
|
-
appender.flushSync();
|
|
152
|
-
}
|
|
153
|
-
finally {
|
|
154
|
-
appender.closeSync();
|
|
155
|
-
}
|
|
136
|
+
await appendColumnBatches(simpleTable.connection, scratch.name, columnTypes, modified.length, (column, start, end) => {
|
|
137
|
+
const key = keys[column];
|
|
138
|
+
return outputGeometry.includes(key)
|
|
139
|
+
? geometryData.get(key).slice(start, end)
|
|
140
|
+
: source.types.has(key)
|
|
141
|
+
? modified.slice(start, end).map((row) => source.toNative(row[key], columnTypes[column]))
|
|
142
|
+
: added.columnsData[newKeys.indexOf(key)].slice(start, end);
|
|
143
|
+
});
|
|
156
144
|
const projection = keys.map((key) => outputGeometry.includes(key)
|
|
157
145
|
? `${geometryFromJSON(quoteIdentifier(key))} AS ${quoteIdentifier(key)}`
|
|
158
146
|
: outputJSON.includes(key)
|
package/llm.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# The Simple Data Analysis (Core) Library
|
|
2
2
|
|
|
3
3
|
- Package: `@nshiab/simple-data-analysis-core`
|
|
4
|
-
- Version: `2.0.
|
|
4
|
+
- Version: `2.0.9`
|
|
5
5
|
|
|
6
6
|
To install the library with Deno, use:
|
|
7
7
|
|
|
@@ -1451,6 +1451,85 @@ await table.createVssIndex("embedding_column", {
|
|
|
1451
1451
|
}).log();
|
|
1452
1452
|
```
|
|
1453
1453
|
|
|
1454
|
+
#### `umap`
|
|
1455
|
+
|
|
1456
|
+
Reduces numeric vectors, such as embeddings, to a two-dimensional UMAP
|
|
1457
|
+
projection. The resulting coordinates are added as `umapX` and `umapY`, while
|
|
1458
|
+
all existing columns (including the source vector column), their values and
|
|
1459
|
+
types, and the input row order are preserved. DuckDB computes neighbors and the
|
|
1460
|
+
fuzzy graph; TypeScript optimizes the coordinates without copying the input
|
|
1461
|
+
vectors into JavaScript. Neighbor search is selected automatically.
|
|
1462
|
+
|
|
1463
|
+
The defaults are a starting point for exploration. To adjust the projection:
|
|
1464
|
+
|
|
1465
|
+
- `neighbors` (default `15`): How many nearby points influence the layout.
|
|
1466
|
+
Smaller values emphasize local detail but can fragment groups. Larger values
|
|
1467
|
+
emphasize broader structure, can hide local detail, and generally require more
|
|
1468
|
+
time and memory.
|
|
1469
|
+
- `metric` (default `"euclidean"`): How similarity is measured. Euclidean
|
|
1470
|
+
compares distance, including differences in vector magnitude. Cosine compares
|
|
1471
|
+
direction, ignoring magnitude. Choose according to what makes vectors similar
|
|
1472
|
+
in your data; neither is universally better.
|
|
1473
|
+
- `epochs` (default `200`): How many passes refine the coordinates. Fewer passes
|
|
1474
|
+
finish sooner but may leave the layout unfinished. More passes allow further
|
|
1475
|
+
refinement and take longer, with diminishing returns.
|
|
1476
|
+
- `minDistance` (default `0.1`): How tightly points can group in the projection.
|
|
1477
|
+
Smaller values allow tighter clumps; larger values spread points out. This
|
|
1478
|
+
changes the layout's appearance, not an accuracy level or the number of
|
|
1479
|
+
optimization passes.
|
|
1480
|
+
- `learningRate` (default `1`): The initial size of coordinate adjustments.
|
|
1481
|
+
Smaller values make gentler adjustments and may need more epochs. Larger
|
|
1482
|
+
values make bigger adjustments but can overshoot useful positions. This does
|
|
1483
|
+
not change the number of passes.
|
|
1484
|
+
- `negativeSamples` (default `5`): How many random points are sampled for
|
|
1485
|
+
repulsion per attractive update. Smaller values reduce work and repulsion;
|
|
1486
|
+
larger values increase both, tending to separate unrelated points more
|
|
1487
|
+
strongly. More samples do not guarantee a better projection.
|
|
1488
|
+
|
|
1489
|
+
##### Signature
|
|
1490
|
+
|
|
1491
|
+
```typescript
|
|
1492
|
+
umap(column: string, options?: { neighbors?: number; metric?: "euclidean" | "cosine"; epochs?: number; seed?: number; minDistance?: number; learningRate?: number; negativeSamples?: number }): this;
|
|
1493
|
+
```
|
|
1494
|
+
|
|
1495
|
+
##### Parameters
|
|
1496
|
+
|
|
1497
|
+
- **`column`**: The column containing numeric vector embeddings.
|
|
1498
|
+
- **`options`**: Optional projection settings.
|
|
1499
|
+
- **`options.neighbors`**: Neighborhood size, including the point itself.
|
|
1500
|
+
Integer of at least 2, clamped to row count minus one. Defaults to 15.
|
|
1501
|
+
- **`options.metric`**: Input distance metric: "euclidean" or "cosine". Defaults
|
|
1502
|
+
to "euclidean".
|
|
1503
|
+
- **`options.epochs`**: Integer number of refinement passes, at least 1.
|
|
1504
|
+
Defaults to 200.
|
|
1505
|
+
- **`options.seed`**: Integer used to initialize random choices. Defaults to 42.
|
|
1506
|
+
- **`options.minDistance`**: Grouping distance parameter between 0 and 1.
|
|
1507
|
+
Defaults to 0.1.
|
|
1508
|
+
- **`options.learningRate`**: Finite, positive initial learning rate. Defaults
|
|
1509
|
+
to 1.
|
|
1510
|
+
- **`options.negativeSamples`**: Integer number of samples used to separate
|
|
1511
|
+
unrelated points, at least 1. Defaults to 5.
|
|
1512
|
+
|
|
1513
|
+
##### Returns
|
|
1514
|
+
|
|
1515
|
+
The table, so methods can be chained.
|
|
1516
|
+
|
|
1517
|
+
##### Examples
|
|
1518
|
+
|
|
1519
|
+
```ts
|
|
1520
|
+
await table.umap("embedding", {
|
|
1521
|
+
metric: "cosine",
|
|
1522
|
+
seed: 42,
|
|
1523
|
+
}).log();
|
|
1524
|
+
```
|
|
1525
|
+
|
|
1526
|
+
```ts
|
|
1527
|
+
await table.umap("embedding", {
|
|
1528
|
+
neighbors: 30,
|
|
1529
|
+
minDistance: 0.25,
|
|
1530
|
+
}).selectColumns(["label", "umapX", "umapY"]).log();
|
|
1531
|
+
```
|
|
1532
|
+
|
|
1454
1533
|
#### `bm25`
|
|
1455
1534
|
|
|
1456
1535
|
Searches a text column using DuckDB's BM25 ranking function, which scores
|
package/llms.txt
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> Lightweight DuckDB-powered TypeScript library for tabular, SQL, CSV, Parquet, and geospatial data analysis on Deno, Node.js, and Bun.
|
|
4
4
|
|
|
5
5
|
- Package: `@nshiab/simple-data-analysis-core`
|
|
6
|
-
- Version: `2.0.
|
|
6
|
+
- Version: `2.0.9`
|
|
7
7
|
|
|
8
8
|
Install with Deno: `deno add jsr:@nshiab/simple-data-analysis-core`
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nshiab/simple-data-analysis-core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Lightweight DuckDB-powered TypeScript library for tabular, SQL, CSV, Parquet, and geospatial data analysis on Deno, Node.js, and Bun.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"data-analysis",
|
|
@@ -757,6 +757,78 @@ export default class SimpleTable extends Simple {
|
|
|
757
757
|
efSearch?: number;
|
|
758
758
|
M?: number;
|
|
759
759
|
}): this;
|
|
760
|
+
/**
|
|
761
|
+
* Reduces numeric vectors, such as embeddings, to a two-dimensional UMAP
|
|
762
|
+
* projection. The resulting coordinates are added as `umapX` and `umapY`,
|
|
763
|
+
* while all existing columns (including the source vector column), their
|
|
764
|
+
* values and types, and the input row order are preserved. DuckDB computes
|
|
765
|
+
* neighbors and the fuzzy graph; TypeScript optimizes the coordinates without
|
|
766
|
+
* copying the input vectors into JavaScript. Neighbor search is selected
|
|
767
|
+
* automatically.
|
|
768
|
+
*
|
|
769
|
+
* The defaults are a starting point for exploration. To adjust the projection:
|
|
770
|
+
*
|
|
771
|
+
* - `neighbors` (default `15`): How many nearby points influence the layout.
|
|
772
|
+
* Smaller values emphasize local detail but can fragment groups. Larger
|
|
773
|
+
* values emphasize broader structure, can hide local detail, and generally
|
|
774
|
+
* require more time and memory.
|
|
775
|
+
* - `metric` (default `"euclidean"`): How similarity is measured. Euclidean
|
|
776
|
+
* compares distance, including differences in vector magnitude. Cosine
|
|
777
|
+
* compares direction, ignoring magnitude. Choose according to what makes
|
|
778
|
+
* vectors similar in your data; neither is universally better.
|
|
779
|
+
* - `epochs` (default `200`): How many passes refine the coordinates. Fewer
|
|
780
|
+
* passes finish sooner but may leave the layout unfinished. More passes
|
|
781
|
+
* allow further refinement and take longer, with diminishing returns.
|
|
782
|
+
* - `minDistance` (default `0.1`): How tightly points can group in the
|
|
783
|
+
* projection. Smaller values allow tighter clumps; larger values spread
|
|
784
|
+
* points out. This changes the layout's appearance, not an accuracy level
|
|
785
|
+
* or the number of optimization passes.
|
|
786
|
+
* - `learningRate` (default `1`): The initial size of coordinate adjustments.
|
|
787
|
+
* Smaller values make gentler adjustments and may need more epochs. Larger
|
|
788
|
+
* values make bigger adjustments but can overshoot useful positions. This
|
|
789
|
+
* does not change the number of passes.
|
|
790
|
+
* - `negativeSamples` (default `5`): How many random points are sampled for
|
|
791
|
+
* repulsion per attractive update. Smaller values reduce work and
|
|
792
|
+
* repulsion; larger values increase both, tending to separate unrelated
|
|
793
|
+
* points more strongly. More samples do not guarantee a better projection.
|
|
794
|
+
*
|
|
795
|
+
* @param column - The column containing numeric vector embeddings.
|
|
796
|
+
* @param options - Optional projection settings.
|
|
797
|
+
* @param options.neighbors - Neighborhood size, including the point itself. Integer of at least 2, clamped to row count minus one. Defaults to 15.
|
|
798
|
+
* @param options.metric - Input distance metric: "euclidean" or "cosine". Defaults to "euclidean".
|
|
799
|
+
* @param options.epochs - Integer number of refinement passes, at least 1. Defaults to 200.
|
|
800
|
+
* @param options.seed - Integer used to initialize random choices. Defaults to 42.
|
|
801
|
+
* @param options.minDistance - Grouping distance parameter between 0 and 1. Defaults to 0.1.
|
|
802
|
+
* @param options.learningRate - Finite, positive initial learning rate. Defaults to 1.
|
|
803
|
+
* @param options.negativeSamples - Integer number of samples used to separate unrelated points, at least 1. Defaults to 5.
|
|
804
|
+
* @returns The table, so methods can be chained.
|
|
805
|
+
* @category Vector Search
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* ```ts
|
|
809
|
+
* await table.umap("embedding", {
|
|
810
|
+
* metric: "cosine",
|
|
811
|
+
* seed: 42,
|
|
812
|
+
* }).log();
|
|
813
|
+
* ```
|
|
814
|
+
*
|
|
815
|
+
* @example
|
|
816
|
+
* ```ts
|
|
817
|
+
* await table.umap("embedding", {
|
|
818
|
+
* neighbors: 30,
|
|
819
|
+
* minDistance: 0.25,
|
|
820
|
+
* }).selectColumns(["label", "umapX", "umapY"]).log();
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
umap(column: string, options?: {
|
|
824
|
+
neighbors?: number;
|
|
825
|
+
metric?: "euclidean" | "cosine";
|
|
826
|
+
epochs?: number;
|
|
827
|
+
seed?: number;
|
|
828
|
+
minDistance?: number;
|
|
829
|
+
learningRate?: number;
|
|
830
|
+
negativeSamples?: number;
|
|
831
|
+
}): this;
|
|
760
832
|
/**
|
|
761
833
|
* Searches a text column using DuckDB's BM25 ranking function, which scores
|
|
762
834
|
* matches using factors including term frequency and document length.
|