]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/boost/boost/histogram/accumulators/weighted_mean.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / histogram / accumulators / weighted_mean.hpp
index cc033c91b7208b2154bc265a24a8caea46751052..fdd5bc9f7a40b48e28bcd564e9cc7e37a32aedf9 100644 (file)
@@ -8,6 +8,7 @@
 #define BOOST_HISTOGRAM_ACCUMULATORS_WEIGHTED_MEAN_HPP
 
 #include <boost/core/nvp.hpp>
+#include <boost/histogram/detail/square.hpp>
 #include <boost/histogram/fwd.hpp> // for weighted_mean<>
 #include <boost/histogram/weight.hpp>
 #include <cassert>
@@ -31,7 +32,7 @@ public:
 
   weighted_mean() = default;
 
-  /// Allow implicit conversion from other weighted_means
+  /// Allow implicit conversion from other weighted_means.
   template <class T>
   weighted_mean(const weighted_mean<T>& o)
       : sum_of_weights_{o.sum_of_weights_}
@@ -39,7 +40,7 @@ public:
       , weighted_mean_{o.weighted_mean_}
       , sum_of_weighted_deltas_squared_{o.sum_of_weighted_deltas_squared_} {}
 
-  /// Initialize to external sum of weights, sum of weights squared, mean, and variance
+  /// Initialize to external sum of weights, sum of weights squared, mean, and variance.
   weighted_mean(const_reference wsum, const_reference wsum2, const_reference mean,
                 const_reference variance)
       : sum_of_weights_(wsum)
@@ -48,10 +49,10 @@ public:
       , sum_of_weighted_deltas_squared_(
             variance * (sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_)) {}
 
-  /// Insert sample x
+  /// Insert sample x.
   void operator()(const_reference x) { operator()(weight(1), x); }
 
-  /// Insert sample x with weight w
+  /// Insert sample x with weight w.
   void operator()(const weight_type<value_type>& w, const_reference x) {
     sum_of_weights_ += w.value;
     sum_of_weights_squared_ += w.value * w.value;
@@ -60,24 +61,33 @@ public:
     sum_of_weighted_deltas_squared_ += w.value * delta * (x - weighted_mean_);
   }
 
-  /// Add another weighted_mean
+  /// Add another weighted_mean.
   weighted_mean& operator+=(const weighted_mean& rhs) {
-    if (sum_of_weights_ != 0 || rhs.sum_of_weights_ != 0) {
-      const auto tmp =
-          weighted_mean_ * sum_of_weights_ + rhs.weighted_mean_ * rhs.sum_of_weights_;
-      sum_of_weights_ += rhs.sum_of_weights_;
-      sum_of_weights_squared_ += rhs.sum_of_weights_squared_;
-      weighted_mean_ = tmp / sum_of_weights_;
-    }
+    if (rhs.sum_of_weights_ == 0) return *this;
+
+    // see mean.hpp for derivation of correct formula
+
+    const auto n1 = sum_of_weights_;
+    const auto mu1 = weighted_mean_;
+    const auto n2 = rhs.sum_of_weights_;
+    const auto mu2 = rhs.weighted_mean_;
+
+    sum_of_weights_ += rhs.sum_of_weights_;
+    sum_of_weights_squared_ += rhs.sum_of_weights_squared_;
+    weighted_mean_ = (n1 * mu1 + n2 * mu2) / sum_of_weights_;
+
     sum_of_weighted_deltas_squared_ += rhs.sum_of_weighted_deltas_squared_;
+    sum_of_weighted_deltas_squared_ += n1 * detail::square(weighted_mean_ - mu1);
+    sum_of_weighted_deltas_squared_ += n2 * detail::square(weighted_mean_ - mu2);
+
     return *this;
   }
 
-  /** Scale by value
+  /** Scale by value.
 
    This acts as if all samples were scaled by the value.
   */
-  weighted_mean& operator*=(const_reference s) {
+  weighted_mean& operator*=(const_reference s) noexcept {
     weighted_mean_ *= s;
     sum_of_weighted_deltas_squared_ *= s * s;
     return *this;
@@ -92,26 +102,39 @@ public:
 
   bool operator!=(const weighted_mean& rhs) const noexcept { return !operator==(rhs); }
 
-  /// Return sum of weights
+  /// Return sum of weights.
   const_reference sum_of_weights() const noexcept { return sum_of_weights_; }
 
-  /// Return sum of weights squared (variance of weight distribution)
+  /// Return sum of weights squared (variance of weight distribution).
   const_reference sum_of_weights_squared() const noexcept {
     return sum_of_weights_squared_;
   }
 
+  /** Return effective counts.
+
+    This corresponds to the equivalent number of unweighted samples that would
+    have the same variance as this sample. count() should be used to check whether
+    value() and variance() are defined, see documentation of value() and variance().
+    count() can be used to compute the variance of the mean by dividing variance()
+    by count().
+  */
+  value_type count() const noexcept {
+    // see https://en.wikipedia.org/wiki/Effective_sample_size#weighted_samples
+    return detail::square(sum_of_weights_) / sum_of_weights_squared_;
+  }
+
   /** Return mean value of accumulated weighted samples.
 
-    The result is undefined, if `sum_of_weights() == 0`.
+    The result is undefined, if count() == 0.
   */
   const_reference value() const noexcept { return weighted_mean_; }
 
-  /** Return variance of accumulated weighted samples
+  /** Return variance of accumulated weighted samples.
 
-    The result is undefined, if `sum_of_weights() == 0` or
-    `sum_of_weights() == sum_of_weights_squared()`.
+    The result is undefined, if count() == 0 or count() == 1.
   */
   value_type variance() const {
+    // see https://en.wikipedia.org/wiki/Weighted_arithmetic_mean#Reliability_weights
     return sum_of_weighted_deltas_squared_ /
            (sum_of_weights_ - sum_of_weights_squared_ / sum_of_weights_);
   }