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 |