Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
RHist.hxx
Go to the documentation of this file.
1 /// \file ROOT/RHist.h
2 /// \ingroup Hist ROOT7
3 /// \author Axel Naumann <axel@cern.ch>
4 /// \date 2015-03-23
5 /// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6 /// is welcome!
7 
8 /*************************************************************************
9  * Copyright (C) 1995-2015, Rene Brun and Fons Rademakers. *
10  * All rights reserved. *
11  * *
12  * For the licensing terms see $ROOTSYS/LICENSE. *
13  * For the list of contributors see $ROOTSYS/README/CREDITS. *
14  *************************************************************************/
15 
16 #ifndef ROOT7_RHist
17 #define ROOT7_RHist
18 
19 #include "ROOT/RSpan.hxx"
20 #include "ROOT/RAxis.hxx"
21 #include "ROOT/RHistBinIter.hxx"
22 #include "ROOT/RHistImpl.hxx"
23 #include "ROOT/RHistData.hxx"
24 #include <initializer_list>
25 
26 namespace ROOT {
27 namespace Experimental {
28 
29 // fwd declare for fwd declare for friend declaration in RHist...
30 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
31 class RHist;
32 
33 // fwd declare for friend declaration in RHist.
34 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
35 class RHist<DIMENSIONS, PRECISION, STAT...>
36 HistFromImpl(std::unique_ptr<typename RHist<DIMENSIONS, PRECISION, STAT...>::ImplBase_t> pHistImpl);
37 
38 /**
39  \class RHist
40  Histogram class for histograms with `DIMENSIONS` dimensions, where each
41  bin count is stored by a value of type `PRECISION`. STAT stores statistical
42  data of the entries filled into the histogram (bin content, uncertainties etc).
43 
44  A histogram counts occurrences of values or n-dimensional combinations thereof.
45  Contrary to for instance a `RTree`, a histogram combines adjacent values. The
46  resolution of this combination is defined by the axis binning, see e.g.
47  http://www.wikiwand.com/en/Histogram
48  */
49 
50 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
51 class RHist {
52 public:
53  /// The type of the `Detail::RHistImplBase` of this histogram.
54  using ImplBase_t =
55  Detail::RHistImplBase<Detail::RHistData<DIMENSIONS, PRECISION, std::vector<PRECISION>, STAT...>>;
56  /// The coordinates type: a `DIMENSIONS`-dimensional `std::array` of `double`.
57  using CoordArray_t = typename ImplBase_t::CoordArray_t;
58  /// The type of weights
59  using Weight_t = PRECISION;
60  /// Pointer type to `HistImpl_t::Fill`, for faster access.
61  using FillFunc_t = typename ImplBase_t::FillFunc_t;
62  /// Range.
63  using AxisRange_t = typename ImplBase_t::AxisIterRange_t;
64 
65  using const_iterator = Detail::RHistBinIter<ImplBase_t>;
66 
67  /// Number of dimensions of the coordinates
68  static constexpr int GetNDim() noexcept { return DIMENSIONS; }
69 
70  RHist() = default;
71  RHist(RHist &&) = default;
72  RHist(const RHist &other): fImpl(other.fImpl->Clone()), fFillFunc(other.fFillFunc)
73  {}
74 
75  /// Create a histogram from an `array` of axes (`RAxisConfig`s). Example code:
76  ///
77  /// Construct a 1-dimensional histogram that can be filled with `floats`s.
78  /// The axis has 10 bins between 0. and 1. The two outermost sets of curly
79  /// braces are to reach the initialization of the `std::array` elements; the
80  /// inner one is for the initialization of a `RAxisCoordinate`.
81  ///
82  /// RHist<1,float> h1f({{ {10, 0., 1.} }});
83  ///
84  /// Construct a 2-dimensional histogram, with the first axis as before, and
85  /// the second axis having non-uniform ("irregular") binning, where all bin-
86  /// edges are specified. As this is itself an array it must be enclosed by
87  /// double curlies.
88  ///
89  /// RHist<2,int> h2i({{ {10, 0., 1.}, {{-1., 0., 1., 10., 100.}} }});
90  explicit RHist(std::array<RAxisConfig, DIMENSIONS> axes);
91 
92  /// Constructor overload taking the histogram title
93  RHist(std::string_view histTitle, std::array<RAxisConfig, DIMENSIONS> axes);
94 
95  /// Constructor overload that's only available for a 1-dimensional histogram.
96  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 1>::type>
97  explicit RHist(const RAxisConfig &xaxis): RHist(std::array<RAxisConfig, 1>{{xaxis}})
98  {}
99 
100  /// Constructor overload that's only available for a 1-dimensional histogram,
101  /// also passing the histogram title.
102  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 1>::type>
103  RHist(std::string_view histTitle, const RAxisConfig &xaxis): RHist(histTitle, std::array<RAxisConfig, 1>{{xaxis}})
104  {}
105 
106  /// Constructor overload that's only available for a 2-dimensional histogram.
107  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 2>::type>
108  RHist(const RAxisConfig &xaxis, const RAxisConfig &yaxis): RHist(std::array<RAxisConfig, 2>{{xaxis, yaxis}})
109  {}
110 
111  /// Constructor overload that's only available for a 2-dimensional histogram,
112  /// also passing the histogram title.
113  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 2>::type>
114  RHist(std::string_view histTitle, const RAxisConfig &xaxis, const RAxisConfig &yaxis)
115  : RHist(histTitle, std::array<RAxisConfig, 2>{{xaxis, yaxis}})
116  {}
117 
118  /// Constructor overload that's only available for a 3-dimensional histogram.
119  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 3>::type>
120  RHist(const RAxisConfig &xaxis, const RAxisConfig &yaxis, const RAxisConfig &zaxis)
121  : RHist(std::array<RAxisConfig, 3>{{xaxis, yaxis, zaxis}})
122  {}
123 
124  /// Constructor overload that's only available for a 3-dimensional histogram,
125  /// also passing the histogram title.
126  template <int ENABLEIF_NDIM = DIMENSIONS, class = typename std::enable_if<ENABLEIF_NDIM == 3>::type>
127  RHist(std::string_view histTitle, const RAxisConfig &xaxis, const RAxisConfig &yaxis, const RAxisConfig &zaxis)
128  : RHist(histTitle, std::array<RAxisConfig, 3>{{xaxis, yaxis, zaxis}})
129  {}
130 
131  /// Access the ImplBase_t this RHist points to.
132  ImplBase_t *GetImpl() const noexcept { return fImpl.get(); }
133 
134  /// "Steal" the ImplBase_t this RHist points to.
135  std::unique_ptr<ImplBase_t> TakeImpl() && noexcept { return std::move(fImpl); }
136 
137  /// Add `weight` to the bin containing coordinate `x`.
138  void Fill(const CoordArray_t &x, Weight_t weight = (Weight_t)1) noexcept { (fImpl.get()->*fFillFunc)(x, weight); }
139 
140  /// For each coordinate in `xN`, add `weightN[i]` to the bin at coordinate
141  /// `xN[i]`. The sizes of `xN` and `weightN` must be the same. This is more
142  /// efficient than many separate calls to `Fill()`.
143  void FillN(const std::span<const CoordArray_t> xN, const std::span<const Weight_t> weightN) noexcept
144  {
145  fImpl->FillN(xN, weightN);
146  }
147 
148  /// Convenience overload: `FillN()` with weight 1.
149  void FillN(const std::span<const CoordArray_t> xN) noexcept { fImpl->FillN(xN); }
150 
151  /// Get the number of entries this histogram was filled with.
152  int64_t GetEntries() const noexcept { return fImpl->GetStat().GetEntries(); }
153 
154  /// Get the content of the bin at `x`.
155  Weight_t GetBinContent(const CoordArray_t &x) const { return fImpl->GetBinContent(x); }
156 
157  /// Get the uncertainty on the content of the bin at `x`.
158  double GetBinUncertainty(const CoordArray_t &x) const { return fImpl->GetBinUncertainty(x); }
159 
160  const_iterator begin() const { return const_iterator(*fImpl); }
161 
162  const_iterator end() const { return const_iterator(*fImpl, fImpl->GetNBins()); }
163 
164  /// Swap *this and other.
165  ///
166  /// Very efficient; swaps the `fImpl` pointers.
167  void swap(RHist<DIMENSIONS, PRECISION, STAT...> &other) noexcept
168  {
169  std::swap(fImpl, other.fImpl);
170  std::swap(fFillFunc, other.fFillFunc);
171  }
172 
173 private:
174  std::unique_ptr<ImplBase_t> fImpl; ///< The actual histogram implementation
175  FillFunc_t fFillFunc = nullptr; ///<! Pinter to RHistImpl::Fill() member function
176 
177  friend RHist HistFromImpl<>(std::unique_ptr<ImplBase_t>);
178 };
179 
180 /// RHist with no STAT parameter uses RHistStatContent by default.
181 template <int DIMENSIONS, class PRECISION>
182 class RHist<DIMENSIONS, PRECISION>: public RHist<DIMENSIONS, PRECISION, RHistStatContent> {
183  using RHist<DIMENSIONS, PRECISION, RHistStatContent>::RHist;
184 };
185 
186 /// Swap two histograms.
187 ///
188 /// Very efficient; swaps the `fImpl` pointers.
189 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
190 void swap(RHist<DIMENSIONS, PRECISION, STAT...> &a, RHist<DIMENSIONS, PRECISION, STAT...> &b) noexcept
191 {
192  a.swap(b);
193 };
194 
195 namespace Internal {
196 /**
197  Generate RHist::fImpl from RHist constructor arguments.
198  */
199 template <int NDIM, int IDIM, class DATA, class... PROCESSEDAXISCONFIG>
200 struct RHistImplGen {
201  /// Select the template argument for the next axis type, and "recurse" into
202  /// RHistImplGen for the next axis.
203  template <RAxisConfig::EKind KIND>
204  std::unique_ptr<Detail::RHistImplBase<DATA>>
205  MakeNextAxis(std::string_view title, const std::array<RAxisConfig, NDIM> &axes,
206  PROCESSEDAXISCONFIG... processedAxisArgs)
207  {
208  using NextAxis_t = typename AxisConfigToType<KIND>::Axis_t;
209  NextAxis_t nextAxis = AxisConfigToType<KIND>()(axes[IDIM]);
210  using HistImpl_t = RHistImplGen<NDIM, IDIM + 1, DATA, PROCESSEDAXISCONFIG..., NextAxis_t>;
211  return HistImpl_t()(title, axes, processedAxisArgs..., nextAxis);
212  }
213 
214  /// Make a RHistImpl-derived object reflecting the RAxisConfig array.
215  ///
216  /// Delegate to the appropriate MakeNextAxis instantiation, depending on the
217  /// axis type selected in the RAxisConfig.
218  /// \param axes - `RAxisConfig` objects describing the axis of the resulting
219  /// RHistImpl.
220  /// \param statConfig - the statConfig parameter to be passed to the RHistImpl
221  /// \param processedAxisArgs - the RAxisBase-derived axis objects describing the
222  /// axes of the resulting RHistImpl. There are `IDIM` of those; in the end
223  /// (`IDIM` == `GetNDim()`), all `axes` have been converted to
224  /// `processedAxisArgs` and the RHistImpl constructor can be invoked, passing
225  /// the `processedAxisArgs`.
226  std::unique_ptr<Detail::RHistImplBase<DATA>> operator()(std::string_view title,
227  const std::array<RAxisConfig, NDIM> &axes,
228  PROCESSEDAXISCONFIG... processedAxisArgs)
229  {
230  switch (axes[IDIM].GetKind()) {
231  case RAxisConfig::kEquidistant: return MakeNextAxis<RAxisConfig::kEquidistant>(title, axes, processedAxisArgs...);
232  case RAxisConfig::kGrow: return MakeNextAxis<RAxisConfig::kGrow>(title, axes, processedAxisArgs...);
233  case RAxisConfig::kIrregular: return MakeNextAxis<RAxisConfig::kIrregular>(title, axes, processedAxisArgs...);
234  default: R__ERROR_HERE("HIST") << "Unhandled axis kind";
235  }
236  return nullptr;
237  }
238 };
239 
240 /// Generate RHist::fImpl from constructor arguments; recursion end.
241 template <int NDIM, class DATA, class... PROCESSEDAXISCONFIG>
242 /// Create the histogram, now that all axis types and initializer objects are
243 /// determined.
244 struct RHistImplGen<NDIM, NDIM, DATA, PROCESSEDAXISCONFIG...> {
245  using HistImplBase_t = ROOT::Experimental::Detail::RHistImplBase<DATA>;
246  std::unique_ptr<HistImplBase_t>
247  operator()(std::string_view title, const std::array<RAxisConfig, DATA::GetNDim()> &, PROCESSEDAXISCONFIG... axisArgs)
248  {
249  using HistImplt_t = Detail::RHistImpl<DATA, PROCESSEDAXISCONFIG...>;
250  return std::make_unique<HistImplt_t>(title, axisArgs...);
251  }
252 };
253 } // namespace Internal
254 
255 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
256 RHist<DIMENSIONS, PRECISION, STAT...>::RHist(std::string_view title, std::array<RAxisConfig, DIMENSIONS> axes)
257  : fImpl{std::move(
258  Internal::RHistImplGen<RHist::GetNDim(), 0,
259  Detail::RHistData<DIMENSIONS, PRECISION, std::vector<PRECISION>, STAT...>>()(
260  title, axes))}
261 {
262  fFillFunc = fImpl->GetFillFunc();
263 }
264 
265 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
266 RHist<DIMENSIONS, PRECISION, STAT...>::RHist(std::array<RAxisConfig, DIMENSIONS> axes): RHist("", axes)
267 {}
268 
269 /// Adopt an external, stand-alone RHistImpl. The RHist will take ownership.
270 template <int DIMENSIONS, class PRECISION, template <int D_, class P_> class... STAT>
271 RHist<DIMENSIONS, PRECISION, STAT...>
272 HistFromImpl(std::unique_ptr<typename RHist<DIMENSIONS, PRECISION, STAT...>::ImplBase_t> pHistImpl)
273 {
274  RHist<DIMENSIONS, PRECISION, STAT...> ret;
275  ret.fFillFunc = pHistImpl->GetFillFunc();
276  std::swap(ret.fImpl, pHistImpl);
277  return ret;
278 };
279 
280 /// \name RHist Typedefs
281 ///\{ Convenience typedefs (ROOT6-compatible type names)
282 
283 // Keep them as typedefs, to make sure old-style documentation tools can understand them.
284 using RH1D = RHist<1, double, RHistStatContent, RHistStatUncertainty>;
285 using RH1F = RHist<1, float, RHistStatContent, RHistStatUncertainty>;
286 using RH1C = RHist<1, char, RHistStatContent>;
287 using RH1I = RHist<1, int, RHistStatContent>;
288 using RH1LL = RHist<1, int64_t, RHistStatContent>;
289 
290 using RH2D = RHist<2, double, RHistStatContent, RHistStatUncertainty>;
291 using RH2F = RHist<2, float, RHistStatContent, RHistStatUncertainty>;
292 using RH2C = RHist<2, char, RHistStatContent>;
293 using RH2I = RHist<2, int, RHistStatContent>;
294 using RH2LL = RHist<2, int64_t, RHistStatContent>;
295 
296 using RH3D = RHist<3, double, RHistStatContent, RHistStatUncertainty>;
297 using RH3F = RHist<3, float, RHistStatContent, RHistStatUncertainty>;
298 using RH3C = RHist<3, char, RHistStatContent>;
299 using RH3I = RHist<3, int, RHistStatContent>;
300 using RH3LL = RHist<3, int64_t, RHistStatContent>;
301 ///\}
302 
303 /// Add two histograms. This is the generic, inefficient version for now; it
304 /// assumes no matching axes.
305 template <int DIMENSIONS, class PRECISION_TO, class PRECISION_FROM,
306  template <int D_, class P_> class... STAT_TO,
307  template <int D_, class P_> class... STAT_FROM>
308 void Add(RHist<DIMENSIONS, PRECISION_TO, STAT_TO...> &to, const RHist<DIMENSIONS, PRECISION_FROM, STAT_FROM...> &from)
309 {
310  auto toImpl = to.GetImpl();
311  auto fillFuncTo = toImpl->GetFillFunc();
312  using HistFrom_t = RHist<DIMENSIONS, PRECISION_FROM, STAT_FROM...>;
313  using FromCoord_t = typename HistFrom_t::CoordArray_t;
314  using FromWeight_t = typename HistFrom_t::Weight_t;
315  auto add = [fillFuncTo, toImpl](const FromCoord_t &x, FromWeight_t c) {
316  (toImpl->*fillFuncTo)(x, c);
317  // RODO: something nice with the uncertainty - depending on whether `to` cares
318  };
319  from.GetImpl()->ApplyXC(add);
320 }
321 
322 } // namespace Experimental
323 } // namespace ROOT
324 
325 #endif