twokeys 2.2.0 → 3.0.1
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/README.md +385 -422
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +150 -105
- package/dist/index.d.ts +150 -105
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +60 -59
package/README.md
CHANGED
|
@@ -1,422 +1,385 @@
|
|
|
1
|
-
# Twokeys
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
[](https://github.com/buley/twokeys/actions/workflows/ci.yml)
|
|
6
|
-
[](https://www.npmjs.com/package/twokeys)
|
|
7
|
-
[](https://opensource.org/licenses/MIT)
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
//
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
|
284
|
-
|
|
285
|
-
| `
|
|
286
|
-
| `
|
|
287
|
-
| `
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
### Data Transformation
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
const series = new Series({ data: skewedData });
|
|
391
|
-
|
|
392
|
-
// Try different transforms to normalize
|
|
393
|
-
const logTransformed = series.logs();
|
|
394
|
-
const sqrtTransformed = series.roots();
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
## Development
|
|
398
|
-
|
|
399
|
-
```bash
|
|
400
|
-
# Install dependencies
|
|
401
|
-
bun install
|
|
402
|
-
|
|
403
|
-
# Run tests
|
|
404
|
-
bun test
|
|
405
|
-
|
|
406
|
-
# Run tests with coverage
|
|
407
|
-
bun test --coverage
|
|
408
|
-
|
|
409
|
-
# Build all packages
|
|
410
|
-
bun run build
|
|
411
|
-
|
|
412
|
-
# Run benchmark
|
|
413
|
-
bun run bench.ts
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
## License
|
|
417
|
-
|
|
418
|
-
MIT
|
|
419
|
-
|
|
420
|
-
---
|
|
421
|
-
|
|
422
|
-
*"The best thing about being a statistician is that you get to play in everyone's backyard."* — John Tukey
|
|
1
|
+
# Twokeys
|
|
2
|
+
|
|
3
|
+
> Exploratory data analysis for graphs, vectors, and series — named after **John Tukey**, the legendary statistician who pioneered EDA.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/buley/twokeys/actions/workflows/ci.yml)
|
|
6
|
+
[](https://www.npmjs.com/package/twokeys)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
## What Is This?
|
|
10
|
+
|
|
11
|
+
Tukey taught us that **looking at data** is just as important as modeling it. Twokeys applies that philosophy to three domains:
|
|
12
|
+
|
|
13
|
+
1. **Graph EDA** — Treat structural properties of graphs (degree distributions, clustering coefficients, assortativity) as data series that deserve full Tukey-style analysis
|
|
14
|
+
2. **Vector Distance & Similarity** — Cosine similarity, Mahalanobis distance, Jaccard similarity, L2 normalization, and more
|
|
15
|
+
3. **Multivariate Analysis** — Centroids, covariance matrices, correlation matrices, and Mahalanobis-based outlier detection via the `Points` class
|
|
16
|
+
4. **1D EDA** — The original `Series` class: medians, fences, letter values, stem-and-leaf plots, smoothing, and everything else Tukey invented
|
|
17
|
+
5. **Graph Algorithms** — Centrality, community detection, shortest paths, flow, clustering (k-means, hierarchical, DBSCAN), TSP approximation, and a GDS-style catalog
|
|
18
|
+
|
|
19
|
+
Zero dependencies. Pure TypeScript. Works everywhere.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install twokeys
|
|
25
|
+
# or
|
|
26
|
+
bun add twokeys
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Graph EDA
|
|
30
|
+
|
|
31
|
+
The core insight: graph structural properties ARE data series. Degree distributions get box plots. Clustering coefficients get outlier detection. Assortativity tells you whether your network is stratified.
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { graphEda, graphOutliers } from 'twokeys';
|
|
35
|
+
|
|
36
|
+
const nodes = ['alice', 'bob', 'carol', 'dave', 'eve'] as const;
|
|
37
|
+
const edges = [
|
|
38
|
+
{ from: 'alice', to: 'bob', weight: 1 },
|
|
39
|
+
{ from: 'alice', to: 'carol', weight: 1 },
|
|
40
|
+
{ from: 'bob', to: 'carol', weight: 1 },
|
|
41
|
+
{ from: 'carol', to: 'dave', weight: 1 },
|
|
42
|
+
{ from: 'dave', to: 'eve', weight: 1 },
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const summary = graphEda(nodes, edges);
|
|
46
|
+
|
|
47
|
+
// Every structural metric, analyzed as a Tukey Series:
|
|
48
|
+
summary.density; // edges / maxPossibleEdges
|
|
49
|
+
summary.degreeDistribution; // Full SeriesDescription with median, fences, outliers
|
|
50
|
+
summary.clusteringDistribution; // Clustering coefficients as EDA
|
|
51
|
+
summary.globalClusteringCoefficient;
|
|
52
|
+
summary.averagePathLength;
|
|
53
|
+
summary.diameter;
|
|
54
|
+
summary.reciprocity;
|
|
55
|
+
summary.degreeAssortativity; // Do hubs connect to hubs?
|
|
56
|
+
|
|
57
|
+
// Find structurally unusual nodes
|
|
58
|
+
const unusual = graphOutliers(nodes, edges, { method: 'combined' });
|
|
59
|
+
// [{ nodeId: 'eve', score: 2.1, reason: 'Low degree + low clustering' }]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Vector Distance & Similarity
|
|
63
|
+
|
|
64
|
+
Standalone functions for vector math. These are the workhorses that `Points`, graph algorithms, and consumers all use.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import {
|
|
68
|
+
cosineSimilarity,
|
|
69
|
+
euclideanDistance,
|
|
70
|
+
manhattanDistance,
|
|
71
|
+
mahalanobisDistance,
|
|
72
|
+
normalizeL2,
|
|
73
|
+
cosineSimilaritySparse,
|
|
74
|
+
jaccardSimilarity,
|
|
75
|
+
overlapCoefficient,
|
|
76
|
+
} from 'twokeys';
|
|
77
|
+
|
|
78
|
+
// Dense vectors
|
|
79
|
+
cosineSimilarity([1, 2, 3], [4, 5, 6]); // 0.974
|
|
80
|
+
euclideanDistance([0, 0], [3, 4]); // 5
|
|
81
|
+
manhattanDistance([0, 0], [3, 4]); // 7
|
|
82
|
+
normalizeL2([3, 4]); // [0.6, 0.8]
|
|
83
|
+
mahalanobisDistance([5, 5], [0, 0], [1, 1]); // 7.07
|
|
84
|
+
|
|
85
|
+
// Sparse vectors (Map<string, number>)
|
|
86
|
+
const a = new Map([['x', 1], ['y', 2]]);
|
|
87
|
+
const b = new Map([['y', 3], ['z', 4]]);
|
|
88
|
+
cosineSimilaritySparse(a, b); // similarity on shared keys
|
|
89
|
+
|
|
90
|
+
// Set similarity
|
|
91
|
+
jaccardSimilarity(new Set([1, 2, 3]), new Set([2, 3, 4])); // 0.5
|
|
92
|
+
overlapCoefficient(new Set([1, 2]), new Set([2, 3, 4])); // 0.5
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Multivariate Analysis (Points)
|
|
96
|
+
|
|
97
|
+
Full multivariate EDA: centroids, covariance, correlation, Mahalanobis outlier detection.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { Points } from 'twokeys';
|
|
101
|
+
|
|
102
|
+
const points = new Points({
|
|
103
|
+
data: [
|
|
104
|
+
[1, 2, 3],
|
|
105
|
+
[4, 5, 6],
|
|
106
|
+
[7, 8, 9],
|
|
107
|
+
[100, 100, 100], // outlier
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
points.centroid(); // [28, 28.75, 29.5]
|
|
112
|
+
points.variances(); // Per-dimension variance
|
|
113
|
+
points.standardDeviations(); // Per-dimension stddev
|
|
114
|
+
points.covarianceMatrix(); // Full covariance matrix
|
|
115
|
+
points.correlationMatrix(); // Pearson correlation matrix
|
|
116
|
+
|
|
117
|
+
// Mahalanobis distance — the multivariate equivalent of z-score
|
|
118
|
+
points.mahalanobis([50, 50, 50]); // Distance of a single point
|
|
119
|
+
points.mahalanobisAll(); // Distance for each stored point
|
|
120
|
+
|
|
121
|
+
// Tukey-style outlier detection for multivariate data
|
|
122
|
+
points.outliersByMahalanobis(); // [[100, 100, 100]]
|
|
123
|
+
|
|
124
|
+
// Normalization
|
|
125
|
+
points.normalizeL2(); // L2-normalize all points (returns new Points)
|
|
126
|
+
points.normalizeZscore(); // Z-score normalize per dimension
|
|
127
|
+
|
|
128
|
+
// Full description — each dimension analyzed as a Series
|
|
129
|
+
const desc = points.describe();
|
|
130
|
+
desc.centroid; // Mean point
|
|
131
|
+
desc.correlationMatrix; // Pearson correlations
|
|
132
|
+
desc.mahalanobisDistances; // Distance from centroid per point
|
|
133
|
+
desc.outlierCount; // How many outliers
|
|
134
|
+
desc.dimensionSummaries; // Each dimension as a full SeriesDescription
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 1D Exploratory Data Analysis (Series)
|
|
138
|
+
|
|
139
|
+
The original Tukey toolkit: everything you need to explore univariate data.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
import { Series } from 'twokeys';
|
|
143
|
+
|
|
144
|
+
const series = new Series({ data: [2, 4, 4, 4, 5, 5, 7, 9] });
|
|
145
|
+
|
|
146
|
+
// Summary statistics
|
|
147
|
+
series.mean(); // 5
|
|
148
|
+
series.median(); // { datum: 4.5, depth: 4.5 }
|
|
149
|
+
series.mode(); // { count: 3, data: [4] }
|
|
150
|
+
series.trimean(); // Tukey's trimean
|
|
151
|
+
|
|
152
|
+
// Dispersion
|
|
153
|
+
series.variance(); // Sample variance
|
|
154
|
+
series.stddev(); // Standard deviation
|
|
155
|
+
series.iqr(); // Interquartile range
|
|
156
|
+
series.skewness(); // Fisher-Pearson skewness
|
|
157
|
+
series.kurtosis(); // Excess kurtosis
|
|
158
|
+
|
|
159
|
+
// Outlier detection (Tukey fences)
|
|
160
|
+
series.fences(); // Inner fence boundaries (1.5 x IQR)
|
|
161
|
+
series.outliers(); // Values outside inner fences
|
|
162
|
+
series.outside(); // Values outside outer fences (3 x IQR)
|
|
163
|
+
|
|
164
|
+
// Time series
|
|
165
|
+
series.ema(0.3); // Exponential moving average
|
|
166
|
+
series.zscore(); // Z-score normalization
|
|
167
|
+
series.hanning(); // Hanning filter
|
|
168
|
+
series.smooth(); // Tukey's 3RSSH smoothing
|
|
169
|
+
series.rough(); // Residuals (original - smooth)
|
|
170
|
+
|
|
171
|
+
// Visualization
|
|
172
|
+
series.stemLeaf(); // Stem-and-leaf plot
|
|
173
|
+
series.letterValues(); // Extended quartiles (M, F, E, D, C, B, A...)
|
|
174
|
+
|
|
175
|
+
// Everything at once
|
|
176
|
+
series.describe();
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Graph Algorithms
|
|
180
|
+
|
|
181
|
+
Centrality, community detection, shortest paths, flow, clustering, and a GDS-style catalog.
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
import {
|
|
185
|
+
// Centrality
|
|
186
|
+
degreeCentrality,
|
|
187
|
+
closenessCentrality,
|
|
188
|
+
betweennessCentrality,
|
|
189
|
+
pageRank,
|
|
190
|
+
|
|
191
|
+
// Community detection
|
|
192
|
+
louvainCommunities,
|
|
193
|
+
labelPropagationCommunities,
|
|
194
|
+
|
|
195
|
+
// Similarity & link prediction
|
|
196
|
+
nodeSimilarity,
|
|
197
|
+
kNearestNeighbors,
|
|
198
|
+
linkPrediction,
|
|
199
|
+
|
|
200
|
+
// Paths & flow
|
|
201
|
+
shortestPath,
|
|
202
|
+
aStarShortestPath,
|
|
203
|
+
yenKShortestPaths,
|
|
204
|
+
allPairsShortestPaths,
|
|
205
|
+
maximumFlow,
|
|
206
|
+
minCostMaxFlow,
|
|
207
|
+
|
|
208
|
+
// Structure
|
|
209
|
+
topologicalSort,
|
|
210
|
+
stronglyConnectedComponents,
|
|
211
|
+
weaklyConnectedComponents,
|
|
212
|
+
minimumSpanningTree,
|
|
213
|
+
articulationPointsAndBridges,
|
|
214
|
+
analyzeGraph,
|
|
215
|
+
|
|
216
|
+
// Clustering
|
|
217
|
+
kMeansClustering,
|
|
218
|
+
kMeansAuto,
|
|
219
|
+
hierarchicalClustering,
|
|
220
|
+
dbscan,
|
|
221
|
+
|
|
222
|
+
// TSP
|
|
223
|
+
travelingSalesmanApprox,
|
|
224
|
+
|
|
225
|
+
// GDS-style catalog
|
|
226
|
+
createGraphCatalog,
|
|
227
|
+
} from 'twokeys';
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Clustering
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { kMeansClustering, hierarchicalClustering, dbscan } from 'twokeys';
|
|
234
|
+
|
|
235
|
+
const points = [
|
|
236
|
+
[1, 1], [1.5, 2], [3, 4],
|
|
237
|
+
[5, 7], [3.5, 5], [4.5, 5],
|
|
238
|
+
[3.5, 4.5],
|
|
239
|
+
];
|
|
240
|
+
|
|
241
|
+
// k-Means
|
|
242
|
+
const km = kMeansClustering(points, 2);
|
|
243
|
+
|
|
244
|
+
// Hierarchical (single, complete, average, or ward linkage)
|
|
245
|
+
const hc = hierarchicalClustering(points, 2, { linkage: 'ward' });
|
|
246
|
+
hc.clusters; // Point indices per cluster
|
|
247
|
+
hc.dendrogram; // Merge history
|
|
248
|
+
hc.silhouette; // Cluster quality score
|
|
249
|
+
|
|
250
|
+
// DBSCAN — density-based, finds natural shapes, handles noise
|
|
251
|
+
const db = dbscan(points, 1.5, 2);
|
|
252
|
+
db.clusters; // Point indices per cluster
|
|
253
|
+
db.noise; // Indices of noise points
|
|
254
|
+
db.clusterCount; // Number of clusters found
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### GDS-Style Catalog
|
|
258
|
+
|
|
259
|
+
In-memory graph projections with procedure wrappers and pipelines, inspired by Neo4j GDS.
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { createGraphCatalog } from 'twokeys';
|
|
263
|
+
|
|
264
|
+
const gds = createGraphCatalog<string>();
|
|
265
|
+
gds.project('social', nodes, edges, { directed: true });
|
|
266
|
+
|
|
267
|
+
const rank = gds.pageRank('social');
|
|
268
|
+
const pipeline = gds.runPipeline('social', [
|
|
269
|
+
{ id: 'rank', kind: 'page-rank' },
|
|
270
|
+
{ id: 'sim', kind: 'similarity', options: { metric: 'jaccard' } },
|
|
271
|
+
{ id: 'links', kind: 'link-prediction', options: { limit: 10 } },
|
|
272
|
+
]);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## API Reference
|
|
276
|
+
|
|
277
|
+
### Distance & Similarity (`distance.ts`)
|
|
278
|
+
|
|
279
|
+
| Function | Description |
|
|
280
|
+
|----------|-------------|
|
|
281
|
+
| `cosineSimilarity(a, b)` | Cosine similarity between dense vectors [-1, 1] |
|
|
282
|
+
| `euclideanDistance(a, b)` | Euclidean (L2) distance |
|
|
283
|
+
| `squaredEuclideanDistance(a, b)` | Squared Euclidean distance (avoids sqrt) |
|
|
284
|
+
| `manhattanDistance(a, b)` | Manhattan (L1) distance |
|
|
285
|
+
| `mahalanobisDistance(point, means, variances, epsilon?)` | Mahalanobis distance |
|
|
286
|
+
| `normalizeL2(vector)` | L2-normalize a vector to unit length |
|
|
287
|
+
| `cosineSimilaritySparse(a, b)` | Cosine similarity for sparse vectors (`Map<string, number>`) |
|
|
288
|
+
| `jaccardSimilarity(a, b)` | Jaccard index for sets |
|
|
289
|
+
| `overlapCoefficient(a, b)` | Overlap coefficient for sets |
|
|
290
|
+
|
|
291
|
+
### Graph EDA (`graph-eda.ts`)
|
|
292
|
+
|
|
293
|
+
| Function | Description |
|
|
294
|
+
|----------|-------------|
|
|
295
|
+
| `graphEda(nodes, edges, options?)` | Full Tukey-style EDA of graph structure |
|
|
296
|
+
| `clusteringCoefficient(nodes, edges, options?)` | Per-node clustering coefficients |
|
|
297
|
+
| `graphOutliers(nodes, edges, options?)` | Structurally unusual nodes |
|
|
298
|
+
|
|
299
|
+
### Series
|
|
300
|
+
|
|
301
|
+
| Category | Methods |
|
|
302
|
+
|----------|---------|
|
|
303
|
+
| **Statistics** | `mean()`, `median()`, `mode()`, `trimean()`, `variance()`, `stddev()`, `skewness()`, `kurtosis()` |
|
|
304
|
+
| **Dispersion** | `extremes()`, `hinges()`, `iqr()`, `fences()`, `outer()` |
|
|
305
|
+
| **Outliers** | `outliers()`, `inside()`, `outside()`, `adjacent()` |
|
|
306
|
+
| **Time Series** | `ema(alpha)`, `zscore()`, `hanning()`, `smooth()`, `rough()` |
|
|
307
|
+
| **Visualization** | `stemLeaf()`, `letterValues()`, `midSummaries()` |
|
|
308
|
+
| **Transforms** | `logs()`, `roots()`, `inverse()` |
|
|
309
|
+
| **Sorting** | `sorted()`, `ranked()`, `counts()`, `binned()` |
|
|
310
|
+
|
|
311
|
+
### Points
|
|
312
|
+
|
|
313
|
+
| Method | Description |
|
|
314
|
+
|--------|-------------|
|
|
315
|
+
| `centroid()` | Mean point across all dimensions |
|
|
316
|
+
| `variances()` | Per-dimension variance |
|
|
317
|
+
| `standardDeviations()` | Per-dimension standard deviation |
|
|
318
|
+
| `covarianceMatrix()` | Full covariance matrix |
|
|
319
|
+
| `correlationMatrix()` | Pearson correlation matrix |
|
|
320
|
+
| `mahalanobis(point)` | Mahalanobis distance of a single point from centroid |
|
|
321
|
+
| `mahalanobisAll()` | Mahalanobis distance for each stored point |
|
|
322
|
+
| `outliersByMahalanobis(threshold?)` | Points with Mahalanobis distance > threshold |
|
|
323
|
+
| `normalizeL2()` | L2-normalize all points (returns new Points) |
|
|
324
|
+
| `normalizeZscore()` | Z-score normalize per dimension (returns new Points) |
|
|
325
|
+
| `describe()` | Full multivariate EDA summary |
|
|
326
|
+
|
|
327
|
+
### Graph Algorithms
|
|
328
|
+
|
|
329
|
+
| Category | Functions |
|
|
330
|
+
|----------|-----------|
|
|
331
|
+
| **Centrality** | `degreeCentrality`, `closenessCentrality`, `betweennessCentrality`, `pageRank` |
|
|
332
|
+
| **Community** | `louvainCommunities`, `labelPropagationCommunities` |
|
|
333
|
+
| **Similarity** | `nodeSimilarity`, `kNearestNeighbors`, `linkPrediction` |
|
|
334
|
+
| **Paths** | `shortestPath`, `aStarShortestPath`, `yenKShortestPaths`, `allPairsShortestPaths` |
|
|
335
|
+
| **Flow** | `maximumFlow`, `minCostMaxFlow` |
|
|
336
|
+
| **Structure** | `topologicalSort`, `stronglyConnectedComponents`, `weaklyConnectedComponents`, `minimumSpanningTree`, `articulationPointsAndBridges`, `analyzeGraph` |
|
|
337
|
+
| **Clustering** | `kMeansClustering`, `kMeansAuto`, `hierarchicalClustering`, `dbscan` |
|
|
338
|
+
| **TSP** | `travelingSalesmanApprox` |
|
|
339
|
+
| **Catalog** | `createGraphCatalog` — GDS-style projections and pipelines |
|
|
340
|
+
|
|
341
|
+
## Packages
|
|
342
|
+
|
|
343
|
+
| Package | Description |
|
|
344
|
+
|---------|-------------|
|
|
345
|
+
| `twokeys` | Core TypeScript library |
|
|
346
|
+
| `@buley/twokeys-wasm` | WebAssembly implementation with TypeScript fallback |
|
|
347
|
+
| `@buley/twokeys-types` | Shared Zod schemas for runtime validation |
|
|
348
|
+
|
|
349
|
+
## Benchmarks
|
|
350
|
+
|
|
351
|
+
Performance on different dataset sizes (operations per second):
|
|
352
|
+
|
|
353
|
+
| Method | 100 elements | 1,000 elements | 10,000 elements |
|
|
354
|
+
|--------|-------------:|---------------:|----------------:|
|
|
355
|
+
| `sorted()` | 224,599 | 14,121 | 874 |
|
|
356
|
+
| `median()` | 199,397 | 14,127 | 876 |
|
|
357
|
+
| `mean()` | 550,610 | 413,551 | 68,399 |
|
|
358
|
+
| `mode()` | 87,665 | 6,738 | 431 |
|
|
359
|
+
| `fences()` | 238,486 | 13,270 | 864 |
|
|
360
|
+
| `outliers()` | 210,058 | 12,584 | 854 |
|
|
361
|
+
| `smooth()` | 61,268 | 1,599 | 31 |
|
|
362
|
+
| `describe()` | 15,642 | 952 | 29 |
|
|
363
|
+
|
|
364
|
+
## Development
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
bun install
|
|
368
|
+
bun test
|
|
369
|
+
bun test --coverage
|
|
370
|
+
bun run build
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## About John Tukey
|
|
374
|
+
|
|
375
|
+
John Wilder Tukey (1915-2000) revolutionized how we look at data. He invented the box plot, coined the terms "bit" and "software," and championed the idea that looking at data is just as important as modeling it. His book *Exploratory Data Analysis* (1977) changed statistics forever.
|
|
376
|
+
|
|
377
|
+
This library is named after him — a founding mind in [data exploration and analysis](http://en.wikipedia.org/wiki/Exploratory_data_analysis) and a personal hero of the author.
|
|
378
|
+
|
|
379
|
+
## License
|
|
380
|
+
|
|
381
|
+
MIT
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
*"The best thing about being a statistician is that you get to play in everyone's backyard."* — John Tukey
|