LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
StatCollector.h
Go to the documentation of this file.
1 
13 #ifndef LARDATA_UTILITIES_STATCOLLECTOR_H
14 #define LARDATA_UTILITIES_STATCOLLECTOR_H
15 
16 // C/C++ standard libraries
17 #include <algorithm> // std::for_each()
18 #include <array>
19 #include <cmath> // std::sqrt()
20 #include <initializer_list>
21 #include <iterator> // std::begin(), std::end()
22 #include <limits> // std::numeric_limits<>
23 #include <stdexcept> // std::range_error
24 #include <tuple>
25 #include <utility> // std::forward()
26 
27 namespace lar {
28  namespace util {
29 
31  struct identity {
32  template <typename T>
33  constexpr auto operator()(T&& v) const noexcept -> decltype(std::forward<T>(v))
34  {
35  return std::forward<T>(v);
36  } // operator()
37  }; // class identity
38 
39  namespace details {
40 
43  template <typename W>
44  class WeightTracker {
45  public:
46  using Weight_t = W;
47 
50  {
51  ++n;
52  w += weight;
53  }
54 
56  void clear()
57  {
58  n = 0;
59  w = Weight_t(0);
60  }
61 
63  int N() const { return n; }
64 
66  Weight_t Weights() const { return w; }
67 
73  Weight_t AverageWeight() const;
74 
76  template <typename V>
77  static constexpr V sqr(V const& v)
78  {
79  return v * v;
80  }
81 
82  protected:
83  int n = 0;
85 
86  }; // class WeightTracker<>
87 
97  template <unsigned int PWR, typename T, typename W = T>
98  class DataTracker {
99  public:
100  static constexpr unsigned int Power = PWR;
101  static_assert(Power >= 1, "DataTracker must have at least power 1");
102 
103  using Data_t = T;
104  using Weight_t = T;
105 
108 
110  void add(Data_t v, Weight_t w)
111  {
112  Weight_t x = w;
113  for (size_t i = 0; i < Power; ++i)
114  sums[i] += (x *= v);
115  }
116 
118  void clear() { sums.fill(Data_t(0)); }
119 
121  template <unsigned int N>
122  Weight_t SumN() const
123  {
124  static_assert((N > 0) && (N <= Power), "Invalid sum of power requested");
125  return sums[N - 1];
126  } // SumN()
127 
129  Weight_t Sum(unsigned int n) const { return sums[n - 1]; }
130 
132  Weight_t Sum() const { return SumN<1>(); }
133 
134  protected:
135  std::array<Weight_t, Power> sums;
136 
137  }; // class DataTracker<>
138 
144  template <typename T, typename W = T, unsigned int PWR = 2>
145  class DataTracker2 : public DataTracker<PWR, T, W> {
147  public:
148  using Base_t::Power;
149  static_assert(Power >= 2, "DataTracker2 must have Power >= 2");
150  using Weight_t = typename Base_t::Weight_t;
151 
153  Weight_t SumSq() const { return Base_t::sums[1]; }
154  //{ return Base_t::SumN<2>(); } // I can't make gcc 4.9.1 digest this...
155 
156  }; // class DataTracker2
157 
163  template <typename T, typename W = T, unsigned int PWR = 3>
164  class DataTracker3 : public DataTracker2<T, W, PWR> {
166  public:
167  using Base_t::Power;
168  static_assert(Power >= 3, "DataTracker3 must have Power >= 3");
169  using Weight_t = typename Base_t::Weight_t;
170 
172  Weight_t SumCube() const { return Base_t::sums[2]; }
173  //{ return Base_t::SumN<3>(); } // I can't make gcc 4.9.1 digest this...
174 
175  }; // class DataTracker3
176 
177  } // namespace details
178 
242  template <typename T, typename W = T>
243  class StatCollector : protected details::WeightTracker<W> {
245  using Base_t::sqr;
246 
247  public:
249  using Data_t = T;
250  using Weight_t = typename Base_t::Weight_t;
251 
252  // default constructor, destructor and all the rest
253 
256 
258  void add(Data_t value, Weight_t weight = Weight_t(1.0));
259 
269  template <typename Iter>
270  void add_unweighted(Iter begin, Iter end)
271  {
272  add_unweighted(begin, end, identity());
273  }
274 
288  template <typename Iter, typename Pred>
289  void add_unweighted(Iter begin, Iter end, Pred extractor);
290 
305  template <typename Cont, typename Pred>
306  void add_unweighted(Cont cont, Pred extractor)
307  {
308  add_unweighted(std::begin(cont), std::end(cont), extractor);
309  }
310 
320  template <typename Cont>
321  void add_unweighted(Cont cont)
322  {
323  add_unweighted(std::begin(cont), std::end(cont));
324  }
325 
349  template <typename VIter, typename WIter, typename VPred, typename WPred = identity>
350  void add_weighted(VIter begin_value,
351  VIter end_value,
352  WIter begin_weight,
353  VPred value_extractor,
354  WPred weight_extractor = WPred());
355 
368  template <typename Iter>
369  void add_weighted(Iter begin, Iter end);
370 
380  template <typename Cont>
381  void add_weighted(Cont cont)
382  {
383  add_weighted(std::begin(cont), std::end(cont));
384  }
385 
387 
389  void clear();
390 
393 
395  int N() const { return Base_t::N(); }
396 
398  Weight_t Weights() const { return Base_t::Weights(); }
399 
401  Weight_t Sum() const { return x.Sum(); }
402 
404  Weight_t SumSq() const { return x.SumSq(); }
405 
411  Weight_t Average() const;
412 
418  Weight_t Variance() const;
419 
426  Weight_t RMS() const;
427 
433  Weight_t AverageWeight() const { return Base_t::AverageWeight(); }
434 
436 
437  protected:
439 
441 
442  }; // class StatCollector<>
443 
476  template <typename T, typename W = T>
478  public:
480  using Base_t::sqr;
481 
482  public:
484  using Data_t = T;
485  using Weight_t = typename Base_t::Weight_t;
486 
487  using Pair_t = std::tuple<Data_t, Data_t>;
488  using WeightedPair_t = std::tuple<Data_t, Data_t, Weight_t>;
489 
490  // default constructor, destructor and all the rest
491 
494 
496  void add(Data_t x, Data_t y, Weight_t weight = Weight_t(1.0));
498 
500  {
501  add(std::get<0>(value), std::get<1>(value), weight);
502  }
503 
504  void add(WeightedPair_t value)
505  {
506  add(std::get<0>(value), std::get<1>(value), std::get<2>(value));
507  }
509 
519  template <typename Iter>
520  void add_unweighted(Iter begin, Iter end)
521  {
522  add_unweighted(begin, end, identity());
523  }
524 
538  template <typename Iter, typename Pred>
539  void add_unweighted(Iter begin, Iter end, Pred extractor);
540 
555  template <typename Cont, typename Pred>
556  void add_unweighted(Cont cont, Pred extractor)
557  {
558  add_unweighted(std::begin(cont), std::end(cont), extractor);
559  }
560 
570  template <typename Cont>
571  void add_unweighted(Cont cont)
572  {
573  add_unweighted(std::begin(cont), std::end(cont));
574  }
575 
599  template <typename VIter, typename WIter, typename VPred, typename WPred = identity>
600  void add_weighted(VIter begin_value,
601  VIter end_value,
602  WIter begin_weight,
603  VPred value_extractor,
604  WPred weight_extractor = WPred());
605 
617  template <typename Iter>
618  void add_weighted(Iter begin, Iter end);
619 
629  template <typename Cont>
630  void add_weighted(Cont cont)
631  {
632  add_weighted(std::begin(cont), std::end(cont));
633  }
634 
636 
638  void clear();
639 
642 
644  int N() const { return Base_t::N(); }
645 
647  Weight_t Weights() const { return Base_t::Weights(); }
648 
650  Weight_t SumX() const { return x.Sum(); }
651 
653  Weight_t SumY() const { return y.Sum(); }
654 
656  Weight_t SumSqX() const { return x.SumSq(); }
657 
659  Weight_t SumSqY() const { return y.SumSq(); }
660 
662  Weight_t SumXY() const { return sum_xy; }
663 
669  Weight_t AverageX() const;
670 
676  Weight_t AverageY() const;
677 
683  Weight_t VarianceX() const;
684 
690  Weight_t VarianceY() const;
691 
697  Weight_t Covariance() const;
698 
705  Weight_t RMSx() const;
706 
713  Weight_t RMSy() const;
714 
721  Weight_t LinearCorrelation() const;
722 
728  Weight_t AverageWeight() const { return Base_t::AverageWeight(); }
729 
731 
732  protected:
734 
737  Weight_t sum_xy = Weight_t(0);
738 
739  }; // class StatCollector2D<>
740 
750  template <typename T>
752  public:
753  using Data_t = T;
755 
757  MinMaxCollector() = default;
759 
761  MinMaxCollector(std::initializer_list<Data_t> init) { add(init); }
762 
769  template <typename Iter>
771  {
772  add(begin, end);
773  }
775 
776  // default copy and move constructor and assignment, and destructor
777 
780 
785  This_t& add(Data_t value);
786 
792  This_t& add(std::initializer_list<Data_t> values);
793 
801  template <typename Iter>
802  This_t& add(Iter begin, Iter end);
804 
806  bool has_data() const { return minimum <= maximum; }
807 
809  Data_t min() const { return minimum; }
810 
812  Data_t max() const { return maximum; }
813 
815  void clear();
816 
817  protected:
819  Data_t minimum = std::numeric_limits<Data_t>::max();
820 
822  Data_t maximum = std::numeric_limits<Data_t>::min();
823 
824  }; // class MinMaxCollector<>
825 
826  } // namespace util
827 } // namespace lar
828 
829 //==============================================================================
830 //=== template implementation
831 //==============================================================================
832 
833 //******************************************************************************
834 //*** details::WeightTracker<>
835 //***
836 
837 template <typename W>
840 {
841  if (N() == 0) throw std::range_error("WeightTracker<>::AverageWeight(): divide by 0");
842  return Weights() / N();
843 } // details::WeightTracker<W>::AverageWeight()
844 
845 //******************************************************************************
846 //*** StatCollector<>
847 //***
848 
849 template <typename T, typename W>
851 {
852  Base_t::add(weight);
853  x.add(value, weight);
854 } // StatCollector<T, W>::add()
855 
856 template <typename T, typename W>
857 template <typename Iter, typename Pred>
859 {
860  std::for_each(begin, end, [this, extractor](auto item) { this->add(extractor(item)); });
861 } // StatCollector<T, W>::add_unweighted(Iter, Iter, Pred)
862 
863 template <typename T, typename W>
864 template <typename VIter, typename WIter, typename VPred, typename WPred>
866  VIter end_value,
867  WIter begin_weight,
868  VPred value_extractor,
869  WPred weight_extractor /* = WPred() */
870 )
871 {
872  while (begin_value != end_value) {
873  add(value_extractor(*begin_value), weight_extractor(*begin_weight));
874  ++begin_value;
875  ++begin_weight;
876  } // while
877 } // StatCollector<T, W>::add_weighted(VIter, VIter, WIter, VPred, WPred)
878 
879 template <typename T, typename W>
880 template <typename Iter>
882 {
883 
884  std::for_each(begin, end, [this](auto p) { this->add(p.first, p.second); });
885 
886 } // StatCollector<T, W>::add_weighted(Iter, Iter)
887 
888 template <typename T, typename W>
890 {
891  Base_t::clear();
892  x.clear();
893 } // StatCollector<T, W>::clear()
894 
895 template <typename T, typename W>
897 {
898  if (Weights() == Weight_t(0)) throw std::range_error("StatCollector<>::Average(): divide by 0");
899  return Sum() / Weights();
900 } // StatCollector<T, W>::Average()
901 
902 template <typename T, typename W>
904 {
905  if (Weights() == Weight_t(0)) throw std::range_error("StatCollector<>::Variance(): divide by 0");
906  return std::max(Weight_t(0), (SumSq() - sqr(Sum()) / Weights()) / Weights());
907 } // StatCollector<T, W>::Variance()
908 
909 template <typename T, typename W>
911 {
912  const Weight_t rms2 = Variance();
913  if (rms2 < Weight_t(0)) return 0.;
914  // throw std::range_error("StatCollector<>::RMS(): negative RMS^2");
915  return std::sqrt(rms2);
916 } // StatCollector<T, W>::RMS()
917 
918 //******************************************************************************
919 //*** StatCollector2D<>
920 //***
921 
922 template <typename T, typename W>
924  Data_t y_value,
925  Weight_t weight /* = Weight_t(1.0) */)
926 {
927  Base_t::add(weight);
928  x.add(x_value, weight);
929  y.add(y_value, weight);
930  sum_xy += weight * x_value * y_value;
931 } // StatCollector2D<T, W>::add()
932 
933 template <typename T, typename W>
934 template <typename Iter, typename Pred>
936 {
937  std::for_each(begin, end, [this, extractor](auto item) { this->add(extractor(item)); });
938 } // StatCollector2D<T, W>::add_unweighted(Iter, Iter, Pred)
939 
940 template <typename T, typename W>
941 template <typename VIter, typename WIter, typename VPred, typename WPred>
943  VIter end_value,
944  WIter begin_weight,
945  VPred value_extractor,
946  WPred weight_extractor /* = WPred() */
947 )
948 {
949  while (begin_value != end_value) {
950  add(value_extractor(*begin_value), weight_extractor(*begin_weight));
951  ++begin_value;
952  ++begin_weight;
953  } // while
954 } // StatCollector2D<T, W>::add_weighted(VIter, VIter, WIter, VPred, WPred)
955 
956 template <typename T, typename W>
957 template <typename Iter>
959 {
960 
961  std::for_each(begin, end, [this](auto p) { this->add(p); });
962 
963 } // StatCollector2D<T, W>::add_weighted(Iter, Iter)
964 
965 template <typename T, typename W>
967 {
968  Base_t::clear();
969  x.clear();
970  y.clear();
971  sum_xy = Weight_t(0);
972 } // StatCollector<T, W>::clear()
973 
974 template <typename T, typename W>
976  const
977 {
978  if (Weights() == Weight_t(0))
979  throw std::range_error("StatCollector2D<>::AverageX(): divide by 0");
980  return SumX() / Weights();
981 } // StatCollector2D<T, W>::AverageX()
982 
983 template <typename T, typename W>
985  const
986 {
987  if (Weights() == Weight_t(0))
988  throw std::range_error("StatCollector2D<>::VarianceX(): divide by 0");
989  return (SumSqX() - sqr(SumX()) / Weights()) / Weights();
990 } // StatCollector2D<T, W>::VarianceX()
991 
992 template <typename T, typename W>
994 {
995  const Weight_t rms2 = VarianceX();
996  if (rms2 < Weight_t(0)) return 0.;
997  // throw std::range_error("StatCollector2D<>::RMSx(): negative RMS^2");
998  return std::sqrt(rms2);
999 } // StatCollector2D<T, W>::RMSx()
1000 
1001 template <typename T, typename W>
1003  const
1004 {
1005  if (Weights() == Weight_t(0))
1006  throw std::range_error("StatCollector2D<>::AverageY(): divide by 0");
1007  return SumY() / Weights();
1008 } // StatCollector2D<T, W>::AverageY()
1009 
1010 template <typename T, typename W>
1012  const
1013 {
1014  if (Weights() == Weight_t(0))
1015  throw std::range_error("StatCollector2D<>::VarianceY(): divide by 0");
1016  return (SumSqY() - sqr(SumY()) / Weights()) / Weights();
1017 } // StatCollector2D<T, W>::VarianceY()
1018 
1019 template <typename T, typename W>
1021  const
1022 {
1023  if (Weights() == Weight_t(0))
1024  throw std::range_error("StatCollector2D<>::Covariance(): divide by 0");
1025  return (SumXY() - SumX() * SumY() / Weights()) / Weights();
1026 } // StatCollector2D<T, W>::VarianceY()
1027 
1028 template <typename T, typename W>
1030 {
1031  const Weight_t rms2 = VarianceY();
1032  if (rms2 < Weight_t(0)) return 0.;
1033  // throw std::range_error("StatCollector2D<>::RMSy(): negative RMS^2");
1034  return std::sqrt(rms2);
1035 } // StatCollector2D<T, W>::RMSy()
1036 
1037 template <typename T, typename W>
1040 {
1041  if (Weights() == Data_t(0))
1042  throw std::range_error("StatCollector2D<>::LinearCorrelation(): divide by 0");
1043 
1044  const Weight_t var_prod = VarianceX() * VarianceY();
1045  if (var_prod <= Weight_t(0))
1046  throw std::range_error("StatCollector2D<>::LinearCorrelation(): variance is 0");
1047 
1048  return Covariance() / std::sqrt(var_prod);
1049 } // StatCollector2D<T, W>::LinearCorrelation()
1050 
1051 //******************************************************************************
1052 //*** MinMaxCollector
1053 //***
1054 
1055 template <typename T>
1057 {
1058  if (value < minimum) minimum = value;
1059  if (value > maximum) maximum = value;
1060  return *this;
1061 } // lar::util::MinMaxCollector<T>::add
1062 
1063 template <typename T>
1065  std::initializer_list<Data_t> values)
1066 {
1067  return add(values.begin(), values.end());
1068 }
1069 
1070 template <typename T>
1071 template <typename Iter>
1073 {
1074  std::for_each(begin, end, [this](Data_t value) { this->add(value); });
1075  return *this;
1076 } // lar::util::MinMaxCollector<T>::add(Iter)
1077 
1078 template <typename T>
1080 {
1081  minimum = std::numeric_limits<Data_t>::max();
1082  maximum = std::numeric_limits<Data_t>::min();
1083 } // lar::util::MinMaxCollector<T>::clear()
1084 
1085 //******************************************************************************
1086 
1087 #endif // LARDATA_UTILITIES_STATCOLLECTOR_H
Float_t x
Definition: compare.C:6
Weight_t Weights() const
Returns the sum of the weights.
Definition: StatCollector.h:66
void add(Data_t v, Weight_t w)
Adds the specified weight to the statistics.
Data_t max() const
Returns the accumulated maximum, or a very small number if no values.
void add_unweighted(Cont cont)
Adds all entries from a container, with weight 1.
bool has_data() const
Returns whether at least one datum has been added.
int N() const
Returns the number of entries added.
Definition: StatCollector.h:63
Namespace for general, non-LArSoft-specific utilities.
Definition: PIDAAlg.h:26
This_t & add(Data_t value)
Include a single value in the statistics.
void add_unweighted(Cont cont, Pred extractor)
Adds all entries from a container, with weight 1.
void add_weighted(VIter begin_value, VIter end_value, WIter begin_weight, VPred value_extractor, WPred weight_extractor=WPred())
Adds entries from a sequence with individually specified weights.
MinMaxCollector(std::initializer_list< Data_t > init)
Constructor: starts with parsing the specified data.
Weight_t SumSqY() const
Returns the weighted sum of the square of the y values.
void add(Data_t x, Data_t y, Weight_t weight=Weight_t(1.0))
Adds one entry with specified values and weight.
Weight_t AverageWeight() const
Returns the arithmetic average of the weights.
static constexpr V sqr(V const &v)
Returns the square of the specified value.
Definition: StatCollector.h:77
Weight_t SumSq() const
Returns the weighted sum of the square of the entries.
Float_t y
Definition: compare.C:6
Weight_t AverageWeight() const
Returns the arithmetic average of the weights.
void add(Pair_t value, Weight_t weight=Weight_t(1.0))
Adds one entry with specified values and weight.
Weight_t SumXY() const
Returns the weighted sum of the product of x and y values.
Weight_t SumCube() const
Returns the weighted sum of the square of the entries.
STL namespace.
MinMaxCollector(Iter begin, Iter end)
Include a sequence of values in the statistics.
Weight_t Covariance() const
Returns the covariance of the (x, y) pair.
typename Base_t::Weight_t Weight_t
type of the weight
Variable_t x
accumulator for variable x
void add(Weight_t weight)
Adds the specified weight to the statistics.
Definition: StatCollector.h:49
Weight_t RMS() const
Returns the root mean square.
Weight_t VarianceY() const
Returns the variance of the y values.
Variable_t y
accumulator for variable y
DataTracker()
Default constructor.
Weight_t Average() const
Returns the value average.
Weight_t Sum(unsigned int n) const
Returns the sum of the values to the power n (1 <= n <= 2, no check)
void add_unweighted(Iter begin, Iter end)
Adds entries from a sequence with weight 1.
void clear()
Resets the count.
Definition: StatCollector.h:56
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
Keeps track of the minimum and maximum value we observed.
void add_unweighted(Cont cont, Pred extractor)
Adds all entries from a container, with weight 1.
Weight_t Variance() const
Returns the square of the RMS of the values.
Data_t min() const
Returns the accumulated minimum, or a very large number if no values.
Weight_t Sum() const
Returns the weighted sum of the values.
int N() const
Returns the number of entries added.
Weight_t AverageWeight() const
Returns the arithmetic average of the weights.
Weight_t AverageY() const
Returns the y value average.
void add_weighted(Cont cont)
Adds entries from a sequence with individually specified weights.
decltype(auto) values(Coll &&coll)
Range-for loop helper iterating across the values of the specified collection.
Weight_t SumSqX() const
Returns the weighted sum of the square of the x values.
void add_unweighted(Iter begin, Iter end)
Adds entries from a sequence with weight 1.
constexpr T sqr(T v)
A unary functor returning its own argument (any type)
Definition: StatCollector.h:31
Weight_t SumN() const
Returns the sum of the values to the power N (1 <= N <= 2)
Weight_t AverageX() const
Returns the x value average.
float Data_t
type of data we collect
Weight_t SumSq() const
Returns the weighted sum of the square of the values.
Class tracking sums of variables up to a specified power.
Definition: StatCollector.h:98
int N() const
Returns the number of entries added.
Weight_t SumX() const
Returns the weighted sum of the x values.
void clear()
Clears all the statistics.
constexpr auto operator()(T &&v) const noexcept-> decltype(std::forward< T >(v))
Definition: StatCollector.h:33
double value
Definition: spectrum.C:18
void add_weighted(Cont cont)
Adds entries from a sequence with individually specified weights.
std::tuple< Data_t, Data_t > Pair_t
void clear()
Resets the count.
void clear()
Removes all statistics and reinitializes the object.
double weight
Definition: plottest35.C:25
void add(WeightedPair_t value)
Adds one entry with specified values and weight.
void add_weighted(VIter begin_value, VIter end_value, WIter begin_weight, VPred value_extractor, WPred weight_extractor=WPred())
Adds entries from a sequence with individually specified weights.
Variable_t x
accumulator for variable x
LArSoft-specific namespace.
Weight_t SumY() const
Returns the weighted sum of the y values.
Class tracking sums of variables up to power 2.
Collects statistics on two homogeneous quantities (weighted)
Weight_t LinearCorrelation() const
Returns the linear correlation.
T Data_t
type of the data
Weight_t RMSy() const
Returns the standard deviation of the y sample.
Char_t n[5]
typename Base_t::Weight_t Weight_t
type of the weight
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
Weight_t RMSx() const
Returns the standard deviation of the x sample.
Weight_t Weights() const
Returns the sum of the weights.
Weight_t VarianceX() const
Returns the variance of the x values.
Weight_t Sum() const
Returns the weighted sum of the entries.
void add_unweighted(Cont cont)
Adds all entries from a container, with weight 1.
void clear()
Clears all the statistics.
Float_t w
Definition: plot.C:20
Collects statistics on a single quantity (weighted)
Class tracking sums of variables up to power 2.
vec_iX clear()
Weight_t Weights() const
Returns the sum of the weights.
void add(Data_t value, Weight_t weight=Weight_t(1.0))
Adds one entry with specified value and weight.
std::tuple< Data_t, Data_t, Weight_t > WeightedPair_t
std::array< Weight_t, Power > sums
T Data_t
type of the data