blob: 019e40ca4d27ff32ff7577d843b2be2848a94551 [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<link rel="import" href="/tracing/base/base.html">
<script>
'use strict';
tr.exportTo('tr.b', function() {
/***
* An object of this class computes basic statistics online in O(1).
* Usage:
* 1. Create an instance.
* 2. Add numbers using the |add| method.
* 3. Query statistics.
* 4. Repeat from step 2.
*/
class RunningStatistics {
constructor() {
this.mean_ = 0;
this.count_ = 0;
this.max_ = -Infinity;
this.min_ = Infinity;
this.sum_ = 0;
this.variance_ = 0;
// Mean of logarithms of absolute values of samples, or undefined if any
// samples were <= 0.
this.meanlogs_ = 0;
}
get count() {
return this.count_;
}
get geometricMean() {
if (this.meanlogs_ === undefined)
return 0;
return Math.exp(this.meanlogs_);
}
get mean() {
if (this.count_ == 0)
return undefined;
return this.mean_;
}
get max() {
return this.max_;
}
get min() {
return this.min_;
}
get sum() {
return this.sum_;
}
get variance() {
if (this.count_ == 0)
return undefined;
if (this.count_ == 1)
return 0;
return this.variance_ / (this.count_ - 1);
}
get stddev() {
if (this.count_ == 0)
return undefined;
return Math.sqrt(this.variance);
}
add(x) {
this.count_++;
this.max_ = Math.max(this.max_, x);
this.min_ = Math.min(this.min_, x);
this.sum_ += x;
// The geometric mean is computed using the arithmetic mean of logarithms.
if (x <= 0)
this.meanlogs_ = undefined;
else if (this.meanlogs_ !== undefined)
this.meanlogs_ += (Math.log(Math.abs(x)) - this.meanlogs_) / this.count;
// The following uses Welford's algorithm for computing running mean
// and variance. See http://www.johndcook.com/blog/standard_deviation.
if (this.count_ === 1) {
this.mean_ = x;
this.variance_ = 0;
} else {
var oldMean = this.mean_;
var oldVariance = this.variance_;
// Using the 2nd formula for updating the mean yields better precision
// but it doesn't work for the case oldMean is Infinity. Hence we handle
// that case separately.
if (oldMean === Infinity || oldMean === -Infinity) {
this.mean_ = this.sum_ / this.count_;
} else {
this.mean_ = oldMean + (x - oldMean) / this.count_;
}
this.variance_ = oldVariance + (x - oldMean) * (x - this.mean_);
}
}
merge(other) {
var result = new RunningStatistics();
result.count_ = this.count_ + other.count_;
result.sum_ = this.sum_ + other.sum_;
result.min_ = Math.min(this.min_, other.min_);
result.max_ = Math.max(this.max_, other.max_);
if (result.count === 0) {
result.mean_ = 0;
result.variance_ = 0;
result.meanlogs_ = 0;
} else {
// Combine the mean and the variance using the formulas from
// https://goo.gl/ddcAep.
result.mean_ = result.sum / result.count;
var deltaMean = (this.mean || 0) - (other.mean || 0);
result.variance_ = this.variance_ + other.variance_ +
(this.count * other.count * deltaMean * deltaMean / result.count);
// Merge the arithmetic means of logarithms of absolute values of
// samples, weighted by counts.
if (this.meanlogs_ === undefined || other.meanlogs_ === undefined) {
result.meanlogs_ = undefined;
} else {
result.meanlogs_ = (this.count * this.meanlogs_ +
other.count * other.meanlogs_) / result.count;
}
}
return result;
}
asDict() {
return {
mean: this.mean,
meanlogs: this.meanlogs_,
count: this.count,
max: this.max,
min: this.min,
sum: this.sum,
variance: this.variance_
};
}
static fromDict(d) {
var result = new RunningStatistics();
result.mean_ = d.mean;
result.count_ = d.count;
result.max_ = d.max;
result.min_ = d.min;
result.sum_ = d.sum;
result.variance_ = d.variance;
result.meanlogs_ = d.meanlogs;
return result;
}
}
return {
RunningStatistics: RunningStatistics
};
});
</script>