Celero
Statistics.h
1 #pragma once
2 
20 
21 #include <algorithm>
22 #include <cmath>
23 #include <cstdint>
24 #include <limits>
25 
26 namespace celero
27 {
39  template <typename T = int64_t>
40  class Statistics
41  {
42  static_assert(std::is_arithmetic<T>::value, "Statistics requres an arithmetic type.");
43 
44  public:
48  Statistics() = default;
49 
53  Statistics(const Statistics& other)
54  : sampleSize(other.sampleSize), M1(other.M1), M2(other.M2), M3(other.M3), M4(other.M4), min(other.min), max(other.max)
55  {
56  }
57 
61  ~Statistics() = default;
62 
66  Statistics operator+(const Statistics<T>& other)
67  {
68  Statistics<T> combined;
69 
70  combined.sampleSize = this->sampleSize + other.sampleSize;
71 
72  const auto delta = other.M1 - this->M1;
73  const auto delta2 = delta * delta;
74  const auto delta3 = delta * delta2;
75  const auto delta4 = delta2 * delta2;
76 
77  combined.M1 = (this->sampleSize * this->M1 + other.sampleSize * other.M1) / combined.sampleSize;
78 
79  combined.M2 = this->M2 + other.M2 + delta2 * this->sampleSize * other.sampleSize / combined.sampleSize;
80 
81  combined.M3 =
82  this->M3 + other.M3
83  + delta3 * this->sampleSize * other.sampleSize * (this->sampleSize - other.sampleSize) / (combined.sampleSize * combined.sampleSize);
84 
85  combined.M3 += 3.0 * delta * (this->sampleSize * other.M2 - other.sampleSize * this->M2) / combined.sampleSize;
86 
87  combined.M4 = this->M4 + other.M4
88  + delta4 * this->sampleSize * other.sampleSize
89  * (this->sampleSize * this->sampleSize - this->sampleSize * other.sampleSize + other.sampleSize * other.sampleSize)
90  / (combined.sampleSize * combined.sampleSize * combined.sampleSize);
91 
92  combined.M4 += 6.0 * delta2 * (this->sampleSize * this->sampleSize * other.M2 + other.sampleSize * other.sampleSize * this->M2)
93  / (combined.sampleSize * combined.sampleSize)
94  + 4.0 * delta * (this->sampleSize * other.M3 - other.sampleSize * this->M3) / combined.sampleSize;
95 
96  combined.min = std::min(this->min, other.min);
97  combined.max = std::max(this->max, other.max);
98 
99  return combined;
100  }
101 
102  Statistics& operator+=(const Statistics& other)
103  {
104  const auto combined = *this + other;
105  *this = combined;
106  return *this;
107  }
108 
109  Statistics& operator=(const Statistics& other)
110  {
111  this->sampleSize = other.sampleSize;
112  this->M1 = other.M1;
113  this->M2 = other.M2;
114  this->M3 = other.M3;
115  this->M4 = other.M4;
116  this->min = other.min;
117  this->max = other.max;
118 
119  return *this;
120  }
121 
125  void reset()
126  {
127  this->sampleSize = 0;
128  this->M1 = 0;
129  this->M2 = 0;
130  this->M3 = 0;
131  this->M4 = 0;
132  this->min = std::numeric_limits<decltype(this->min)>::max();
133  this->max = std::numeric_limits<decltype(this->max)>::min();
134  }
135 
139  void addSample(T x)
140  {
141  const auto n1 = this->sampleSize;
142  this->sampleSize++;
143 
144  const auto delta = x - this->M1;
145  const auto delta_n = delta / static_cast<decltype(delta)>(this->sampleSize);
146  const auto delta_n2 = delta_n * delta_n;
147  const auto term1 = delta * delta_n * n1;
148 
149  this->M1 += delta_n;
150  this->M4 += term1 * delta_n2 * (this->sampleSize * this->sampleSize - 3 * this->sampleSize + 3) + 6 * delta_n2 * this->M2
151  - 4 * delta_n * this->M3;
152  this->M3 += term1 * delta_n * (this->sampleSize - 2) - 3 * delta_n * this->M2;
153  this->M2 += term1;
154 
155  this->min = std::min(this->min, x);
156  this->max = std::max(this->max, x);
157  }
158 
159  size_t getSize() const
160  {
161  return this->sampleSize;
162  }
163 
164  double getMean() const
165  {
166  return this->M1;
167  }
168 
169  double getVariance() const
170  {
171  if(this->sampleSize > 1)
172  {
173  return this->M2 / (this->sampleSize - 1);
174  }
175 
176  return 0.0;
177  }
178 
179  double getStandardDeviation() const
180  {
181  return std::sqrt(this->getVariance());
182  }
183 
184  double getSkewness() const
185  {
186  if(this->sampleSize > 2)
187  {
188  return sqrt(this->sampleSize) * this->M3 / pow(this->M2, 1.5);
189  }
190 
191  return 0.0;
192  }
193 
194  double getKurtosis() const
195  {
196  if(this->sampleSize > 3)
197  {
198  if(this->M2 != 0)
199  {
200  return this->sampleSize * this->M4 / (this->M2 * this->M2) - 3.0;
201  }
202  }
203 
204  return 0.0;
205  }
206 
212  double getZScore() const
213  {
214  const auto sd = this->getStandardDeviation();
215 
216  if(sd != 0.0)
217  {
218  return (this->getMean() - static_cast<double>(this->getMin())) / sd;
219  }
220 
221  return 0.0;
222  }
223 
224  T getMin() const
225  {
226  return this->min;
227  }
228 
229  T getMax() const
230  {
231  return this->max;
232  }
233 
234  private:
235  size_t sampleSize{0};
236  double M1{0.0};
237  double M2{0.0};
238  double M3{0.0};
239  double M4{0.0};
240  T min{std::numeric_limits<T>::max()};
241  T max{std::numeric_limits<T>::min()};
242  };
243 } // namespace celero
244 
Definition: Archive.h:25
double getZScore() const
Computed as (mean - hypothesis)/standard_deviation.
Definition: Statistics.h:212
Definition: Statistics.h:40
Statistics()=default
Default constructor.
void addSample(T x)
Adds a statistical sample.
Definition: Statistics.h:139
void reset()
Resets all accumulated statistics.
Definition: Statistics.h:125