1 <!DOCTYPE html> 2 <!-- 3 Copyright 2016 The Chromium Authors. All rights reserved. 4 Use of this source code is governed by a BSD-style license that can be 5 found in the LICENSE file. 6 --> 7 8 <link rel="import" href="/tracing/base/base.html"> 9 10 <script> 11 'use strict'; 12 13 tr.exportTo('tr.b', function() { 14 /*** 15 * An object of this class computes basic statistics online in O(1). 16 * Usage: 17 * 1. Create an instance. 18 * 2. Add numbers using the |add| method. 19 * 3. Query statistics. 20 * 4. Repeat from step 2. 21 */ 22 class RunningStatistics { 23 constructor() { 24 this.mean_ = 0; 25 this.count_ = 0; 26 this.max_ = -Infinity; 27 this.min_ = Infinity; 28 this.sum_ = 0; 29 this.variance_ = 0; 30 31 // Mean of logarithms of absolute values of samples, or undefined if any 32 // samples were <= 0. 33 this.meanlogs_ = 0; 34 } 35 36 get count() { 37 return this.count_; 38 } 39 40 get geometricMean() { 41 if (this.meanlogs_ === undefined) 42 return 0; 43 return Math.exp(this.meanlogs_); 44 } 45 46 get mean() { 47 if (this.count_ == 0) 48 return undefined; 49 return this.mean_; 50 } 51 52 get max() { 53 return this.max_; 54 } 55 56 get min() { 57 return this.min_; 58 } 59 60 get sum() { 61 return this.sum_; 62 } 63 64 get variance() { 65 if (this.count_ == 0) 66 return undefined; 67 if (this.count_ == 1) 68 return 0; 69 return this.variance_ / (this.count_ - 1); 70 } 71 72 get stddev() { 73 if (this.count_ == 0) 74 return undefined; 75 return Math.sqrt(this.variance); 76 } 77 78 add(x) { 79 this.count_++; 80 this.max_ = Math.max(this.max_, x); 81 this.min_ = Math.min(this.min_, x); 82 this.sum_ += x; 83 84 // The geometric mean is computed using the arithmetic mean of logarithms. 85 if (x <= 0) 86 this.meanlogs_ = undefined; 87 else if (this.meanlogs_ !== undefined) 88 this.meanlogs_ += (Math.log(Math.abs(x)) - this.meanlogs_) / this.count; 89 90 // The following uses Welford's algorithm for computing running mean 91 // and variance. See http://www.johndcook.com/blog/standard_deviation. 92 if (this.count_ === 1) { 93 this.mean_ = x; 94 this.variance_ = 0; 95 } else { 96 var oldMean = this.mean_; 97 var oldVariance = this.variance_; 98 // Using the 2nd formula for updating the mean yields better precision 99 // but it doesn't work for the case oldMean is Infinity. Hence we handle 100 // that case separately. 101 if (oldMean === Infinity || oldMean === -Infinity) { 102 this.mean_ = this.sum_ / this.count_; 103 } else { 104 this.mean_ = oldMean + (x - oldMean) / this.count_; 105 } 106 this.variance_ = oldVariance + (x - oldMean) * (x - this.mean_); 107 } 108 } 109 110 merge(other) { 111 var result = new RunningStatistics(); 112 result.count_ = this.count_ + other.count_; 113 result.sum_ = this.sum_ + other.sum_; 114 result.min_ = Math.min(this.min_, other.min_); 115 result.max_ = Math.max(this.max_, other.max_); 116 if (result.count === 0) { 117 result.mean_ = 0; 118 result.variance_ = 0; 119 result.meanlogs_ = 0; 120 } else { 121 // Combine the mean and the variance using the formulas from 122 // https://goo.gl/ddcAep. 123 result.mean_ = result.sum / result.count; 124 var deltaMean = (this.mean || 0) - (other.mean || 0); 125 result.variance_ = this.variance_ + other.variance_ + 126 (this.count * other.count * deltaMean * deltaMean / result.count); 127 128 // Merge the arithmetic means of logarithms of absolute values of 129 // samples, weighted by counts. 130 if (this.meanlogs_ === undefined || other.meanlogs_ === undefined) { 131 result.meanlogs_ = undefined; 132 } else { 133 result.meanlogs_ = (this.count * this.meanlogs_ + 134 other.count * other.meanlogs_) / result.count; 135 } 136 } 137 return result; 138 } 139 140 asDict() { 141 return { 142 mean: this.mean, 143 meanlogs: this.meanlogs_, 144 count: this.count, 145 max: this.max, 146 min: this.min, 147 sum: this.sum, 148 variance: this.variance_ 149 }; 150 } 151 152 static fromDict(d) { 153 var result = new RunningStatistics(); 154 result.mean_ = d.mean; 155 result.count_ = d.count; 156 result.max_ = d.max; 157 result.min_ = d.min; 158 result.sum_ = d.sum; 159 result.variance_ = d.variance; 160 result.meanlogs_ = d.meanlogs; 161 return result; 162 } 163 } 164 165 return { 166 RunningStatistics: RunningStatistics 167 }; 168 }); 169 </script> 170