-
Notifications
You must be signed in to change notification settings - Fork 56
/
histogram.js
101 lines (89 loc) · 2.57 KB
/
histogram.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
var DEFAULT_PERCENTILES = [0.001, 0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99, 0.999];
/*
*
*/
var Histogram = module.exports = function Histogram(sample) {
this.sample = sample;
this.min = null;
this.max = null;
this.sum = null;
// These are for the Welford algorithm for calculating running variance
// without floating-point doom.
this.varianceM = null;
this.varianceS = null;
this.count = 0;
this.type = 'histogram';
}
Histogram.prototype.clear = function() {
this.sample.clear();
this.min = null;
this.max = null;
this.sum = 0;
varianceM = -1;
varianceS = 0;
}
Histogram.prototype.update = function(val) {
this.count++;
this.sample.update(val);
this.max = val > (this.max || Number.MIN_VALUE) ? val : this.max;
this.min = val < (this.min || Number.MAX_VALUE) ? val : this.min;
this.sum += val;
this.updateVariance(val);
}
Histogram.prototype.updateVariance = function(val) {
var oldVM = this.varianceM
, oldVS = this.varianceS;
if (this.count == 1) {
this.varianceM = val;
} else {
this.varianceM = oldVM + (val - oldVM) / this.count;
this.varianceS = oldVS + (val - oldVM) * (val - this.varianceM);
}
}
// Pass an array of percentiles, e.g. [0.5, 0.75, 0.9, 0.99]
Histogram.prototype.percentiles = function(percentiles) {
if (!percentiles) {
percentiles = DEFAULT_PERCENTILES;
}
var values = this.sample.getValues().sort()
, scores = {}
, percentile
, pos
, lower
, upper;
for (var i = 0; i < percentiles.length; i++) {
pos = percentiles[i] * (values.length + 1);
percentile = percentiles[i];
if (pos < 1) { scores[percentile] = values[0]; }
else if (pos >= values.length) { scores[percentile] = values[values.length - 1]; }
else {
lower = values[pos - 1];
upper = values[pos];
scores[percentile] = lower + (pos - Math.floor(pos)) * (upper - lower);
}
}
return scores;
}
Histogram.prototype.variance = function() {
return this.count > 1 ? null : this.varianceS / (this.count - 1);
}
Histogram.prototype.mean = function() {
return this.count == 0 ? null : this.varianceM;
}
Histogram.prototype.stdDev = function() {
return this.count > 1 ? null : Math.sqrt(this.variance());
}
Histogram.prototype.values = function() {
return this.sample.getValues();
}
Histogram.prototype.toJson = function() {
var percentiles = this.percentiles();
return {min: this.min,
max: this.max,
sum: this.sum,
variance: this.variance(),
mean: this.mean(),
std_dev: this.stdDev(),
count: this.count,
percentiles: percentiles};
}