@thi.ng/k-means 1.1.1 → 2.0.0
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/CHANGELOG.md +15 -1
- package/README.md +11 -2
- package/api.d.ts +2 -2
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/kmeans.d.ts +1 -1
- package/kmeans.js +6 -7
- package/mean-cut.d.ts +29 -0
- package/mean-cut.js +44 -0
- package/package.json +13 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2025-06-
|
|
3
|
+
- **Last updated**: 2025-06-24T21:39:38Z
|
|
4
4
|
- **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
|
|
5
5
|
|
|
6
6
|
All notable changes to this project will be documented in this file.
|
|
@@ -11,6 +11,20 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
11
11
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
12
12
|
and/or version bumps of transitive dependencies.
|
|
13
13
|
|
|
14
|
+
# [2.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@2.0.0) (2025-06-24)
|
|
15
|
+
|
|
16
|
+
#### 🛑 Breaking changes
|
|
17
|
+
|
|
18
|
+
- update KMeansInit & kmeansPlusPlus() ([58d7eb1](https://github.com/thi-ng/umbrella/commit/58d7eb1))
|
|
19
|
+
- BREAKING CHANGE: update `KMeansInit` to return vectors not IDs
|
|
20
|
+
- update `KMeansInit` return type
|
|
21
|
+
- update `KMeansOpts.initial`
|
|
22
|
+
- rename `initKmeanspp()` => `kmeansPlusPlus()`
|
|
23
|
+
|
|
24
|
+
#### 🚀 Features
|
|
25
|
+
|
|
26
|
+
- add meanCut/medianCut() ([867889d](https://github.com/thi-ng/umbrella/commit/867889d))
|
|
27
|
+
|
|
14
28
|
## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@1.1.0) (2025-06-14)
|
|
15
29
|
|
|
16
30
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -25,7 +25,16 @@
|
|
|
25
25
|
|
|
26
26
|
## About
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
k-means & k-medians with customizable distance functions and centroid initializations for n-D vectors.
|
|
29
|
+
|
|
30
|
+
In addition to the main
|
|
31
|
+
[`kmeans()`](https://docs.thi.ng/umbrella/k-means/functions/kmeans.html)
|
|
32
|
+
implementation, the following k-means centroid initialization functions are
|
|
33
|
+
provided (can also be used in isolation to extract cluster centroids):
|
|
34
|
+
|
|
35
|
+
- [`kmeansPlusPlus()`](https://docs.thi.ng/umbrella/k-means/functions/kmeansPlusPlus.html)
|
|
36
|
+
- [`meanCut()`](https://docs.thi.ng/umbrella/k-means/functions/meanCut.html)
|
|
37
|
+
- [`medianCut()`](https://docs.thi.ng/umbrella/k-means/functions/medianCut.html)
|
|
29
38
|
|
|
30
39
|
## Status
|
|
31
40
|
|
|
@@ -59,7 +68,7 @@ For Node.js REPL:
|
|
|
59
68
|
const kmeans = await import("@thi.ng/k-means");
|
|
60
69
|
```
|
|
61
70
|
|
|
62
|
-
Package sizes (brotli'd, pre-treeshake): ESM:
|
|
71
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 1.10 KB
|
|
63
72
|
|
|
64
73
|
## Dependencies
|
|
65
74
|
|
package/api.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface KMeansOpts {
|
|
|
8
8
|
* array given to {@link kmeans}) or a function producing such. If omitted,
|
|
9
9
|
* {@link initKmeanspp} is used as default.
|
|
10
10
|
*/
|
|
11
|
-
initial:
|
|
11
|
+
initial: ReadonlyVec[] | KMeansInit;
|
|
12
12
|
/**
|
|
13
13
|
* Distance function/metric to use for finding nearest centroid.
|
|
14
14
|
*/
|
|
@@ -42,7 +42,7 @@ export interface KMeansOpts {
|
|
|
42
42
|
/**
|
|
43
43
|
* k-means initialization function, e.g. {@link initKmeanspp}.
|
|
44
44
|
*/
|
|
45
|
-
export type KMeansInit
|
|
45
|
+
export type KMeansInit = (k: number, samples: ReadonlyVec[], dist?: IDistance<ReadonlyVec>, rnd?: IRandom) => ReadonlyVec[];
|
|
46
46
|
export type CentroidStrategy = Fn<number, {
|
|
47
47
|
update: Fn<ReadonlyVec, void>;
|
|
48
48
|
finish: Fn0<Maybe<Vec>>;
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/kmeans.d.ts
CHANGED
|
@@ -41,7 +41,7 @@ export declare const kmeans: <T extends ReadonlyVec>(k: number, samples: T[], op
|
|
|
41
41
|
* @param rnd -
|
|
42
42
|
* @param exponent -
|
|
43
43
|
*/
|
|
44
|
-
export declare const
|
|
44
|
+
export declare const kmeansPlusPlus: <T extends ReadonlyVec>(k: number, samples: T[], dist?: IDistance<ReadonlyVec>, rnd?: IRandom, exponent?: number) => T[];
|
|
45
45
|
/**
|
|
46
46
|
* Default centroid strategy forming new centroids by averaging the position of
|
|
47
47
|
* participating samples.
|
package/kmeans.js
CHANGED
|
@@ -18,10 +18,9 @@ const kmeans = (k, samples, opts = {}) => {
|
|
|
18
18
|
rnd
|
|
19
19
|
} = opts;
|
|
20
20
|
const num = samples.length;
|
|
21
|
-
const
|
|
22
|
-
assert(
|
|
23
|
-
k =
|
|
24
|
-
const centroids = centroidIDs.map((i) => samples[i]);
|
|
21
|
+
const centroids = Array.isArray(initial) ? initial : initial ? initial(k, samples, dist, rnd) : kmeansPlusPlus(k, samples, dist, rnd, exponent);
|
|
22
|
+
assert(centroids.length > 0, `missing initial centroids`);
|
|
23
|
+
k = centroids.length;
|
|
25
24
|
const clusters = new Uint32Array(num).fill(k);
|
|
26
25
|
let update = true;
|
|
27
26
|
while (update && maxIter-- > 0) {
|
|
@@ -38,7 +37,7 @@ const kmeans = (k, samples, opts = {}) => {
|
|
|
38
37
|
}
|
|
39
38
|
return __buildClusters(centroids, clusters);
|
|
40
39
|
};
|
|
41
|
-
const
|
|
40
|
+
const kmeansPlusPlus = (k, samples, dist = DIST_SQ, rnd = SYSTEM, exponent = 2) => {
|
|
42
41
|
const num = samples.length;
|
|
43
42
|
assert(num > 0, `missing samples`);
|
|
44
43
|
k = Math.min(k, num);
|
|
@@ -61,7 +60,7 @@ const initKmeanspp = (k, samples, dist = DIST_SQ, rnd = SYSTEM, exponent = 2) =>
|
|
|
61
60
|
centroidIDs.push(id);
|
|
62
61
|
centroids.push(samples[id]);
|
|
63
62
|
}
|
|
64
|
-
return
|
|
63
|
+
return centroids;
|
|
65
64
|
};
|
|
66
65
|
const __assign = (samples, centroids, assignments, dist) => {
|
|
67
66
|
let update = false;
|
|
@@ -124,8 +123,8 @@ const meansLatLon = () => {
|
|
|
124
123
|
};
|
|
125
124
|
};
|
|
126
125
|
export {
|
|
127
|
-
initKmeanspp,
|
|
128
126
|
kmeans,
|
|
127
|
+
kmeansPlusPlus,
|
|
129
128
|
means,
|
|
130
129
|
meansLatLon,
|
|
131
130
|
medians
|
package/mean-cut.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Fn } from "@thi.ng/api";
|
|
2
|
+
import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
3
|
+
/**
|
|
4
|
+
* Mean-cut clustering, also usable as {@link kmeans} /
|
|
5
|
+
* {@link KMeansOpts.initial} centroid initialization. Returns up to `k`
|
|
6
|
+
* centroids for given `samples`.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Only recommended for low-dimensional data.
|
|
10
|
+
*
|
|
11
|
+
* @param k
|
|
12
|
+
* @param samples
|
|
13
|
+
*/
|
|
14
|
+
export declare const meanCut: <T extends ReadonlyVec>(k: number, samples: T[]) => import("@thi.ng/vectors").Vec<number>[];
|
|
15
|
+
/**
|
|
16
|
+
* Median-cut clustering, also usable as {@link kmeans} /
|
|
17
|
+
* {@link KMeansOpts.initial} centroid initialization. Returns up to `k`
|
|
18
|
+
* centroids for given `samples`.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* Only recommended for low-dimensional data.
|
|
22
|
+
*
|
|
23
|
+
* @param k
|
|
24
|
+
* @param samples
|
|
25
|
+
*/
|
|
26
|
+
export declare const medianCut: <T extends ReadonlyVec>(k: number, samples: T[]) => import("@thi.ng/vectors").Vec<number>[];
|
|
27
|
+
/** @internal */
|
|
28
|
+
export declare const computeCutWith: (cut: Fn<ReadonlyVec, number>, samples: ReadonlyVec[], dim: number, depth: number) => ReadonlyVec[][];
|
|
29
|
+
//# sourceMappingURL=mean-cut.d.ts.map
|
package/mean-cut.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { mean, vmean } from "@thi.ng/vectors/mean";
|
|
2
|
+
import { vmedian } from "@thi.ng/vectors/median";
|
|
3
|
+
const meanCut = (k, samples) => computeCutWith(vmean, samples, samples[0].length, k).map(
|
|
4
|
+
(x) => mean([], x)
|
|
5
|
+
);
|
|
6
|
+
const medianCut = (k, samples) => computeCutWith(vmedian, samples, samples[0].length, k).map(
|
|
7
|
+
(x) => mean([], x)
|
|
8
|
+
);
|
|
9
|
+
const computeCutWith = (cut, samples, dim, depth) => {
|
|
10
|
+
if (!samples.length) return [];
|
|
11
|
+
if (depth <= 1) return [samples];
|
|
12
|
+
const channels = new Array(dim);
|
|
13
|
+
let maxRange = 0, maxRangeID = 0, i = 0, j, n = samples.length, min, max, value, range, channel;
|
|
14
|
+
for (; i < dim; i++) {
|
|
15
|
+
channel = channels[i] = new Array(n);
|
|
16
|
+
min = Infinity;
|
|
17
|
+
max = -Infinity;
|
|
18
|
+
for (j = 0; j < n; j++) {
|
|
19
|
+
value = channel[j] = samples[j][i];
|
|
20
|
+
if (value < min) min = value;
|
|
21
|
+
if (value > max) max = value;
|
|
22
|
+
}
|
|
23
|
+
range = max - min;
|
|
24
|
+
if (range > maxRange) {
|
|
25
|
+
maxRange = range;
|
|
26
|
+
maxRangeID = i;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
channel = channels[maxRangeID];
|
|
30
|
+
const split = cut(channel);
|
|
31
|
+
const lo = [];
|
|
32
|
+
const hi = [];
|
|
33
|
+
for (j = 0; j < n; j++) {
|
|
34
|
+
(channel[j] <= split ? lo : hi).push(samples[j]);
|
|
35
|
+
}
|
|
36
|
+
return computeCutWith(cut, lo, dim, depth >> 1).concat(
|
|
37
|
+
computeCutWith(cut, hi, dim, depth + 1 >> 1)
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
export {
|
|
41
|
+
computeCutWith,
|
|
42
|
+
meanCut,
|
|
43
|
+
medianCut
|
|
44
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/k-means",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "k-means & k-medians with customizable distance functions and centroid initializations for n-D vectors",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
7
7
|
"typings": "./index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@thi.ng/api": "^8.11.29",
|
|
43
|
-
"@thi.ng/distance": "^3.0.
|
|
43
|
+
"@thi.ng/distance": "^3.0.2",
|
|
44
44
|
"@thi.ng/errors": "^2.5.35",
|
|
45
45
|
"@thi.ng/random": "^4.1.20",
|
|
46
46
|
"@thi.ng/vectors": "^8.3.1"
|
|
@@ -51,12 +51,18 @@
|
|
|
51
51
|
"typescript": "^5.8.3"
|
|
52
52
|
},
|
|
53
53
|
"keywords": [
|
|
54
|
+
"centroid",
|
|
54
55
|
"cluster",
|
|
55
56
|
"distance",
|
|
57
|
+
"eucledian",
|
|
58
|
+
"haversine",
|
|
56
59
|
"k-means",
|
|
57
60
|
"k-means++",
|
|
58
61
|
"k-medians",
|
|
62
|
+
"mean",
|
|
63
|
+
"median",
|
|
59
64
|
"nd",
|
|
65
|
+
"partition",
|
|
60
66
|
"self-organizing",
|
|
61
67
|
"typescript"
|
|
62
68
|
],
|
|
@@ -79,6 +85,9 @@
|
|
|
79
85
|
},
|
|
80
86
|
"./kmeans": {
|
|
81
87
|
"default": "./kmeans.js"
|
|
88
|
+
},
|
|
89
|
+
"./mean-cut": {
|
|
90
|
+
"default": "./mean-cut.js"
|
|
82
91
|
}
|
|
83
92
|
},
|
|
84
93
|
"thi.ng": {
|
|
@@ -87,5 +96,5 @@
|
|
|
87
96
|
"year": 2021,
|
|
88
97
|
"screenshot": "examples/kmeans-viz.jpg"
|
|
89
98
|
},
|
|
90
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "45e91ee75236e39fc87ea10fbabac1272bef62e3\n"
|
|
91
100
|
}
|