mean.hpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright 2015-2018 Hans Dembinski
  2. //
  3. // Distributed under the Boost Software License, version 1.0.
  4. // (See accompanying file LICENSE_1_0.txt
  5. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP
  7. #define BOOST_HISTOGRAM_ACCUMULATORS_MEAN_HPP
  8. #include <boost/core/nvp.hpp>
  9. #include <boost/histogram/detail/square.hpp>
  10. #include <boost/histogram/fwd.hpp> // for mean<>
  11. #include <type_traits> // for std::integral_constant, std::common_type
  12. namespace boost {
  13. namespace histogram {
  14. namespace accumulators {
  15. /** Calculates mean and variance of sample.
  16. Uses Welfords's incremental algorithm to improve the numerical
  17. stability of mean and variance computation.
  18. */
  19. template <class ValueType>
  20. class mean {
  21. public:
  22. using value_type = ValueType;
  23. using const_reference = const value_type&;
  24. mean() = default;
  25. /// Allow implicit conversion from mean<T>.
  26. template <class T>
  27. mean(const mean<T>& o) noexcept
  28. : sum_{o.sum_}, mean_{o.mean_}, sum_of_deltas_squared_{o.sum_of_deltas_squared_} {}
  29. /// Initialize to external count, mean, and variance.
  30. mean(const_reference n, const_reference mean, const_reference variance) noexcept
  31. : sum_(n), mean_(mean), sum_of_deltas_squared_(variance * (n - 1)) {}
  32. /// Insert sample x.
  33. void operator()(const_reference x) noexcept {
  34. sum_ += static_cast<value_type>(1);
  35. const auto delta = x - mean_;
  36. mean_ += delta / sum_;
  37. sum_of_deltas_squared_ += delta * (x - mean_);
  38. }
  39. /// Insert sample x with weight w.
  40. void operator()(const weight_type<value_type>& w, const_reference x) noexcept {
  41. sum_ += w.value;
  42. const auto delta = x - mean_;
  43. mean_ += w.value * delta / sum_;
  44. sum_of_deltas_squared_ += w.value * delta * (x - mean_);
  45. }
  46. /// Add another mean accumulator.
  47. mean& operator+=(const mean& rhs) noexcept {
  48. if (rhs.sum_ == 0) return *this;
  49. /*
  50. sum_of_deltas_squared
  51. = sum_i (x_i - mu)^2
  52. = sum_i (x_i - mu)^2 + sum_k (x_k - mu)^2
  53. = sum_i (x_i - mu1 + (mu1 - mu))^2 + sum_k (x_k - mu2 + (mu2 - mu))^2
  54. first part:
  55. sum_i (x_i - mu1 + (mu1 - mu))^2
  56. = sum_i (x_i - mu1)^2 + n1 (mu1 - mu))^2 + 2 (mu1 - mu) sum_i (x_i - mu1)
  57. = sum_i (x_i - mu1)^2 + n1 (mu1 - mu))^2
  58. since sum_i (x_i - mu1) = n1 mu1 - n1 mu1 = 0
  59. Putting it together:
  60. sum_of_deltas_squared
  61. = sum_of_deltas_squared_1 + n1 (mu1 - mu))^2
  62. + sum_of_deltas_squared_2 + n2 (mu2 - mu))^2
  63. */
  64. const auto n1 = sum_;
  65. const auto mu1 = mean_;
  66. const auto n2 = rhs.sum_;
  67. const auto mu2 = rhs.mean_;
  68. sum_ += rhs.sum_;
  69. mean_ = (n1 * mu1 + n2 * mu2) / sum_;
  70. sum_of_deltas_squared_ += rhs.sum_of_deltas_squared_;
  71. sum_of_deltas_squared_ += n1 * detail::square(mean_ - mu1);
  72. sum_of_deltas_squared_ += n2 * detail::square(mean_ - mu2);
  73. return *this;
  74. }
  75. /** Scale by value.
  76. This acts as if all samples were scaled by the value.
  77. */
  78. mean& operator*=(const_reference s) noexcept {
  79. mean_ *= s;
  80. sum_of_deltas_squared_ *= s * s;
  81. return *this;
  82. }
  83. bool operator==(const mean& rhs) const noexcept {
  84. return sum_ == rhs.sum_ && mean_ == rhs.mean_ &&
  85. sum_of_deltas_squared_ == rhs.sum_of_deltas_squared_;
  86. }
  87. bool operator!=(const mean& rhs) const noexcept { return !operator==(rhs); }
  88. /** Return how many samples were accumulated.
  89. count() should be used to check whether value() and variance() are defined,
  90. see documentation of value() and variance(). count() can be used to compute
  91. the variance of the mean by dividing variance() by count().
  92. */
  93. const_reference count() const noexcept { return sum_; }
  94. /** Return mean value of accumulated samples.
  95. The result is undefined, if `count() < 1`.
  96. */
  97. const_reference value() const noexcept { return mean_; }
  98. /** Return variance of accumulated samples.
  99. The result is undefined, if `count() < 2`.
  100. */
  101. value_type variance() const noexcept { return sum_of_deltas_squared_ / (sum_ - 1); }
  102. template <class Archive>
  103. void serialize(Archive& ar, unsigned version) {
  104. if (version == 0) {
  105. // read only
  106. std::size_t sum;
  107. ar& make_nvp("sum", sum);
  108. sum_ = static_cast<value_type>(sum);
  109. } else {
  110. ar& make_nvp("sum", sum_);
  111. }
  112. ar& make_nvp("mean", mean_);
  113. ar& make_nvp("sum_of_deltas_squared", sum_of_deltas_squared_);
  114. }
  115. private:
  116. value_type sum_{};
  117. value_type mean_{};
  118. value_type sum_of_deltas_squared_{};
  119. };
  120. } // namespace accumulators
  121. } // namespace histogram
  122. } // namespace boost
  123. #ifndef BOOST_HISTOGRAM_DOXYGEN_INVOKED
  124. namespace boost {
  125. namespace serialization {
  126. template <class T>
  127. struct version;
  128. // version 1 for boost::histogram::accumulators::mean<T>
  129. template <class T>
  130. struct version<boost::histogram::accumulators::mean<T>> : std::integral_constant<int, 1> {
  131. };
  132. } // namespace serialization
  133. } // namespace boost
  134. namespace std {
  135. template <class T, class U>
  136. /// Specialization for boost::histogram::accumulators::mean.
  137. struct common_type<boost::histogram::accumulators::mean<T>,
  138. boost::histogram::accumulators::mean<U>> {
  139. using type = boost::histogram::accumulators::mean<common_type_t<T, U>>;
  140. };
  141. } // namespace std
  142. #endif
  143. #endif