Statistics  1.0.0
A C++17 for profiling and constant time statistical calculation
statistics.h
Go to the documentation of this file.
1 //--------------------------------------------------------------------------------------------------
2 //
3 // STATISTICS
4 //
5 //--------------------------------------------------------------------------------------------------
6 //
7 // The MIT License (MIT)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy of this software
10 // and associated documentation files (the "Software"), to deal in the Software without
11 // restriction, including without limitation the rights to use, copy, modify, merge, publish,
12 // distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
14 //
15 // The above copyright notice and this permission notice shall be included in all copies or
16 // substantial portions of the Software.
17 //
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
19 // BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
21 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //--------------------------------------------------------------------------------------------------
25 //
26 // Copyright (c) 2018 Nic Holthaus
27 //
28 //--------------------------------------------------------------------------------------------------
29 //
30 // ATTRIBUTION:
31 //
32 //
33 //--------------------------------------------------------------------------------------------------
34 //
35 /// @file statistics.h
36 /// @brief A class to measure various statistics of a population. All operations (except iterator
37 /// insert), are O(1), and the Statistics class uses a minimal, fixed amount of memory (i.e.
38 /// it does NOT keep a list of all members of its population);
39 //
40 //--------------------------------------------------------------------------------------------------
41 
42 #pragma once
43 #ifndef statistics_h__
44 #define statistics_h__
45 
46 //------------------------------
47 // INCLUDES
48 //------------------------------
49 
50 #include <algorithm>
51 #include <initializer_list>
52 #include <limits>
53 #include <utility>
54 
55 //--------------------------------------------------------------------------------------------------
56 // CLASS STATISTICS
57 //--------------------------------------------------------------------------------------------------
58 /// @brief Keeps a simple average of a series of measurements
59 /// @details
60 //--------------------------------------------------------------------------------------------------
61 template<class T>
63 {
64 public:
65 
66  using T2 = decltype(T{} * T{});
67 
68 public:
69 
70  /**
71  * @brief Default Constructor
72  */
73  inline Statistics()
74  : m_count(T{ 0 })
75  , m_max(std::numeric_limits<T>::lowest())
76  , m_min(std::numeric_limits<T>::max())
77  , m_sum (T{ 0 })
78  , m_sumOfSquares(T2{ 0 })
79  {
80 
81  }
82 
83  /**
84  * @brief Constructor
85  * @details O(1) complexity
86  * @param[in] measurement Initial measurement
87  */
88  inline explicit Statistics(T measurement)
89  : m_count(1)
90  , m_max(measurement)
91  , m_min(measurement)
92  , m_sum(measurement)
93  , m_sumOfSquares(measurement * measurement)
94  {
95 
96  }
97 
98  /**
99  * @brief Construct from initializer list
100  * @details O(N) complexity.
101  * @param[in] init initializer list containing the population
102  */
103  inline Statistics(std::initializer_list<T> init)
104  : Statistics()
105  {
106  for (const auto& measurement : init)
107  *this += measurement;
108  }
109 
110  /**
111  * @brief Constructor from input iterators
112  * @details O(1) complexity
113  * @param[in] seed Starting value for the average. Defaults to 0.
114  */
115  template<class InputIt>
116  inline explicit Statistics(InputIt first, InputIt last)
117  : Statistics()
118  {
119  for (InputIt itr = first; itr != last; ++itr)
120  *this += *itr;
121  }
122 
123  /*
124  *@brief Clears statistics
125  * @details O(1) complexity.
126  */
127  inline void clear() noexcept
128  {
129  *this = std::move(Statistics());
130  }
131 
132  /**
133  * @brief Measurement count.
134  * @details O(1) complexity.
135  * @returns The number of measurements taken
136  */
137  inline size_t count() const noexcept
138  {
139  return m_count;
140  }
141 
142  /**
143  * @brief Insert a measurement into the population
144  * @details O(1) complexity. Equivalent to `+=`.
145  * @param[in] measurement Measurement to add to the population
146  * @returns Reference to `this`
147  */
148  inline Statistics& insert(const T& measurement) noexcept
149  {
150  *this += measurement;
151  return *this;
152  }
153 
154  /**
155  * @brief Insert measurements into the population
156  * @details O(N) complexity.
157  * @param[in] first begin iterator
158  * @param[in] last end iterator
159  * @returns Reference to `this`
160  */
161  template<class InputIt>
162  inline Statistics& insert(const InputIt& first, const InputIt& last) noexcept
163  {
164  for(InputIt itr = first; itr != last; ++itr)
165  *this += *itr;
166 
167  return *this;
168  }
169 
170  /**
171  * @brief Value of the mean.
172  * @details O(1) complexity. Returns 0 if no measurements have been taken.
173  * @returns Current value of the average
174  */
175  inline T mean() const noexcept
176  {
177  return m_sum / std::max<size_t>(m_count, 1);
178  }
179 
180  /**
181  * @brief Returns minimum sampled value.
182  * @details O(1) complexity.
183  */
184  inline T min() const noexcept
185  {
186  return m_min;
187  }
188 
189  /**
190  * @brief Returns maximum sampled value.
191  * @details O(1) complexity.
192  */
193  inline T max() const noexcept
194  {
195  return m_max;
196  }
197 
198  /**
199  * @brief Sum of the samples
200  * @details O(1) complexity.
201  * @returns The sum of all sampled values
202  */
203  inline T sum() const noexcept
204  {
205  return m_sum;
206  }
207 
208  /**
209  * @brief Returns the sum of squares of all measurements
210  * @details O(1) complexity.
211  * @returns T2
212  */
213  inline T2 sumOfSquares() const noexcept
214  {
215  return m_sumOfSquares;
216  }
217 
218  /**
219  * @brief Calculates the variance of the population
220  * @details O(1) complexity.
221  * @returns variance
222  */
223  inline auto variance() const noexcept
224  {
225  auto denom = std::max<size_t>(m_count, 1);
226  return ((m_sumOfSquares / denom) - (m_sum / denom) * (m_sum / denom));
227  }
228 
229  /**
230  * @brief Calculates the standard deviation of the population
231  * @details O(1) complexity.
232  * @returns standard deviation
233  */
234  inline T standardDeviation() const noexcept
235  {
236  return sqrt(variance());
237  }
238 
239  /**
240  * @brief Returns the average updated with the given measurement.
241  * @details O(1) complexity.
242  * @param[in] measurement value to add to the average.
243  * @returns Reference to this.
244  */
245  inline Statistics& operator+=(const T& measurement) noexcept
246  {
247  if (measurement < m_min)
248  m_min = measurement;
249  else if (measurement > m_max)
250  m_max = measurement;
251 
252  m_sum += measurement;
253  m_sumOfSquares += measurement * measurement;
254 
255  ++m_count;
256 
257  return *this;
258  }
259 
260  /**
261  * @brief Returns the average updated with the given measurement.
262  * @details O(1) complexity.
263  * @param[in] measurement value to add to the average.
264  * @returns Reference to this.
265  */
266  inline Statistics& operator+=(const Statistics& rhs) noexcept
267  {
268  m_count += rhs.m_count;
269  m_max = std::max(m_max, rhs.m_max);
270  m_min = std::min(m_min, rhs.m_min);
271  m_sum += rhs.m_sum;
272  m_sumOfSquares += rhs.m_sumOfSquares;
273 
274  return *this;
275  }
276 
277  //------------------------------
278  // FRIEND OPERATORS
279  //------------------------------
280 
281  template<class U> friend Statistics<U> operator+(const Statistics<U>& lhs, const T& rhs) noexcept;
282  template<class U> friend Statistics<U> operator+(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
283  template<class U> friend bool operator==(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
284  template<class U> friend bool operator!=(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
285  template<class U> friend bool operator<(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
286  template<class U> friend bool operator<=(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
287  template<class U> friend bool operator>(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
288  template<class U> friend bool operator>=(const Statistics<U>& lhs, const Statistics<U>& rhs) noexcept;
289 
290 private:
291 
292  size_t m_count;
293  T m_max;
294  T m_min;
295  T m_sum;
296  T2 m_sumOfSquares;
297 };
298 
299 /**
300  * @brief Adds a measurement to the population
301  * @details O(1) complexity.
302  * @param[in] population population to add the measurement to
303  * @param[in] measurement measurement to add
304  * @returns The resulting population
305  */
306 template<class T>
307 inline Statistics<T> operator+(const Statistics<T>& lhs, const T& rhs) noexcept
308 {
309  Statistics<T> s;
310  s += lhs;
311  s += rhs;
312  return s;
313 }
314 
315 /**
316  * @brief Combine two populations
317  * @details O(1) complexity.
318  * @param[in] lhs first population
319  * @param[in] rhs second population
320  * @returns The resulting population
321  */
322 template<class T>
323 inline Statistics<T>& operator+(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
324 {
325  Statistics<T> s;
326  s += lhs;
327  s += rhs;
328  return s;
329 }
330 
331 /**
332  * @brief Equality operator
333  * @details O(1) complexity.
334  * @param[in] lhs left hand population
335  * @param[in] rhs right hand population
336  */
337 template<class T>
338 inline bool operator==(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
339 {
340  if (lhs.m_count != rhs.m_count)
341  return false;
342  else if (lhs.m_min != rhs.m_min)
343  return false;
344  else if (lhs.m_max != rhs.m_max)
345  return false;
346  else if (lhs.m_sum != rhs.m_sum)
347  return false;
348  else
349  return true;
350 }
351 
352 /**
353  * @brief Inequality operator
354  * @details O(1) complexity.
355  * @param[in] lhs left hand population
356  * @param[in] rhs right hand population
357  */
358 template<class T>
359 inline bool operator!=(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
360 {
361  return !(lhs == rhs);
362 }
363 
364 /**
365  * @brief Less than operator
366  * @details O(1) complexity.
367  * @param[in] lhs left hand population
368  * @param[in] rhs right hand population
369  */
370 template<class T>
371 inline bool operator<(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
372 {
373  if (lhs.m_count < rhs.m_count)
374  return true;
375  else if (lhs.m_min < rhs.m_min)
376  return true;
377  else if (lhs.m_max < rhs.m_max)
378  return true;
379  else if (lhs.m_sum < rhs.m_sum)
380  return true;
381  else
382  return false;
383 }
384 
385 /**
386  * @brief Less than or equal to operator
387  * @details O(1) complexity.
388  * @param[in] lhs left hand population
389  * @param[in] rhs right hand population
390  */
391 template<class T>
392 inline bool operator<=(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
393 {
394  return !(rhs < lhs);
395 }
396 
397 /**
398  * @brief Greater than operator
399  * @details O(1) complexity.
400  * @param[in] lhs left hand population
401  * @param[in] rhs right hand population
402  */
403 template<class T>
404 inline bool operator>(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
405 {
406  return rhs < lhs;
407 }
408 
409 /**
410  * @brief Greater than or equal to operator
411  * @details O(1) complexity.
412  * @param[in] lhs left hand population
413  * @param[in] rhs right hand population
414  */
415 template<class T>
416 inline bool operator>=(const Statistics<T>& lhs, const Statistics<T>& rhs) noexcept
417 {
418  return !(lhs < rhs);
419 }
420 
421 #endif // average_h__
Statistics::Statistics
Statistics(std::initializer_list< T > init)
Construct from initializer list.
Definition: statistics.h:103
Statistics::insert
Statistics & insert(const T &measurement) noexcept
Insert a measurement into the population.
Definition: statistics.h:148
Statistics::Statistics
Statistics(T measurement)
Constructor.
Definition: statistics.h:88
Statistics::Statistics
Statistics()
Default Constructor.
Definition: statistics.h:73
Statistics::standardDeviation
T standardDeviation() const noexcept
Calculates the standard deviation of the population.
Definition: statistics.h:234
Statistics::Statistics
Statistics(InputIt first, InputIt last)
Constructor from input iterators.
Definition: statistics.h:116
Statistics::variance
auto variance() const noexcept
Calculates the variance of the population.
Definition: statistics.h:223
Statistics::sum
T sum() const noexcept
Sum of the samples.
Definition: statistics.h:203
Statistics::insert
Statistics & insert(const InputIt &first, const InputIt &last) noexcept
Insert measurements into the population.
Definition: statistics.h:162
operator!=
bool operator!=(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Inequality operator.
Definition: statistics.h:359
Statistics::min
T min() const noexcept
Returns minimum sampled value.
Definition: statistics.h:184
Statistics
Keeps a simple average of a series of measurements.
Definition: statistics.h:62
Statistics::operator+=
Statistics & operator+=(const Statistics &rhs) noexcept
Returns the average updated with the given measurement.
Definition: statistics.h:266
Statistics::operator+=
Statistics & operator+=(const T &measurement) noexcept
Returns the average updated with the given measurement.
Definition: statistics.h:245
operator<=
bool operator<=(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Less than or equal to operator.
Definition: statistics.h:392
operator==
bool operator==(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Equality operator.
Definition: statistics.h:338
Statistics::mean
T mean() const noexcept
Value of the mean.
Definition: statistics.h:175
Statistics::max
T max() const noexcept
Returns maximum sampled value.
Definition: statistics.h:193
operator<
bool operator<(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Less than operator.
Definition: statistics.h:371
Statistics::count
size_t count() const noexcept
Measurement count.
Definition: statistics.h:137
Statistics::sumOfSquares
T2 sumOfSquares() const noexcept
Returns the sum of squares of all measurements.
Definition: statistics.h:213
operator>
bool operator>(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Greater than operator.
Definition: statistics.h:404
operator+
Statistics< T > operator+(const Statistics< T > &lhs, const T &rhs) noexcept
Adds a measurement to the population.
Definition: statistics.h:307
operator+
Statistics< T > & operator+(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Combine two populations.
Definition: statistics.h:323
operator>=
bool operator>=(const Statistics< T > &lhs, const Statistics< T > &rhs) noexcept
Greater than or equal to operator.
Definition: statistics.h:416