xref: /aosp_15_r20/external/webrtc/rtc_base/numerics/running_statistics.h (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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