Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RVariableBinAxis.hxx
Go to the documentation of this file.
1/// \file
2/// \warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
3/// Feedback is welcome!
4
5#ifndef ROOT_RVariableBinAxis
6#define ROOT_RVariableBinAxis
7
8#include "RBinIndex.hxx"
9#include "RBinIndexRange.hxx"
10#include "RLinearizedIndex.hxx"
11#include "RSliceSpec.hxx"
12
13#include <cassert>
14#include <cmath>
15#include <cstddef>
16#include <cstdint>
17#include <stdexcept>
18#include <string>
19#include <utility>
20#include <vector>
21
22class TBuffer;
23
24namespace ROOT {
25namespace Experimental {
26
27/**
28An axis with variable bins defined by their edges.
29
30For example, the following creates an axis with 3 log-spaced bins:
31\code
32std::vector<double> binEdges = {1, 10, 100, 1000};
33ROOT::Experimental::RVariableBinAxis axis(binEdges);
34\endcode
35
36It is possible to disable underflow and overflow bins by passing `enableFlowBins = false`. In that case, arguments
37outside the axis will be silently discarded.
38
39\warning This is part of the %ROOT 7 prototype! It will change without notice. It might trigger earthquakes.
40Feedback is welcome!
41*/
43public:
45
46private:
47 /// The (ordered) edges of the normal bins
48 std::vector<double> fBinEdges;
49 /// Whether underflow and overflow bins are enabled
51
52public:
53 /// Construct an axis object with variable bins.
54 ///
55 /// \param[in] binEdges the (ordered) edges of the normal bins, must define at least one bin (i.e. size >= 2)
56 /// \param[in] enableFlowBins whether to enable underflow and overflow bins
57 explicit RVariableBinAxis(std::vector<double> binEdges, bool enableFlowBins = true)
59 {
60 if (fBinEdges.size() < 2) {
61 throw std::invalid_argument("must have >= 2 bin edges");
62 }
63 if (std::isnan(fBinEdges[0])) {
64 throw std::invalid_argument("bin egde 0 is NaN");
65 }
66 for (std::size_t i = 1; i < fBinEdges.size(); i++) {
67 if (std::isnan(fBinEdges[i])) {
68 throw std::invalid_argument("bin egde " + std::to_string(i) + " is NaN");
69 }
70 if (fBinEdges[i - 1] >= fBinEdges[i]) {
71 std::string msg = "binEdges must be sorted, but for bin " + std::to_string(i - 1) + ": ";
72 msg += std::to_string(fBinEdges[i - 1]) + " >= " + std::to_string(fBinEdges[i]);
73 throw std::invalid_argument(msg);
74 }
75 }
76 }
77
78 std::uint64_t GetNNormalBins() const { return fBinEdges.size() - 1; }
79 std::uint64_t GetTotalNBins() const { return fEnableFlowBins ? fBinEdges.size() + 1 : fBinEdges.size() - 1; }
80 const std::vector<double> &GetBinEdges() const { return fBinEdges; }
81 bool HasFlowBins() const { return fEnableFlowBins; }
82
84 {
85 return lhs.fBinEdges == rhs.fBinEdges && lhs.fEnableFlowBins == rhs.fEnableFlowBins;
86 }
87
88 /// Compute the linarized index for a single argument.
89 ///
90 /// If flow bins are disabled, the normal bins have indices \f$0\f$ to \f$fBinEdges.size() - 2\f$. Otherwise the
91 /// underflow bin has index \f$0\$, the indices of all normal bins shift by one, and the overflow bin has index
92 /// \f$fBinEdges.size()\f$. If the argument is outside all bin edges and the flow bins are disabled, the return value
93 /// is invalid.
94 ///
95 /// \param[in] x the argument
96 /// \return the linearized index that may be invalid
97 RLinearizedIndex ComputeLinearizedIndex(double x) const
98 {
99 bool underflow = x < fBinEdges.front();
100 // Put NaNs into overflow bin.
101 bool overflow = !(x < fBinEdges.back());
102 if (underflow) {
103 return {0, fEnableFlowBins};
104 } else if (overflow) {
105 return {fBinEdges.size(), fEnableFlowBins};
106 }
107
108 // TODO (for later): The following can be optimized with binary search...
109 for (std::size_t bin = 0; bin < fBinEdges.size() - 2; bin++) {
110 if (x < fBinEdges[bin + 1]) {
111 // If the underflow bin is enabled, shift the normal bins by one.
112 if (fEnableFlowBins) {
113 bin += 1;
114 }
115 return {bin, true};
116 }
117 }
118 std::size_t bin = fBinEdges.size() - 2;
119 // If the underflow bin is enabled, shift the normal bins by one.
120 if (fEnableFlowBins) {
121 bin += 1;
122 }
123 return {bin, true};
124 }
125
126 /// Get the linearized index for an RBinIndex.
127 ///
128 /// If flow bins are disabled, the normal bins have indices \f$0\f$ to \f$fBinEdges.size() - 2\f$. Otherwise the
129 /// underflow bin has index \f$0\$, the indices of all normal bins shift by one, and the overflow bin has index
130 /// \f$fBinEdges.size()\f$.
131 ///
132 /// \param[in] index the RBinIndex
133 /// \return the linearized index that may be invalid
135 {
136 if (index.IsUnderflow()) {
137 return {0, fEnableFlowBins};
138 } else if (index.IsOverflow()) {
139 return {fBinEdges.size(), fEnableFlowBins};
140 } else if (index.IsInvalid()) {
141 return {0, false};
142 }
143 assert(index.IsNormal());
144 std::uint64_t bin = index.GetIndex();
145 if (bin >= fBinEdges.size() - 1) {
146 // Index is out of range and invalid.
147 return {bin, false};
148 }
149 // If the underflow bin is enabled, shift the normal bins by one.
150 if (fEnableFlowBins) {
151 bin += 1;
152 }
153 return {bin, true};
154 }
155
156 /// Get the range of all normal bins.
157 ///
158 /// \return the bin index range from the first to the last normal bin, inclusive
160 {
162 }
163
164 /// Get a range of normal bins.
165 ///
166 /// \param[in] begin the begin of the bin index range (inclusive), must be normal
167 /// \param[in] end the end of the bin index range (exclusive), must be normal and >= begin
168 /// \return a bin index range \f$[begin, end)\f$
170 {
171 if (!begin.IsNormal()) {
172 throw std::invalid_argument("begin must be a normal bin");
173 }
174 if (begin.GetIndex() >= fBinEdges.size() - 1) {
175 throw std::invalid_argument("begin must be inside the axis");
176 }
177 if (!end.IsNormal()) {
178 throw std::invalid_argument("end must be a normal bin");
179 }
180 if (end.GetIndex() > fBinEdges.size() - 1) {
181 throw std::invalid_argument("end must be inside or past the axis");
182 }
183 if (!(end >= begin)) {
184 throw std::invalid_argument("end must be >= begin");
185 }
186 return Internal::CreateBinIndexRange(begin, end, 0);
187 }
188
189 /// Get the full range of all bins.
190 ///
191 /// This includes underflow and overflow bins, if enabled.
192 ///
193 /// \return the bin index range of all bins
199
200 /// Slice this axis according to the specification.
201 ///
202 /// Throws an exception if the axis cannot be sliced:
203 /// * A sum operation makes the dimension disappear.
204 /// * The rebin operation must divide the number of normal bins.
205 ///
206 /// \param[in] sliceSpec the slice specification
207 /// \return the sliced axis, with enabled underflow and overflow bins
209 {
210 if (sliceSpec.GetOperationSum() != nullptr) {
211 throw std::runtime_error("sum operation makes dimension disappear");
212 }
213
214 // Figure out the properties of the sliced axis.
215 std::size_t nNormalBins = fBinEdges.size() - 1;
216 std::size_t begin = 0;
217 std::size_t end = nNormalBins;
218
219 const auto &range = sliceSpec.GetRange();
220 if (!range.IsInvalid()) {
221 if (range.GetBegin().IsNormal()) {
222 begin = range.GetBegin().GetIndex();
223 }
224 if (range.GetEnd().IsNormal()) {
225 end = range.GetEnd().GetIndex();
226 }
227 nNormalBins = end - begin;
228 }
229
230 std::uint64_t nGroup = 1;
231 if (auto *opRebin = sliceSpec.GetOperationRebin()) {
232 nGroup = opRebin->GetNGroup();
233 if (nNormalBins % nGroup != 0) {
234 throw std::runtime_error("rebin operation does not divide number of normal bins");
235 }
236 }
237
238 // Prepare the bin edges.
239 std::vector<double> binEdges;
240 for (std::size_t bin = begin; bin < end; bin += nGroup) {
241 binEdges.push_back(fBinEdges[bin]);
242 }
243 binEdges.push_back(fBinEdges[end]);
244
245 // The sliced axis always has flow bins enabled to preserve all entries. This is the least confusing for users,
246 // even if not always strictly necessary.
247 bool enableFlowBins = true;
248 return RVariableBinAxis(std::move(binEdges), enableFlowBins);
249 }
250
251 /// %ROOT Streamer function to throw when trying to store an object of this class.
252 void Streamer(TBuffer &) { throw std::runtime_error("unable to store RVariableBinAxis"); }
253};
254
255} // namespace Experimental
256} // namespace ROOT
257
258#endif
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
A bin index with special values for underflow and overflow bins.
Definition RBinIndex.hxx:23
bool IsNormal() const
A bin index is normal iff it is not one of the special values.
Definition RBinIndex.hxx:80
std::uint64_t GetIndex() const
Return the index for a normal bin.
Definition RBinIndex.hxx:71
static RBinIndex Underflow()
Specification of a slice operation along one dimension.
An axis with variable bins defined by their edges.
RLinearizedIndex GetLinearizedIndex(RBinIndex index) const
Compute the linarized index for a single argument.
RBinIndexRange GetNormalRange(RBinIndex begin, RBinIndex end) const
Get a range of normal bins.
RVariableBinAxis Slice(const RSliceSpec &sliceSpec) const
Slice this axis according to the specification.
RBinIndexRange GetFullRange() const
Get the full range of all bins.
bool fEnableFlowBins
Whether underflow and overflow bins are enabled.
void Streamer(TBuffer &)
ROOT Streamer function to throw when trying to store an object of this class.
std::vector< double > fBinEdges
The (ordered) edges of the normal bins.
RVariableBinAxis(std::vector< double > binEdges, bool enableFlowBins=true)
Construct an axis object with variable bins.
friend bool operator==(const RVariableBinAxis &lhs, const RVariableBinAxis &rhs)
const std::vector< double > & GetBinEdges() const
RBinIndexRange GetNormalRange() const
Get the range of all normal bins.
Buffer base class used for serializing objects.
Definition TBuffer.h:43
Double_t x[n]
Definition legend1.C:17
static RBinIndexRange CreateBinIndexRange(RBinIndex begin, RBinIndex end, std::uint64_t nNormalBins)
Internal function to create RBinIndexRange.
A linearized index that can be invalid.