/
spectral.js
97 lines (89 loc) · 1.86 KB
/
spectral.js
1
2
3
4
5
6
7
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
88
89
90
91
92
93
94
95
96
97
import { KMeanspp } from './kmeans.js'
import LaplacianEigenmaps from './laplacian_eigenmaps.js'
/**
* Spectral clustering
*/
export default class SpectralClustering {
// https://mr-r-i-c-e.hatenadiary.org/entry/20121214/1355499195
/**
* @param {'rbf' | 'knn'} [affinity] Affinity type name
* @param {object} [param] Config
* @param {number} [param.sigma] Sigma of normal distribution
* @param {number} [param.k] Number of neighborhoods
*/
constructor(affinity = 'rbf', param = {}) {
this._size = 0
this._epoch = 0
this._clustering = new KMeanspp()
this._affinity = affinity
this._sigma = param.sigma || 1.0
this._k = param.k || 10
}
/**
* Number of clusters.
*
* @type {number}
*/
get size() {
return this._size
}
/**
* Epoch.
*
* @type {number}
*/
get epoch() {
return this._epoch
}
/**
* Initialize model.
*
* @param {Array<Array<number>>} datas Training data
*/
init(datas) {
const n = datas.length
this._n = n
const le = new LaplacianEigenmaps(datas[0].length, this._affinity, this._k, this._sigma, 'normalized')
this.ready = false
le.predict(datas)
this._ev = le._ev
this._ev.flip(1)
}
/**
* Add a new cluster.
*/
add() {
this._size++
this._clustering.clear()
const s_ev = this._ev.slice(this._n - this._size, this._n, 1)
this._s_ev = s_ev.toArray()
for (let i = 0; i < this._size; i++) {
this._clustering.add(this._s_ev)
}
}
/**
* Clear all clusters.
*/
clear() {
this._size = 0
this._epoch = 0
this._clustering.clear()
}
/**
* Returns predicted categories.
*
* @returns {number[]} Predicted values
*/
predict() {
return this._clustering.predict(this._s_ev)
}
/**
* Fit and returns total distance the centroid has moved.
*
* @returns {number} Total distance the centroid has moved
*/
fit() {
this._epoch++
return this._clustering.fit(this._s_ev)
}
}