@thi.ng/k-means 0.5.39 → 0.6.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 +10 -1
- package/README.md +4 -4
- package/kmeans.d.ts +2 -1
- package/kmeans.js +22 -25
- package/package.json +12 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
-
- **Last updated**: 2023-
|
|
3
|
+
- **Last updated**: 2023-02-10T13:55:29Z
|
|
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.
|
|
@@ -9,6 +9,15 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
|
|
|
9
9
|
**Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
|
|
10
10
|
and/or version bumps of transitive dependencies.
|
|
11
11
|
|
|
12
|
+
## [0.6.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@0.6.0) (2023-02-10)
|
|
13
|
+
|
|
14
|
+
#### 🚀 Features
|
|
15
|
+
|
|
16
|
+
- filter result clusters, minor optimizations ([78169e6](https://github.com/thi-ng/umbrella/commit/78169e6))
|
|
17
|
+
- use u32 array for internal cluster assignments
|
|
18
|
+
- remove attempt to start new cluster if one became unused
|
|
19
|
+
- update buildClusters to filter out empty
|
|
20
|
+
|
|
12
21
|
## [0.5.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/k-means@0.5.0) (2021-11-17)
|
|
13
22
|
|
|
14
23
|
#### 🚀 Features
|
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ For Node.js REPL:
|
|
|
48
48
|
const kMeans = await import("@thi.ng/k-means");
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
-
Package sizes (brotli'd, pre-treeshake): ESM:
|
|
51
|
+
Package sizes (brotli'd, pre-treeshake): ESM: 878 bytes
|
|
52
52
|
|
|
53
53
|
## Dependencies
|
|
54
54
|
|
|
@@ -82,7 +82,7 @@ import { kmeans, meansLatLon } from "@thi.ng/k-means";
|
|
|
82
82
|
import { HAVERSINE_LATLON } from "@thi.ng/distance";
|
|
83
83
|
|
|
84
84
|
// data from: https://simplemaps.com/data/world-cities
|
|
85
|
-
const
|
|
85
|
+
const cities = [
|
|
86
86
|
{ id: "berlin", latlon: [52.5167, 13.3833] },
|
|
87
87
|
{ id: "boston", latlon: [42.3188, -71.0846] },
|
|
88
88
|
{ id: "detroit", latlon: [42.3834, -83.1024] },
|
|
@@ -99,7 +99,7 @@ const items = [
|
|
|
99
99
|
// cluster based on lat/lon
|
|
100
100
|
const clusters = kmeans(
|
|
101
101
|
3,
|
|
102
|
-
|
|
102
|
+
cities.map((x) => x.latlon),
|
|
103
103
|
{
|
|
104
104
|
// custom centroid calc for geo locations
|
|
105
105
|
// https://docs.thi.ng/umbrella/k-means/modules.html#meansLatLon
|
|
@@ -111,7 +111,7 @@ const clusters = kmeans(
|
|
|
111
111
|
|
|
112
112
|
// print each cluster
|
|
113
113
|
for (let c of clusters) {
|
|
114
|
-
console.log(c.items.map((i) =>
|
|
114
|
+
console.log(c.items.map((i) => cities[i].id));
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// [ 'boston', 'detroit', 'new york', 'philadelphia' ]
|
package/kmeans.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { ReadonlyVec } from "@thi.ng/vectors";
|
|
|
3
3
|
import type { CentroidStrategy, Cluster, KMeansOpts } from "./api.js";
|
|
4
4
|
/**
|
|
5
5
|
* Takes an array of n-dimensional `samples` and attempts to assign them to up
|
|
6
|
-
* to `k` clusters, using the behavior defined by
|
|
6
|
+
* to `k` clusters (might produce less), using the behavior defined by
|
|
7
|
+
* (optionally) given `opts`.
|
|
7
8
|
*
|
|
8
9
|
* @remarks
|
|
9
10
|
* https://en.wikipedia.org/wiki/K-medians_clustering
|
package/kmeans.js
CHANGED
|
@@ -2,7 +2,6 @@ import { argmin } from "@thi.ng/distance/argmin";
|
|
|
2
2
|
import { DIST_SQ } from "@thi.ng/distance/squared";
|
|
3
3
|
import { assert } from "@thi.ng/errors/assert";
|
|
4
4
|
import { SYSTEM } from "@thi.ng/random/system";
|
|
5
|
-
import { uniqueIndices } from "@thi.ng/random/unique-indices";
|
|
6
5
|
import { weightedRandom } from "@thi.ng/random/weighted-random";
|
|
7
6
|
import { add } from "@thi.ng/vectors/add";
|
|
8
7
|
import { median } from "@thi.ng/vectors/median";
|
|
@@ -10,7 +9,8 @@ import { mulN } from "@thi.ng/vectors/muln";
|
|
|
10
9
|
import { zeroes } from "@thi.ng/vectors/setn";
|
|
11
10
|
/**
|
|
12
11
|
* Takes an array of n-dimensional `samples` and attempts to assign them to up
|
|
13
|
-
* to `k` clusters, using the behavior defined by
|
|
12
|
+
* to `k` clusters (might produce less), using the behavior defined by
|
|
13
|
+
* (optionally) given `opts`.
|
|
14
14
|
*
|
|
15
15
|
* @remarks
|
|
16
16
|
* https://en.wikipedia.org/wiki/K-medians_clustering
|
|
@@ -32,27 +32,20 @@ export const kmeans = (k, samples, opts) => {
|
|
|
32
32
|
assert(centroidIDs.length > 0, `missing initial centroids`);
|
|
33
33
|
k = centroidIDs.length;
|
|
34
34
|
const centroids = centroidIDs.map((i) => samples[i]);
|
|
35
|
-
const clusters =
|
|
35
|
+
const clusters = new Uint32Array(num).fill(k);
|
|
36
36
|
let update = true;
|
|
37
|
-
|
|
37
|
+
while (update && maxIter-- > 0) {
|
|
38
38
|
update = assign(samples, centroids, clusters, dist);
|
|
39
|
+
if (!update)
|
|
40
|
+
break;
|
|
39
41
|
for (let i = 0; i < k; i++) {
|
|
40
42
|
const impl = strategy(dim);
|
|
41
43
|
for (let j = 0; j < num; j++) {
|
|
42
44
|
i === clusters[j] && impl.update(samples[j]);
|
|
43
45
|
}
|
|
44
46
|
const centroid = impl.finish();
|
|
45
|
-
if (centroid)
|
|
47
|
+
if (centroid)
|
|
46
48
|
centroids[i] = centroid;
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
const numC = centroidIDs.length;
|
|
50
|
-
uniqueIndices(1, num, centroidIDs, undefined, rnd);
|
|
51
|
-
if (centroidIDs.length === numC)
|
|
52
|
-
break outer;
|
|
53
|
-
centroids[i] = samples[centroidIDs[numC]];
|
|
54
|
-
update = true;
|
|
55
|
-
}
|
|
56
49
|
}
|
|
57
50
|
}
|
|
58
51
|
return buildClusters(centroids, clusters);
|
|
@@ -104,25 +97,29 @@ export const initKmeanspp = (k, samples, dist = DIST_SQ, rnd = SYSTEM) => {
|
|
|
104
97
|
}
|
|
105
98
|
return centroidIDs;
|
|
106
99
|
};
|
|
107
|
-
const assign = (samples, centroids,
|
|
100
|
+
const assign = (samples, centroids, assignments, dist) => {
|
|
108
101
|
let update = false;
|
|
109
|
-
for (let i =
|
|
102
|
+
for (let i = samples.length; i-- > 0;) {
|
|
110
103
|
const id = argmin(samples[i], centroids, dist);
|
|
111
|
-
if (id !==
|
|
112
|
-
|
|
104
|
+
if (id !== assignments[i]) {
|
|
105
|
+
assignments[i] = id;
|
|
113
106
|
update = true;
|
|
114
107
|
}
|
|
115
108
|
}
|
|
116
109
|
return update;
|
|
117
110
|
};
|
|
118
|
-
const buildClusters = (centroids,
|
|
119
|
-
const
|
|
120
|
-
for (let i = 0, n =
|
|
121
|
-
const
|
|
122
|
-
(
|
|
123
|
-
(
|
|
111
|
+
const buildClusters = (centroids, assignments) => {
|
|
112
|
+
const clusters = [];
|
|
113
|
+
for (let i = 0, n = assignments.length; i < n; i++) {
|
|
114
|
+
const id = assignments[i];
|
|
115
|
+
(clusters[id] ||
|
|
116
|
+
(clusters[id] = {
|
|
117
|
+
id,
|
|
118
|
+
centroid: centroids[id],
|
|
119
|
+
items: [],
|
|
120
|
+
})).items.push(i);
|
|
124
121
|
}
|
|
125
|
-
return
|
|
122
|
+
return clusters.filter((x) => !!x);
|
|
126
123
|
};
|
|
127
124
|
/**
|
|
128
125
|
* Default centroid strategy forming new centroids by averaging the position of
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/k-means",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.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",
|
|
@@ -34,19 +34,19 @@
|
|
|
34
34
|
"test": "testament test"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@thi.ng/api": "^8.
|
|
38
|
-
"@thi.ng/distance": "^2.1
|
|
39
|
-
"@thi.ng/errors": "^2.2.
|
|
40
|
-
"@thi.ng/random": "^3.3.
|
|
41
|
-
"@thi.ng/vectors": "^7.
|
|
37
|
+
"@thi.ng/api": "^8.7.1",
|
|
38
|
+
"@thi.ng/distance": "^2.2.1",
|
|
39
|
+
"@thi.ng/errors": "^2.2.10",
|
|
40
|
+
"@thi.ng/random": "^3.3.23",
|
|
41
|
+
"@thi.ng/vectors": "^7.6.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@microsoft/api-extractor": "^7.
|
|
45
|
-
"@thi.ng/testament": "^0.3.
|
|
46
|
-
"rimraf": "^
|
|
44
|
+
"@microsoft/api-extractor": "^7.34.2",
|
|
45
|
+
"@thi.ng/testament": "^0.3.10",
|
|
46
|
+
"rimraf": "^4.1.2",
|
|
47
47
|
"tools": "^0.0.1",
|
|
48
|
-
"typedoc": "^0.23.
|
|
49
|
-
"typescript": "^4.9.
|
|
48
|
+
"typedoc": "^0.23.24",
|
|
49
|
+
"typescript": "^4.9.5"
|
|
50
50
|
},
|
|
51
51
|
"keywords": [
|
|
52
52
|
"cluster",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
"status": "beta",
|
|
83
83
|
"year": 2021
|
|
84
84
|
},
|
|
85
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "2b5a99a8af71670780875637299be9118b01084d\n"
|
|
86
86
|
}
|