@thi.ng/k-means 1.0.22 → 1.1.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-05-28T12:02:40Z
3
+ - **Last updated**: 2025-06-14T20:56:27Z
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,14 @@ 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
+ ## [1.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@1.1.0) (2025-06-14)
15
+
16
+ #### 🚀 Features
17
+
18
+ - update initKmeanspp(), add configurable distance exponent ([2aed319](https://github.com/thi-ng/umbrella/commit/2aed319))
19
+ - add KMeansOpts.exponent
20
+ - update doc strings
21
+
14
22
  ### [0.7.10](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@0.7.10) (2024-10-05)
15
23
 
16
24
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
8
8
 
9
9
  > [!NOTE]
10
- > This is one of 208 standalone projects, maintained as part
10
+ > This is one of 209 standalone projects, maintained as part
11
11
  > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
12
12
  > and anti-framework.
13
13
  >
@@ -59,7 +59,7 @@ For Node.js REPL:
59
59
  const kmeans = await import("@thi.ng/k-means");
60
60
  ```
61
61
 
62
- Package sizes (brotli'd, pre-treeshake): ESM: 884 bytes
62
+ Package sizes (brotli'd, pre-treeshake): ESM: 898 bytes
63
63
 
64
64
  ## Dependencies
65
65
 
package/api.d.ts CHANGED
@@ -13,6 +13,10 @@ export interface KMeansOpts {
13
13
  * Distance function/metric to use for finding nearest centroid.
14
14
  */
15
15
  dist: IDistance<ReadonlyVec>;
16
+ /**
17
+ * Sample dimensions. If omitted uses length of first sample vector.
18
+ */
19
+ dim: number;
16
20
  /**
17
21
  * Max. iteration count
18
22
  */
@@ -25,6 +29,15 @@ export interface KMeansOpts {
25
29
  * Centroid refinement strategy (default: {@link means}).
26
30
  */
27
31
  strategy: CentroidStrategy;
32
+ /**
33
+ * Only used if no {@link KMeansOpts.initial} is given and the
34
+ * {@link initKmeanspp} default is used. There the `exponent` is applied to
35
+ * scale the distances to nearest centroid, which will be used to control
36
+ * the weight distribution for choosing next centroid. A higher exponent
37
+ * means that points with larger distances will be more prioritized in the
38
+ * random selection.
39
+ */
40
+ exponent: number;
28
41
  }
29
42
  /**
30
43
  * k-means initialization function, e.g. {@link initKmeanspp}.
package/kmeans.d.ts CHANGED
@@ -24,6 +24,11 @@ export declare const kmeans: <T extends ReadonlyVec>(k: number, samples: T[], op
24
24
  * fulfilled (e.g. due to lower number of samples and/or distance metric).
25
25
  * Throws an error if `samples` are empty.
26
26
  *
27
+ * The optional `exponent` (default: 2) is applied to scale the distances to
28
+ * nearest centroid, which will be used to control the weight distribution for
29
+ * choosing next centroid. A higher exponent means that points with larger
30
+ * distances will be more prioritized in the random selection.
31
+ *
27
32
  * References:
28
33
  *
29
34
  * - https://en.wikipedia.org/wiki/K-means%2B%2B
@@ -34,8 +39,9 @@ export declare const kmeans: <T extends ReadonlyVec>(k: number, samples: T[], op
34
39
  * @param samples -
35
40
  * @param dist -
36
41
  * @param rnd -
42
+ * @param exponent -
37
43
  */
38
- export declare const initKmeanspp: <T extends ReadonlyVec>(k: number, samples: T[], dist?: IDistance<ReadonlyVec>, rnd?: IRandom) => number[];
44
+ export declare const initKmeanspp: <T extends ReadonlyVec>(k: number, samples: T[], dist?: IDistance<ReadonlyVec>, rnd?: IRandom, exponent?: number) => number[];
39
45
  /**
40
46
  * Default centroid strategy forming new centroids by averaging the position of
41
47
  * participating samples.
package/kmeans.js CHANGED
@@ -7,16 +7,18 @@ import { add } from "@thi.ng/vectors/add";
7
7
  import { median } from "@thi.ng/vectors/median";
8
8
  import { mulN } from "@thi.ng/vectors/muln";
9
9
  import { zeroes } from "@thi.ng/vectors/setn";
10
- const kmeans = (k, samples, opts) => {
11
- let { dist, initial, maxIter, rnd, strategy } = {
12
- dist: DIST_SQ,
13
- maxIter: 32,
14
- strategy: means,
15
- ...opts
16
- };
10
+ const kmeans = (k, samples, opts = {}) => {
11
+ let {
12
+ dim = samples[0].length,
13
+ dist = DIST_SQ,
14
+ maxIter = 32,
15
+ strategy = means,
16
+ exponent,
17
+ initial,
18
+ rnd
19
+ } = opts;
17
20
  const num = samples.length;
18
- const dim = samples[0].length;
19
- const centroidIDs = Array.isArray(initial) ? initial : initial ? initial(k, samples, dist, rnd) : initKmeanspp(k, samples, dist, rnd);
21
+ const centroidIDs = Array.isArray(initial) ? initial : initial ? initial(k, samples, dist, rnd) : initKmeanspp(k, samples, dist, rnd, exponent);
20
22
  assert(centroidIDs.length > 0, `missing initial centroids`);
21
23
  k = centroidIDs.length;
22
24
  const centroids = centroidIDs.map((i) => samples[i]);
@@ -36,7 +38,7 @@ const kmeans = (k, samples, opts) => {
36
38
  }
37
39
  return __buildClusters(centroids, clusters);
38
40
  };
39
- const initKmeanspp = (k, samples, dist = DIST_SQ, rnd = SYSTEM) => {
41
+ const initKmeanspp = (k, samples, dist = DIST_SQ, rnd = SYSTEM, exponent = 2) => {
40
42
  const num = samples.length;
41
43
  assert(num > 0, `missing samples`);
42
44
  k = Math.min(k, num);
@@ -47,7 +49,7 @@ const initKmeanspp = (k, samples, dist = DIST_SQ, rnd = SYSTEM) => {
47
49
  while (centroidIDs.length < k) {
48
50
  let psum = 0;
49
51
  const probs = samples.map((p) => {
50
- const d = dist.from(metric(p, centroids[argmin(p, centroids, dist)])) ** 2;
52
+ const d = dist.from(metric(p, centroids[argmin(p, centroids, dist)])) ** exponent;
51
53
  psum += d;
52
54
  return d;
53
55
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/k-means",
3
- "version": "1.0.22",
3
+ "version": "1.1.0",
4
4
  "description": "Configurable k-means & k-medians (with k-means++ initialization) for n-D vectors",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -39,11 +39,11 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.11.28",
43
- "@thi.ng/distance": "^2.4.120",
44
- "@thi.ng/errors": "^2.5.34",
45
- "@thi.ng/random": "^4.1.19",
46
- "@thi.ng/vectors": "^8.2.1"
42
+ "@thi.ng/api": "^8.11.29",
43
+ "@thi.ng/distance": "^3.0.0",
44
+ "@thi.ng/errors": "^2.5.35",
45
+ "@thi.ng/random": "^4.1.20",
46
+ "@thi.ng/vectors": "^8.3.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "esbuild": "^0.25.5",
@@ -87,5 +87,5 @@
87
87
  "year": 2021,
88
88
  "screenshot": "examples/kmeans-viz.jpg"
89
89
  },
90
- "gitHead": "61c3833b7ef7d044621454b5ea4af885d39f065e\n"
90
+ "gitHead": "14e994e531d32053e948768998324d443436a542\n"
91
91
  }