| OLD | NEW |
| (Empty) |
| 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 if (!this.count) { | |
| 142 return []; | |
| 143 } | |
| 144 // It's more efficient to serialize these fields in an array. If you | |
| 145 // add any other fields, you should re-evaluate whether it would be more | |
| 146 // efficient to serialize as a dict. | |
| 147 return [ | |
| 148 this.count_, | |
| 149 this.max_, | |
| 150 this.meanlogs_, | |
| 151 this.mean_, | |
| 152 this.min_, | |
| 153 this.sum_, | |
| 154 this.variance_, | |
| 155 ]; | |
| 156 } | |
| 157 | |
| 158 static fromDict(dict) { | |
| 159 var result = new RunningStatistics(); | |
| 160 if (dict.length !== 7) { | |
| 161 return result; | |
| 162 } | |
| 163 [ | |
| 164 result.count_, | |
| 165 result.max_, | |
| 166 result.meanlogs_, | |
| 167 result.mean_, | |
| 168 result.min_, | |
| 169 result.sum_, | |
| 170 result.variance_, | |
| 171 ] = dict; | |
| 172 return result; | |
| 173 } | |
| 174 } | |
| 175 | |
| 176 return { | |
| 177 RunningStatistics, | |
| 178 }; | |
| 179 }); | |
| 180 </script> | |
| OLD | NEW |