| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 <!-- | 2 <!-- |
| 3 Copyright 2014 The Chromium Authors. All rights reserved. | 3 Copyright 2014 The Chromium Authors. All rights reserved. |
| 4 Use of this source code is governed by a BSD-style license that can be | 4 Use of this source code is governed by a BSD-style license that can be |
| 5 found in the LICENSE file. | 5 found in the LICENSE file. |
| 6 --> | 6 --> |
| 7 | 7 |
| 8 <link rel="import" href="/tracing/base/math/math.html"> | 8 <link rel="import" href="/tracing/base/math/math.html"> |
| 9 <link rel="import" href="/tracing/base/math/range.html"> | 9 <link rel="import" href="/tracing/base/math/range.html"> |
| 10 <script src="/mannwhitneyu/mannwhitneyu.js"></script> | 10 <script src="/mannwhitneyu/mannwhitneyu.js"></script> |
| (...skipping 24 matching lines...) Expand all Loading... |
| 35 // TODO(charliea): Remove: | 35 // TODO(charliea): Remove: |
| 36 /* eslint-disable catapult-camelcase */ | 36 /* eslint-disable catapult-camelcase */ |
| 37 | 37 |
| 38 tr.exportTo('tr.b.math', function() { | 38 tr.exportTo('tr.b.math', function() { |
| 39 var identity = x => x; | 39 var identity = x => x; |
| 40 | 40 |
| 41 var Statistics = {}; | 41 var Statistics = {}; |
| 42 | 42 |
| 43 /* Returns the quotient, or zero if the denominator is zero.*/ | 43 /* Returns the quotient, or zero if the denominator is zero.*/ |
| 44 Statistics.divideIfPossibleOrZero = function(numerator, denominator) { | 44 Statistics.divideIfPossibleOrZero = function(numerator, denominator) { |
| 45 if (denominator === 0) | 45 if (denominator === 0) return 0; |
| 46 return 0; | |
| 47 return numerator / denominator; | 46 return numerator / denominator; |
| 48 }; | 47 }; |
| 49 | 48 |
| 50 Statistics.sum = function(ary, opt_func, opt_this) { | 49 Statistics.sum = function(ary, opt_func, opt_this) { |
| 51 var func = opt_func || identity; | 50 var func = opt_func || identity; |
| 52 var ret = 0; | 51 var ret = 0; |
| 53 var i = 0; | 52 var i = 0; |
| 54 for (var elt of ary) | 53 for (var elt of ary) { |
| 55 ret += func.call(opt_this, elt, i++); | 54 ret += func.call(opt_this, elt, i++); |
| 55 } |
| 56 return ret; | 56 return ret; |
| 57 }; | 57 }; |
| 58 | 58 |
| 59 Statistics.mean = function(ary, opt_func, opt_this) { | 59 Statistics.mean = function(ary, opt_func, opt_this) { |
| 60 var func = opt_func || identity; | 60 var func = opt_func || identity; |
| 61 var sum = 0; | 61 var sum = 0; |
| 62 var i = 0; | 62 var i = 0; |
| 63 | 63 |
| 64 for (var elt of ary) | 64 for (var elt of ary) { |
| 65 sum += func.call(opt_this, elt, i++); | 65 sum += func.call(opt_this, elt, i++); |
| 66 } |
| 66 | 67 |
| 67 if (i === 0) | 68 if (i === 0) return undefined; |
| 68 return undefined; | |
| 69 | 69 |
| 70 return sum / i; | 70 return sum / i; |
| 71 }; | 71 }; |
| 72 | 72 |
| 73 Statistics.geometricMean = function(ary, opt_func, opt_this) { | 73 Statistics.geometricMean = function(ary, opt_func, opt_this) { |
| 74 var func = opt_func || identity; | 74 var func = opt_func || identity; |
| 75 var i = 0; | 75 var i = 0; |
| 76 var logsum = 0; | 76 var logsum = 0; |
| 77 | 77 |
| 78 // The geometric mean is expressed as the arithmetic mean of logarithms | 78 // The geometric mean is expressed as the arithmetic mean of logarithms |
| 79 // in order to prevent overflow. | 79 // in order to prevent overflow. |
| 80 for (var elt of ary) { | 80 for (var elt of ary) { |
| 81 var x = func.call(opt_this, elt, i++); | 81 var x = func.call(opt_this, elt, i++); |
| 82 if (x <= 0) | 82 if (x <= 0) return 0; |
| 83 return 0; | |
| 84 logsum += Math.log(Math.abs(x)); | 83 logsum += Math.log(Math.abs(x)); |
| 85 } | 84 } |
| 86 | 85 |
| 87 if (i === 0) | 86 if (i === 0) return 1; |
| 88 return 1; | |
| 89 | 87 |
| 90 return Math.exp(logsum / i); | 88 return Math.exp(logsum / i); |
| 91 }; | 89 }; |
| 92 | 90 |
| 93 // Returns undefined if the sum of the weights is zero. | 91 // Returns undefined if the sum of the weights is zero. |
| 94 Statistics.weightedMean = function( | 92 Statistics.weightedMean = function( |
| 95 ary, weightCallback, opt_valueCallback, opt_this) { | 93 ary, weightCallback, opt_valueCallback, opt_this) { |
| 96 var valueCallback = opt_valueCallback || identity; | 94 var valueCallback = opt_valueCallback || identity; |
| 97 var numerator = 0; | 95 var numerator = 0; |
| 98 var denominator = 0; | 96 var denominator = 0; |
| 99 var i = -1; | 97 var i = -1; |
| 100 | 98 |
| 101 for (var elt of ary) { | 99 for (var elt of ary) { |
| 102 i++; | 100 i++; |
| 103 var value = valueCallback.call(opt_this, elt, i); | 101 var value = valueCallback.call(opt_this, elt, i); |
| 104 if (value === undefined) | 102 if (value === undefined) continue; |
| 105 continue; | |
| 106 var weight = weightCallback.call(opt_this, elt, i, value); | 103 var weight = weightCallback.call(opt_this, elt, i, value); |
| 107 numerator += weight * value; | 104 numerator += weight * value; |
| 108 denominator += weight; | 105 denominator += weight; |
| 109 } | 106 } |
| 110 | 107 |
| 111 if (denominator === 0) | 108 if (denominator === 0) return undefined; |
| 112 return undefined; | |
| 113 | 109 |
| 114 return numerator / denominator; | 110 return numerator / denominator; |
| 115 }; | 111 }; |
| 116 | 112 |
| 117 Statistics.variance = function(ary, opt_func, opt_this) { | 113 Statistics.variance = function(ary, opt_func, opt_this) { |
| 118 if (ary.length === 0) | 114 if (ary.length === 0) return undefined; |
| 119 return undefined; | 115 if (ary.length === 1) return 0; |
| 120 if (ary.length === 1) | |
| 121 return 0; | |
| 122 var func = opt_func || identity; | 116 var func = opt_func || identity; |
| 123 var mean = Statistics.mean(ary, func, opt_this); | 117 var mean = Statistics.mean(ary, func, opt_this); |
| 124 var sumOfSquaredDistances = Statistics.sum( | 118 var sumOfSquaredDistances = Statistics.sum( |
| 125 ary, | 119 ary, |
| 126 function(d, i) { | 120 function(d, i) { |
| 127 var v = func.call(this, d, i) - mean; | 121 var v = func.call(this, d, i) - mean; |
| 128 return v * v; | 122 return v * v; |
| 129 }, | 123 }, |
| 130 opt_this); | 124 opt_this); |
| 131 return sumOfSquaredDistances / (ary.length - 1); | 125 return sumOfSquaredDistances / (ary.length - 1); |
| 132 }; | 126 }; |
| 133 | 127 |
| 134 Statistics.stddev = function(ary, opt_func, opt_this) { | 128 Statistics.stddev = function(ary, opt_func, opt_this) { |
| 135 if (ary.length === 0) | 129 if (ary.length === 0) return undefined; |
| 136 return undefined; | |
| 137 return Math.sqrt( | 130 return Math.sqrt( |
| 138 Statistics.variance(ary, opt_func, opt_this)); | 131 Statistics.variance(ary, opt_func, opt_this)); |
| 139 }; | 132 }; |
| 140 | 133 |
| 141 Statistics.max = function(ary, opt_func, opt_this) { | 134 Statistics.max = function(ary, opt_func, opt_this) { |
| 142 var func = opt_func || identity; | 135 var func = opt_func || identity; |
| 143 var ret = -Infinity; | 136 var ret = -Infinity; |
| 144 var i = 0; | 137 var i = 0; |
| 145 for (var elt of ary) | 138 for (var elt of ary) { |
| 146 ret = Math.max(ret, func.call(opt_this, elt, i++)); | 139 ret = Math.max(ret, func.call(opt_this, elt, i++)); |
| 140 } |
| 147 return ret; | 141 return ret; |
| 148 }; | 142 }; |
| 149 | 143 |
| 150 Statistics.min = function(ary, opt_func, opt_this) { | 144 Statistics.min = function(ary, opt_func, opt_this) { |
| 151 var func = opt_func || identity; | 145 var func = opt_func || identity; |
| 152 var ret = Infinity; | 146 var ret = Infinity; |
| 153 var i = 0; | 147 var i = 0; |
| 154 for (var elt of ary) | 148 for (var elt of ary) { |
| 155 ret = Math.min(ret, func.call(opt_this, elt, i++)); | 149 ret = Math.min(ret, func.call(opt_this, elt, i++)); |
| 150 } |
| 156 return ret; | 151 return ret; |
| 157 }; | 152 }; |
| 158 | 153 |
| 159 Statistics.range = function(ary, opt_func, opt_this) { | 154 Statistics.range = function(ary, opt_func, opt_this) { |
| 160 var func = opt_func || identity; | 155 var func = opt_func || identity; |
| 161 var ret = new tr.b.math.Range(); | 156 var ret = new tr.b.math.Range(); |
| 162 var i = 0; | 157 var i = 0; |
| 163 for (var elt of ary) | 158 for (var elt of ary) { |
| 164 ret.addValue(func.call(opt_this, elt, i++)); | 159 ret.addValue(func.call(opt_this, elt, i++)); |
| 160 } |
| 165 return ret; | 161 return ret; |
| 166 }; | 162 }; |
| 167 | 163 |
| 168 Statistics.percentile = function(ary, percent, opt_func, opt_this) { | 164 Statistics.percentile = function(ary, percent, opt_func, opt_this) { |
| 169 if (!(percent >= 0 && percent <= 1)) | 165 if (!(percent >= 0 && percent <= 1)) { |
| 170 throw new Error('percent must be [0,1]'); | 166 throw new Error('percent must be [0,1]'); |
| 167 } |
| 171 | 168 |
| 172 var func = opt_func || identity; | 169 var func = opt_func || identity; |
| 173 var tmp = new Array(ary.length); | 170 var tmp = new Array(ary.length); |
| 174 var i = 0; | 171 var i = 0; |
| 175 for (var elt of ary) | 172 for (var elt of ary) { |
| 176 tmp[i] = func.call(opt_this, elt, i++); | 173 tmp[i] = func.call(opt_this, elt, i++); |
| 174 } |
| 177 tmp.sort((a, b) => a - b); | 175 tmp.sort((a, b) => a - b); |
| 178 var idx = Math.floor((ary.length - 1) * percent); | 176 var idx = Math.floor((ary.length - 1) * percent); |
| 179 return tmp[idx]; | 177 return tmp[idx]; |
| 180 }; | 178 }; |
| 181 | 179 |
| 182 /** | 180 /** |
| 183 * Sorts the samples, and map them linearly to the range [0,1]. | 181 * Sorts the samples, and map them linearly to the range [0,1]. |
| 184 * | 182 * |
| 185 * They're mapped such that for the N samples, the first sample is 0.5/N and | 183 * They're mapped such that for the N samples, the first sample is 0.5/N and |
| 186 * the last sample is (N-0.5)/N. | 184 * the last sample is (N-0.5)/N. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 /** | 228 /** |
| 231 * Computes the discrepancy of a set of 1D samples from the interval [0,1]. | 229 * Computes the discrepancy of a set of 1D samples from the interval [0,1]. |
| 232 * | 230 * |
| 233 * The samples must be sorted. We define the discrepancy of an empty set | 231 * The samples must be sorted. We define the discrepancy of an empty set |
| 234 * of samples to be zero. | 232 * of samples to be zero. |
| 235 * | 233 * |
| 236 * http://en.wikipedia.org/wiki/Low-discrepancy_sequence | 234 * http://en.wikipedia.org/wiki/Low-discrepancy_sequence |
| 237 * http://mathworld.wolfram.com/Discrepancy.html | 235 * http://mathworld.wolfram.com/Discrepancy.html |
| 238 */ | 236 */ |
| 239 Statistics.discrepancy = function(samples, opt_locationCount) { | 237 Statistics.discrepancy = function(samples, opt_locationCount) { |
| 240 if (samples.length === 0) | 238 if (samples.length === 0) return 0.0; |
| 241 return 0.0; | |
| 242 | 239 |
| 243 var maxLocalDiscrepancy = 0; | 240 var maxLocalDiscrepancy = 0; |
| 244 var invSampleCount = 1.0 / samples.length; | 241 var invSampleCount = 1.0 / samples.length; |
| 245 var locations = []; | 242 var locations = []; |
| 246 // For each location, stores the number of samples less than that location. | 243 // For each location, stores the number of samples less than that location. |
| 247 var countLess = []; | 244 var countLess = []; |
| 248 // For each location, stores the number of samples less than or equal to | 245 // For each location, stores the number of samples less than or equal to |
| 249 // that location. | 246 // that location. |
| 250 var countLessEqual = []; | 247 var countLessEqual = []; |
| 251 | 248 |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 353 * | 350 * |
| 354 * The time stamp series C = [0,2,3,4] and D = [0,2,3,4,5] have the same | 351 * The time stamp series C = [0,2,3,4] and D = [0,2,3,4,5] have the same |
| 355 * absolute discrepancy, but D has lower relative discrepancy than C. | 352 * absolute discrepancy, but D has lower relative discrepancy than C. |
| 356 * | 353 * |
| 357 * |timestamps| may be a list of lists S = [S_1, S_2, ..., S_N], where each | 354 * |timestamps| may be a list of lists S = [S_1, S_2, ..., S_N], where each |
| 358 * S_i is a time stamp series. In that case, the discrepancy D(S) is: | 355 * S_i is a time stamp series. In that case, the discrepancy D(S) is: |
| 359 * D(S) = max(D(S_1), D(S_2), ..., D(S_N)) | 356 * D(S) = max(D(S_1), D(S_2), ..., D(S_N)) |
| 360 **/ | 357 **/ |
| 361 Statistics.timestampsDiscrepancy = function(timestamps, opt_absolute, | 358 Statistics.timestampsDiscrepancy = function(timestamps, opt_absolute, |
| 362 opt_locationCount) { | 359 opt_locationCount) { |
| 363 if (timestamps.length === 0) | 360 if (timestamps.length === 0) return 0.0; |
| 364 return 0.0; | |
| 365 | 361 |
| 366 if (opt_absolute === undefined) | 362 if (opt_absolute === undefined) opt_absolute = true; |
| 367 opt_absolute = true; | |
| 368 | 363 |
| 369 if (Array.isArray(timestamps[0])) { | 364 if (Array.isArray(timestamps[0])) { |
| 370 var rangeDiscrepancies = timestamps.map(function(r) { | 365 var rangeDiscrepancies = timestamps.map(function(r) { |
| 371 return Statistics.timestampsDiscrepancy(r); | 366 return Statistics.timestampsDiscrepancy(r); |
| 372 }); | 367 }); |
| 373 return Math.max.apply(null, rangeDiscrepancies); | 368 return Math.max.apply(null, rangeDiscrepancies); |
| 374 } | 369 } |
| 375 | 370 |
| 376 var s = Statistics.normalizeSamples(timestamps); | 371 var s = Statistics.normalizeSamples(timestamps); |
| 377 var samples = s.normalized_samples; | 372 var samples = s.normalized_samples; |
| (...skipping 23 matching lines...) Expand all Loading... |
| 401 * Because timestamp discrepancy is defined in terms of timestamps, we first | 396 * Because timestamp discrepancy is defined in terms of timestamps, we first |
| 402 * convert the list of durations to monotonically increasing timestamps. | 397 * convert the list of durations to monotonically increasing timestamps. |
| 403 * | 398 * |
| 404 * Args: | 399 * Args: |
| 405 * durations: List of interval lengths in milliseconds. | 400 * durations: List of interval lengths in milliseconds. |
| 406 * absolute: See TimestampsDiscrepancy. | 401 * absolute: See TimestampsDiscrepancy. |
| 407 * opt_locationCount: See TimestampsDiscrepancy. | 402 * opt_locationCount: See TimestampsDiscrepancy. |
| 408 **/ | 403 **/ |
| 409 Statistics.durationsDiscrepancy = function( | 404 Statistics.durationsDiscrepancy = function( |
| 410 durations, opt_absolute, opt_locationCount) { | 405 durations, opt_absolute, opt_locationCount) { |
| 411 if (durations.length === 0) | 406 if (durations.length === 0) return 0.0; |
| 412 return 0.0; | |
| 413 | 407 |
| 414 var timestamps = durations.reduce(function(prev, curr, index, array) { | 408 var timestamps = durations.reduce(function(prev, curr, index, array) { |
| 415 prev.push(prev[prev.length - 1] + curr); | 409 prev.push(prev[prev.length - 1] + curr); |
| 416 return prev; | 410 return prev; |
| 417 }, [0]); | 411 }, [0]); |
| 418 return Statistics.timestampsDiscrepancy( | 412 return Statistics.timestampsDiscrepancy( |
| 419 timestamps, opt_absolute, opt_locationCount); | 413 timestamps, opt_absolute, opt_locationCount); |
| 420 }; | 414 }; |
| 421 | 415 |
| 422 /** | 416 /** |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 455 * | 449 * |
| 456 * Args: | 450 * Args: |
| 457 * samples: Array of elements that have already been selected. Start with []. | 451 * samples: Array of elements that have already been selected. Start with []. |
| 458 * streamLength: The current length of the stream, up to |newElement|. | 452 * streamLength: The current length of the stream, up to |newElement|. |
| 459 * newElement: The element that was just extracted from the stream. | 453 * newElement: The element that was just extracted from the stream. |
| 460 * numSamples: The total number of samples desired. | 454 * numSamples: The total number of samples desired. |
| 461 **/ | 455 **/ |
| 462 Statistics.uniformlySampleStream = function(samples, streamLength, newElement, | 456 Statistics.uniformlySampleStream = function(samples, streamLength, newElement, |
| 463 numSamples) { | 457 numSamples) { |
| 464 if (streamLength <= numSamples) { | 458 if (streamLength <= numSamples) { |
| 465 if (samples.length >= streamLength) | 459 if (samples.length >= streamLength) { |
| 466 samples[streamLength - 1] = newElement; | 460 samples[streamLength - 1] = newElement; |
| 467 else | 461 } else { |
| 468 samples.push(newElement); | 462 samples.push(newElement); |
| 463 } |
| 469 return; | 464 return; |
| 470 } | 465 } |
| 471 | 466 |
| 472 var probToKeep = numSamples / streamLength; | 467 var probToKeep = numSamples / streamLength; |
| 473 if (Math.random() > probToKeep) | 468 if (Math.random() > probToKeep) return; // New sample was rejected. |
| 474 return; // New sample was rejected. | |
| 475 | 469 |
| 476 // Keeping it, replace an alement randomly. | 470 // Keeping it, replace an alement randomly. |
| 477 var index = Math.floor(Math.random() * numSamples); | 471 var index = Math.floor(Math.random() * numSamples); |
| 478 samples[index] = newElement; | 472 samples[index] = newElement; |
| 479 }; | 473 }; |
| 480 | 474 |
| 481 /** | 475 /** |
| 482 * A mechanism to merge two arrays of uniformly sampled elements in a way that | 476 * A mechanism to merge two arrays of uniformly sampled elements in a way that |
| 483 * ensures elements in the final array are still sampled uniformly. | 477 * ensures elements in the final array are still sampled uniformly. |
| 484 * | 478 * |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 626 /* An alternative measure of how spread out the distribution is, | 620 /* An alternative measure of how spread out the distribution is, |
| 627 * the variance is the square of the standard deviation. | 621 * the variance is the square of the standard deviation. |
| 628 * @return {number} variance. | 622 * @return {number} variance. |
| 629 */ | 623 */ |
| 630 get variance() { | 624 get variance() { |
| 631 throw Error('Not implemented'); | 625 throw Error('Not implemented'); |
| 632 } | 626 } |
| 633 }; | 627 }; |
| 634 | 628 |
| 635 Statistics.UniformDistribution = function(opt_range) { | 629 Statistics.UniformDistribution = function(opt_range) { |
| 636 if (!opt_range) | 630 if (!opt_range) opt_range = tr.b.math.Range.fromExplicitRange(0, 1); |
| 637 opt_range = tr.b.math.Range.fromExplicitRange(0, 1); | |
| 638 this.range = opt_range; | 631 this.range = opt_range; |
| 639 }; | 632 }; |
| 640 | 633 |
| 641 Statistics.UniformDistribution.prototype = { | 634 Statistics.UniformDistribution.prototype = { |
| 642 __proto__: Distribution.prototype, | 635 __proto__: Distribution.prototype, |
| 643 | 636 |
| 644 computeDensity: function(x) { | 637 computeDensity: function(x) { |
| 645 return 1 / this.range.range; | 638 return 1 / this.range.range; |
| 646 }, | 639 }, |
| 647 | 640 |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 839 result.significance = Statistics.Significance.INSIGNIFICANT; | 832 result.significance = Statistics.Significance.INSIGNIFICANT; |
| 840 } | 833 } |
| 841 return result; | 834 return result; |
| 842 }; | 835 }; |
| 843 | 836 |
| 844 return { | 837 return { |
| 845 Statistics, | 838 Statistics, |
| 846 }; | 839 }; |
| 847 }); | 840 }); |
| 848 </script> | 841 </script> |
| OLD | NEW |