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