/
pcm.js
95 lines (88 loc) · 1.96 KB
/
pcm.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
import Matrix from '../util/matrix.js'
/**
* Possibilistic c-means
*/
export default class PossibilisticCMeans {
// https://github.com/holtskinner/PossibilisticCMeans
/**
* @param {number} [m] Fuzziness factor
*/
constructor(m = 2) {
this._m = m
this._c = []
this._u = []
this._k = 1
}
_distance2(a, b) {
return a.reduce((s, v, i) => s + (v - b[i]) ** 2, 0)
}
/**
* Initialize model.
*
* @param {Array<Array<number>>} datas Training data
*/
init(datas) {
this._x = datas
}
/**
* Add a new cluster.
*/
add() {
let cp = null
while (true) {
const i = Math.floor(Math.random() * this._x.length)
cp = this._x[i]
if (this._c.every(c => this._distance2(cp, c) > 0)) {
break
}
}
this._c.push(cp.concat())
const u = []
const d = this._x.map(v => this._distance2(cp, v))
const eta = (this._k * d.reduce((s, v) => s + v, 0)) / d.length
for (let i = 0; i < this._x.length; i++) {
u[i] = 1 / (1 + (d[i] ** 2 / eta) ** (1 / (this._m - 1)))
}
this._u.push(u)
}
/**
* Fit model.
*/
fit() {
const m = this._x[0].length
for (let k = 0; k < this._u.length; k++) {
const d = this._x.map(v => this._distance2(this._c[k], v))
let s = 0
let eta = 0
for (let i = 0; i < this._x.length; i++) {
s += this._u[k][i] ** this._m
eta += d[i] * this._u[k][i] ** this._m
}
eta *= this._k / s
for (let i = 0; i < this._x.length; i++) {
this._u[k][i] = 1 / (1 + (d[i] / eta) ** (1 / (this._m - 1)))
}
}
const c = []
for (let k = 0; k < this._u.length; k++) {
const ck = Array(m).fill(0)
let s = 0
for (let i = 0; i < this._x.length; i++) {
s += this._u[k][i] ** this._m
for (let j = 0; j < m; j++) {
ck[j] += this._x[i][j] * this._u[k][i] ** this._m
}
}
c.push(ck.map(v => v / s))
}
this._c = c
}
/**
* Returns predicted coefficients.
*
* @returns {Array<Array<number>>} Predicted values
*/
predict() {
return Matrix.fromArray(this._u).t.toArray()
}
}