/
mona.js
121 lines (116 loc) · 2.35 KB
/
mona.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* MONothetic Analysis Clustering
*/
export default class MONA {
// https://stat.ethz.ch/R-manual/R-devel/library/cluster/html/mona.html
constructor() {}
/**
* Number of clusters
*
* @type {number}
*/
get size() {
return this._tree.leafs.length
}
/**
* Initialize model.
*
* @param {Array<Array<0 | 1>>} datas Training data
*/
init(datas) {
this._x = datas
this._tree = {
idx: this._x.map((_, i) => i),
children: [],
get leafs() {
return this.children.length === 0 ? [this] : this.children.reduce((c, v) => c.concat(v.leafs), [])
},
}
}
/**
* Fit model.
*/
fit() {
const leafs = this._tree.leafs
const d = this._x[0].length
for (const leaf of leafs) {
let maxa = -Infinity
let maxi = -1
for (let i = 0; i < d; i++) {
if (leaf.idx.every(k => this._x[k][i] === this._x[leaf.idx[0]][i])) {
continue
}
let ta = 0
for (let j = 0; j < d; j++) {
if (i === j) {
continue
}
let a = 0,
b = 0,
c = 0,
d = 0
for (const idx of leaf.idx) {
if (this._x[idx][i] === 1 && this._x[idx][j] === 1) {
a++
} else if (this._x[idx][i] === 1 && this._x[idx][j] === 0) {
b++
} else if (this._x[idx][i] === 0 && this._x[idx][j] === 1) {
c++
} else if (this._x[idx][i] === 0 && this._x[idx][j] === 0) {
d++
}
}
ta += a * d - b * c
}
if (maxa < ta) {
maxa = ta
maxi = i
}
}
if (maxi < 0) {
continue
}
const v0 = leaf.idx.filter(v => this._x[v][maxi] === 0)
const v1 = leaf.idx.filter(v => this._x[v][maxi] === 1)
if (v0.length === 0 || v1.length === 0) {
continue
}
leaf.feature = maxi
leaf.children.push(
{
idx: v0,
children: [],
get leafs() {
return this.children.length === 0
? [this]
: this.children.reduce((c, v) => c.concat(v.leafs), [])
},
},
{
idx: v1,
children: [],
get leafs() {
return this.children.length === 0
? [this]
: this.children.reduce((c, v) => c.concat(v.leafs), [])
},
}
)
}
}
/**
* Returns predicted categories.
*
* @returns {number[]} Predicted values
*/
predict() {
const p = []
const leafs = this._tree.leafs
for (let k = 0; k < leafs.length; k++) {
for (const i of leafs[k].idx) {
p[i] = k
}
}
return p
}
}