1*d9f75844SAndroid Build Coastguard Worker /* 2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved. 3*d9f75844SAndroid Build Coastguard Worker * 4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license 5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source 6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found 7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may 8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree. 9*d9f75844SAndroid Build Coastguard Worker */ 10*d9f75844SAndroid Build Coastguard Worker 11*d9f75844SAndroid Build Coastguard Worker #ifndef API_NUMERICS_RUNNING_STATISTICS_H_ 12*d9f75844SAndroid Build Coastguard Worker #define API_NUMERICS_RUNNING_STATISTICS_H_ 13*d9f75844SAndroid Build Coastguard Worker 14*d9f75844SAndroid Build Coastguard Worker #include <algorithm> 15*d9f75844SAndroid Build Coastguard Worker #include <cmath> 16*d9f75844SAndroid Build Coastguard Worker #include <limits> 17*d9f75844SAndroid Build Coastguard Worker 18*d9f75844SAndroid Build Coastguard Worker #include "absl/types/optional.h" 19*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h" 20*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/numerics/math_utils.h" 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Worker namespace webrtc { 23*d9f75844SAndroid Build Coastguard Worker namespace webrtc_impl { 24*d9f75844SAndroid Build Coastguard Worker 25*d9f75844SAndroid Build Coastguard Worker // tl;dr: Robust and efficient online computation of statistics, 26*d9f75844SAndroid Build Coastguard Worker // using Welford's method for variance. [1] 27*d9f75844SAndroid Build Coastguard Worker // 28*d9f75844SAndroid Build Coastguard Worker // This should be your go-to class if you ever need to compute 29*d9f75844SAndroid Build Coastguard Worker // min, max, mean, variance and standard deviation. 30*d9f75844SAndroid Build Coastguard Worker // If you need to get percentiles, please use webrtc::SamplesStatsCounter. 31*d9f75844SAndroid Build Coastguard Worker // 32*d9f75844SAndroid Build Coastguard Worker // Please note RemoveSample() won't affect min and max. 33*d9f75844SAndroid Build Coastguard Worker // If you want a full-fledged moving window over N last samples, 34*d9f75844SAndroid Build Coastguard Worker // please use webrtc::RollingAccumulator. 35*d9f75844SAndroid Build Coastguard Worker // 36*d9f75844SAndroid Build Coastguard Worker // The measures return absl::nullopt if no samples were fed (Size() == 0), 37*d9f75844SAndroid Build Coastguard Worker // otherwise the returned optional is guaranteed to contain a value. 38*d9f75844SAndroid Build Coastguard Worker // 39*d9f75844SAndroid Build Coastguard Worker // [1] 40*d9f75844SAndroid Build Coastguard Worker // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm 41*d9f75844SAndroid Build Coastguard Worker 42*d9f75844SAndroid Build Coastguard Worker // The type T is a scalar which must be convertible to double. 43*d9f75844SAndroid Build Coastguard Worker // Rationale: we often need greater precision for measures 44*d9f75844SAndroid Build Coastguard Worker // than for the samples themselves. 45*d9f75844SAndroid Build Coastguard Worker template <typename T> 46*d9f75844SAndroid Build Coastguard Worker class RunningStatistics { 47*d9f75844SAndroid Build Coastguard Worker public: 48*d9f75844SAndroid Build Coastguard Worker // Update stats //////////////////////////////////////////// 49*d9f75844SAndroid Build Coastguard Worker 50*d9f75844SAndroid Build Coastguard Worker // Add a value participating in the statistics in O(1) time. AddSample(T sample)51*d9f75844SAndroid Build Coastguard Worker void AddSample(T sample) { 52*d9f75844SAndroid Build Coastguard Worker max_ = std::max(max_, sample); 53*d9f75844SAndroid Build Coastguard Worker min_ = std::min(min_, sample); 54*d9f75844SAndroid Build Coastguard Worker ++size_; 55*d9f75844SAndroid Build Coastguard Worker // Welford's incremental update. 56*d9f75844SAndroid Build Coastguard Worker const double delta = sample - mean_; 57*d9f75844SAndroid Build Coastguard Worker mean_ += delta / size_; 58*d9f75844SAndroid Build Coastguard Worker const double delta2 = sample - mean_; 59*d9f75844SAndroid Build Coastguard Worker cumul_ += delta * delta2; 60*d9f75844SAndroid Build Coastguard Worker } 61*d9f75844SAndroid Build Coastguard Worker 62*d9f75844SAndroid Build Coastguard Worker // Remove a previously added value in O(1) time. 63*d9f75844SAndroid Build Coastguard Worker // Nb: This doesn't affect min or max. 64*d9f75844SAndroid Build Coastguard Worker // Calling RemoveSample when Size()==0 is incorrect. RemoveSample(T sample)65*d9f75844SAndroid Build Coastguard Worker void RemoveSample(T sample) { 66*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(Size(), 0); 67*d9f75844SAndroid Build Coastguard Worker // In production, just saturate at 0. 68*d9f75844SAndroid Build Coastguard Worker if (Size() == 0) { 69*d9f75844SAndroid Build Coastguard Worker return; 70*d9f75844SAndroid Build Coastguard Worker } 71*d9f75844SAndroid Build Coastguard Worker // Since samples order doesn't matter, this is the 72*d9f75844SAndroid Build Coastguard Worker // exact reciprocal of Welford's incremental update. 73*d9f75844SAndroid Build Coastguard Worker --size_; 74*d9f75844SAndroid Build Coastguard Worker const double delta = sample - mean_; 75*d9f75844SAndroid Build Coastguard Worker mean_ -= delta / size_; 76*d9f75844SAndroid Build Coastguard Worker const double delta2 = sample - mean_; 77*d9f75844SAndroid Build Coastguard Worker cumul_ -= delta * delta2; 78*d9f75844SAndroid Build Coastguard Worker } 79*d9f75844SAndroid Build Coastguard Worker 80*d9f75844SAndroid Build Coastguard Worker // Merge other stats, as if samples were added one by one, but in O(1). MergeStatistics(const RunningStatistics<T> & other)81*d9f75844SAndroid Build Coastguard Worker void MergeStatistics(const RunningStatistics<T>& other) { 82*d9f75844SAndroid Build Coastguard Worker if (other.size_ == 0) { 83*d9f75844SAndroid Build Coastguard Worker return; 84*d9f75844SAndroid Build Coastguard Worker } 85*d9f75844SAndroid Build Coastguard Worker max_ = std::max(max_, other.max_); 86*d9f75844SAndroid Build Coastguard Worker min_ = std::min(min_, other.min_); 87*d9f75844SAndroid Build Coastguard Worker const int64_t new_size = size_ + other.size_; 88*d9f75844SAndroid Build Coastguard Worker const double new_mean = 89*d9f75844SAndroid Build Coastguard Worker (mean_ * size_ + other.mean_ * other.size_) / new_size; 90*d9f75844SAndroid Build Coastguard Worker // Each cumulant must be corrected. 91*d9f75844SAndroid Build Coastguard Worker // * from: sum((x_i - mean_)²) 92*d9f75844SAndroid Build Coastguard Worker // * to: sum((x_i - new_mean)²) 93*d9f75844SAndroid Build Coastguard Worker auto delta = [new_mean](const RunningStatistics<T>& stats) { 94*d9f75844SAndroid Build Coastguard Worker return stats.size_ * (new_mean * (new_mean - 2 * stats.mean_) + 95*d9f75844SAndroid Build Coastguard Worker stats.mean_ * stats.mean_); 96*d9f75844SAndroid Build Coastguard Worker }; 97*d9f75844SAndroid Build Coastguard Worker cumul_ = cumul_ + delta(*this) + other.cumul_ + delta(other); 98*d9f75844SAndroid Build Coastguard Worker mean_ = new_mean; 99*d9f75844SAndroid Build Coastguard Worker size_ = new_size; 100*d9f75844SAndroid Build Coastguard Worker } 101*d9f75844SAndroid Build Coastguard Worker 102*d9f75844SAndroid Build Coastguard Worker // Get Measures //////////////////////////////////////////// 103*d9f75844SAndroid Build Coastguard Worker 104*d9f75844SAndroid Build Coastguard Worker // Returns number of samples involved via AddSample() or MergeStatistics(), 105*d9f75844SAndroid Build Coastguard Worker // minus number of times RemoveSample() was called. Size()106*d9f75844SAndroid Build Coastguard Worker int64_t Size() const { return size_; } 107*d9f75844SAndroid Build Coastguard Worker 108*d9f75844SAndroid Build Coastguard Worker // Returns minimum among all seen samples, in O(1) time. 109*d9f75844SAndroid Build Coastguard Worker // This isn't affected by RemoveSample(). GetMin()110*d9f75844SAndroid Build Coastguard Worker absl::optional<T> GetMin() const { 111*d9f75844SAndroid Build Coastguard Worker if (size_ == 0) { 112*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 113*d9f75844SAndroid Build Coastguard Worker } 114*d9f75844SAndroid Build Coastguard Worker return min_; 115*d9f75844SAndroid Build Coastguard Worker } 116*d9f75844SAndroid Build Coastguard Worker 117*d9f75844SAndroid Build Coastguard Worker // Returns maximum among all seen samples, in O(1) time. 118*d9f75844SAndroid Build Coastguard Worker // This isn't affected by RemoveSample(). GetMax()119*d9f75844SAndroid Build Coastguard Worker absl::optional<T> GetMax() const { 120*d9f75844SAndroid Build Coastguard Worker if (size_ == 0) { 121*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 122*d9f75844SAndroid Build Coastguard Worker } 123*d9f75844SAndroid Build Coastguard Worker return max_; 124*d9f75844SAndroid Build Coastguard Worker } 125*d9f75844SAndroid Build Coastguard Worker 126*d9f75844SAndroid Build Coastguard Worker // Returns mean in O(1) time. GetMean()127*d9f75844SAndroid Build Coastguard Worker absl::optional<double> GetMean() const { 128*d9f75844SAndroid Build Coastguard Worker if (size_ == 0) { 129*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 130*d9f75844SAndroid Build Coastguard Worker } 131*d9f75844SAndroid Build Coastguard Worker return mean_; 132*d9f75844SAndroid Build Coastguard Worker } 133*d9f75844SAndroid Build Coastguard Worker 134*d9f75844SAndroid Build Coastguard Worker // Returns unbiased sample variance in O(1) time. GetVariance()135*d9f75844SAndroid Build Coastguard Worker absl::optional<double> GetVariance() const { 136*d9f75844SAndroid Build Coastguard Worker if (size_ == 0) { 137*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 138*d9f75844SAndroid Build Coastguard Worker } 139*d9f75844SAndroid Build Coastguard Worker return cumul_ / size_; 140*d9f75844SAndroid Build Coastguard Worker } 141*d9f75844SAndroid Build Coastguard Worker 142*d9f75844SAndroid Build Coastguard Worker // Returns unbiased standard deviation in O(1) time. GetStandardDeviation()143*d9f75844SAndroid Build Coastguard Worker absl::optional<double> GetStandardDeviation() const { 144*d9f75844SAndroid Build Coastguard Worker if (size_ == 0) { 145*d9f75844SAndroid Build Coastguard Worker return absl::nullopt; 146*d9f75844SAndroid Build Coastguard Worker } 147*d9f75844SAndroid Build Coastguard Worker return std::sqrt(*GetVariance()); 148*d9f75844SAndroid Build Coastguard Worker } 149*d9f75844SAndroid Build Coastguard Worker 150*d9f75844SAndroid Build Coastguard Worker private: 151*d9f75844SAndroid Build Coastguard Worker int64_t size_ = 0; // Samples seen. 152*d9f75844SAndroid Build Coastguard Worker T min_ = infinity_or_max<T>(); 153*d9f75844SAndroid Build Coastguard Worker T max_ = minus_infinity_or_min<T>(); 154*d9f75844SAndroid Build Coastguard Worker double mean_ = 0; 155*d9f75844SAndroid Build Coastguard Worker double cumul_ = 0; // Variance * size_, sometimes noted m2. 156*d9f75844SAndroid Build Coastguard Worker }; 157*d9f75844SAndroid Build Coastguard Worker 158*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc_impl 159*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc 160*d9f75844SAndroid Build Coastguard Worker 161*d9f75844SAndroid Build Coastguard Worker #endif // API_NUMERICS_RUNNING_STATISTICS_H_ 162