-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathknear.js
128 lines (118 loc) · 4.27 KB
/
knear.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
122
123
124
125
126
127
128
// https://www.npmjs.com/package/knear with added error messages and constructor to init with dataset
export default class kNear {
constructor(k, training = []) {
this.k = k
this.training = training;
if (this.training.length === 0) {
this.array_size = -1;
} else {
this.array_size = this.training[0].v.length;
}
}
//compute the euclidean distance between two vectors
//function assumes vectors are arrays of equal length
dist(v1, v2) {
let sum = 0
v1.forEach( (val, index) => {
sum += Math.pow(val - v2[index], 2)
})
return Math.sqrt(sum)
};
updateMax(val, arr) {
let max = 0
for(let obj of arr) {
max = Math.max(max, obj.d)
}
return max
}
mode(store) {
let frequency = {} // array of frequency.
let max = 0 // holds the max frequency.
let result // holds the max frequency element.
for (let v in store) {
frequency[store[v]] = (frequency[store[v]] || 0) + 1; // increment frequency.
if (frequency[store[v]] > max) { // is this frequency > max so far ?
max = frequency[store[v]] // update max.
result = store[v] // update result.
}
}
return result
}
checkInput(v) {
if (Array.isArray(v)) {
// array is correct
if (v.length > 0) {
// array contains values
if (typeof v[0] == 'number') {
// first value is a number
if (this.array_size > -1) {
// training has data to compare the size to
if (v.length == this.array_size) {
// size of the array is correct
return true;
} else {
console.error(`Learn en classify verwachten een array met numbers van dezelfde lengte, je stuurt nu een array met lengte ${v.length}, terwijl je eerder lengte ${this.array_size} gebruikt hebt.`);
}
} else {
// first value set training size
this.array_size = v.length;
return true;
}
} else {
console.error(`Learn en classify verwachten een array met numbers, je stuurt nu array met ${typeof v[0]}.`);
}
} else {
console.error("Learn en classify verwachten een array met numbers, je stuurt nu lege array.");
}
} else {
console.error(`Learn en classify verwachten een array met numbers, je stuurt nu geen array, maar ${typeof v}.`);
}
// something was wrong for this vector
return false
}
//
// PUBLIC : learn, classify
//
//add a point to the training set
learn(vector, label) {
this.checkInput(vector)
let obj = { v: vector, lab: label }
this.training.push(obj)
}
// classify a new unknown point
classify(v) {
this.checkInput(v)
let voteBloc = []
let maxD = 0
for(let obj of this.training) {
let o = { d: this.dist(v, obj.v), vote: obj.lab }
if (voteBloc.length < this.k) {
voteBloc.push(o);
maxD = this.updateMax(maxD, voteBloc)
} else {
if (o.d < maxD) {
let bool = true
let count = 0
while (bool) {
if (Number(voteBloc[count].d) === maxD) {
voteBloc.splice(count, 1, o)
maxD = this.updateMax(maxD, voteBloc)
bool = false
} else {
if (count < voteBloc.length - 1) {
count++
} else {
bool = false
}
}
}
}
}
}
let votes = []
for(let el of voteBloc) {
votes.push(el.vote)
}
return this.mode(votes)
}
}